bank_api_account_withdrawal.c (17892B)
1 #include <curl/curl.h> 2 #include <curl/easy.h> 3 #include <gnunet/gnunet_common.h> 4 #include <jansson.h> 5 #include <microhttpd.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <taler/platform.h> 9 #include "taler_bank_service_cash2ecash.h" 10 #include <taler/taler_curl_lib.h> 11 #include <gnunet/gnunet_json_lib.h> 12 #include <taler/taler_json_lib.h> 13 #include "bank_api_common.h" 14 15 16 17 18 19 struct TALER_BANK_AccountWithdrawalHandle 20 { 21 /** 22 *The url for this request. 23 */ 24 char *request_url; 25 26 /** 27 POST context. 28 */ 29 struct TALER_CURL_PostContext post_ctx; 30 31 /** 32 * Handle for the request. 33 */ 34 struct GNUNET_CURL_Job *job; 35 36 /** 37 * Function to call with the result. 38 */ 39 TALER_BANK_AccountWithdrawalCallback cb; 40 41 /** 42 * Closure for @a cb. 43 */ 44 void *cb_cls; 45 }; 46 47 48 49 50 51 52 /** 53 * Function called when we're done processing the 54 * HTTP /account/$ACC/withdrawals request. 55 * 56 * @param cls the `struct TALER_BANK_AccountWithdrawalHandle` 57 * @param response_code HTTP response code, 0 on error 58 * @param response parsed JSON result, NULL on error 59 */ 60 static void 61 handle_account_withdrawal_finished (void *cls, 62 long response_code, 63 const void *response) 64 { 65 struct TALER_BANK_AccountWithdrawalHandle *aai = cls; 66 const json_t *j = response; 67 struct TALER_BANK_AccountWithdrawalResponse ir = { 68 .http_status = response_code, 69 .response = response 70 }; 71 72 aai->job = NULL; 73 switch (response_code) 74 { 75 case 0: 76 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 77 break; 78 case MHD_HTTP_OK: 79 { 80 struct GNUNET_JSON_Specification spec[] = { 81 GNUNET_JSON_spec_string ("withdrawal_id", 82 &ir.details.ok.withdrawal_id), 83 GNUNET_JSON_spec_string ("taler_withdraw_uri", 84 &ir.details.ok.taler_withdraw_uri), 85 GNUNET_JSON_spec_end () 86 }; 87 88 if (GNUNET_OK != 89 GNUNET_JSON_parse (j, 90 spec, 91 NULL, NULL)) 92 { 93 GNUNET_break_op (0); 94 ir.http_status = 0; 95 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 96 break; 97 } 98 } 99 break; 100 case MHD_HTTP_BAD_REQUEST: 101 /* This should never happen, either us or the bank is buggy 102 (or API version conflict); just pass JSON reply to the application */ 103 GNUNET_break_op (0); 104 ir.ec = TALER_JSON_get_error_code (j); 105 break; 106 case MHD_HTTP_FORBIDDEN: 107 /* Access denied */ 108 ir.ec = TALER_JSON_get_error_code (j); 109 break; 110 case MHD_HTTP_UNAUTHORIZED: 111 /* Nothing really to verify, bank says invalid or missing credentials; we should 112 pass the JSON reply to the application */ 113 ir.ec = TALER_JSON_get_error_code (j); 114 break; 115 case MHD_HTTP_NOT_FOUND: 116 /* Nothing really to verify, maybe account really does not exist. 117 We should pass the JSON reply to the application */ 118 ir.ec = TALER_JSON_get_error_code (j); 119 break; 120 case MHD_HTTP_INTERNAL_SERVER_ERROR: 121 /* Server had an internal issue; we should retry, but this API 122 leaves this to the application */ 123 ir.ec = TALER_JSON_get_error_code (j); 124 break; 125 case MHD_HTTP_CONFLICT: 126 /* The account does not have sufficient funds */ 127 ir.ec = TALER_JSON_get_error_code (j); 128 break; 129 default: 130 /* unexpected response code */ 131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 132 "Unexpected response code %u\n", 133 (unsigned int) response_code); 134 GNUNET_break (0); 135 ir.ec = TALER_JSON_get_error_code (j); 136 break; 137 } 138 aai->cb (aai->cb_cls, 139 &ir); 140 TALER_BANK_account_withdrawal_cancel (aai); 141 } 142 143 144 145 146 struct TALER_BANK_AccountWithdrawalHandle * 147 TALER_BANK_account_withdrawal ( 148 struct GNUNET_CURL_Context *ctx, 149 const struct TALER_BANK_AuthenticationData *auth, 150 const char *account_name, 151 const struct TALER_Amount *amount, 152 const struct TALER_Amount *suggested_amount, 153 const bool *no_amount_to_wallet, 154 TALER_BANK_AccountWithdrawalCallback res_cb, 155 void *res_cb_cls) 156 { 157 struct TALER_BANK_AccountWithdrawalHandle *awh; 158 json_t *withdrawal_req; 159 CURL *eh; 160 161 withdrawal_req = GNUNET_JSON_PACK( 162 GNUNET_JSON_pack_allow_null(TALER_JSON_pack_amount("amount", amount)), 163 GNUNET_JSON_pack_allow_null( 164 TALER_JSON_pack_amount("suggested_amount", suggested_amount)), 165 GNUNET_JSON_pack_allow_null( 166 GNUNET_JSON_pack_bool("no_amount_to_wallet", *no_amount_to_wallet))); 167 if (NULL == withdrawal_req) 168 { 169 GNUNET_break (0); 170 return NULL; 171 } 172 awh = GNUNET_new (struct TALER_BANK_AccountWithdrawalHandle); 173 awh->cb = res_cb; 174 awh->cb_cls = res_cb_cls; 175 { 176 char *path; 177 178 GNUNET_asprintf (&path, 179 "accounts/%s/withdrawals", 180 account_name); 181 awh->request_url = TALER_url_join (auth->wire_gateway_url, 182 path, 183 NULL); 184 GNUNET_free (path); 185 } 186 if (NULL == awh->request_url) 187 { 188 GNUNET_free (awh); 189 json_decref (withdrawal_req); 190 return NULL; 191 } 192 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 193 "Posting withdrawal request at `%s'\n", 194 awh->request_url); 195 awh->post_ctx.headers 196 = curl_slist_append ( 197 awh->post_ctx.headers, 198 "Content-Type: application/json"); 199 200 eh = curl_easy_init (); 201 if ( (NULL == eh) || 202 (GNUNET_OK != 203 TALER_BANK_setup_auth_ (eh, 204 auth)) || 205 (CURLE_OK != 206 curl_easy_setopt (eh, 207 CURLOPT_URL, 208 awh->request_url)) || 209 (GNUNET_OK != 210 TALER_curl_easy_post (&awh->post_ctx, 211 eh, 212 withdrawal_req)) ) 213 { 214 GNUNET_break (0); 215 TALER_BANK_account_withdrawal_cancel (awh); 216 if (NULL != eh) 217 curl_easy_cleanup (eh); 218 json_decref (withdrawal_req); 219 return NULL; 220 } 221 json_decref (withdrawal_req); 222 awh->job = GNUNET_CURL_job_add2 (ctx, 223 eh, 224 awh->post_ctx.headers, 225 &handle_account_withdrawal_finished, 226 awh); 227 GNUNET_assert (NULL != awh->job); 228 return awh; 229 } 230 231 void 232 TALER_BANK_account_withdrawal_cancel ( 233 struct TALER_BANK_AccountWithdrawalHandle *awh) 234 { 235 if (NULL != awh->job) 236 { 237 GNUNET_CURL_job_cancel (awh->job); 238 awh->job = NULL; 239 } 240 TALER_curl_easy_post_finished (&awh->post_ctx); 241 GNUNET_free (awh->request_url); 242 GNUNET_free (awh); 243 } 244 245 246 247 248 /* ********************* /accounts/$ACC/withdrawals/$WITHDRAWAL_ID/confirm *********************** */ 249 250 struct TALER_BANK_AccountWithdrawalConfirmHandle 251 { 252 /** 253 *The url for this request. 254 */ 255 char *request_url; 256 257 /** 258 POST context. 259 */ 260 struct TALER_CURL_PostContext post_ctx; 261 262 /** 263 * Handle for the request. 264 */ 265 struct GNUNET_CURL_Job *job; 266 267 /** 268 * Function to call with the result. 269 */ 270 TALER_BANK_AccountWithdrawalConfirmCallback cb; 271 272 /** 273 * Closure for @a cb. 274 */ 275 void *cb_cls; 276 }; 277 278 /** 279 * Function called when we're done processing the 280 * HTTP /account/$ACC/withdrawals/$WITHDRAWAL_ID/confirm request. 281 * 282 * @param cls the `struct TALER_BANK_AccountWithdrawalConfirmHandle` 283 * @param response_code HTTP response code, 0 on error 284 * @param response parsed JSON result, NULL on error 285 */ 286 static void 287 handle_account_withdrawal_confirm_finished (void *cls, 288 long response_code, 289 const void *response) 290 { 291 struct TALER_BANK_AccountWithdrawalConfirmHandle *aai = cls; 292 const json_t *j = response; 293 struct TALER_BANK_AccountWithdrawalConfirmResponse ir = { 294 .http_status = response_code, 295 .response = response 296 }; 297 298 aai->job = NULL; 299 switch (response_code) 300 { 301 case 0: 302 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 303 break; 304 case MHD_HTTP_ACCEPTED: 305 fprintf(stderr, "There is no implementation for 2FA yet"); 306 ir.ec =TALER_JSON_get_error_code(j); 307 break; 308 case MHD_HTTP_NO_CONTENT: 309 break; 310 case MHD_HTTP_BAD_REQUEST: 311 /* This should never happen, either us or the bank is buggy 312 (or API version conflict); just pass JSON reply to the application */ 313 GNUNET_break_op (0); 314 ir.ec = TALER_JSON_get_error_code (j); 315 break; 316 case MHD_HTTP_FORBIDDEN: 317 /* Access denied */ 318 ir.ec = TALER_JSON_get_error_code (j); 319 break; 320 case MHD_HTTP_UNAUTHORIZED: 321 /* Nothing really to verify, bank says invalid or missing credentials; we should 322 pass the JSON reply to the application */ 323 ir.ec = TALER_JSON_get_error_code (j); 324 break; 325 case MHD_HTTP_NOT_FOUND: 326 /* Nothing really to verify, maybe account really does not exist. 327 We should pass the JSON reply to the application */ 328 ir.ec = TALER_JSON_get_error_code (j); 329 break; 330 case MHD_HTTP_INTERNAL_SERVER_ERROR: 331 /* Server had an internal issue; we should retry, but this API 332 leaves this to the application */ 333 ir.ec = TALER_JSON_get_error_code (j); 334 break; 335 case MHD_HTTP_CONFLICT: 336 /* The account does not have sufficient funds */ 337 ir.ec = TALER_JSON_get_error_code (j); 338 break; 339 default: 340 /* unexpected response code */ 341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 342 "Unexpected response code %u\n", 343 (unsigned int) response_code); 344 GNUNET_break (0); 345 ir.ec = TALER_JSON_get_error_code (j); 346 break; 347 } 348 aai->cb (aai->cb_cls, 349 &ir); 350 TALER_BANK_account_withdrawal_confirm_cancel (aai); 351 } 352 353 354 355 356 struct TALER_BANK_AccountWithdrawalConfirmHandle * 357 TALER_BANK_account_withdrawal_confirm ( 358 struct GNUNET_CURL_Context *ctx, 359 const struct TALER_BANK_AuthenticationData *auth, 360 const char *account_name, 361 const char *withdrawal_id, 362 const struct TALER_Amount *amount, 363 TALER_BANK_AccountWithdrawalConfirmCallback res_cb, 364 void *res_cb_cls) 365 { 366 struct TALER_BANK_AccountWithdrawalConfirmHandle *awch; 367 json_t *wconfirm_req; 368 CURL *eh; 369 370 wconfirm_req = GNUNET_JSON_PACK( 371 GNUNET_JSON_pack_allow_null( 372 TALER_JSON_pack_amount("amount", 373 amount))); 374 if (NULL == wconfirm_req) 375 { 376 GNUNET_break (0); 377 return NULL; 378 } 379 awch = GNUNET_new (struct TALER_BANK_AccountWithdrawalConfirmHandle); 380 awch->cb = res_cb; 381 awch->cb_cls = res_cb_cls; 382 { 383 char *path; 384 385 GNUNET_asprintf (&path, 386 "accounts/%s/withdrawals/%s/confirm", 387 account_name, withdrawal_id); 388 awch->request_url = TALER_url_join (auth->wire_gateway_url, 389 path, 390 NULL); 391 GNUNET_free (path); 392 } 393 if (NULL == awch->request_url) 394 { 395 GNUNET_free (awch); 396 json_decref (wconfirm_req); 397 return NULL; 398 } 399 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 400 "Posting withdrawal confirmation request at `%s'\n", 401 awch->request_url); 402 awch->post_ctx.headers 403 = curl_slist_append ( 404 awch->post_ctx.headers, 405 "Content-Type: application/json"); 406 407 eh = curl_easy_init (); 408 if ( (NULL == eh) || 409 (GNUNET_OK != 410 TALER_BANK_setup_auth_ (eh, 411 auth)) || 412 (CURLE_OK != 413 curl_easy_setopt (eh, 414 CURLOPT_URL, 415 awch->request_url)) || 416 (GNUNET_OK != 417 TALER_curl_easy_post (&awch->post_ctx, 418 eh, 419 wconfirm_req)) ) 420 { 421 GNUNET_break (0); 422 TALER_BANK_account_withdrawal_confirm_cancel (awch); 423 if (NULL != eh) 424 curl_easy_cleanup (eh); 425 json_decref (wconfirm_req); 426 return NULL; 427 } 428 json_decref (wconfirm_req); 429 awch->job = GNUNET_CURL_job_add2 (ctx, 430 eh, 431 awch->post_ctx.headers, 432 &handle_account_withdrawal_confirm_finished, 433 awch); 434 GNUNET_assert (NULL != awch->job); 435 return awch; 436 } 437 438 void 439 TALER_BANK_account_withdrawal_confirm_cancel ( 440 struct TALER_BANK_AccountWithdrawalConfirmHandle *awch) 441 { 442 if (NULL != awch->job) 443 { 444 GNUNET_CURL_job_cancel (awch->job); 445 awch->job = NULL; 446 } 447 TALER_curl_easy_post_finished (&awch->post_ctx); 448 GNUNET_free (awch->request_url); 449 GNUNET_free (awch); 450 } 451 452 /* ********************* /withdrawals/$WITHDRAWAL_ID *********************** */ 453 454 struct TALER_BANK_WithdrawalIDInfoHandle 455 { 456 /** 457 *The url for this request. 458 */ 459 char *request_url; 460 461 /** 462 POST context. 463 */ 464 struct TALER_CURL_PostContext post_ctx; 465 466 /** 467 * Handle for the request. 468 */ 469 struct GNUNET_CURL_Job *job; 470 471 /** 472 * Function to call with the result. 473 */ 474 TALER_BANK_WithdrawalIDInfoCallback cb; 475 476 /** 477 * Closure for @a cb. 478 */ 479 void *cb_cls; 480 }; 481 482 /** 483 * Function called when we're done processing the 484 * HTTP /withdrawals/$WITHDRAWAL_ID request. 485 * 486 * @param cls the `struct TALER_BANK_WithdrawalIDInfoandle` 487 * @param response_code HTTP response code, 0 on error 488 * @param response parsed JSON result, NULL on error 489 */ 490 static void 491 handle_withdrawalID_info_finished (void *cls, 492 long response_code, 493 const void *response) 494 { 495 struct TALER_BANK_WithdrawalIDInfoHandle *aai = cls; 496 const json_t *j = response; 497 struct TALER_BANK_WithdrawalIDInfoResponse ir = { 498 .http_status = response_code, 499 .response = response 500 }; 501 502 aai->job = NULL; 503 switch (response_code) 504 { 505 case 0: 506 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 507 break; 508 case MHD_HTTP_OK: 509 { 510 struct GNUNET_JSON_Specification spec[] = { 511 GNUNET_JSON_spec_string ("status", 512 &ir.details.ok.status), 513 GNUNET_JSON_spec_mark_optional( 514 TALER_JSON_spec_amount_any("amount", 515 &ir.details.ok.amount), NULL), 516 GNUNET_JSON_spec_mark_optional( 517 TALER_JSON_spec_amount_any("suggested_amount", 518 &ir.details.ok.suggested_amount), NULL), 519 GNUNET_JSON_spec_mark_optional( 520 GNUNET_JSON_spec_bool("no_amount_to_wallet", &ir.details.ok.no_amount_to_wallet), NULL), 521 GNUNET_JSON_spec_string ("username", 522 &ir.details.ok.username), 523 GNUNET_JSON_spec_mark_optional( 524 GNUNET_JSON_spec_string ("selected_reserve_pub", 525 &ir.details.ok.selected_reserve_pub), NULL), 526 GNUNET_JSON_spec_mark_optional( 527 GNUNET_JSON_spec_string ("selected_exchange_account", 528 &ir.details.ok.selected_exchange_account), NULL), 529 GNUNET_JSON_spec_end () 530 }; 531 532 if (GNUNET_OK != 533 GNUNET_JSON_parse (j, 534 spec, 535 NULL, NULL)) 536 { 537 GNUNET_break_op (0); 538 ir.http_status = 0; 539 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 540 break; 541 } 542 } 543 break; 544 case MHD_HTTP_BAD_REQUEST: 545 /* This should never happen, either us or the bank is buggy 546 (or API version conflict); just pass JSON reply to the application */ 547 GNUNET_break_op (0); 548 ir.ec = TALER_JSON_get_error_code (j); 549 break; 550 case MHD_HTTP_NOT_FOUND: 551 /* Nothing really to verify, maybe withdrawal id really does not exist. 552 We should pass the JSON reply to the application */ 553 ir.ec = TALER_JSON_get_error_code (j); 554 break; 555 case MHD_HTTP_INTERNAL_SERVER_ERROR: 556 /* Server had an internal issue; we should retry, but this API 557 leaves this to the application */ 558 ir.ec = TALER_JSON_get_error_code (j); 559 break; 560 default: 561 /* unexpected response code */ 562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 563 "Unexpected response code %u\n", 564 (unsigned int) response_code); 565 GNUNET_break (0); 566 ir.ec = TALER_JSON_get_error_code (j); 567 break; 568 } 569 aai->cb (aai->cb_cls, 570 &ir); 571 TALER_BANK_withdrawalID_info_cancel (aai); 572 } 573 574 575 576 577 struct TALER_BANK_WithdrawalIDInfoHandle * 578 TALER_BANK_withdrawalID_info ( 579 struct GNUNET_CURL_Context *ctx, 580 const struct TALER_BANK_AuthenticationData *auth, 581 const char *withdrawal_id, 582 TALER_BANK_WithdrawalIDInfoCallback res_cb, 583 void *res_cb_cls) 584 { 585 struct TALER_BANK_WithdrawalIDInfoHandle *awch; 586 CURL *eh; 587 588 awch = GNUNET_new (struct TALER_BANK_WithdrawalIDInfoHandle); 589 awch->cb = res_cb; 590 awch->cb_cls = res_cb_cls; 591 { 592 char *path; 593 594 GNUNET_asprintf (&path, 595 "withdrawals/%s", 596 withdrawal_id); 597 awch->request_url = TALER_url_join (auth->wire_gateway_url, 598 path, 599 NULL); 600 GNUNET_free (path); 601 } 602 if (NULL == awch->request_url) 603 { 604 GNUNET_free (awch); 605 return NULL; 606 } 607 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 608 "Posting withdrawal id info request at `%s'\n", 609 awch->request_url); 610 611 eh = curl_easy_init (); 612 if ( (NULL == eh) || 613 (CURLE_OK != 614 curl_easy_setopt (eh, 615 CURLOPT_URL, 616 awch->request_url)) || 617 (CURLE_OK != 618 curl_easy_setopt(eh, CURLOPT_HTTPGET, 1))) 619 { 620 GNUNET_break (0); 621 TALER_BANK_withdrawalID_info_cancel (awch); 622 if (NULL != eh) 623 curl_easy_cleanup (eh); 624 return NULL; 625 } 626 awch->job = GNUNET_CURL_job_add (ctx, 627 eh, 628 &handle_withdrawalID_info_finished, 629 awch); 630 GNUNET_assert (NULL != awch->job); 631 return awch; 632 } 633 634 void 635 TALER_BANK_withdrawalID_info_cancel ( 636 struct TALER_BANK_WithdrawalIDInfoHandle *awch) 637 { 638 if (NULL != awch->job) 639 { 640 GNUNET_CURL_job_cancel (awch->job); 641 awch->job = NULL; 642 } 643 644 TALER_curl_easy_post_finished (&awch->post_ctx); //might not work; are there any headers? 645 /*Inside easy post finished 646 curl_slist_free_all (ctx->headers); 647 ctx->headers = NULL; 648 GNUNET_free (ctx->json_enc); 649 ctx->json_enc = NULL; 650 */ 651 GNUNET_free (awch->request_url); 652 GNUNET_free (awch); 653 } 654 655