exchange_api_transfers_get.c (12402B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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 lib/exchange_api_transfers_get.c 19 * @brief Implementation of the GET /transfers/ request 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include <jansson.h> 24 #include <microhttpd.h> /* just for HTTP status codes */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler/taler_exchange_service.h" 28 #include "taler/taler_json_lib.h" 29 #include "exchange_api_handle.h" 30 #include "taler/taler_signatures.h" 31 #include "exchange_api_curl_defaults.h" 32 33 34 /** 35 * @brief A /transfers/ GET Handle 36 */ 37 struct TALER_EXCHANGE_TransfersGetHandle 38 { 39 40 /** 41 * The keys of the exchange this request handle will use 42 */ 43 struct TALER_EXCHANGE_Keys *keys; 44 45 /** 46 * The url for this request. 47 */ 48 char *url; 49 50 /** 51 * Handle for the request. 52 */ 53 struct GNUNET_CURL_Job *job; 54 55 /** 56 * Function to call with the result. 57 */ 58 TALER_EXCHANGE_TransfersGetCallback cb; 59 60 /** 61 * Closure for @a cb. 62 */ 63 void *cb_cls; 64 65 }; 66 67 68 /** 69 * We got a #MHD_HTTP_OK response for the /transfers/ request. 70 * Check that the response is well-formed and if it is, call the 71 * callback. If not, return an error code. 72 * 73 * This code is very similar to 74 * merchant_api_track_transfer.c::check_transfers_get_response_ok. 75 * Any changes should likely be reflected there as well. 76 * 77 * @param wdh handle to the operation 78 * @param json response we got 79 * @return #GNUNET_OK if we are done and all is well, 80 * #GNUNET_SYSERR if the response was bogus 81 */ 82 static enum GNUNET_GenericReturnValue 83 check_transfers_get_response_ok ( 84 struct TALER_EXCHANGE_TransfersGetHandle *wdh, 85 const json_t *json) 86 { 87 const json_t *details_j; 88 struct TALER_Amount total_expected; 89 struct TALER_MerchantPublicKeyP merchant_pub; 90 struct TALER_EXCHANGE_TransfersGetResponse tgr = { 91 .hr.reply = json, 92 .hr.http_status = MHD_HTTP_OK 93 }; 94 struct TALER_EXCHANGE_TransferData *td 95 = &tgr.details.ok.td; 96 struct GNUNET_JSON_Specification spec[] = { 97 TALER_JSON_spec_amount_any ("total", 98 &td->total_amount), 99 TALER_JSON_spec_amount_any ("wire_fee", 100 &td->wire_fee), 101 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 102 &merchant_pub), 103 GNUNET_JSON_spec_fixed_auto ("h_payto", 104 &td->h_payto), 105 GNUNET_JSON_spec_timestamp ("execution_time", 106 &td->execution_time), 107 GNUNET_JSON_spec_array_const ("deposits", 108 &details_j), 109 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 110 &td->exchange_sig), 111 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 112 &td->exchange_pub), 113 GNUNET_JSON_spec_end () 114 }; 115 116 if (GNUNET_OK != 117 GNUNET_JSON_parse (json, 118 spec, 119 NULL, NULL)) 120 { 121 GNUNET_break_op (0); 122 return GNUNET_SYSERR; 123 } 124 if (GNUNET_OK != 125 TALER_amount_set_zero (td->total_amount.currency, 126 &total_expected)) 127 { 128 GNUNET_break_op (0); 129 return GNUNET_SYSERR; 130 } 131 if (GNUNET_OK != 132 TALER_EXCHANGE_test_signing_key ( 133 wdh->keys, 134 &td->exchange_pub)) 135 { 136 GNUNET_break_op (0); 137 return GNUNET_SYSERR; 138 } 139 td->details_length = json_array_size (details_j); 140 { 141 struct GNUNET_HashContext *hash_context; 142 struct TALER_TrackTransferDetails *details; 143 144 details = GNUNET_new_array (td->details_length, 145 struct TALER_TrackTransferDetails); 146 td->details = details; 147 hash_context = GNUNET_CRYPTO_hash_context_start (); 148 for (unsigned int i = 0; i<td->details_length; i++) 149 { 150 struct TALER_TrackTransferDetails *detail = &details[i]; 151 struct json_t *detail_j = json_array_get (details_j, i); 152 struct GNUNET_JSON_Specification spec_detail[] = { 153 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 154 &detail->h_contract_terms), 155 GNUNET_JSON_spec_fixed_auto ("coin_pub", &detail->coin_pub), 156 TALER_JSON_spec_amount ("deposit_value", 157 total_expected.currency, 158 &detail->coin_value), 159 TALER_JSON_spec_amount ("deposit_fee", 160 total_expected.currency, 161 &detail->coin_fee), 162 GNUNET_JSON_spec_mark_optional ( 163 TALER_JSON_spec_amount ("refund_total", 164 total_expected.currency, 165 &detail->refund_total), 166 NULL), 167 GNUNET_JSON_spec_end () 168 }; 169 170 GNUNET_assert (GNUNET_OK == 171 TALER_amount_set_zero (td->total_amount.currency, 172 &detail->refund_total)); 173 if ( (GNUNET_OK != 174 GNUNET_JSON_parse (detail_j, 175 spec_detail, 176 NULL, NULL)) || 177 (0 > 178 TALER_amount_add (&total_expected, 179 &total_expected, 180 &detail->coin_value)) || 181 (0 > 182 TALER_amount_subtract (&total_expected, 183 &total_expected, 184 &detail->coin_fee)) ) 185 { 186 GNUNET_break_op (0); 187 GNUNET_CRYPTO_hash_context_abort (hash_context); 188 GNUNET_free (details); 189 return GNUNET_SYSERR; 190 } 191 /* build up big hash for signature checking later */ 192 TALER_exchange_online_wire_deposit_append ( 193 hash_context, 194 &detail->h_contract_terms, 195 td->execution_time, 196 &detail->coin_pub, 197 &detail->coin_value, 198 &detail->coin_fee); 199 } 200 /* Check signature */ 201 GNUNET_CRYPTO_hash_context_finish (hash_context, 202 &td->h_details); 203 if (GNUNET_OK != 204 TALER_exchange_online_wire_deposit_verify ( 205 &td->total_amount, 206 &td->wire_fee, 207 &merchant_pub, 208 &td->h_payto, 209 &td->h_details, 210 &td->exchange_pub, 211 &td->exchange_sig)) 212 { 213 GNUNET_break_op (0); 214 GNUNET_free (details); 215 return GNUNET_SYSERR; 216 } 217 if (0 > 218 TALER_amount_subtract (&total_expected, 219 &total_expected, 220 &td->wire_fee)) 221 { 222 GNUNET_break_op (0); 223 GNUNET_free (details); 224 return GNUNET_SYSERR; 225 } 226 if (0 != 227 TALER_amount_cmp (&total_expected, 228 &td->total_amount)) 229 { 230 GNUNET_break_op (0); 231 GNUNET_free (details); 232 return GNUNET_SYSERR; 233 } 234 wdh->cb (wdh->cb_cls, 235 &tgr); 236 GNUNET_free (details); 237 } 238 return GNUNET_OK; 239 } 240 241 242 /** 243 * Function called when we're done processing the 244 * HTTP /transfers/ request. 245 * 246 * @param cls the `struct TALER_EXCHANGE_TransfersGetHandle` 247 * @param response_code HTTP response code, 0 on error 248 * @param response parsed JSON result, NULL on error 249 */ 250 static void 251 handle_transfers_get_finished (void *cls, 252 long response_code, 253 const void *response) 254 { 255 struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls; 256 const json_t *j = response; 257 struct TALER_EXCHANGE_TransfersGetResponse tgr = { 258 .hr.reply = j, 259 .hr.http_status = (unsigned int) response_code 260 }; 261 262 wdh->job = NULL; 263 switch (response_code) 264 { 265 case 0: 266 tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 267 break; 268 case MHD_HTTP_OK: 269 if (GNUNET_OK == 270 check_transfers_get_response_ok (wdh, 271 j)) 272 { 273 TALER_EXCHANGE_transfers_get_cancel (wdh); 274 return; 275 } 276 GNUNET_break_op (0); 277 tgr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 278 tgr.hr.http_status = 0; 279 break; 280 case MHD_HTTP_BAD_REQUEST: 281 /* This should never happen, either us or the exchange is buggy 282 (or API version conflict); just pass JSON reply to the application */ 283 tgr.hr.ec = TALER_JSON_get_error_code (j); 284 tgr.hr.hint = TALER_JSON_get_error_hint (j); 285 break; 286 case MHD_HTTP_FORBIDDEN: 287 /* Nothing really to verify, exchange says one of the signatures is 288 invalid; as we checked them, this should never happen, we 289 should pass the JSON reply to the application */ 290 tgr.hr.ec = TALER_JSON_get_error_code (j); 291 tgr.hr.hint = TALER_JSON_get_error_hint (j); 292 break; 293 case MHD_HTTP_NOT_FOUND: 294 /* Exchange does not know about transaction; 295 we should pass the reply to the application */ 296 tgr.hr.ec = TALER_JSON_get_error_code (j); 297 tgr.hr.hint = TALER_JSON_get_error_hint (j); 298 break; 299 case MHD_HTTP_INTERNAL_SERVER_ERROR: 300 /* Server had an internal issue; we should retry, but this API 301 leaves this to the application */ 302 tgr.hr.ec = TALER_JSON_get_error_code (j); 303 tgr.hr.hint = TALER_JSON_get_error_hint (j); 304 break; 305 default: 306 /* unexpected response code */ 307 GNUNET_break_op (0); 308 tgr.hr.ec = TALER_JSON_get_error_code (j); 309 tgr.hr.hint = TALER_JSON_get_error_hint (j); 310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 311 "Unexpected response code %u/%d for transfers get\n", 312 (unsigned int) response_code, 313 (int) tgr.hr.ec); 314 break; 315 } 316 wdh->cb (wdh->cb_cls, 317 &tgr); 318 TALER_EXCHANGE_transfers_get_cancel (wdh); 319 } 320 321 322 struct TALER_EXCHANGE_TransfersGetHandle * 323 TALER_EXCHANGE_transfers_get ( 324 struct GNUNET_CURL_Context *ctx, 325 const char *url, 326 struct TALER_EXCHANGE_Keys *keys, 327 const struct TALER_WireTransferIdentifierRawP *wtid, 328 TALER_EXCHANGE_TransfersGetCallback cb, 329 void *cb_cls) 330 { 331 struct TALER_EXCHANGE_TransfersGetHandle *wdh; 332 CURL *eh; 333 char arg_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2 + 32]; 334 335 wdh = GNUNET_new (struct TALER_EXCHANGE_TransfersGetHandle); 336 wdh->cb = cb; 337 wdh->cb_cls = cb_cls; 338 339 { 340 char wtid_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2]; 341 char *end; 342 343 end = GNUNET_STRINGS_data_to_string (wtid, 344 sizeof (struct 345 TALER_WireTransferIdentifierRawP), 346 wtid_str, 347 sizeof (wtid_str)); 348 *end = '\0'; 349 GNUNET_snprintf (arg_str, 350 sizeof (arg_str), 351 "transfers/%s", 352 wtid_str); 353 } 354 wdh->url = TALER_url_join (url, 355 arg_str, 356 NULL); 357 if (NULL == wdh->url) 358 { 359 GNUNET_free (wdh); 360 return NULL; 361 } 362 eh = TALER_EXCHANGE_curl_easy_get_ (wdh->url); 363 if (NULL == eh) 364 { 365 GNUNET_break (0); 366 GNUNET_free (wdh->url); 367 GNUNET_free (wdh); 368 return NULL; 369 } 370 wdh->keys = TALER_EXCHANGE_keys_incref (keys); 371 wdh->job = GNUNET_CURL_job_add_with_ct_json (ctx, 372 eh, 373 &handle_transfers_get_finished, 374 wdh); 375 return wdh; 376 } 377 378 379 /** 380 * Cancel wire deposits request. This function cannot be used on a request 381 * handle if a response is already served for it. 382 * 383 * @param wdh the wire deposits request handle 384 */ 385 void 386 TALER_EXCHANGE_transfers_get_cancel ( 387 struct TALER_EXCHANGE_TransfersGetHandle *wdh) 388 { 389 if (NULL != wdh->job) 390 { 391 GNUNET_CURL_job_cancel (wdh->job); 392 wdh->job = NULL; 393 } 394 GNUNET_free (wdh->url); 395 TALER_EXCHANGE_keys_decref (wdh->keys); 396 GNUNET_free (wdh); 397 } 398 399 400 /* end of exchange_api_transfers_get.c */