exchange_api_post-management-wire.c (12180B)
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_FORBIDDEN: 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_NOT_FOUND: 164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 165 "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", 166 pmwh->url); 167 if (NULL != json) 168 { 169 res.hr.ec = TALER_JSON_get_error_code (json); 170 res.hr.hint = TALER_JSON_get_error_hint (json); 171 } 172 else 173 { 174 res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 175 res.hr.hint = TALER_ErrorCode_get_hint (res.hr.ec); 176 } 177 break; 178 case MHD_HTTP_CONFLICT: 179 res.hr.ec = TALER_JSON_get_error_code (json); 180 res.hr.hint = TALER_JSON_get_error_hint (json); 181 break; 182 default: 183 /* unexpected response code */ 184 GNUNET_break_op (0); 185 res.hr.ec = TALER_JSON_get_error_code (json); 186 res.hr.hint = TALER_JSON_get_error_hint (json); 187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 188 "Unexpected response code %u/%d for exchange management enable wire\n", 189 (unsigned int) response_code, 190 (int) res.hr.ec); 191 break; 192 } 193 if (NULL != pmwh->cb) 194 { 195 pmwh->cb (pmwh->cb_cls, 196 &res); 197 pmwh->cb = NULL; 198 } 199 TALER_EXCHANGE_post_management_wire_cancel (pmwh); 200 } 201 202 203 struct TALER_EXCHANGE_PostManagementWireHandle * 204 TALER_EXCHANGE_post_management_wire_create ( 205 struct GNUNET_CURL_Context *ctx, 206 const char *url, 207 const struct TALER_FullPayto payto_uri, 208 struct GNUNET_TIME_Timestamp validity_start, 209 const struct TALER_MasterSignatureP *master_sig1, 210 const struct TALER_MasterSignatureP *master_sig2) 211 { 212 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh; 213 char *msg; 214 215 msg = TALER_payto_validate (payto_uri); 216 if (NULL != msg) 217 { 218 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 219 "payto URI is malformed: %s\n", 220 msg); 221 GNUNET_free (msg); 222 return NULL; 223 } 224 pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle); 225 pmwh->ctx = ctx; 226 pmwh->base_url = GNUNET_strdup (url); 227 pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto); 228 pmwh->debit_restrictions = json_array (); 229 GNUNET_assert (NULL != pmwh->debit_restrictions); 230 pmwh->credit_restrictions = json_array (); 231 GNUNET_assert (NULL != pmwh->credit_restrictions); 232 pmwh->validity_start = validity_start; 233 pmwh->master_sig1 = *master_sig1; 234 pmwh->master_sig2 = *master_sig2; 235 return pmwh; 236 } 237 238 239 enum GNUNET_GenericReturnValue 240 TALER_EXCHANGE_post_management_wire_set_options_ ( 241 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 242 unsigned int num_options, 243 const struct TALER_EXCHANGE_PostManagementWireOptionValue options[]) 244 { 245 for (unsigned int i = 0; i < num_options; i++) 246 { 247 const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt 248 = &options[i]; 249 250 switch (opt->option) 251 { 252 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END: 253 return GNUNET_OK; 254 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL: 255 GNUNET_free (pmwh->bank_label); 256 pmwh->bank_label = (NULL != opt->details.bank_label) 257 ? GNUNET_strdup (opt->details.bank_label) 258 : NULL; 259 break; 260 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY: 261 pmwh->priority = opt->details.priority; 262 break; 263 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CONVERSION_URL: 264 GNUNET_free (pmwh->conversion_url); 265 pmwh->conversion_url = (NULL != opt->details.conversion_url) 266 ? GNUNET_strdup (opt->details.conversion_url) 267 : NULL; 268 break; 269 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_OPEN_BANKING_GATEWAY: 270 GNUNET_free (pmwh->open_banking_gateway); 271 pmwh->open_banking_gateway = (NULL != opt->details.open_banking_gateway) 272 ? GNUNET_strdup (opt->details.open_banking_gateway) 273 : NULL; 274 break; 275 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_WIRE_TRANSFER_GATEWAY: 276 GNUNET_free (pmwh->wire_transfer_gateway); 277 pmwh->wire_transfer_gateway = (NULL != opt->details.wire_transfer_gateway) 278 ? GNUNET_strdup (opt->details.wire_transfer_gateway) 279 : NULL; 280 break; 281 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_CREDIT_RESTRICTIONS: 282 json_decref (pmwh->credit_restrictions); 283 pmwh->credit_restrictions = (NULL != opt->details.credit_restrictions) 284 ? json_incref ((json_t *) opt->details.credit_restrictions) 285 : json_array (); 286 break; 287 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_DEBIT_RESTRICTIONS: 288 json_decref (pmwh->debit_restrictions); 289 pmwh->debit_restrictions = (NULL != opt->details.debit_restrictions) 290 ? json_incref ((json_t *) opt->details.debit_restrictions) 291 : json_array (); 292 break; 293 default: 294 GNUNET_break (0); 295 return GNUNET_SYSERR; 296 } 297 } 298 return GNUNET_OK; 299 } 300 301 302 enum TALER_ErrorCode 303 TALER_EXCHANGE_post_management_wire_start ( 304 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 305 TALER_EXCHANGE_PostManagementWireCallback cb, 306 TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls) 307 { 308 CURL *eh; 309 json_t *body; 310 struct TALER_FullPayto payto_uri = { 311 .full_payto = pmwh->payto_uri_str 312 }; 313 314 pmwh->cb = cb; 315 pmwh->cb_cls = cb_cls; 316 pmwh->url = TALER_url_join (pmwh->base_url, 317 "management/wire", 318 NULL); 319 if (NULL == pmwh->url) 320 { 321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 322 "Could not construct request URL.\n"); 323 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 324 } 325 body = GNUNET_JSON_PACK ( 326 TALER_JSON_pack_full_payto ("payto_uri", 327 payto_uri), 328 GNUNET_JSON_pack_array_incref ("debit_restrictions", 329 pmwh->debit_restrictions), 330 GNUNET_JSON_pack_array_incref ("credit_restrictions", 331 pmwh->credit_restrictions), 332 GNUNET_JSON_pack_allow_null ( 333 GNUNET_JSON_pack_string ("conversion_url", 334 pmwh->conversion_url)), 335 GNUNET_JSON_pack_allow_null ( 336 GNUNET_JSON_pack_string ("open_banking_gateway", 337 pmwh->open_banking_gateway)), 338 GNUNET_JSON_pack_allow_null ( 339 GNUNET_JSON_pack_string ("wire_transfer_gateway", 340 pmwh->wire_transfer_gateway)), 341 GNUNET_JSON_pack_allow_null ( 342 GNUNET_JSON_pack_string ("bank_label", 343 pmwh->bank_label)), 344 GNUNET_JSON_pack_int64 ("priority", 345 pmwh->priority), 346 GNUNET_JSON_pack_data_auto ("master_sig_add", 347 &pmwh->master_sig1), 348 GNUNET_JSON_pack_data_auto ("master_sig_wire", 349 &pmwh->master_sig2), 350 GNUNET_JSON_pack_timestamp ("validity_start", 351 pmwh->validity_start)); 352 eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url); 353 if ( (NULL == eh) || 354 (GNUNET_OK != 355 TALER_curl_easy_post (&pmwh->post_ctx, 356 eh, 357 body)) ) 358 { 359 GNUNET_break (0); 360 if (NULL != eh) 361 curl_easy_cleanup (eh); 362 json_decref (body); 363 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 364 } 365 json_decref (body); 366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 367 "Requesting URL '%s'\n", 368 pmwh->url); 369 pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx, 370 eh, 371 pmwh->post_ctx.headers, 372 &handle_wire_finished, 373 pmwh); 374 if (NULL == pmwh->job) 375 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 376 return TALER_EC_NONE; 377 } 378 379 380 void 381 TALER_EXCHANGE_post_management_wire_cancel ( 382 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh) 383 { 384 if (NULL != pmwh->job) 385 { 386 GNUNET_CURL_job_cancel (pmwh->job); 387 pmwh->job = NULL; 388 } 389 TALER_curl_easy_post_finished (&pmwh->post_ctx); 390 json_decref (pmwh->debit_restrictions); 391 json_decref (pmwh->credit_restrictions); 392 GNUNET_free (pmwh->payto_uri_str); 393 GNUNET_free (pmwh->conversion_url); 394 GNUNET_free (pmwh->open_banking_gateway); 395 GNUNET_free (pmwh->wire_transfer_gateway); 396 GNUNET_free (pmwh->bank_label); 397 GNUNET_free (pmwh->url); 398 GNUNET_free (pmwh->base_url); 399 GNUNET_free (pmwh); 400 } 401 402 403 /* end of exchange_api_post-management-wire.c */