exchange_api_post-management-wire.c (10482B)
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 * JSON encoding of debit restrictions (we hold a reference). 83 */ 84 json_t *debit_restrictions; 85 86 /** 87 * JSON encoding of credit restrictions (we hold a reference). 88 */ 89 json_t *credit_restrictions; 90 91 /** 92 * When was this decided? 93 */ 94 struct GNUNET_TIME_Timestamp validity_start; 95 96 /** 97 * Signature affirming the wire addition. 98 */ 99 struct TALER_MasterSignatureP master_sig1; 100 101 /** 102 * Signature affirming the validity of the account for clients. 103 */ 104 struct TALER_MasterSignatureP master_sig2; 105 106 /** 107 * Label to use when showing the account to users (or NULL). 108 */ 109 char *bank_label; 110 111 /** 112 * Priority for ordering the bank accounts. 113 */ 114 int64_t priority; 115 116 }; 117 118 119 /** 120 * Function called when we're done processing the 121 * HTTP POST /management/wire request. 122 * 123 * @param cls the `struct TALER_EXCHANGE_PostManagementWireHandle` 124 * @param response_code HTTP response code, 0 on error 125 * @param response response body, NULL if not in JSON 126 */ 127 static void 128 handle_wire_finished (void *cls, 129 long response_code, 130 const void *response) 131 { 132 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh = cls; 133 const json_t *json = response; 134 struct TALER_EXCHANGE_PostManagementWireResponse res = { 135 .hr.http_status = (unsigned int) response_code, 136 .hr.reply = json 137 }; 138 139 pmwh->job = NULL; 140 switch (response_code) 141 { 142 case 0: 143 /* no reply */ 144 res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 145 res.hr.hint = "server offline?"; 146 break; 147 case MHD_HTTP_NO_CONTENT: 148 break; 149 case MHD_HTTP_FORBIDDEN: 150 res.hr.ec = TALER_JSON_get_error_code (json); 151 res.hr.hint = TALER_JSON_get_error_hint (json); 152 break; 153 case MHD_HTTP_NOT_FOUND: 154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 155 "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", 156 pmwh->url); 157 if (NULL != json) 158 { 159 res.hr.ec = TALER_JSON_get_error_code (json); 160 res.hr.hint = TALER_JSON_get_error_hint (json); 161 } 162 else 163 { 164 res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 165 res.hr.hint = TALER_ErrorCode_get_hint (res.hr.ec); 166 } 167 break; 168 case MHD_HTTP_CONFLICT: 169 res.hr.ec = TALER_JSON_get_error_code (json); 170 res.hr.hint = TALER_JSON_get_error_hint (json); 171 break; 172 default: 173 /* unexpected response code */ 174 GNUNET_break_op (0); 175 res.hr.ec = TALER_JSON_get_error_code (json); 176 res.hr.hint = TALER_JSON_get_error_hint (json); 177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 178 "Unexpected response code %u/%d for exchange management enable wire\n", 179 (unsigned int) response_code, 180 (int) res.hr.ec); 181 break; 182 } 183 if (NULL != pmwh->cb) 184 { 185 pmwh->cb (pmwh->cb_cls, 186 &res); 187 pmwh->cb = NULL; 188 } 189 TALER_EXCHANGE_post_management_wire_cancel (pmwh); 190 } 191 192 193 struct TALER_EXCHANGE_PostManagementWireHandle * 194 TALER_EXCHANGE_post_management_wire_create ( 195 struct GNUNET_CURL_Context *ctx, 196 const char *url, 197 const struct TALER_FullPayto payto_uri, 198 const char *conversion_url, 199 const json_t *debit_restrictions, 200 const json_t *credit_restrictions, 201 struct GNUNET_TIME_Timestamp validity_start, 202 const struct TALER_MasterSignatureP *master_sig1, 203 const struct TALER_MasterSignatureP *master_sig2) 204 { 205 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh; 206 char *msg; 207 208 msg = TALER_payto_validate (payto_uri); 209 if (NULL != msg) 210 { 211 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 212 "payto URI is malformed: %s\n", 213 msg); 214 GNUNET_free (msg); 215 return NULL; 216 } 217 pmwh = GNUNET_new (struct TALER_EXCHANGE_PostManagementWireHandle); 218 pmwh->ctx = ctx; 219 pmwh->base_url = GNUNET_strdup (url); 220 pmwh->payto_uri_str = GNUNET_strdup (payto_uri.full_payto); 221 pmwh->conversion_url = (NULL != conversion_url) 222 ? GNUNET_strdup (conversion_url) 223 : NULL; 224 pmwh->debit_restrictions = json_incref ((json_t *) debit_restrictions); 225 pmwh->credit_restrictions = json_incref ((json_t *) credit_restrictions); 226 pmwh->validity_start = validity_start; 227 pmwh->master_sig1 = *master_sig1; 228 pmwh->master_sig2 = *master_sig2; 229 return pmwh; 230 } 231 232 233 enum GNUNET_GenericReturnValue 234 TALER_EXCHANGE_post_management_wire_set_options_ ( 235 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 236 unsigned int num_options, 237 const struct TALER_EXCHANGE_PostManagementWireOptionValue options[]) 238 { 239 for (unsigned int i = 0; i < num_options; i++) 240 { 241 const struct TALER_EXCHANGE_PostManagementWireOptionValue *opt 242 = &options[i]; 243 244 switch (opt->option) 245 { 246 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_END: 247 return GNUNET_OK; 248 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_BANK_LABEL: 249 GNUNET_free (pmwh->bank_label); 250 pmwh->bank_label = (NULL != opt->details.bank_label) 251 ? GNUNET_strdup (opt->details.bank_label) 252 : NULL; 253 break; 254 case TALER_EXCHANGE_POST_MANAGEMENT_WIRE_OPTION_PRIORITY: 255 pmwh->priority = opt->details.priority; 256 break; 257 default: 258 GNUNET_break (0); 259 return GNUNET_SYSERR; 260 } 261 } 262 return GNUNET_OK; 263 } 264 265 266 enum TALER_ErrorCode 267 TALER_EXCHANGE_post_management_wire_start ( 268 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh, 269 TALER_EXCHANGE_PostManagementWireCallback cb, 270 TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE *cb_cls) 271 { 272 CURL *eh; 273 json_t *body; 274 struct TALER_FullPayto payto_uri = { 275 .full_payto = pmwh->payto_uri_str 276 }; 277 278 pmwh->cb = cb; 279 pmwh->cb_cls = cb_cls; 280 pmwh->url = TALER_url_join (pmwh->base_url, 281 "management/wire", 282 NULL); 283 if (NULL == pmwh->url) 284 { 285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 286 "Could not construct request URL.\n"); 287 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 288 } 289 body = GNUNET_JSON_PACK ( 290 TALER_JSON_pack_full_payto ("payto_uri", 291 payto_uri), 292 GNUNET_JSON_pack_array_incref ("debit_restrictions", 293 pmwh->debit_restrictions), 294 GNUNET_JSON_pack_array_incref ("credit_restrictions", 295 pmwh->credit_restrictions), 296 GNUNET_JSON_pack_allow_null ( 297 GNUNET_JSON_pack_string ("conversion_url", 298 pmwh->conversion_url)), 299 GNUNET_JSON_pack_allow_null ( 300 GNUNET_JSON_pack_string ("bank_label", 301 pmwh->bank_label)), 302 GNUNET_JSON_pack_int64 ("priority", 303 pmwh->priority), 304 GNUNET_JSON_pack_data_auto ("master_sig_add", 305 &pmwh->master_sig1), 306 GNUNET_JSON_pack_data_auto ("master_sig_wire", 307 &pmwh->master_sig2), 308 GNUNET_JSON_pack_timestamp ("validity_start", 309 pmwh->validity_start)); 310 eh = TALER_EXCHANGE_curl_easy_get_ (pmwh->url); 311 if ( (NULL == eh) || 312 (GNUNET_OK != 313 TALER_curl_easy_post (&pmwh->post_ctx, 314 eh, 315 body)) ) 316 { 317 GNUNET_break (0); 318 if (NULL != eh) 319 curl_easy_cleanup (eh); 320 json_decref (body); 321 GNUNET_free (pmwh->url); 322 pmwh->url = NULL; 323 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 324 } 325 json_decref (body); 326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 327 "Requesting URL '%s'\n", 328 pmwh->url); 329 pmwh->job = GNUNET_CURL_job_add2 (pmwh->ctx, 330 eh, 331 pmwh->post_ctx.headers, 332 &handle_wire_finished, 333 pmwh); 334 if (NULL == pmwh->job) 335 { 336 TALER_curl_easy_post_finished (&pmwh->post_ctx); 337 GNUNET_free (pmwh->url); 338 pmwh->url = NULL; 339 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 340 } 341 return TALER_EC_NONE; 342 } 343 344 345 void 346 TALER_EXCHANGE_post_management_wire_cancel ( 347 struct TALER_EXCHANGE_PostManagementWireHandle *pmwh) 348 { 349 if (NULL != pmwh->job) 350 { 351 GNUNET_CURL_job_cancel (pmwh->job); 352 pmwh->job = NULL; 353 } 354 TALER_curl_easy_post_finished (&pmwh->post_ctx); 355 json_decref (pmwh->debit_restrictions); 356 json_decref (pmwh->credit_restrictions); 357 GNUNET_free (pmwh->payto_uri_str); 358 GNUNET_free (pmwh->conversion_url); 359 GNUNET_free (pmwh->bank_label); 360 GNUNET_free (pmwh->url); 361 GNUNET_free (pmwh->base_url); 362 GNUNET_free (pmwh); 363 } 364 365 366 /* end of exchange_api_post-management-wire.c */