taler-exchange-httpd_post-purses-PURSE_PUB-merge.c (23189B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_post-purses-PURSE_PUB-merge.c 18 * @brief Handle /purses/$PID/merge requests; parses the POST and JSON and 19 * verifies the reserve signature before handing things off 20 * to the database. 21 * @author Christian Grothoff 22 */ 23 #include "taler/platform.h" 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_json_lib.h> 26 #include <jansson.h> 27 #include <microhttpd.h> 28 #include <pthread.h> 29 #include "taler/taler_dbevents.h" 30 #include "taler/taler_json_lib.h" 31 #include "taler/taler_kyclogic_lib.h" 32 #include "taler/taler_mhd_lib.h" 33 #include "taler-exchange-httpd_common_kyc.h" 34 #include "taler-exchange-httpd_post-purses-PURSE_PUB-merge.h" 35 #include "taler-exchange-httpd_responses.h" 36 #include "taler/taler_exchangedb_lib.h" 37 #include "taler-exchange-httpd_get-keys.h" 38 39 40 /** 41 * Closure for #merge_transaction. 42 */ 43 struct PurseMergeContext 44 { 45 46 /** 47 * Kept in a DLL. 48 */ 49 struct PurseMergeContext *next; 50 51 /** 52 * Kept in a DLL. 53 */ 54 struct PurseMergeContext *prev; 55 56 /** 57 * Our request. 58 */ 59 struct TEH_RequestContext *rc; 60 61 /** 62 * Handle for the legitimization check. 63 */ 64 struct TEH_LegitimizationCheckHandle *lch; 65 66 /** 67 * Fees that apply to this operation. 68 */ 69 const struct TALER_WireFeeSet *wf; 70 71 /** 72 * Base URL of the exchange provider hosting the reserve. 73 */ 74 char *provider_url; 75 76 /** 77 * URI of the account the purse is to be merged into. 78 * Must be of the form 'payto://taler-reserve/$EXCHANGE_URL/RESERVE_PUB'. 79 */ 80 struct TALER_NormalizedPayto payto_uri; 81 82 /** 83 * Response to return, if set. 84 */ 85 struct MHD_Response *response; 86 87 /** 88 * Public key of the purse we are creating. 89 */ 90 struct TALER_PurseContractPublicKeyP purse_pub; 91 92 /** 93 * Total amount to be put into the purse. 94 */ 95 struct TALER_Amount target_amount; 96 97 /** 98 * Current amount in the purse. 99 */ 100 struct TALER_Amount balance; 101 102 /** 103 * When should the purse expire. 104 */ 105 struct GNUNET_TIME_Timestamp purse_expiration; 106 107 /** 108 * When the client signed the merge. 109 */ 110 struct GNUNET_TIME_Timestamp merge_timestamp; 111 112 /** 113 * Our current time. 114 */ 115 struct GNUNET_TIME_Timestamp exchange_timestamp; 116 117 /** 118 * Merge key for the purse. 119 */ 120 struct TALER_PurseMergePublicKeyP merge_pub; 121 122 /** 123 * Signature of the reservce affiming this request. 124 */ 125 struct TALER_ReserveSignatureP reserve_sig; 126 127 /** 128 * Signature of the client affiming the merge. 129 */ 130 struct TALER_PurseMergeSignatureP merge_sig; 131 132 /** 133 * Public key of the reserve (account), as extracted from @e payto_uri. 134 */ 135 union TALER_AccountPublicKeyP account_pub; 136 137 /** 138 * Hash of the contract terms of the purse. 139 */ 140 struct TALER_PrivateContractHashP h_contract_terms; 141 142 /** 143 * Hash of the @e payto_uri. 144 */ 145 struct TALER_NormalizedPaytoHashP h_payto; 146 147 /** 148 * KYC status of the operation. 149 */ 150 struct TALER_EXCHANGEDB_KycStatus kyc; 151 152 /** 153 * HTTP status to return with @e response, or 0. 154 */ 155 unsigned int http_status; 156 157 /** 158 * Minimum age for deposits into this purse. 159 */ 160 uint32_t min_age; 161 162 /** 163 * Set to true if this request was suspended. 164 */ 165 bool suspended; 166 }; 167 168 169 /** 170 * Kept in a DLL. 171 */ 172 static struct PurseMergeContext *pmc_head; 173 174 /** 175 * Kept in a DLL. 176 */ 177 static struct PurseMergeContext *pmc_tail; 178 179 180 void 181 TEH_purses_merge_cleanup () 182 { 183 struct PurseMergeContext *pmc; 184 185 while (NULL != (pmc = pmc_head)) 186 { 187 GNUNET_CONTAINER_DLL_remove (pmc_head, 188 pmc_tail, 189 pmc); 190 MHD_resume_connection (pmc->rc->connection); 191 } 192 } 193 194 195 /** 196 * Function called with the result of a legitimization 197 * check. 198 * 199 * @param cls closure 200 * @param lcr legitimization check result 201 */ 202 static void 203 legi_result_cb ( 204 void *cls, 205 const struct TEH_LegitimizationCheckResult *lcr) 206 { 207 struct PurseMergeContext *pmc = cls; 208 209 pmc->lch = NULL; 210 MHD_resume_connection (pmc->rc->connection); 211 GNUNET_CONTAINER_DLL_remove (pmc_head, 212 pmc_tail, 213 pmc); 214 TALER_MHD_daemon_trigger (); 215 if (NULL != lcr->response) 216 { 217 pmc->response = lcr->response; 218 pmc->http_status = lcr->http_status; 219 return; 220 } 221 pmc->kyc = lcr->kyc; 222 } 223 224 225 /** 226 * Send confirmation of purse creation success to client. 227 * 228 * @param pmc details about the request that succeeded 229 * @return MHD result code 230 */ 231 static MHD_RESULT 232 reply_merge_success (const struct PurseMergeContext *pmc) 233 { 234 struct MHD_Connection *connection = pmc->rc->connection; 235 struct TALER_ExchangePublicKeyP pub; 236 struct TALER_ExchangeSignatureP sig; 237 enum TALER_ErrorCode ec; 238 struct TALER_Amount merge_amount; 239 240 if (0 < 241 TALER_amount_cmp (&pmc->balance, 242 &pmc->target_amount)) 243 { 244 GNUNET_break (0); 245 return TALER_MHD_REPLY_JSON_PACK ( 246 connection, 247 MHD_HTTP_INTERNAL_SERVER_ERROR, 248 TALER_JSON_pack_amount ("balance", 249 &pmc->balance), 250 TALER_JSON_pack_amount ("target_amount", 251 &pmc->target_amount)); 252 } 253 if ( (NULL == pmc->provider_url) || 254 (0 == strcmp (pmc->provider_url, 255 TEH_base_url)) ) 256 { 257 /* wad fee is always zero if we stay at our own exchange */ 258 merge_amount = pmc->target_amount; 259 } 260 else 261 { 262 #if WAD_NOT_IMPLEMENTED /* #7271 */ 263 /* FIXME: figure out partner, lookup wad fee by partner! #7271 */ 264 if (0 > 265 TALER_amount_subtract (&merge_amount, 266 &pmc->target_amount, 267 &wad_fee)) 268 { 269 GNUNET_assert (GNUNET_OK == 270 TALER_amount_set_zero (TEH_currency, 271 &merge_amount)); 272 } 273 #else 274 merge_amount = pmc->target_amount; 275 #endif 276 } 277 if (TALER_EC_NONE != 278 (ec = TALER_exchange_online_purse_merged_sign ( 279 &TEH_keys_exchange_sign_, 280 pmc->exchange_timestamp, 281 pmc->purse_expiration, 282 &merge_amount, 283 &pmc->purse_pub, 284 &pmc->h_contract_terms, 285 &pmc->account_pub.reserve_pub, 286 (NULL != pmc->provider_url) 287 ? pmc->provider_url 288 : TEH_base_url, 289 &pub, 290 &sig))) 291 { 292 GNUNET_break (0); 293 return TALER_MHD_reply_with_ec (connection, 294 ec, 295 NULL); 296 } 297 return TALER_MHD_REPLY_JSON_PACK ( 298 connection, 299 MHD_HTTP_OK, 300 TALER_JSON_pack_amount ("merge_amount", 301 &merge_amount), 302 GNUNET_JSON_pack_timestamp ("exchange_timestamp", 303 pmc->exchange_timestamp), 304 GNUNET_JSON_pack_data_auto ("exchange_sig", 305 &sig), 306 GNUNET_JSON_pack_data_auto ("exchange_pub", 307 &pub)); 308 } 309 310 311 /** 312 * Function called to iterate over KYC-relevant 313 * transaction amounts for a particular time range. 314 * Called within a database transaction, so must 315 * not start a new one. 316 * 317 * @param cls a `struct PurseMergeContext` 318 * @param limit maximum time-range for which events 319 * should be fetched (timestamp in the past) 320 * @param cb function to call on each event found, 321 * events must be returned in reverse chronological 322 * order 323 * @param cb_cls closure for @a cb 324 * @return transaction status 325 */ 326 static enum GNUNET_DB_QueryStatus 327 amount_iterator (void *cls, 328 struct GNUNET_TIME_Absolute limit, 329 TALER_EXCHANGEDB_KycAmountCallback cb, 330 void *cb_cls) 331 { 332 struct PurseMergeContext *pmc = cls; 333 enum GNUNET_GenericReturnValue ret; 334 enum GNUNET_DB_QueryStatus qs; 335 336 ret = cb (cb_cls, 337 &pmc->target_amount, 338 GNUNET_TIME_absolute_get ()); 339 GNUNET_break (GNUNET_SYSERR != ret); 340 if (GNUNET_OK != ret) 341 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 342 qs = TEH_plugin->select_merge_amounts_for_kyc_check ( 343 TEH_plugin->cls, 344 &pmc->h_payto, 345 limit, 346 cb, 347 cb_cls); 348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 349 "Got %d additional transactions for this merge and limit %llu\n", 350 qs, 351 (unsigned long long) limit.abs_value_us); 352 GNUNET_break (qs >= 0); 353 return qs; 354 } 355 356 357 /** 358 * Execute database transaction for /purses/$PID/merge. Runs the transaction 359 * logic; IF it returns a non-error code, the transaction logic MUST NOT queue 360 * a MHD response. IF it returns an hard error, the transaction logic MUST 361 * queue a MHD response and set @a mhd_ret. IF it returns the soft error 362 * code, the function MAY be called again to retry and MUST not queue a MHD 363 * response. 364 * 365 * @param cls a `struct PurseMergeContext` 366 * @param connection MHD request context 367 * @param[out] mhd_ret set to MHD status on error 368 * @return transaction status 369 */ 370 static enum GNUNET_DB_QueryStatus 371 merge_transaction (void *cls, 372 struct MHD_Connection *connection, 373 MHD_RESULT *mhd_ret) 374 { 375 struct PurseMergeContext *pmc = cls; 376 enum GNUNET_DB_QueryStatus qs; 377 bool in_conflict = true; 378 bool no_balance = true; 379 bool no_partner = true; 380 381 qs = TEH_plugin->do_purse_merge ( 382 TEH_plugin->cls, 383 &pmc->purse_pub, 384 &pmc->merge_sig, 385 pmc->merge_timestamp, 386 &pmc->reserve_sig, 387 pmc->provider_url, 388 &pmc->account_pub.reserve_pub, 389 &no_partner, 390 &no_balance, 391 &in_conflict); 392 if (qs < 0) 393 { 394 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 395 return qs; 396 GNUNET_break (0); 397 *mhd_ret = 398 TALER_MHD_reply_with_error (connection, 399 MHD_HTTP_INTERNAL_SERVER_ERROR, 400 TALER_EC_GENERIC_DB_STORE_FAILED, 401 "purse merge"); 402 return qs; 403 } 404 if (no_partner) 405 { 406 *mhd_ret = 407 TALER_MHD_reply_with_error (connection, 408 MHD_HTTP_NOT_FOUND, 409 TALER_EC_EXCHANGE_MERGE_PURSE_PARTNER_UNKNOWN, 410 pmc->provider_url); 411 return GNUNET_DB_STATUS_HARD_ERROR; 412 } 413 if (no_balance) 414 { 415 *mhd_ret = 416 TALER_MHD_reply_with_error (connection, 417 MHD_HTTP_PAYMENT_REQUIRED, 418 TALER_EC_EXCHANGE_PURSE_NOT_FULL, 419 NULL); 420 return GNUNET_DB_STATUS_HARD_ERROR; 421 } 422 if (in_conflict) 423 { 424 struct TALER_PurseMergeSignatureP merge_sig; 425 struct GNUNET_TIME_Timestamp merge_timestamp; 426 char *partner_url = NULL; 427 struct TALER_ReservePublicKeyP reserve_pub; 428 bool refunded; 429 430 qs = TEH_plugin->select_purse_merge (TEH_plugin->cls, 431 &pmc->purse_pub, 432 &merge_sig, 433 &merge_timestamp, 434 &partner_url, 435 &reserve_pub, 436 &refunded); 437 if (qs <= 0) 438 { 439 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 440 return qs; 441 TALER_LOG_WARNING ( 442 "Failed to fetch merge purse information from database\n"); 443 *mhd_ret = 444 TALER_MHD_reply_with_error (connection, 445 MHD_HTTP_INTERNAL_SERVER_ERROR, 446 TALER_EC_GENERIC_DB_FETCH_FAILED, 447 "select purse merge"); 448 return qs; 449 } 450 if (refunded) 451 { 452 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 453 "Purse was already refunded\n"); 454 *mhd_ret = TALER_MHD_reply_with_error (connection, 455 MHD_HTTP_GONE, 456 TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED, 457 NULL); 458 GNUNET_free (partner_url); 459 return GNUNET_DB_STATUS_HARD_ERROR; 460 } 461 if (0 != 462 GNUNET_memcmp (&merge_sig, 463 &pmc->merge_sig)) 464 { 465 *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( 466 connection, 467 MHD_HTTP_CONFLICT, 468 GNUNET_JSON_pack_timestamp ("merge_timestamp", 469 merge_timestamp), 470 GNUNET_JSON_pack_data_auto ("merge_sig", 471 &merge_sig), 472 GNUNET_JSON_pack_allow_null ( 473 GNUNET_JSON_pack_string ("partner_url", 474 partner_url)), 475 GNUNET_JSON_pack_data_auto ("reserve_pub", 476 &reserve_pub)); 477 GNUNET_free (partner_url); 478 return GNUNET_DB_STATUS_HARD_ERROR; 479 } 480 /* idempotent! */ 481 *mhd_ret = reply_merge_success (pmc); 482 GNUNET_free (partner_url); 483 return GNUNET_DB_STATUS_HARD_ERROR; 484 } 485 486 return qs; 487 } 488 489 490 /** 491 * Purse-merge-specific cleanup routine. Function called 492 * upon completion of the request that should 493 * clean up @a rh_ctx. Can be NULL. 494 * 495 * @param rc request context to clean up 496 */ 497 static void 498 clean_purse_merge_rc (struct TEH_RequestContext *rc) 499 { 500 struct PurseMergeContext *pmc = rc->rh_ctx; 501 502 if (NULL != pmc->lch) 503 { 504 TEH_legitimization_check_cancel (pmc->lch); 505 pmc->lch = NULL; 506 } 507 GNUNET_free (pmc->provider_url); 508 GNUNET_free (pmc); 509 } 510 511 512 MHD_RESULT 513 TEH_handler_purses_merge ( 514 struct TEH_RequestContext *rc, 515 const struct TALER_PurseContractPublicKeyP *purse_pub, 516 const json_t *root) 517 { 518 struct PurseMergeContext *pmc = rc->rh_ctx; 519 520 if (NULL == pmc) 521 { 522 pmc = GNUNET_new (struct PurseMergeContext); 523 rc->rh_ctx = pmc; 524 rc->rh_cleaner = &clean_purse_merge_rc; 525 pmc->rc = rc; 526 pmc->purse_pub = *purse_pub; 527 pmc->exchange_timestamp 528 = GNUNET_TIME_timestamp_get (); 529 530 { 531 struct GNUNET_JSON_Specification spec[] = { 532 TALER_JSON_spec_normalized_payto_uri ("payto_uri", 533 &pmc->payto_uri), 534 GNUNET_JSON_spec_fixed_auto ("reserve_sig", 535 &pmc->reserve_sig), 536 GNUNET_JSON_spec_fixed_auto ("merge_sig", 537 &pmc->merge_sig), 538 GNUNET_JSON_spec_timestamp ("merge_timestamp", 539 &pmc->merge_timestamp), 540 GNUNET_JSON_spec_end () 541 }; 542 enum GNUNET_GenericReturnValue res; 543 544 res = TALER_MHD_parse_json_data (rc->connection, 545 root, 546 spec); 547 if (GNUNET_SYSERR == res) 548 { 549 GNUNET_break (0); 550 return MHD_NO; /* hard failure */ 551 } 552 if (GNUNET_NO == res) 553 { 554 GNUNET_break_op (0); 555 return MHD_YES; /* failure */ 556 } 557 } 558 559 { 560 struct TALER_PurseContractSignatureP purse_sig; 561 enum GNUNET_DB_QueryStatus qs; 562 563 /* Fetch purse details */ 564 qs = TEH_plugin->get_purse_request ( 565 TEH_plugin->cls, 566 &pmc->purse_pub, 567 &pmc->merge_pub, 568 &pmc->purse_expiration, 569 &pmc->h_contract_terms, 570 &pmc->min_age, 571 &pmc->target_amount, 572 &pmc->balance, 573 &purse_sig); 574 switch (qs) 575 { 576 case GNUNET_DB_STATUS_HARD_ERROR: 577 GNUNET_break (0); 578 return TALER_MHD_reply_with_error ( 579 rc->connection, 580 MHD_HTTP_INTERNAL_SERVER_ERROR, 581 TALER_EC_GENERIC_DB_FETCH_FAILED, 582 "select purse request"); 583 case GNUNET_DB_STATUS_SOFT_ERROR: 584 GNUNET_break (0); 585 return TALER_MHD_reply_with_error ( 586 rc->connection, 587 MHD_HTTP_INTERNAL_SERVER_ERROR, 588 TALER_EC_GENERIC_DB_FETCH_FAILED, 589 "select purse request"); 590 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 591 return TALER_MHD_reply_with_error ( 592 rc->connection, 593 MHD_HTTP_NOT_FOUND, 594 TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN, 595 NULL); 596 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 597 /* continued below */ 598 break; 599 } 600 } 601 602 /* check signatures */ 603 if (GNUNET_OK != 604 TALER_wallet_purse_merge_verify ( 605 pmc->payto_uri, 606 pmc->merge_timestamp, 607 &pmc->purse_pub, 608 &pmc->merge_pub, 609 &pmc->merge_sig)) 610 { 611 GNUNET_break_op (0); 612 return TALER_MHD_reply_with_error ( 613 rc->connection, 614 MHD_HTTP_FORBIDDEN, 615 TALER_EC_EXCHANGE_PURSE_MERGE_INVALID_MERGE_SIGNATURE, 616 NULL); 617 } 618 619 /* parse 'payto_uri' into pmc->account_pub and provider_url */ 620 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 621 "Received payto: `%s'\n", 622 pmc->payto_uri.normalized_payto); 623 if ( (0 != strncmp (pmc->payto_uri.normalized_payto, 624 "payto://taler-reserve/", 625 strlen ("payto://taler-reserve/"))) && 626 (0 != strncmp (pmc->payto_uri.normalized_payto, 627 "payto://taler-reserve-http/", 628 strlen ("payto://taler-reserve-http/"))) ) 629 { 630 GNUNET_break_op (0); 631 return TALER_MHD_reply_with_error ( 632 rc->connection, 633 MHD_HTTP_BAD_REQUEST, 634 TALER_EC_GENERIC_PARAMETER_MALFORMED, 635 "payto_uri"); 636 } 637 638 { 639 bool http; 640 const char *host; 641 const char *slash; 642 643 http = (0 == strncmp (pmc->payto_uri.normalized_payto, 644 "payto://taler-reserve-http/", 645 strlen ("payto://taler-reserve-http/"))); 646 host = &pmc->payto_uri.normalized_payto[http 647 ? strlen ("payto://taler-reserve-http/") 648 : strlen ("payto://taler-reserve/")]; 649 slash = strchr (host, 650 '/'); 651 if (NULL == slash) 652 { 653 GNUNET_break_op (0); 654 return TALER_MHD_reply_with_error ( 655 rc->connection, 656 MHD_HTTP_BAD_REQUEST, 657 TALER_EC_GENERIC_PARAMETER_MALFORMED, 658 "payto_uri"); 659 } 660 GNUNET_asprintf (&pmc->provider_url, 661 "%s://%.*s/", 662 http ? "http" : "https", 663 (int) (slash - host), 664 host); 665 slash++; 666 if (GNUNET_OK != 667 GNUNET_STRINGS_string_to_data ( 668 slash, 669 strlen (slash), 670 &pmc->account_pub.reserve_pub, 671 sizeof (pmc->account_pub.reserve_pub))) 672 { 673 GNUNET_break_op (0); 674 return TALER_MHD_reply_with_error ( 675 rc->connection, 676 MHD_HTTP_BAD_REQUEST, 677 TALER_EC_GENERIC_PARAMETER_MALFORMED, 678 "payto_uri"); 679 } 680 } 681 TALER_normalized_payto_hash (pmc->payto_uri, 682 &pmc->h_payto); 683 if (0 == strcmp (pmc->provider_url, 684 TEH_base_url)) 685 { 686 /* we use NULL to represent 'self' as the provider */ 687 GNUNET_free (pmc->provider_url); 688 } 689 else 690 { 691 char *method = GNUNET_strdup ("FIXME-WAD #7271"); 692 693 /* FIXME-#7271: lookup wire method by pmc.provider_url! */ 694 pmc->wf = TEH_wire_fees_by_time (pmc->exchange_timestamp, 695 method); 696 if (NULL == pmc->wf) 697 { 698 MHD_RESULT res; 699 700 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 701 "Cannot merge purse: wire fees not configured!\n"); 702 res = TALER_MHD_reply_with_error ( 703 rc->connection, 704 MHD_HTTP_INTERNAL_SERVER_ERROR, 705 TALER_EC_EXCHANGE_GENERIC_WIRE_FEES_MISSING, 706 method); 707 GNUNET_free (method); 708 return res; 709 } 710 GNUNET_free (method); 711 } 712 713 { 714 struct TALER_Amount zero_purse_fee; 715 716 GNUNET_assert (GNUNET_OK == 717 TALER_amount_set_zero ( 718 pmc->target_amount.currency, 719 &zero_purse_fee)); 720 if (GNUNET_OK != 721 TALER_wallet_account_merge_verify ( 722 pmc->merge_timestamp, 723 &pmc->purse_pub, 724 pmc->purse_expiration, 725 &pmc->h_contract_terms, 726 &pmc->target_amount, 727 &zero_purse_fee, 728 pmc->min_age, 729 TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE, 730 &pmc->account_pub.reserve_pub, 731 &pmc->reserve_sig)) 732 { 733 GNUNET_break_op (0); 734 return TALER_MHD_reply_with_error ( 735 rc->connection, 736 MHD_HTTP_FORBIDDEN, 737 TALER_EC_EXCHANGE_PURSE_MERGE_INVALID_RESERVE_SIGNATURE, 738 NULL); 739 } 740 } 741 { 742 struct TALER_FullPayto fake_full_payto; 743 744 GNUNET_asprintf (&fake_full_payto.full_payto, 745 "%s?receiver-name=wallet", 746 pmc->payto_uri.normalized_payto); 747 pmc->lch = TEH_legitimization_check ( 748 &rc->async_scope_id, 749 TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE, 750 fake_full_payto, 751 &pmc->h_payto, 752 &pmc->account_pub, 753 &amount_iterator, 754 pmc, 755 &legi_result_cb, 756 pmc); 757 GNUNET_free (fake_full_payto.full_payto); 758 } 759 GNUNET_assert (NULL != pmc->lch); 760 MHD_suspend_connection (rc->connection); 761 GNUNET_CONTAINER_DLL_insert (pmc_head, 762 pmc_tail, 763 pmc); 764 return MHD_YES; 765 } 766 if (NULL != pmc->response) 767 { 768 return MHD_queue_response (rc->connection, 769 pmc->http_status, 770 pmc->response); 771 } 772 if (! pmc->kyc.ok) 773 return TEH_RESPONSE_reply_kyc_required ( 774 rc->connection, 775 &pmc->h_payto, 776 &pmc->kyc, 777 false); 778 779 /* execute merge transaction */ 780 { 781 MHD_RESULT mhd_ret; 782 783 if (GNUNET_OK != 784 TEH_DB_run_transaction (rc->connection, 785 "execute purse merge", 786 TEH_MT_REQUEST_PURSE_MERGE, 787 &mhd_ret, 788 &merge_transaction, 789 pmc)) 790 { 791 return mhd_ret; 792 } 793 } 794 795 { 796 struct TALER_PurseEventP rep = { 797 .header.size = htons (sizeof (rep)), 798 .header.type = htons (TALER_DBEVENT_EXCHANGE_PURSE_MERGED), 799 .purse_pub = pmc->purse_pub 800 }; 801 802 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 803 "Notifying about purse merge\n"); 804 TEH_plugin->event_notify (TEH_plugin->cls, 805 &rep.header, 806 NULL, 807 0); 808 } 809 810 /* generate regular response */ 811 return reply_merge_success (pmc); 812 } 813 814 815 /* end of taler-exchange-httpd_purses_merge.c */