taler-merchant-httpd_get-private-orders-ORDER_ID.c (65987B)
1 /* 2 This file is part of TALER 3 (C) 2017-2024, 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 <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c 18 * @brief implementation of GET /private/orders/ID handler 19 * @author Florian Dold 20 * @author Christian Grothoff 21 * @author Bohdan Potuzhnyi 22 * @author Iván Ávalos 23 */ 24 #include "platform.h" 25 #include <taler/taler_json_lib.h> 26 #include <taler/taler_dbevents.h> 27 #include <taler/taler_error_codes.h> 28 #include <taler/taler_util.h> 29 #include <gnunet/gnunet_common.h> 30 #include <gnunet/gnunet_json_lib.h> 31 #include "taler/taler_merchant_util.h" 32 #include "taler-merchant-httpd_helper.h" 33 #include "taler-merchant-httpd_get-private-orders.h" 34 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h" 35 #include "merchant-database/get_contract_terms_status.h" 36 #include "merchant-database/iterate_deposits_by_order.h" 37 #include "merchant-database/get_order.h" 38 #include "merchant-database/get_order_by_fulfillment.h" 39 #include "merchant-database/iterate_refunds_detailed.h" 40 #include "merchant-database/iterate_external_refunds.h" 41 #include "merchant-database/iterate_transfer_details_by_order.h" 42 #include "merchant-database/update_to_contract_terms_wired.h" 43 #include "merchant-database/preflight.h" 44 #include "merchant-database/event_listen.h" 45 46 /** 47 * Data structure we keep for a check payment request. 48 */ 49 struct GetOrderRequestContext; 50 51 52 /** 53 * Request to an exchange for details about wire transfers 54 * in response to a coin's deposit operation. 55 */ 56 struct TransferQuery 57 { 58 59 /** 60 * Kept in a DLL. 61 */ 62 struct TransferQuery *next; 63 64 /** 65 * Kept in a DLL. 66 */ 67 struct TransferQuery *prev; 68 69 /** 70 * Base URL of the exchange. 71 */ 72 char *exchange_url; 73 74 /** 75 * Overall request this TQ belongs with. 76 */ 77 struct GetOrderRequestContext *gorc; 78 79 /** 80 * Hash of the merchant's bank account the transfer (presumably) went to. 81 */ 82 struct TALER_MerchantWireHashP h_wire; 83 84 /** 85 * Value deposited (including deposit fee). 86 */ 87 struct TALER_Amount amount_with_fee; 88 89 /** 90 * Deposit fee paid for this coin. 91 */ 92 struct TALER_Amount deposit_fee; 93 94 /** 95 * Total amount refunded so far for this coin in this order. The 96 * exchange only returns @e deposit_fee to us once this reaches 97 * @e amount_with_fee. 98 */ 99 struct TALER_Amount refunded_total; 100 101 /** 102 * Public key of the coin this is about. 103 */ 104 struct TALER_CoinSpendPublicKeyP coin_pub; 105 106 /** 107 * Which deposit operation is this about? 108 */ 109 uint64_t deposit_serial; 110 111 }; 112 113 114 /** 115 * Phases of order processing. 116 */ 117 enum GetOrderPhase 118 { 119 /** 120 * Initialization. 121 */ 122 GOP_INIT = 0, 123 124 /** 125 * Obtain contract terms from database. 126 */ 127 GOP_FETCH_CONTRACT = 1, 128 129 /** 130 * Parse the contract terms. 131 */ 132 GOP_PARSE_CONTRACT = 2, 133 134 /** 135 * Check if the contract was fully paid. 136 */ 137 GOP_CHECK_PAID = 3, 138 139 /** 140 * Check if the wallet may have purchased an equivalent 141 * order before and we need to redirect the wallet to 142 * an existing paid order. 143 */ 144 GOP_CHECK_REPURCHASE = 4, 145 146 /** 147 * Terminate processing of unpaid orders, either by 148 * suspending until payment or by returning the 149 * unpaid order status. 150 */ 151 GOP_UNPAID_FINISH = 5, 152 153 /** 154 * Load all deposits associated with the order. 155 */ 156 GOP_CHECK_DEPOSITS = 6, 157 158 /** 159 * Check if the (paid) order was refunded. 160 */ 161 GOP_CHECK_REFUNDS = 7, 162 163 /** 164 * Check local records for transfers of funds to 165 * the merchant. 166 */ 167 GOP_CHECK_LOCAL_TRANSFERS = 8, 168 169 /** 170 * Generate final comprehensive result. 171 */ 172 GOP_REPLY_RESULT = 9, 173 174 /** 175 * End with the HTTP status and error code in 176 * wire_hc and wire_ec. 177 */ 178 GOP_ERROR = 10, 179 180 /** 181 * We are suspended awaiting payment. 182 */ 183 GOP_SUSPENDED_ON_UNPAID = 11, 184 185 /** 186 * Processing is done, return #MHD_YES. 187 */ 188 GOP_END_YES = 12, 189 190 /** 191 * Processing is done, return #MHD_NO. 192 */ 193 GOP_END_NO = 13 194 195 }; 196 197 198 /** 199 * Data structure we keep for a check payment request. 200 */ 201 struct GetOrderRequestContext 202 { 203 204 /** 205 * Processing phase we are in. 206 */ 207 enum GetOrderPhase phase; 208 209 /** 210 * Entry in the #resume_timeout_heap for this check payment, if we are 211 * suspended. 212 */ 213 struct TMH_SuspendedConnection sc; 214 215 /** 216 * Which merchant instance is this for? 217 */ 218 struct TMH_HandlerContext *hc; 219 220 /** 221 * session of the client 222 */ 223 const char *session_id; 224 225 /** 226 * Kept in a DLL while suspended on exchange. 227 */ 228 struct GetOrderRequestContext *next; 229 230 /** 231 * Kept in a DLL while suspended on exchange. 232 */ 233 struct GetOrderRequestContext *prev; 234 235 /** 236 * Head of DLL of individual queries for transfer data. 237 */ 238 struct TransferQuery *tq_head; 239 240 /** 241 * Tail of DLL of individual queries for transfer data. 242 */ 243 struct TransferQuery *tq_tail; 244 245 /** 246 * Timeout task while waiting on exchange. 247 */ 248 struct GNUNET_SCHEDULER_Task *tt; 249 250 /** 251 * Database event we are waiting on to be resuming 252 * for payment or refunds. 253 */ 254 struct GNUNET_DB_EventHandler *eh; 255 256 /** 257 * Database event we are waiting on to be resuming 258 * for session capture. 259 */ 260 struct GNUNET_DB_EventHandler *session_eh; 261 262 /** 263 * Contract terms of the payment we are checking. NULL when they 264 * are not (yet) known. 265 */ 266 json_t *contract_terms_json; 267 268 /** 269 * Parsed contract terms, NULL when parsing failed 270 */ 271 struct TALER_MERCHANT_Contract *contract_terms; 272 273 /** 274 * Proto-contract. Be careful: do NOT free this 275 * if @e contract_terms is not NULL! 276 */ 277 struct TALER_MERCHANT_ProtoContract *pc; 278 279 /** 280 * Order terms of the payment we are checking. NULL when we have 281 * a contract. 282 */ 283 json_t *order_json; 284 285 /** 286 * Parsed order details, NULL when parsing failed or we have a contract. 287 */ 288 struct TALER_MERCHANT_Order *order; 289 290 /** 291 * Common terms of @e order and @e contract. 292 */ 293 const struct TALER_MERCHANT_ContractBaseTerms *ct; 294 295 /** 296 * Timestamp of the contract or order. 297 */ 298 struct GNUNET_TIME_Timestamp timestamp; 299 300 /** 301 * Claim token of the order. 302 */ 303 struct TALER_ClaimTokenP claim_token; 304 305 /** 306 * Timestamp of the last payment. 307 */ 308 struct GNUNET_TIME_Timestamp last_payment; 309 310 /** 311 * Wire details for the payment, to be returned in the reply. NULL 312 * if not available. 313 */ 314 json_t *wire_details; 315 316 /** 317 * Details about refunds, NULL if there are no refunds. 318 */ 319 json_t *refund_details; 320 321 /** 322 * Details about external refunds, empty array if there are none. 323 */ 324 json_t *refunds_external; 325 326 /** 327 * Amount of the order, unset for unpaid v1 orders. 328 */ 329 struct TALER_Amount contract_amount; 330 331 /** 332 * Maximum fee the merchant is willing to pay for this order/choice. 333 * Only valid if @e has_max_fee is true. 334 */ 335 struct TALER_Amount max_fee; 336 337 /** 338 * True if @e max_fee was provided by the contract. 339 */ 340 bool has_max_fee; 341 342 /** 343 * Hash over the @e contract_terms. 344 */ 345 struct TALER_PrivateContractHashP h_contract_terms; 346 347 /** 348 * Set to the Etag of a response already known to the 349 * client. We should only return from long-polling 350 * on timeout (with "Not Modified") or when the Etag 351 * of the response differs from what is given here. 352 * Only set if @a have_lp_not_etag is true. 353 * Set from "lp_etag" query parameter. 354 */ 355 struct GNUNET_ShortHashCode lp_not_etag; 356 357 /** 358 * Total amount the exchange deposited into our bank account 359 * (confirmed or unconfirmed), excluding fees. 360 */ 361 struct TALER_Amount deposits_total; 362 363 /** 364 * Total amount in deposit fees we paid for all coins. 365 */ 366 struct TALER_Amount deposit_fees_total; 367 368 /** 369 * Total amount in deposit fees cancelled due to refunds for all coins. 370 */ 371 struct TALER_Amount deposit_fees_refunded_total; 372 373 /** 374 * Total value of the coins that the exchange deposited into our bank 375 * account (confirmed or unconfirmed), including deposit fees. 376 */ 377 struct TALER_Amount value_total; 378 379 /** 380 * Serial ID of the order. 381 */ 382 uint64_t order_serial; 383 384 /** 385 * Index of selected choice from ``choices`` array in the contract_terms. 386 * Is -1 for orders without choices. 387 */ 388 int16_t choice_index; 389 390 /** 391 * Total refunds granted for this payment. Only initialized 392 * if @e refunded is set to true. 393 */ 394 struct TALER_Amount refund_amount; 395 396 /** 397 * Exchange HTTP error code encountered while trying to determine wire transfer 398 * details. #TALER_EC_NONE for no error encountered. 399 */ 400 unsigned int exchange_hc; 401 402 /** 403 * Exchange error code encountered while trying to determine wire transfer 404 * details. #TALER_EC_NONE for no error encountered. 405 */ 406 enum TALER_ErrorCode exchange_ec; 407 408 /** 409 * Error code encountered while trying to determine wire transfer 410 * details. #TALER_EC_NONE for no error encountered. 411 */ 412 enum TALER_ErrorCode wire_ec; 413 414 /** 415 * Set to YES if refunded orders should be included when 416 * doing repurchase detection. 417 */ 418 enum TALER_EXCHANGE_YesNoAll allow_refunded_for_repurchase; 419 420 /** 421 * HTTP status to return with @e wire_ec, 0 if @e wire_ec is #TALER_EC_NONE. 422 */ 423 unsigned int wire_hc; 424 425 /** 426 * Did we suspend @a connection and are thus in 427 * the #gorc_head DLL (#GNUNET_YES). Set to 428 * #GNUNET_NO if we are not suspended, and to 429 * #GNUNET_SYSERR if we should close the connection 430 * without a response due to shutdown. 431 */ 432 enum GNUNET_GenericReturnValue suspended; 433 434 /** 435 * Set to true if this payment has been refunded and 436 * @e refund_amount is initialized. 437 */ 438 bool refunded; 439 440 /** 441 * True if @e lp_not_etag was given. 442 */ 443 bool have_lp_not_etag; 444 445 /** 446 * True if the order was paid. 447 */ 448 bool paid; 449 450 /** 451 * True if the paid session in the database matches 452 * our @e session_id. 453 */ 454 bool paid_session_matches; 455 456 /** 457 * When we install the @e session_eh listener, we already checked 458 * the order status. We could not do it earlier, because the 459 * fulfillment URL was unavailable. However, this leaves a chance 460 * for a payment event to be missed if it happens before the first 461 * order status check and us installing the event listener. Thus, 462 * if we afterwards suspend, we should once *immediately* check the 463 * order status a 2nd time before we really suspend. This flag is 464 * set to true to handle this case. 465 */ 466 bool instant_retry; 467 468 /** 469 * True if the exchange wired the money to the merchant. 470 */ 471 bool wired; 472 473 /** 474 * True if the order remains unclaimed. 475 */ 476 bool order_only; 477 478 /** 479 * Set to true if this payment has been refunded and 480 * some refunds remain to be picked up by the wallet. 481 */ 482 bool refund_pending; 483 484 /** 485 * Set to true if our database (incorrectly) has refunds 486 * in a different currency than the currency of the 487 * original payment for the order. 488 */ 489 bool refund_currency_mismatch; 490 491 /** 492 * Set to true if our database (incorrectly) has deposits 493 * in a different currency than the currency of the 494 * original payment for the order. 495 */ 496 bool deposit_currency_mismatch; 497 }; 498 499 500 /** 501 * Head of list of suspended requests waiting on the exchange. 502 */ 503 static struct GetOrderRequestContext *gorc_head; 504 505 /** 506 * Tail of list of suspended requests waiting on the exchange. 507 */ 508 static struct GetOrderRequestContext *gorc_tail; 509 510 511 void 512 TMH_force_gorc_resume (void) 513 { 514 struct GetOrderRequestContext *gorc; 515 516 while (NULL != (gorc = gorc_head)) 517 { 518 GNUNET_CONTAINER_DLL_remove (gorc_head, 519 gorc_tail, 520 gorc); 521 GNUNET_assert (GNUNET_YES == gorc->suspended); 522 gorc->suspended = GNUNET_SYSERR; 523 MHD_resume_connection (gorc->sc.con); 524 } 525 } 526 527 528 /** 529 * Discard the list of deposits collected for @a gorc. 530 * 531 * @param[in,out] gorc order context to update 532 */ 533 static void 534 clear_transfer_queries (struct GetOrderRequestContext *gorc) 535 { 536 struct TransferQuery *tq; 537 538 while (NULL != (tq = gorc->tq_head)) 539 { 540 GNUNET_CONTAINER_DLL_remove (gorc->tq_head, 541 gorc->tq_tail, 542 tq); 543 GNUNET_free (tq->exchange_url); 544 GNUNET_free (tq); 545 } 546 } 547 548 549 /** 550 * We have received a trigger from the database 551 * that we should (possibly) resume the request. 552 * 553 * @param cls a `struct GetOrderRequestContext` to resume 554 * @param extra string encoding refund amount (or NULL) 555 * @param extra_size number of bytes in @a extra 556 */ 557 static void 558 resume_by_event (void *cls, 559 const void *extra, 560 size_t extra_size) 561 { 562 struct GetOrderRequestContext *gorc = cls; 563 564 (void) extra; 565 (void) extra_size; 566 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 567 "Resuming request for order %s by trigger\n", 568 gorc->hc->infix); 569 if (GNUNET_NO == gorc->suspended) 570 return; /* duplicate event is possible */ 571 gorc->suspended = GNUNET_NO; 572 gorc->phase = GOP_FETCH_CONTRACT; 573 GNUNET_CONTAINER_DLL_remove (gorc_head, 574 gorc_tail, 575 gorc); 576 MHD_resume_connection (gorc->sc.con); 577 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 578 } 579 580 581 /** 582 * Clean up the session state for a GET /private/order/ID request. 583 * 584 * @param cls closure, must be a `struct GetOrderRequestContext *` 585 */ 586 static void 587 gorc_cleanup (void *cls) 588 { 589 struct GetOrderRequestContext *gorc = cls; 590 591 clear_transfer_queries (gorc); 592 if (NULL != gorc->contract_terms_json) 593 { 594 json_decref (gorc->contract_terms_json); 595 gorc->contract_terms_json = NULL; 596 } 597 if (NULL != gorc->order_json) 598 { 599 json_decref (gorc->order_json); 600 gorc->order_json = NULL; 601 } 602 if (NULL != gorc->contract_terms) 603 { 604 TALER_MERCHANT_contract_free (gorc->contract_terms); 605 gorc->contract_terms = NULL; 606 gorc->pc = NULL; /* was an alias! */ 607 } 608 if (NULL != gorc->pc) 609 { 610 TALER_MERCHANT_proto_contract_free (gorc->pc); 611 gorc->pc = NULL; 612 } 613 if (NULL != gorc->order) 614 { 615 TALER_MERCHANT_order_free (gorc->order); 616 gorc->order = NULL; 617 } 618 gorc->ct = NULL; /* avoid dangling pointer */ 619 if (NULL != gorc->wire_details) 620 json_decref (gorc->wire_details); 621 if (NULL != gorc->refund_details) 622 json_decref (gorc->refund_details); 623 if (NULL != gorc->refunds_external) 624 json_decref (gorc->refunds_external); 625 if (NULL != gorc->tt) 626 { 627 GNUNET_SCHEDULER_cancel (gorc->tt); 628 gorc->tt = NULL; 629 } 630 if (NULL != gorc->eh) 631 { 632 TALER_MERCHANTDB_event_listen_cancel (gorc->eh); 633 gorc->eh = NULL; 634 } 635 if (NULL != gorc->session_eh) 636 { 637 TALER_MERCHANTDB_event_listen_cancel (gorc->session_eh); 638 gorc->session_eh = NULL; 639 } 640 GNUNET_free (gorc); 641 } 642 643 644 /** 645 * Processing the request @a gorc is finished, set the 646 * final return value in phase based on @a mret. 647 * 648 * @param[in,out] gorc order context to initialize 649 * @param mret MHD HTTP response status to return 650 */ 651 static void 652 phase_end (struct GetOrderRequestContext *gorc, 653 enum MHD_Result mret) 654 { 655 gorc->phase = (MHD_YES == mret) 656 ? GOP_END_YES 657 : GOP_END_NO; 658 } 659 660 661 /** 662 * Initialize event callbacks for the order processing. 663 * 664 * @param[in,out] gorc order context to initialize 665 */ 666 static void 667 phase_init (struct GetOrderRequestContext *gorc) 668 { 669 struct TMH_HandlerContext *hc = gorc->hc; 670 struct TMH_OrderPayEventP pay_eh = { 671 .header.size = htons (sizeof (pay_eh)), 672 .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED), 673 .merchant_pub = hc->instance->merchant_pub 674 }; 675 676 if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout)) 677 { 678 gorc->phase++; 679 return; 680 } 681 682 GNUNET_CRYPTO_hash (hc->infix, 683 strlen (hc->infix), 684 &pay_eh.h_order_id); 685 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 686 "Subscribing to payment triggers for %p\n", 687 gorc); 688 gorc->eh = TALER_MERCHANTDB_event_listen ( 689 TMH_db, 690 &pay_eh.header, 691 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 692 &resume_by_event, 693 gorc); 694 gorc->phase++; 695 } 696 697 698 /** 699 * Obtain latest contract terms from the database. 700 * 701 * @param[in,out] gorc order context to update 702 */ 703 static void 704 phase_fetch_contract (struct GetOrderRequestContext *gorc) 705 { 706 struct TMH_HandlerContext *hc = gorc->hc; 707 enum GNUNET_DB_QueryStatus qs; 708 709 if (NULL != gorc->contract_terms_json) 710 { 711 /* Free memory filled with old contract terms before fetching the latest 712 ones from the DB. Note that we cannot simply skip the database 713 interaction as the contract terms loaded previously might be from an 714 earlier *unclaimed* order state (which we loaded in a previous 715 invocation of this function and we are back here due to long polling) 716 and thus the contract terms could have changed during claiming. Thus, 717 we need to fetch the latest contract terms from the DB again. */ 718 json_decref (gorc->contract_terms_json); 719 gorc->contract_terms_json = NULL; 720 gorc->order_only = false; 721 } 722 TALER_MERCHANTDB_preflight (TMH_db); 723 qs = TALER_MERCHANTDB_get_contract_terms_status (TMH_db, 724 hc->instance->settings.id, 725 hc->infix, 726 gorc->session_id, 727 &gorc->contract_terms_json, 728 &gorc->order_serial, 729 &gorc->paid, 730 &gorc->wired, 731 &gorc->paid_session_matches, 732 &gorc->claim_token, 733 &gorc->choice_index); 734 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 735 "get_contract_terms (%s) returned %d\n", 736 hc->infix, 737 (int) qs); 738 if (0 > qs) 739 { 740 /* single, read-only SQL statements should never cause 741 serialization problems */ 742 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 743 /* Always report on hard error as well to enable diagnostics */ 744 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 745 phase_end (gorc, 746 TALER_MHD_reply_with_error (gorc->sc.con, 747 MHD_HTTP_INTERNAL_SERVER_ERROR, 748 TALER_EC_GENERIC_DB_FETCH_FAILED, 749 "contract terms")); 750 return; 751 } 752 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 753 { 754 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 755 "Order %s is %s (%s) according to database, choice %d\n", 756 hc->infix, 757 gorc->paid ? "paid" : "unpaid", 758 gorc->wired ? "wired" : "unwired", 759 (int) gorc->choice_index); 760 gorc->phase++; 761 return; 762 } 763 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs); 764 GNUNET_assert (! gorc->paid); 765 /* No contract, only order, fetch from orders table */ 766 gorc->order_only = true; 767 { 768 struct TALER_MerchantPostDataHashP unused; 769 770 /* We need the order for two cases: Either when the contract doesn't exist yet, 771 * or when the order is claimed but unpaid, and we need the claim token. */ 772 qs = TALER_MERCHANTDB_get_order (TMH_db, 773 hc->instance->settings.id, 774 hc->infix, 775 &gorc->claim_token, 776 &unused, 777 &gorc->contract_terms_json); 778 } 779 if (0 > qs) 780 { 781 /* single, read-only SQL statements should never cause 782 serialization problems */ 783 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 784 /* Always report on hard error as well to enable diagnostics */ 785 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 786 phase_end (gorc, 787 TALER_MHD_reply_with_error (gorc->sc.con, 788 MHD_HTTP_INTERNAL_SERVER_ERROR, 789 TALER_EC_GENERIC_DB_FETCH_FAILED, 790 "order")); 791 return; 792 } 793 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 794 { 795 phase_end (gorc, 796 TALER_MHD_reply_with_error (gorc->sc.con, 797 MHD_HTTP_NOT_FOUND, 798 TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, 799 hc->infix)); 800 return; 801 } 802 gorc->phase++; 803 } 804 805 806 /** 807 * Obtain parse contract terms of the order. Extracts the fulfillment URL, 808 * total amount, summary and timestamp from the contract terms! 809 * 810 * @param[in,out] gorc order context to update 811 */ 812 static void 813 phase_parse_contract (struct GetOrderRequestContext *gorc) 814 { 815 struct TMH_HandlerContext *hc = gorc->hc; 816 817 if ( (NULL == gorc->order) && 818 (NULL != gorc->order_json) ) 819 { 820 gorc->order = TALER_MERCHANT_order_parse ( 821 gorc->order_json); 822 823 if (NULL == gorc->order) 824 { 825 GNUNET_break (0); 826 phase_end (gorc, 827 TALER_MHD_reply_with_error ( 828 gorc->sc.con, 829 MHD_HTTP_INTERNAL_SERVER_ERROR, 830 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 831 hc->infix)); 832 return; 833 } 834 gorc->ct = gorc->order->base; 835 gorc->timestamp = gorc->order->timestamp; 836 } 837 if ( (NULL == gorc->contract_terms) && 838 (NULL != gorc->contract_terms_json) ) 839 { 840 if (NULL == 841 json_object_get (gorc->contract_terms_json, 842 "nonce")) 843 { 844 /* only have a proto contract */ 845 gorc->pc = TALER_MERCHANT_proto_contract_parse ( 846 gorc->contract_terms_json); 847 848 if (NULL == gorc->pc) 849 { 850 GNUNET_break (0); 851 phase_end (gorc, 852 TALER_MHD_reply_with_error ( 853 gorc->sc.con, 854 MHD_HTTP_INTERNAL_SERVER_ERROR, 855 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 856 hc->infix)); 857 return; 858 } 859 gorc->ct = gorc->pc->base; 860 gorc->timestamp = gorc->pc->timestamp; 861 } 862 else 863 { 864 gorc->contract_terms = TALER_MERCHANT_contract_parse ( 865 gorc->contract_terms_json); 866 if (NULL == gorc->contract_terms) 867 { 868 GNUNET_break (0); 869 phase_end (gorc, 870 TALER_MHD_reply_with_error ( 871 gorc->sc.con, 872 MHD_HTTP_INTERNAL_SERVER_ERROR, 873 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 874 hc->infix)); 875 return; 876 } 877 gorc->pc = gorc->contract_terms->pc; 878 gorc->ct = gorc->pc->base; 879 gorc->timestamp = gorc->contract_terms->pc->timestamp; 880 } 881 } 882 883 /* Now that the contract terms (and thus the fulfillment URL) are 884 available, subscribe to session-capture triggers if requested. 885 The @e session_eh guard makes this idempotent across the phase 886 re-runs of the long-polling loop; @e gorc->session_eh is cancelled 887 in gorc_cleanup(). */ 888 if ( (NULL != gorc->session_id) && 889 (NULL != gorc->ct->fulfillment_url) && 890 (NULL == gorc->session_eh) ) 891 { 892 struct TMH_SessionEventP session_eh = { 893 .header.size = htons (sizeof (session_eh)), 894 .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED), 895 .merchant_pub = hc->instance->merchant_pub 896 }; 897 898 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 899 "Subscribing to session triggers for %p\n", 900 gorc); 901 GNUNET_CRYPTO_hash (gorc->session_id, 902 strlen (gorc->session_id), 903 &session_eh.h_session_id); 904 GNUNET_CRYPTO_hash (gorc->ct->fulfillment_url, 905 strlen (gorc->ct->fulfillment_url), 906 &session_eh.h_fulfillment_url); 907 gorc->session_eh 908 = TALER_MERCHANTDB_event_listen ( 909 TMH_db, 910 &session_eh.header, 911 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 912 &resume_by_event, 913 gorc); 914 gorc->instant_retry = true; 915 } 916 917 switch (gorc->ct->version) 918 { 919 case TALER_MERCHANT_CONTRACT_VERSION_0: 920 gorc->contract_amount 921 = (NULL != gorc->pc) 922 ? gorc->pc->details.v0.brutto 923 : gorc->order->details.v0.brutto; 924 gorc->max_fee 925 = (NULL != gorc->pc) 926 ? gorc->pc->details.v0.max_fee 927 : gorc->order->details.v0.max_fee; 928 gorc->has_max_fee 929 = (NULL != gorc->pc) 930 ? true 931 : ! gorc->order->details.v0.no_max_fee; 932 break; 933 case TALER_MERCHANT_CONTRACT_VERSION_1: 934 if (gorc->choice_index >= 0) 935 { 936 if (gorc->choice_index >= 937 ( (NULL != gorc->pc) 938 ? gorc->pc->details.v1.choices_len 939 : gorc->order->details.v1.choices_len) ) 940 { 941 GNUNET_break (0); 942 phase_end (gorc, 943 TALER_MHD_reply_with_error ( 944 gorc->sc.con, 945 MHD_HTTP_INTERNAL_SERVER_ERROR, 946 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 947 NULL)); 948 return; 949 } 950 gorc->contract_amount 951 = (NULL != gorc->pc) 952 ? gorc->pc->details.v1.choices[gorc->choice_index].amount 953 : gorc->order->details.v1.choices[gorc->choice_index].amount; 954 gorc->max_fee 955 = (NULL != gorc->pc) 956 ? gorc->pc->details.v1.choices[gorc->choice_index].max_fee 957 : gorc->order->details.v1.choices[gorc->choice_index].max_fee; 958 gorc->has_max_fee 959 = (NULL != gorc->pc) 960 ? true 961 : ! gorc->order->details.v1.choices[gorc->choice_index].no_max_fee; 962 } 963 else 964 { 965 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 966 "Choice index %i is invalid", 967 gorc->choice_index); 968 } 969 break; 970 default: 971 { 972 GNUNET_break (0); 973 phase_end (gorc, 974 TALER_MHD_reply_with_error ( 975 gorc->sc.con, 976 MHD_HTTP_INTERNAL_SERVER_ERROR, 977 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION, 978 NULL)); 979 return; 980 } 981 } 982 983 if ( (NULL != gorc->contract_terms) && 984 (GNUNET_OK != 985 TALER_JSON_contract_hash (gorc->contract_terms_json, 986 &gorc->h_contract_terms)) ) 987 { 988 GNUNET_break (0); 989 phase_end (gorc, 990 TALER_MHD_reply_with_error (gorc->sc.con, 991 MHD_HTTP_INTERNAL_SERVER_ERROR, 992 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 993 NULL)); 994 return; 995 } 996 GNUNET_assert ( (NULL != gorc->contract_terms_json) || 997 (NULL != gorc->order_json) ); 998 GNUNET_assert ( (NULL != gorc->pc) || 999 (NULL != gorc->order) ); 1000 gorc->phase++; 1001 } 1002 1003 1004 /** 1005 * Check payment status of the order. 1006 * 1007 * @param[in,out] gorc order context to update 1008 */ 1009 static void 1010 phase_check_paid (struct GetOrderRequestContext *gorc) 1011 { 1012 struct TMH_HandlerContext *hc = gorc->hc; 1013 1014 if (gorc->order_only) 1015 { 1016 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1017 "Order %s unclaimed, no need to lookup payment status\n", 1018 hc->infix); 1019 GNUNET_assert (! gorc->paid); 1020 GNUNET_assert (! gorc->wired); 1021 gorc->phase++; 1022 return; 1023 } 1024 if (NULL == gorc->session_id) 1025 { 1026 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1027 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n", 1028 gorc->paid ? "paid" : "unpaid", 1029 gorc->wired ? "wired" : "unwired"); 1030 gorc->phase++; 1031 return; 1032 } 1033 if (! gorc->paid_session_matches) 1034 { 1035 gorc->paid = false; 1036 gorc->wired = false; 1037 } 1038 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1039 "Order %s %s for session %s (%s)\n", 1040 hc->infix, 1041 gorc->paid ? "paid" : "unpaid", 1042 gorc->session_id, 1043 gorc->wired ? "wired" : "unwired"); 1044 gorc->phase++; 1045 } 1046 1047 1048 /** 1049 * Check if the @a reply satisfies the long-poll not_etag 1050 * constraint. If so, return it as a response for @a gorc, 1051 * otherwise suspend and wait for a change. 1052 * 1053 * @param[in,out] gorc request to handle 1054 * @param reply body for JSON response (#MHD_HTTP_OK) 1055 */ 1056 static void 1057 check_reply (struct GetOrderRequestContext *gorc, 1058 const json_t *reply) 1059 { 1060 struct GNUNET_ShortHashCode sh; 1061 unsigned int http_response_code; 1062 bool not_modified; 1063 struct MHD_Response *response; 1064 char *can; 1065 1066 can = TALER_JSON_canonicalize (reply); 1067 GNUNET_assert (GNUNET_YES == 1068 GNUNET_CRYPTO_hkdf_gnunet (&sh, 1069 sizeof (sh), 1070 "GOR-SALT", 1071 strlen ("GOR-SALT"), 1072 can, 1073 strlen (can))); 1074 not_modified = gorc->have_lp_not_etag && 1075 (0 == GNUNET_memcmp (&sh, 1076 &gorc->lp_not_etag)); 1077 1078 if (not_modified && 1079 (! GNUNET_TIME_absolute_is_past (gorc->sc.long_poll_timeout)) ) 1080 { 1081 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1082 "Status unchanged, not returning response yet\n"); 1083 GNUNET_assert (GNUNET_NO == gorc->suspended); 1084 if (gorc->instant_retry) 1085 { 1086 gorc->instant_retry = false; 1087 gorc->phase = GOP_FETCH_CONTRACT; 1088 GNUNET_free (can); 1089 return; 1090 } 1091 /* note: not necessarily actually unpaid ... */ 1092 GNUNET_CONTAINER_DLL_insert (gorc_head, 1093 gorc_tail, 1094 gorc); 1095 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1096 gorc->suspended = GNUNET_YES; 1097 MHD_suspend_connection (gorc->sc.con); 1098 GNUNET_free (can); 1099 return; 1100 } 1101 { 1102 const char *inm; 1103 1104 inm = MHD_lookup_connection_value (gorc->sc.con, 1105 MHD_HEADER_KIND, 1106 MHD_HTTP_HEADER_IF_NONE_MATCH); 1107 if ( (NULL == inm) || 1108 ('"' != inm[0]) || 1109 ('"' != inm[strlen (inm) - 1]) || 1110 (0 != strncmp (inm + 1, 1111 can, 1112 strlen (can))) ) 1113 not_modified = false; /* must return full response */ 1114 } 1115 GNUNET_free (can); 1116 http_response_code = not_modified 1117 ? MHD_HTTP_NOT_MODIFIED 1118 : MHD_HTTP_OK; 1119 response = TALER_MHD_make_json (reply); 1120 { 1121 char *etag; 1122 char *qetag; 1123 1124 etag = GNUNET_STRINGS_data_to_string_alloc (&sh, 1125 sizeof (sh)); 1126 GNUNET_asprintf (&qetag, 1127 "\"%s\"", 1128 etag); 1129 GNUNET_break (MHD_YES == 1130 MHD_add_response_header (response, 1131 MHD_HTTP_HEADER_ETAG, 1132 qetag)); 1133 GNUNET_free (qetag); 1134 GNUNET_free (etag); 1135 } 1136 1137 { 1138 enum MHD_Result ret; 1139 1140 ret = MHD_queue_response (gorc->sc.con, 1141 http_response_code, 1142 response); 1143 MHD_destroy_response (response); 1144 phase_end (gorc, 1145 ret); 1146 } 1147 } 1148 1149 1150 /** 1151 * Check if re-purchase detection applies to the order. 1152 * 1153 * @param[in,out] gorc order context to update 1154 */ 1155 static void 1156 phase_check_repurchase (struct GetOrderRequestContext *gorc) 1157 { 1158 struct TMH_HandlerContext *hc = gorc->hc; 1159 char *already_paid_order_id = NULL; 1160 enum GNUNET_DB_QueryStatus qs; 1161 char *taler_pay_uri; 1162 char *order_status_url; 1163 json_t *reply; 1164 1165 if ( (gorc->paid) || 1166 (NULL == gorc->ct->fulfillment_url) || 1167 (NULL == gorc->session_id) ) 1168 { 1169 /* Repurchase cannot apply */ 1170 gorc->phase++; 1171 return; 1172 } 1173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1174 "Running re-purchase detection for %s/%s\n", 1175 gorc->session_id, 1176 gorc->ct->fulfillment_url); 1177 qs = TALER_MERCHANTDB_get_order_by_fulfillment ( 1178 TMH_db, 1179 hc->instance->settings.id, 1180 gorc->ct->fulfillment_url, 1181 gorc->session_id, 1182 TALER_EXCHANGE_YNA_NO != 1183 gorc->allow_refunded_for_repurchase, 1184 &already_paid_order_id); 1185 if (0 > qs) 1186 { 1187 /* single, read-only SQL statements should never cause 1188 serialization problems, and the entry should exist as per above */ 1189 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1190 phase_end (gorc, 1191 TALER_MHD_reply_with_error (gorc->sc.con, 1192 MHD_HTTP_INTERNAL_SERVER_ERROR, 1193 TALER_EC_GENERIC_DB_FETCH_FAILED, 1194 "order by fulfillment")); 1195 return; 1196 } 1197 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1198 { 1199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1200 "No already paid order for %s/%s\n", 1201 gorc->session_id, 1202 gorc->ct->fulfillment_url); 1203 gorc->phase++; 1204 return; 1205 } 1206 1207 /* User did pay for this order, but under a different session; ask wallet to 1208 switch order ID */ 1209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1210 "Found already paid order %s\n", 1211 already_paid_order_id); 1212 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1213 hc->infix, 1214 gorc->session_id, 1215 hc->instance->settings.id, 1216 &gorc->claim_token); 1217 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1218 hc->infix, 1219 gorc->session_id, 1220 hc->instance->settings.id, 1221 &gorc->claim_token, 1222 NULL); 1223 if ( (NULL == taler_pay_uri) || 1224 (NULL == order_status_url) ) 1225 { 1226 GNUNET_break_op (0); 1227 GNUNET_free (taler_pay_uri); 1228 GNUNET_free (order_status_url); 1229 phase_end (gorc, 1230 TALER_MHD_reply_with_error (gorc->sc.con, 1231 MHD_HTTP_BAD_REQUEST, 1232 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 1233 "host")); 1234 return; 1235 } 1236 reply = GNUNET_JSON_PACK ( 1237 GNUNET_JSON_pack_string ("taler_pay_uri", 1238 taler_pay_uri), 1239 GNUNET_JSON_pack_string ("order_status_url", 1240 order_status_url), 1241 GNUNET_JSON_pack_string ("order_status", 1242 "unpaid"), 1243 GNUNET_JSON_pack_string ("already_paid_order_id", 1244 already_paid_order_id), 1245 GNUNET_JSON_pack_string ("already_paid_fulfillment_url", 1246 gorc->ct->fulfillment_url), 1247 /* undefined for unpaid v1 contracts */ 1248 GNUNET_JSON_pack_allow_null ( 1249 TALER_JSON_pack_amount ("total_amount", 1250 TALER_amount_is_valid (&gorc->contract_amount) 1251 ? &gorc->contract_amount 1252 : NULL)), 1253 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1254 gorc->contract_terms_json), 1255 GNUNET_JSON_pack_string ("summary", 1256 gorc->ct->summary), 1257 GNUNET_JSON_pack_timestamp ("pay_deadline", 1258 NULL != gorc->pc 1259 ? gorc->pc->pay_deadline 1260 : gorc->order->pay_deadline), 1261 GNUNET_JSON_pack_timestamp ("creation_time", 1262 gorc->timestamp)); 1263 1264 GNUNET_free (order_status_url); 1265 GNUNET_free (taler_pay_uri); 1266 GNUNET_free (already_paid_order_id); 1267 check_reply (gorc, 1268 reply); 1269 json_decref (reply); 1270 } 1271 1272 1273 /** 1274 * Check if we should suspend until the order is paid. 1275 * 1276 * @param[in,out] gorc order context to update 1277 */ 1278 static void 1279 phase_unpaid_finish (struct GetOrderRequestContext *gorc) 1280 { 1281 struct TMH_HandlerContext *hc = gorc->hc; 1282 char *order_status_url; 1283 1284 if (gorc->paid) 1285 { 1286 gorc->phase++; 1287 return; 1288 } 1289 /* User never paid for this order, suspend waiting 1290 on payment or return details. */ 1291 if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout) && 1292 (! gorc->have_lp_not_etag) ) 1293 { 1294 if (gorc->instant_retry) 1295 { 1296 gorc->instant_retry = false; 1297 gorc->phase = GOP_FETCH_CONTRACT; 1298 return; 1299 } 1300 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1301 "Suspending GET /private/orders/%s\n", 1302 hc->infix); 1303 GNUNET_CONTAINER_DLL_insert (gorc_head, 1304 gorc_tail, 1305 gorc); 1306 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1307 gorc->suspended = GNUNET_YES; 1308 MHD_suspend_connection (gorc->sc.con); 1309 return; 1310 } 1311 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1312 hc->infix, 1313 gorc->session_id, 1314 hc->instance->settings.id, 1315 &gorc->claim_token, 1316 NULL); 1317 if (! gorc->order_only) 1318 { 1319 json_t *reply; 1320 1321 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1322 "Order %s claimed but not paid yet\n", 1323 hc->infix); 1324 reply = GNUNET_JSON_PACK ( 1325 GNUNET_JSON_pack_string ("order_status_url", 1326 order_status_url), 1327 GNUNET_JSON_pack_object_incref ("contract_terms", 1328 gorc->contract_terms_json), 1329 GNUNET_JSON_pack_string ("order_status", 1330 "claimed")); 1331 GNUNET_free (order_status_url); 1332 check_reply (gorc, 1333 reply); 1334 json_decref (reply); 1335 return; 1336 } 1337 { 1338 char *taler_pay_uri; 1339 json_t *reply; 1340 1341 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1342 hc->infix, 1343 gorc->session_id, 1344 hc->instance->settings.id, 1345 &gorc->claim_token); 1346 reply = GNUNET_JSON_PACK ( 1347 GNUNET_JSON_pack_string ("taler_pay_uri", 1348 taler_pay_uri), 1349 GNUNET_JSON_pack_string ("order_status_url", 1350 order_status_url), 1351 GNUNET_JSON_pack_string ("order_status", 1352 "unpaid"), 1353 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1354 gorc->contract_terms_json), 1355 /* undefined for unpaid v1 contracts */ 1356 GNUNET_JSON_pack_allow_null ( 1357 TALER_JSON_pack_amount ("total_amount", 1358 &gorc->contract_amount)), 1359 GNUNET_JSON_pack_string ("summary", 1360 gorc->ct->summary), 1361 GNUNET_JSON_pack_timestamp ("creation_time", 1362 gorc->timestamp)); 1363 check_reply (gorc, 1364 reply); 1365 json_decref (reply); 1366 GNUNET_free (taler_pay_uri); 1367 } 1368 GNUNET_free (order_status_url); 1369 } 1370 1371 1372 /** 1373 * Function called with each @a coin_pub that was deposited into the 1374 * @a h_wire account of the merchant for the @a deposit_serial as part 1375 * of the payment for the order identified by @a cls. 1376 * 1377 * Queries the exchange for the payment status associated with the 1378 * given coin. 1379 * 1380 * @param cls a `struct GetOrderRequestContext` 1381 * @param deposit_serial identifies the deposit operation 1382 * @param exchange_url URL of the exchange that issued @a coin_pub 1383 * @param h_wire hash of the merchant's wire account into which the deposit was made 1384 * @param deposit_timestamp when was the deposit made 1385 * @param amount_with_fee amount the exchange will deposit for this coin 1386 * @param deposit_fee fee the exchange will charge for this coin 1387 * @param coin_pub public key of the deposited coin 1388 */ 1389 static void 1390 deposit_cb ( 1391 void *cls, 1392 uint64_t deposit_serial, 1393 const char *exchange_url, 1394 const struct TALER_MerchantWireHashP *h_wire, 1395 struct GNUNET_TIME_Timestamp deposit_timestamp, 1396 const struct TALER_Amount *amount_with_fee, 1397 const struct TALER_Amount *deposit_fee, 1398 const struct TALER_CoinSpendPublicKeyP *coin_pub) 1399 { 1400 struct GetOrderRequestContext *gorc = cls; 1401 struct TransferQuery *tq; 1402 1403 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1404 "Checking deposit status for coin %s (over %s)\n", 1405 TALER_B2S (coin_pub), 1406 TALER_amount2s (amount_with_fee)); 1407 if ( (GNUNET_OK != 1408 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1409 amount_with_fee)) || 1410 (GNUNET_OK != 1411 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1412 deposit_fee)) ) 1413 { 1414 GNUNET_break (0); 1415 gorc->deposit_currency_mismatch = true; 1416 return; 1417 } 1418 gorc->last_payment 1419 = GNUNET_TIME_timestamp_max (gorc->last_payment, 1420 deposit_timestamp); 1421 tq = GNUNET_new (struct TransferQuery); 1422 tq->gorc = gorc; 1423 tq->exchange_url = GNUNET_strdup (exchange_url); 1424 tq->deposit_serial = deposit_serial; 1425 GNUNET_CONTAINER_DLL_insert (gorc->tq_head, 1426 gorc->tq_tail, 1427 tq); 1428 tq->coin_pub = *coin_pub; 1429 tq->h_wire = *h_wire; 1430 tq->amount_with_fee = *amount_with_fee; 1431 tq->deposit_fee = *deposit_fee; 1432 GNUNET_assert (GNUNET_OK == 1433 TALER_amount_set_zero (amount_with_fee->currency, 1434 &tq->refunded_total)); 1435 GNUNET_assert (0 <= 1436 TALER_amount_add (&gorc->deposit_fees_total, 1437 &gorc->deposit_fees_total, 1438 deposit_fee)); 1439 } 1440 1441 1442 /** 1443 * Check wire transfer status for the order at the exchange. 1444 * 1445 * @param[in,out] gorc order context to update 1446 */ 1447 static void 1448 phase_check_deposits (struct GetOrderRequestContext *gorc) 1449 { 1450 GNUNET_assert (! gorc->order_only); 1451 GNUNET_assert (gorc->paid); 1452 1453 /* amount must be always valid for paid orders */ 1454 GNUNET_assert (GNUNET_OK == 1455 TALER_amount_is_valid (&gorc->contract_amount)); 1456 /* We may be running again due to long-polling; 1457 clear left-overs from previous iterations */ 1458 clear_transfer_queries (gorc); 1459 GNUNET_assert (GNUNET_OK == 1460 TALER_amount_set_zero (gorc->contract_amount.currency, 1461 &gorc->deposits_total)); 1462 GNUNET_assert (GNUNET_OK == 1463 TALER_amount_set_zero (gorc->contract_amount.currency, 1464 &gorc->deposit_fees_total)); 1465 GNUNET_assert (GNUNET_OK == 1466 TALER_amount_set_zero (gorc->contract_amount.currency, 1467 &gorc->deposit_fees_refunded_total)); 1468 TALER_MERCHANTDB_iterate_deposits_by_order (TMH_db, 1469 gorc->order_serial, 1470 &deposit_cb, 1471 gorc); 1472 if (gorc->deposit_currency_mismatch) 1473 { 1474 GNUNET_break (0); 1475 phase_end (gorc, 1476 TALER_MHD_reply_with_error ( 1477 gorc->sc.con, 1478 MHD_HTTP_INTERNAL_SERVER_ERROR, 1479 TALER_EC_GENERIC_DB_FETCH_FAILED, 1480 "deposits in different currency than original order price")); 1481 return; 1482 } 1483 gorc->phase++; 1484 } 1485 1486 1487 /** 1488 * Function called with information about a refund. 1489 * It is responsible for summing up the refund amount. 1490 * 1491 * @param cls closure 1492 * @param refund_serial unique serial number of the refund 1493 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 1494 * @param coin_pub public coin from which the refund comes from 1495 * @param exchange_url URL of the exchange that issued @a coin_pub 1496 * @param rtransaction_id identificator of the refund 1497 * @param reason human-readable explanation of the refund 1498 * @param refund_amount refund amount which is being taken from @a coin_pub 1499 * @param pending true if the this refund was not yet processed by the wallet/exchange 1500 */ 1501 static void 1502 process_refunds_cb ( 1503 void *cls, 1504 uint64_t refund_serial, 1505 struct GNUNET_TIME_Timestamp timestamp, 1506 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1507 const char *exchange_url, 1508 uint64_t rtransaction_id, 1509 const char *reason, 1510 const struct TALER_Amount *refund_amount, 1511 bool pending) 1512 { 1513 struct GetOrderRequestContext *gorc = cls; 1514 1515 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1516 "Found refund %llu over %s for reason %s\n", 1517 (unsigned long long) rtransaction_id, 1518 TALER_amount2s (refund_amount), 1519 reason); 1520 GNUNET_assert ( 1521 0 == 1522 json_array_append_new ( 1523 gorc->refund_details, 1524 GNUNET_JSON_PACK ( 1525 TALER_JSON_pack_amount ("amount", 1526 refund_amount), 1527 GNUNET_JSON_pack_bool ("pending", 1528 pending), 1529 GNUNET_JSON_pack_timestamp ("timestamp", 1530 timestamp), 1531 GNUNET_JSON_pack_string ("reason", 1532 reason)))); 1533 /* Add up the refunds per coin; #phase_check_refunds() checks if 1534 the total is high enough to void the deposit fee. */ 1535 for (struct TransferQuery *tq = gorc->tq_head; 1536 NULL != tq; 1537 tq = tq->next) 1538 { 1539 if (0 != 1540 strcmp (exchange_url, 1541 tq->exchange_url)) 1542 continue; 1543 if (0 != 1544 GNUNET_memcmp (&tq->coin_pub, 1545 coin_pub)) 1546 continue; 1547 if (GNUNET_OK != 1548 TALER_amount_cmp_currency ( 1549 &tq->refunded_total, 1550 refund_amount)) 1551 { 1552 gorc->refund_currency_mismatch = true; 1553 return; 1554 } 1555 GNUNET_assert ( 1556 0 <= 1557 TALER_amount_add (&tq->refunded_total, 1558 &tq->refunded_total, 1559 refund_amount)); 1560 } 1561 if (GNUNET_OK != 1562 TALER_amount_cmp_currency ( 1563 &gorc->refund_amount, 1564 refund_amount)) 1565 { 1566 gorc->refund_currency_mismatch = true; 1567 return; 1568 } 1569 GNUNET_assert (0 <= 1570 TALER_amount_add (&gorc->refund_amount, 1571 &gorc->refund_amount, 1572 refund_amount)); 1573 gorc->refunded = true; 1574 gorc->refund_pending |= pending; 1575 } 1576 1577 1578 /** 1579 * Function called with information about an external refund. 1580 * Appends the refund entry to the "refunds_external" array. 1581 * 1582 * @param cls a `struct GetOrderRequestContext` 1583 * @param refund_id identifier of the refund within the order 1584 * @param refund_timestamp when was the refund recorded 1585 * @param method external payment method used for the refund 1586 * @param payment_id id of the original external payment entry 1587 * @param amount amount returned to the customer 1588 * @param reason human-readable refund justification 1589 */ 1590 static void 1591 process_external_refunds_cb (void *cls, 1592 const char *refund_id, 1593 struct GNUNET_TIME_Timestamp refund_timestamp, 1594 const char *method, 1595 const char *payment_id, 1596 const struct TALER_Amount *amount, 1597 const char *reason) 1598 { 1599 struct GetOrderRequestContext *gorc = cls; 1600 1601 GNUNET_assert ( 1602 0 == 1603 json_array_append_new ( 1604 gorc->refunds_external, 1605 GNUNET_JSON_PACK ( 1606 GNUNET_JSON_pack_string ("id", 1607 refund_id), 1608 GNUNET_JSON_pack_string ("method", 1609 method), 1610 GNUNET_JSON_pack_allow_null ( 1611 GNUNET_JSON_pack_string ("payment_id", 1612 payment_id)), 1613 TALER_JSON_pack_amount ("amount", 1614 amount), 1615 GNUNET_JSON_pack_string ("reason", 1616 reason), 1617 GNUNET_JSON_pack_timestamp ("timestamp", 1618 refund_timestamp)))); 1619 } 1620 1621 1622 /** 1623 * Check refund status for the order. 1624 * 1625 * @param[in,out] gorc order context to update 1626 */ 1627 static void 1628 phase_check_refunds (struct GetOrderRequestContext *gorc) 1629 { 1630 struct TMH_HandlerContext *hc = gorc->hc; 1631 enum GNUNET_DB_QueryStatus qs; 1632 1633 GNUNET_assert (! gorc->order_only); 1634 GNUNET_assert (gorc->paid); 1635 1636 /* Accumulate refunds, if any. */ 1637 GNUNET_assert (GNUNET_OK == 1638 TALER_amount_set_zero (gorc->contract_amount.currency, 1639 &gorc->refund_amount)); 1640 json_array_clear (gorc->refund_details); 1641 qs = TALER_MERCHANTDB_iterate_refunds_detailed ( 1642 TMH_db, 1643 hc->instance->settings.id, 1644 &gorc->h_contract_terms, 1645 &process_refunds_cb, 1646 gorc); 1647 if (0 > qs) 1648 { 1649 GNUNET_break (0); 1650 phase_end (gorc, 1651 TALER_MHD_reply_with_error ( 1652 gorc->sc.con, 1653 MHD_HTTP_INTERNAL_SERVER_ERROR, 1654 TALER_EC_GENERIC_DB_FETCH_FAILED, 1655 "detailed refunds")); 1656 return; 1657 } 1658 if (gorc->refund_currency_mismatch) 1659 { 1660 GNUNET_break (0); 1661 phase_end (gorc, 1662 TALER_MHD_reply_with_error ( 1663 gorc->sc.con, 1664 MHD_HTTP_INTERNAL_SERVER_ERROR, 1665 TALER_EC_GENERIC_DB_FETCH_FAILED, 1666 "refunds in different currency than original order price")); 1667 return; 1668 } 1669 /* Exchange cancels deposit fees of fully refunded coins, check for that case */ 1670 for (struct TransferQuery *tq = gorc->tq_head; 1671 NULL != tq; 1672 tq = tq->next) 1673 { 1674 if (0 != 1675 TALER_amount_cmp (&tq->refunded_total, 1676 &tq->amount_with_fee)) 1677 continue; /* not refunded in full, we still owe the deposit fee */ 1678 GNUNET_assert ( 1679 0 <= 1680 TALER_amount_add (&gorc->deposit_fees_refunded_total, 1681 &gorc->deposit_fees_refunded_total, 1682 &tq->deposit_fee)); 1683 } 1684 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1685 "Total refunds are %s\n", 1686 TALER_amount2s (&gorc->refund_amount)); 1687 json_array_clear (gorc->refunds_external); 1688 qs = TALER_MERCHANTDB_iterate_external_refunds ( 1689 TMH_db, 1690 hc->instance->settings.id, 1691 hc->infix, 1692 &process_external_refunds_cb, 1693 gorc); 1694 if (0 > qs) 1695 { 1696 GNUNET_break (0); 1697 phase_end (gorc, 1698 TALER_MHD_reply_with_error ( 1699 gorc->sc.con, 1700 MHD_HTTP_INTERNAL_SERVER_ERROR, 1701 TALER_EC_GENERIC_DB_FETCH_FAILED, 1702 "external refunds")); 1703 return; 1704 } 1705 gorc->phase++; 1706 } 1707 1708 1709 /** 1710 * Function called with available wire details, to be added to 1711 * the response. 1712 * 1713 * @param cls a `struct GetOrderRequestContext` 1714 * @param wtid wire transfer subject of the wire transfer for the coin 1715 * @param exchange_url base URL of the exchange that made the payment 1716 * @param execution_time when was the payment made 1717 * @param deposit_value contribution of the coin to the total wire transfer value 1718 * @param deposit_fee deposit fee charged by the exchange for the coin 1719 * @param transfer_confirmed did the merchant confirm that a wire transfer with 1720 * @a wtid over the total amount happened? 1721 * @param expected_credit_serial row for the expected wire transfer this 1722 * entry references 1723 */ 1724 static void 1725 process_transfer_details ( 1726 void *cls, 1727 const struct TALER_WireTransferIdentifierRawP *wtid, 1728 const char *exchange_url, 1729 struct GNUNET_TIME_Timestamp execution_time, 1730 const struct TALER_Amount *deposit_value, 1731 const struct TALER_Amount *deposit_fee, 1732 bool transfer_confirmed, 1733 uint64_t expected_credit_serial) 1734 { 1735 struct GetOrderRequestContext *gorc = cls; 1736 json_t *wire_details = gorc->wire_details; 1737 struct TALER_Amount wired; 1738 1739 if ( (GNUNET_OK != 1740 TALER_amount_cmp_currency (&gorc->deposits_total, 1741 deposit_value)) || 1742 (GNUNET_OK != 1743 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1744 deposit_fee)) ) 1745 { 1746 GNUNET_break (0); 1747 gorc->deposit_currency_mismatch = true; 1748 return; 1749 } 1750 1751 /* Compute total amount *wired*. Note that @e deposit_fees_total is 1752 deliberately not touched here: it covers every coin of the order, 1753 while this callback only sees the coins whose wire transfer we 1754 already know about. */ 1755 GNUNET_assert (0 <= 1756 TALER_amount_add (&gorc->deposits_total, 1757 &gorc->deposits_total, 1758 deposit_value)); 1759 GNUNET_assert (0 <= TALER_amount_subtract (&wired, 1760 deposit_value, 1761 deposit_fee)); 1762 GNUNET_assert (0 == 1763 json_array_append_new ( 1764 wire_details, 1765 GNUNET_JSON_PACK ( 1766 GNUNET_JSON_pack_data_auto ("wtid", 1767 wtid), 1768 GNUNET_JSON_pack_string ("exchange_url", 1769 exchange_url), 1770 TALER_JSON_pack_amount ("amount", 1771 &wired), 1772 TALER_JSON_pack_amount ("deposit_fee", 1773 deposit_fee), 1774 GNUNET_JSON_pack_timestamp ("execution_time", 1775 execution_time), 1776 GNUNET_JSON_pack_bool ("confirmed", 1777 transfer_confirmed), 1778 GNUNET_JSON_pack_uint64 ("expected_transfer_serial_id", 1779 expected_credit_serial)))); 1780 } 1781 1782 1783 /** 1784 * Check transfer status in local database. 1785 * 1786 * @param[in,out] gorc order context to update 1787 */ 1788 static void 1789 phase_check_local_transfers (struct GetOrderRequestContext *gorc) 1790 { 1791 struct TMH_HandlerContext *hc = gorc->hc; 1792 enum GNUNET_DB_QueryStatus qs; 1793 1794 GNUNET_assert (gorc->paid); 1795 GNUNET_assert (! gorc->order_only); 1796 1797 GNUNET_assert (GNUNET_OK == 1798 TALER_amount_set_zero (gorc->contract_amount.currency, 1799 &gorc->deposits_total)); 1800 GNUNET_assert (NULL != gorc->wire_details); 1801 /* We may be running again due to long-polling, clear state first */ 1802 json_array_clear (gorc->wire_details); 1803 qs = TALER_MERCHANTDB_iterate_transfer_details_by_order (TMH_db, 1804 gorc->order_serial, 1805 &process_transfer_details, 1806 gorc); 1807 if (0 > qs) 1808 { 1809 GNUNET_break (0); 1810 phase_end (gorc, 1811 TALER_MHD_reply_with_error (gorc->sc.con, 1812 MHD_HTTP_INTERNAL_SERVER_ERROR, 1813 TALER_EC_GENERIC_DB_FETCH_FAILED, 1814 "transfer details")); 1815 return; 1816 } 1817 if (gorc->deposit_currency_mismatch) 1818 { 1819 GNUNET_break (0); 1820 phase_end (gorc, 1821 TALER_MHD_reply_with_error (gorc->sc.con, 1822 MHD_HTTP_INTERNAL_SERVER_ERROR, 1823 TALER_EC_GENERIC_DB_FETCH_FAILED, 1824 "deposits in different currency than original order price")); 1825 return; 1826 } 1827 1828 if (! gorc->wired) 1829 { 1830 /* we believe(d) the wire transfer did not happen yet, check if maybe 1831 in light of new evidence it did */ 1832 struct TALER_Amount expect_total; 1833 enum GNUNET_GenericReturnValue fee_ret; 1834 1835 fee_ret = TMH_compute_expected_transfer ( 1836 &gorc->contract_amount, 1837 &gorc->refund_amount, 1838 &gorc->deposit_fees_total, 1839 &gorc->deposit_fees_refunded_total, 1840 gorc->has_max_fee ? &gorc->max_fee : NULL, 1841 gorc->has_max_fee, 1842 &expect_total); 1843 switch (fee_ret) 1844 { 1845 case GNUNET_OK: 1846 break; 1847 case GNUNET_SYSERR: 1848 GNUNET_break (0); 1849 phase_end (gorc, 1850 TALER_MHD_reply_with_error ( 1851 gorc->sc.con, 1852 MHD_HTTP_INTERNAL_SERVER_ERROR, 1853 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1854 "refund or refunded deposit fees exceed allowed total")); 1855 return; 1856 case GNUNET_NO: 1857 GNUNET_break (0); 1858 phase_end (gorc, 1859 TALER_MHD_reply_with_error ( 1860 gorc->sc.con, 1861 MHD_HTTP_INTERNAL_SERVER_ERROR, 1862 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1863 "deposit fees exceed total minus refunds")); 1864 return; 1865 default: 1866 GNUNET_break (0); 1867 phase_end (gorc, 1868 TALER_MHD_reply_with_error ( 1869 gorc->sc.con, 1870 MHD_HTTP_INTERNAL_SERVER_ERROR, 1871 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1872 "unexpected fee computation result")); 1873 return; 1874 } 1875 if (0 >= 1876 TALER_amount_cmp (&expect_total, 1877 &gorc->deposits_total)) 1878 { 1879 /* expect_total <= gorc->deposits_total: good: we got the wire transfer */ 1880 gorc->wired = true; 1881 qs = TALER_MERCHANTDB_update_to_contract_terms_wired (TMH_db, 1882 gorc->order_serial); 1883 GNUNET_break (qs >= 0); /* just warn if transaction failed */ 1884 TMH_notify_order_change (hc->instance, 1885 TMH_OSF_PAID 1886 | TMH_OSF_WIRED, 1887 gorc->timestamp, 1888 gorc->order_serial); 1889 } 1890 } 1891 gorc->phase++; 1892 } 1893 1894 1895 /** 1896 * Generate final result for the status request. 1897 * 1898 * @param[in,out] gorc order context to update 1899 */ 1900 static void 1901 phase_reply_result (struct GetOrderRequestContext *gorc) 1902 { 1903 struct TMH_HandlerContext *hc = gorc->hc; 1904 char *order_status_url; 1905 1906 GNUNET_assert (gorc->paid); 1907 GNUNET_assert (! gorc->order_only); 1908 1909 { 1910 struct TALER_PrivateContractHashP *h_contract = NULL; 1911 1912 /* In a session-bound payment, allow the browser to check the order 1913 * status page (e.g. to get a refund). 1914 * 1915 * Note that we don't allow this outside of session-based payment, as 1916 * otherwise this becomes an oracle to convert order_id to h_contract. 1917 */ 1918 if (NULL != gorc->session_id) 1919 h_contract = &gorc->h_contract_terms; 1920 1921 order_status_url = 1922 TMH_make_order_status_url (gorc->sc.con, 1923 hc->infix, 1924 gorc->session_id, 1925 hc->instance->settings.id, 1926 &gorc->claim_token, 1927 h_contract); 1928 } 1929 if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time)) 1930 { 1931 GNUNET_break (GNUNET_YES == 1932 TALER_amount_is_zero (&gorc->contract_amount)); 1933 gorc->last_payment = gorc->timestamp; 1934 } 1935 { 1936 json_t *reply; 1937 1938 reply = GNUNET_JSON_PACK ( 1939 // Deprecated in protocol v6! 1940 GNUNET_JSON_pack_array_steal ("wire_reports", 1941 json_array ()), 1942 GNUNET_JSON_pack_uint64 ("exchange_code", 1943 gorc->exchange_ec), 1944 GNUNET_JSON_pack_uint64 ("exchange_http_status", 1945 gorc->exchange_hc), 1946 /* legacy: */ 1947 GNUNET_JSON_pack_uint64 ("exchange_ec", 1948 gorc->exchange_ec), 1949 /* legacy: */ 1950 GNUNET_JSON_pack_uint64 ("exchange_hc", 1951 gorc->exchange_hc), 1952 TALER_JSON_pack_amount ("deposit_total", 1953 &gorc->deposits_total), 1954 GNUNET_JSON_pack_object_incref ("contract_terms", 1955 gorc->contract_terms_json), 1956 GNUNET_JSON_pack_string ("order_status", 1957 "paid"), 1958 GNUNET_JSON_pack_timestamp ("last_payment", 1959 gorc->last_payment), 1960 GNUNET_JSON_pack_bool ("refunded", 1961 gorc->refunded), 1962 GNUNET_JSON_pack_bool ("wired", 1963 gorc->wired), 1964 GNUNET_JSON_pack_bool ("refund_pending", 1965 gorc->refund_pending), 1966 GNUNET_JSON_pack_allow_null ( 1967 TALER_JSON_pack_amount ("refund_amount", 1968 &gorc->refund_amount)), 1969 GNUNET_JSON_pack_array_incref ("wire_details", 1970 gorc->wire_details), 1971 GNUNET_JSON_pack_array_incref ("refund_details", 1972 gorc->refund_details), 1973 GNUNET_JSON_pack_array_incref ("refunds_external", 1974 gorc->refunds_external), 1975 GNUNET_JSON_pack_string ("order_status_url", 1976 order_status_url), 1977 (gorc->choice_index >= 0) 1978 ? GNUNET_JSON_pack_int64 ("choice_index", 1979 gorc->choice_index) 1980 : GNUNET_JSON_pack_end_ ()); 1981 check_reply (gorc, 1982 reply); 1983 json_decref (reply); 1984 } 1985 GNUNET_free (order_status_url); 1986 } 1987 1988 1989 /** 1990 * End with error status in wire_hc and wire_ec. 1991 * 1992 * @param[in,out] gorc order context to update 1993 */ 1994 static void 1995 phase_error (struct GetOrderRequestContext *gorc) 1996 { 1997 GNUNET_assert (TALER_EC_NONE != gorc->wire_ec); 1998 phase_end (gorc, 1999 TALER_MHD_reply_with_error (gorc->sc.con, 2000 gorc->wire_hc, 2001 gorc->wire_ec, 2002 NULL)); 2003 } 2004 2005 2006 enum MHD_Result 2007 TMH_private_get_orders_ID ( 2008 const struct TMH_RequestHandler *rh, 2009 struct MHD_Connection *connection, 2010 struct TMH_HandlerContext *hc) 2011 { 2012 struct GetOrderRequestContext *gorc = hc->ctx; 2013 2014 if (NULL == gorc) 2015 { 2016 /* First time here, parse request and check order is known */ 2017 GNUNET_assert (NULL != hc->infix); 2018 gorc = GNUNET_new (struct GetOrderRequestContext); 2019 hc->cc = &gorc_cleanup; 2020 hc->ctx = gorc; 2021 gorc->sc.con = connection; 2022 gorc->hc = hc; 2023 gorc->wire_details = json_array (); 2024 GNUNET_assert (NULL != gorc->wire_details); 2025 gorc->refund_details = json_array (); 2026 GNUNET_assert (NULL != gorc->refund_details); 2027 gorc->refunds_external = json_array (); 2028 GNUNET_assert (NULL != gorc->refunds_external); 2029 gorc->session_id = MHD_lookup_connection_value (connection, 2030 MHD_GET_ARGUMENT_KIND, 2031 "session_id"); 2032 if (! (TALER_MHD_arg_to_yna (connection, 2033 "allow_refunded_for_repurchase", 2034 TALER_EXCHANGE_YNA_NO, 2035 &gorc->allow_refunded_for_repurchase)) ) 2036 return TALER_MHD_reply_with_error (connection, 2037 MHD_HTTP_BAD_REQUEST, 2038 TALER_EC_GENERIC_PARAMETER_MALFORMED, 2039 "allow_refunded_for_repurchase"); 2040 TALER_MHD_parse_request_timeout (connection, 2041 &gorc->sc.long_poll_timeout); 2042 TALER_MHD_parse_request_arg_auto (connection, 2043 "lp_not_etag", 2044 &gorc->lp_not_etag, 2045 gorc->have_lp_not_etag); 2046 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2047 "Starting GET /private/orders/%s processing with timeout %s\n", 2048 hc->infix, 2049 GNUNET_STRINGS_absolute_time_to_string ( 2050 gorc->sc.long_poll_timeout)); 2051 } 2052 if (GNUNET_SYSERR == gorc->suspended) 2053 return MHD_NO; /* we are in shutdown */ 2054 while (1) 2055 { 2056 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2057 "Processing order %s in phase %d\n", 2058 hc->infix, 2059 (int) gorc->phase); 2060 switch (gorc->phase) 2061 { 2062 case GOP_INIT: 2063 phase_init (gorc); 2064 break; 2065 case GOP_FETCH_CONTRACT: 2066 phase_fetch_contract (gorc); 2067 break; 2068 case GOP_PARSE_CONTRACT: 2069 phase_parse_contract (gorc); 2070 break; 2071 case GOP_CHECK_PAID: 2072 phase_check_paid (gorc); 2073 break; 2074 case GOP_CHECK_REPURCHASE: 2075 phase_check_repurchase (gorc); 2076 break; 2077 case GOP_UNPAID_FINISH: 2078 phase_unpaid_finish (gorc); 2079 break; 2080 case GOP_CHECK_DEPOSITS: 2081 phase_check_deposits (gorc); 2082 break; 2083 case GOP_CHECK_REFUNDS: 2084 phase_check_refunds (gorc); 2085 break; 2086 case GOP_CHECK_LOCAL_TRANSFERS: 2087 phase_check_local_transfers (gorc); 2088 break; 2089 case GOP_REPLY_RESULT: 2090 phase_reply_result (gorc); 2091 break; 2092 case GOP_ERROR: 2093 phase_error (gorc); 2094 break; 2095 case GOP_SUSPENDED_ON_UNPAID: 2096 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2097 "Suspending order request awaiting payment\n"); 2098 return MHD_YES; 2099 case GOP_END_YES: 2100 return MHD_YES; 2101 case GOP_END_NO: 2102 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2103 "Closing connection, no response generated\n"); 2104 return MHD_NO; 2105 } 2106 } /* end first-time per-request initialization */ 2107 }