exchange_api_post-management-wire.c (11836B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_post-management-wire.c 19 * @brief functions to enable an exchange wire method / bank account 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include "taler/taler_json_lib.h" 24 #include <gnunet/gnunet_curl_lib.h> 25 #include <microhttpd.h> 26 #include "taler/taler_exchange_service.h" 27 #include "taler/taler-exchange/post-management-wire.h" 28 #include "exchange_api_curl_defaults.h" 29 #include "taler/taler_signatures.h" 30 #include "taler/taler_curl_lib.h" 31 32 33 struct TALER_EXCHANGE_PostManagementWireHandle 34 { 35 36 /** 37 * The base URL for this request. 38 */ 39 char *base_url; 40 41 /** 42 * The full URL for this request, set during _start. 43 */ 44 char *url; 45 46 /** 47 * Minor context that holds body and headers. 48 */ 49 struct TALER_CURL_PostContext post_ctx; 50 51 /** 52 * Handle for the request. 53 */ 54 struct GNUNET_CURL_Job *job; 55 56 /** 57 * Function to call with the result. 58 */ 59 TALER_EXCHANGE_PostManagementWireCallback cb; 60 61 /** 62 * Closure for @a cb. 63 */ 64 TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls; 65 66 /** 67 * Reference to the execution context. 68 */ 69 struct GNUNET_CURL_Context *ctx; 70 71 /** 72 * Payto URI of the exchange's bank account. 73 */ 74 char *payto_uri_str; 75 76 /** 77 * URL of the conversion service, or NULL. 78 */ 79 char *conversion_url; 80 81 /** 82 * URL of the open banking gateway, or NULL. 83 */ 84 char *open_banking_gateway; 85 86 /** 87 * URL of the wire transfer gateway, or NULL. 88 */ 89 char *wire_transfer_gateway; 90 91 /** 92 * JSON encoding of debit restrictions (we hold a reference). 93 */ 94 json_t *debit_restrictions; 95 96 /** 97 * JSON encoding of credit restrictions (we hold a reference). 98 */ 99 json_t *credit_restrictions; 100 101 /** 102 * When was this decided? 103 */ 104 struct GNUNET_TIME_Timestamp validity_start; 105 106 /** 107 * Signature affirming the wire addition. 108 */ 109 struct TALER_MasterSignatureP master_sig1; 110 111 /** 112 * Signature affirming the validity of the account for clients. 113 */ 114 struct TALER_MasterSignatureP master_sig2; 115 116 /** 117 * Label to use when showing the account to users (or NULL). 118 */ 119 char *bank_label; 120 121 /** 122 * Priority for ordering the bank accounts. 123 */ 124 int64_t priority; 125 126 }; 127 128 129 /** 130 * Function called when we're done processing the 131 * HTTP POST /management/wire request. 132 * 133 * @param cls the `struct TALER_EXCHANGE_PostManagementWireHandle` 134 * @param response_code HTTP response code, 0 on error 135 * @param response response body, NULL if not in JSON 136 */ 137 static void 138 handle_wire_finished (void *cls, 139 long response_code, 140 const void *response) 141 { 142 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh = cls; 143 const json_t *json = response; 144 struct TALER_EXCHANGE_PostManagementWireResponse res = { 145 .hr.http_status = (unsigned int) response_code, 146 .hr.reply = json 147 }; 148 149 pmwh->job = NULL; 150 switch (response_code) 151 { 152 case 0: 153 /* no reply */ 154 res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 155 res.hr.hint = "server offline?"; 156 break; 157 case MHD_HTTP_NO_CONTENT: 158 break; 159 case MHD_HTTP_BAD_REQUEST: 160 res.hr.ec = TALER_JSON_get_error_code (json); 161 res.hr.hint = TALER_JSON_get_error_hint (json); 162 break; 163 case MHD_HTTP_FORBIDDEN: 164 res.hr.ec = TALER_JSON_get_error_code (json); 165 res.hr.hint = TALER_JSON_get_error_hint (json); 166 break; 167 case MHD_HTTP_CONFLICT: 168 res.hr.ec = TALER_JSON_get_error_code (json); 169 res.hr.hint = TALER_JSON_get_error_hint (json); 170 break; 171 default: 172 /* unexpected response code */ 173 GNUNET_break_op (0); 174 res.hr.ec = TALER_JSON_get_error_code (json); 175 res.hr.hint = TALER_JSON_get_error_hint (json); 176 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 177 "Unexpected response code %u/%d for exchange management enable wire\n", 178 (unsigned int) response_code, 179 (int) res.hr.ec); 180 break; 181 } 182 if (NULL != pmwh->cb) 183 { 184 pmwh->cb (pmwh->cb_cls, 185 &res); 186 pmwh->cb = NULL; 187 } 188 TALER_EXCHANGE_post_management_wire_cancel (pmwh); 189 } 190 191 192 struct TALER_EXCHANGE_PostManagementWireHandle * 193 TALER_EXCHANGE_post_management_wire_create ( 194 struct GNUNET_CURL_Context *ctx, 195 const char *url, 196 const struct TALER_FullPayto payto_uri, 197 struct GNUNET_TIME_Timestamp validity_start, 198 const struct TALER_MasterSignatureP *master_sig1, 199 const struct TALER_MasterSignatureP *master_sig2) 200 { 201 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh; 202 char *msg; 203 204 msg = TALER_payto_validate (payto_uri); 205 if (NULL != msg) 206 { 207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 208 "payto URI is malformed: %s\n", 209 msg); 210 GNUNET_free (msg); 211 return NULL; 212 } 213 pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle); 214 pmwh->ctx = ctx; 215 pmwh->base_url = GNUNET_strdup (url); 216 pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto); 217 pmwh->debit_restrictions = json_array (); 218 GNUNET_assert (NULL != pmwh->debit_restrictions); 219 pmwh->credit_restrictions = json_array (); 220 GNUNET_assert (NULL != pmwh->credit_restrictions); 221 pmwh->validity_start = validity_start; 222 pmwh->master_sig1 = *master_sig1; 223 pmwh->master_sig2 = *master_sig2; 224 return pmwh; 225 } 226 227 228 enum GNUNET_GenericReturnValue 229 TALER_EXCHANGE_post_management_wire_set_options_ ( 230 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 231 unsigned int num_options, 232 const struct TALER_EXCHANGE_PostManagementWireOptionValue options[]) 233 { 234 for (unsigned int i = 0; i < num_options; i++) 235 { 236 const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt 237 = &options[i]; 238 239 switch (opt->option) 240 { 241 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END: 242 return GNUNET_OK; 243 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL: 244 GNUNET_free (pmwh->bank_label); 245 pmwh->bank_label = (NULL != opt->details.bank_label) 246 ? GNUNET_strdup (opt->details.bank_label) 247 : NULL; 248 break; 249 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY: 250 pmwh->priority = opt->details.priority; 251 break; 252 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CONVERSION_URL: 253 GNUNET_free (pmwh->conversion_url); 254 pmwh->conversion_url = (NULL != opt->details.conversion_url) 255 ? GNUNET_strdup (opt->details.conversion_url) 256 : NULL; 257 break; 258 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_OPEN_BANKING_GATEWAY: 259 GNUNET_free (pmwh->open_banking_gateway); 260 pmwh->open_banking_gateway = (NULL != opt->details.open_banking_gateway) 261 ? GNUNET_strdup (opt->details.open_banking_gateway) 262 : NULL; 263 break; 264 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_WIRE_TRANSFER_GATEWAY: 265 GNUNET_free (pmwh->wire_transfer_gateway); 266 pmwh->wire_transfer_gateway = (NULL != opt->details.wire_transfer_gateway) 267 ? GNUNET_strdup (opt->details.wire_transfer_gateway) 268 : NULL; 269 break; 270 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CREDIT_RESTRICTIONS: 271 json_decref (pmwh->credit_restrictions); 272 pmwh->credit_restrictions = (NULL != opt->details.credit_restrictions) 273 ? json_incref ((json_t *) opt->details.credit_restrictions) 274 : json_array (); 275 break; 276 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_DEBIT_RESTRICTIONS: 277 json_decref (pmwh->debit_restrictions); 278 pmwh->debit_restrictions = (NULL != opt->details.debit_restrictions) 279 ? json_incref ((json_t *) opt->details.debit_restrictions) 280 : json_array (); 281 break; 282 default: 283 GNUNET_break (0); 284 return GNUNET_SYSERR; 285 } 286 } 287 return GNUNET_OK; 288 } 289 290 291 enum TALER_ErrorCode 292 TALER_EXCHANGE_post_management_wire_start ( 293 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 294 TALER_EXCHANGE_PostManagementWireCallback cb, 295 TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls) 296 { 297 CURL *eh; 298 json_t *body; 299 struct TALER_FullPayto payto_uri = { 300 .full_payto = pmwh->payto_uri_str 301 }; 302 303 pmwh->cb = cb; 304 pmwh->cb_cls = cb_cls; 305 pmwh->url = TALER_url_join (pmwh->base_url, 306 "management/wire", 307 NULL); 308 if (NULL == pmwh->url) 309 { 310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 311 "Could not construct request URL.\n"); 312 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 313 } 314 body = GNUNET_JSON_PACK ( 315 TALER_JSON_pack_full_payto ("payto_uri", 316 payto_uri), 317 GNUNET_JSON_pack_array_incref ("debit_restrictions", 318 pmwh->debit_restrictions), 319 GNUNET_JSON_pack_array_incref ("credit_restrictions", 320 pmwh->credit_restrictions), 321 GNUNET_JSON_pack_allow_null ( 322 GNUNET_JSON_pack_string ("conversion_url", 323 pmwh->conversion_url)), 324 GNUNET_JSON_pack_allow_null ( 325 GNUNET_JSON_pack_string ("open_banking_gateway", 326 pmwh->open_banking_gateway)), 327 GNUNET_JSON_pack_allow_null ( 328 GNUNET_JSON_pack_string ("wire_transfer_gateway", 329 pmwh->wire_transfer_gateway)), 330 GNUNET_JSON_pack_allow_null ( 331 GNUNET_JSON_pack_string ("bank_label", 332 pmwh->bank_label)), 333 GNUNET_JSON_pack_int64 ("priority", 334 pmwh->priority), 335 GNUNET_JSON_pack_data_auto ("master_sig_add", 336 &pmwh->master_sig1), 337 GNUNET_JSON_pack_data_auto ("master_sig_wire", 338 &pmwh->master_sig2), 339 GNUNET_JSON_pack_timestamp ("validity_start", 340 pmwh->validity_start)); 341 eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url); 342 if ( (NULL == eh) || 343 (GNUNET_OK != 344 TALER_curl_easy_post (&pmwh->post_ctx, 345 eh, 346 body)) ) 347 { 348 GNUNET_break (0); 349 if (NULL != eh) 350 curl_easy_cleanup (eh); 351 json_decref (body); 352 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 353 } 354 json_decref (body); 355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 356 "Requesting URL '%s'\n", 357 pmwh->url); 358 pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx, 359 eh, 360 pmwh->post_ctx.headers, 361 &handle_wire_finished, 362 pmwh); 363 if (NULL == pmwh->job) 364 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 365 return TALER_EC_NONE; 366 } 367 368 369 void 370 TALER_EXCHANGE_post_management_wire_cancel ( 371 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh) 372 { 373 if (NULL != pmwh->job) 374 { 375 GNUNET_CURL_job_cancel (pmwh->job); 376 pmwh->job = NULL; 377 } 378 TALER_curl_easy_post_finished (&pmwh->post_ctx); 379 json_decref (pmwh->debit_restrictions); 380 json_decref (pmwh->credit_restrictions); 381 GNUNET_free (pmwh->payto_uri_str); 382 GNUNET_free (pmwh->conversion_url); 383 GNUNET_free (pmwh->open_banking_gateway); 384 GNUNET_free (pmwh->wire_transfer_gateway); 385 GNUNET_free (pmwh->bank_label); 386 GNUNET_free (pmwh->url); 387 GNUNET_free (pmwh->base_url); 388 GNUNET_free (pmwh); 389 } 390 391 392 /* end of exchange_api_post-management-wire.c */