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