bank_api_transfer.c (9689B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015--2023 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 bank-lib/bank_api_transfer.c 19 * @brief Implementation of the /transfer/ requests of the bank's HTTP API 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include "bank_api_common.h" 24 #include <microhttpd.h> /* just for HTTP status codes */ 25 #include "taler/taler_signatures.h" 26 #include "taler/taler_curl_lib.h" 27 #include "taler/taler_bank_service.h" 28 29 30 GNUNET_NETWORK_STRUCT_BEGIN 31 32 /** 33 * Data structure serialized in the prepare stage. 34 */ 35 struct WirePackP 36 { 37 /** 38 * Random unique identifier for the request. 39 */ 40 struct GNUNET_HashCode request_uid; 41 42 /** 43 * Amount to be transferred. 44 */ 45 struct TALER_AmountNBO amount; 46 47 /** 48 * Wire transfer identifier to use. 49 */ 50 struct TALER_WireTransferIdentifierRawP wtid; 51 52 /** 53 * Length of the payto:// URL of the target account, 54 * including 0-terminator, in network byte order. 55 */ 56 uint32_t account_len GNUNET_PACKED; 57 58 /** 59 * Length of the exchange's base URL, 60 * including 0-terminator, in network byte order. 61 */ 62 uint32_t exchange_url_len GNUNET_PACKED; 63 64 }; 65 66 GNUNET_NETWORK_STRUCT_END 67 68 69 void 70 TALER_BANK_prepare_transfer ( 71 const struct TALER_FullPayto destination_account_payto_uri, 72 const struct TALER_Amount *amount, 73 const char *exchange_base_url, 74 const struct TALER_WireTransferIdentifierRawP *wtid, 75 void **buf, 76 size_t *buf_size) 77 { 78 const char *payto = destination_account_payto_uri.full_payto; 79 struct WirePackP *wp; 80 size_t d_len = strlen (payto) + 1; 81 size_t u_len = strlen (exchange_base_url) + 1; 82 char *end; 83 84 if ( (d_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) || 85 (u_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) || 86 (d_len + u_len + sizeof (*wp) >= GNUNET_MAX_MALLOC_CHECKED) ) 87 { 88 GNUNET_break (0); /* that's some long URL... */ 89 *buf = NULL; 90 *buf_size = 0; 91 return; 92 } 93 *buf_size = sizeof (*wp) + d_len + u_len; 94 wp = GNUNET_malloc (*buf_size); 95 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE, 96 &wp->request_uid); 97 TALER_amount_hton (&wp->amount, 98 amount); 99 wp->wtid = *wtid; 100 wp->account_len = htonl ((uint32_t) d_len); 101 wp->exchange_url_len = htonl ((uint32_t) u_len); 102 end = (char *) &wp[1]; 103 GNUNET_memcpy (end, 104 payto, 105 d_len); 106 GNUNET_memcpy (end + d_len, 107 exchange_base_url, 108 u_len); 109 *buf = (char *) wp; 110 } 111 112 113 /** 114 * @brief Handle for an active wire transfer. 115 */ 116 struct TALER_BANK_TransferHandle 117 { 118 119 /** 120 * The url for this request. 121 */ 122 char *request_url; 123 124 /** 125 * POST context. 126 */ 127 struct TALER_CURL_PostContext post_ctx; 128 129 /** 130 * Handle for the request. 131 */ 132 struct GNUNET_CURL_Job *job; 133 134 /** 135 * Function to call with the result. 136 */ 137 TALER_BANK_TransferCallback cb; 138 139 /** 140 * Closure for @a cb. 141 */ 142 void *cb_cls; 143 144 }; 145 146 147 /** 148 * Function called when we're done processing the 149 * HTTP /transfer request. 150 * 151 * @param cls the `struct TALER_BANK_TransferHandle` 152 * @param response_code HTTP response code, 0 on error 153 * @param response parsed JSON result, NULL on error 154 */ 155 static void 156 handle_transfer_finished (void *cls, 157 long response_code, 158 const void *response) 159 { 160 struct TALER_BANK_TransferHandle *th = cls; 161 const json_t *j = response; 162 struct TALER_BANK_TransferResponse tr = { 163 .http_status = response_code, 164 .response = j 165 }; 166 167 th->job = NULL; 168 switch (response_code) 169 { 170 case 0: 171 tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 172 break; 173 case MHD_HTTP_OK: 174 { 175 struct GNUNET_JSON_Specification spec[] = { 176 GNUNET_JSON_spec_uint64 ("row_id", 177 &tr.details.ok.row_id), 178 GNUNET_JSON_spec_timestamp ("timestamp", 179 &tr.details.ok.timestamp), 180 GNUNET_JSON_spec_end () 181 }; 182 183 if (GNUNET_OK != 184 GNUNET_JSON_parse (j, 185 spec, 186 NULL, NULL)) 187 { 188 GNUNET_break_op (0); 189 tr.http_status = 0; 190 tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 191 break; 192 } 193 } 194 break; 195 case MHD_HTTP_BAD_REQUEST: 196 /* This should never happen, either us or the bank is buggy 197 (or API version conflict); just pass JSON reply to the application */ 198 GNUNET_break_op (0); 199 tr.ec = TALER_JSON_get_error_code (j); 200 break; 201 case MHD_HTTP_UNAUTHORIZED: 202 /* Nothing really to verify, bank says our credentials are 203 invalid. We should pass the JSON reply to the application. */ 204 tr.ec = TALER_JSON_get_error_code (j); 205 break; 206 case MHD_HTTP_NOT_FOUND: 207 /* Nothing really to verify, endpoint wrong -- could be user unknown */ 208 tr.ec = TALER_JSON_get_error_code (j); 209 break; 210 case MHD_HTTP_CONFLICT: 211 /* Nothing really to verify. Server says we used the same transfer request 212 UID before, but with different details. Should not happen if the user 213 properly used #TALER_BANK_prepare_transfer() and our PRNG is not 214 broken... */ 215 tr.ec = TALER_JSON_get_error_code (j); 216 break; 217 case MHD_HTTP_INTERNAL_SERVER_ERROR: 218 /* Server had an internal issue; we should retry, but this API 219 leaves this to the application */ 220 tr.ec = TALER_JSON_get_error_code (j); 221 break; 222 default: 223 /* unexpected response code */ 224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 225 "Unexpected response code %u\n", 226 (unsigned int) response_code); 227 GNUNET_break (0); 228 tr.ec = TALER_JSON_get_error_code (j); 229 break; 230 } 231 th->cb (th->cb_cls, 232 &tr); 233 TALER_BANK_transfer_cancel (th); 234 } 235 236 237 struct TALER_BANK_TransferHandle * 238 TALER_BANK_transfer ( 239 struct GNUNET_CURL_Context *ctx, 240 const struct TALER_BANK_AuthenticationData *auth, 241 const void *buf, 242 size_t buf_size, 243 TALER_BANK_TransferCallback cc, 244 void *cc_cls) 245 { 246 struct TALER_BANK_TransferHandle *th; 247 json_t *transfer_obj; 248 CURL *eh; 249 const struct WirePackP *wp = buf; 250 uint32_t d_len; 251 uint32_t u_len; 252 const char *destination_account_uri; 253 const char *exchange_base_url; 254 struct TALER_Amount amount; 255 256 if (sizeof (*wp) > buf_size) 257 { 258 GNUNET_break (0); 259 return NULL; 260 } 261 d_len = ntohl (wp->account_len); 262 u_len = ntohl (wp->exchange_url_len); 263 if ( (sizeof (*wp) + d_len + u_len != buf_size) || 264 (d_len > buf_size) || 265 (u_len > buf_size) || 266 (d_len + u_len > buf_size) ) 267 { 268 GNUNET_break (0); 269 return NULL; 270 } 271 destination_account_uri = (const char *) &wp[1]; 272 exchange_base_url = destination_account_uri + d_len; 273 if ( ('\0' != destination_account_uri[d_len - 1]) || 274 ('\0' != exchange_base_url[u_len - 1]) ) 275 { 276 GNUNET_break (0); 277 return NULL; 278 } 279 if (NULL == auth->wire_gateway_url) 280 { 281 GNUNET_break (0); 282 return NULL; 283 } 284 TALER_amount_ntoh (&amount, 285 &wp->amount); 286 th = GNUNET_new (struct TALER_BANK_TransferHandle); 287 th->cb = cc; 288 th->cb_cls = cc_cls; 289 th->request_url = TALER_url_join (auth->wire_gateway_url, 290 "transfer", 291 NULL); 292 if (NULL == th->request_url) 293 { 294 GNUNET_free (th); 295 GNUNET_break (0); 296 return NULL; 297 } 298 transfer_obj = GNUNET_JSON_PACK ( 299 GNUNET_JSON_pack_data_auto ("request_uid", 300 &wp->request_uid), 301 TALER_JSON_pack_amount ("amount", 302 &amount), 303 GNUNET_JSON_pack_string ("exchange_base_url", 304 exchange_base_url), 305 GNUNET_JSON_pack_data_auto ("wtid", 306 &wp->wtid), 307 GNUNET_JSON_pack_string ("credit_account", 308 destination_account_uri)); 309 if (NULL == transfer_obj) 310 { 311 GNUNET_break (0); 312 return NULL; 313 } 314 eh = curl_easy_init (); 315 if ( (NULL == eh) || 316 (GNUNET_OK != 317 TALER_BANK_setup_auth_ (eh, 318 auth)) || 319 (CURLE_OK != 320 curl_easy_setopt (eh, 321 CURLOPT_URL, 322 th->request_url)) || 323 (GNUNET_OK != 324 TALER_curl_easy_post (&th->post_ctx, 325 eh, 326 transfer_obj)) ) 327 { 328 GNUNET_break (0); 329 TALER_BANK_transfer_cancel (th); 330 if (NULL != eh) 331 curl_easy_cleanup (eh); 332 json_decref (transfer_obj); 333 return NULL; 334 } 335 json_decref (transfer_obj); 336 th->job = GNUNET_CURL_job_add2 (ctx, 337 eh, 338 th->post_ctx.headers, 339 &handle_transfer_finished, 340 th); 341 return th; 342 } 343 344 345 void 346 TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th) 347 { 348 if (NULL != th->job) 349 { 350 GNUNET_CURL_job_cancel (th->job); 351 th->job = NULL; 352 } 353 TALER_curl_easy_post_finished (&th->post_ctx); 354 GNUNET_free (th->request_url); 355 GNUNET_free (th); 356 } 357 358 359 /* end of bank_api_transfer.c */