taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c (20688B)
1 /* 2 This file is part of TALER 3 (C) 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 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 General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License 14 along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c 18 * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <jansson.h> 23 #include <microhttpd.h> 24 #include <gnunet/gnunet_json_lib.h> 25 #include <taler/taler_json_lib.h> 26 #include <taler/taler_mhd_lib.h> 27 #include <taler/taler_bank_service.h> 28 #include "taler-merchant-httpd.h" 29 #include "taler-merchant-httpd_exchanges.h" 30 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h" 31 #include "merchant-database/select_account.h" 32 33 34 /** 35 * Processing phases for a kycauth POST request. 36 */ 37 enum KycAuthPhase 38 { 39 /** 40 * Parse the request body and URL path. 41 */ 42 KP_PARSE = 0, 43 44 /** 45 * Fetch exchange /keys (and optionally register with bank gateway). 46 * The connection is suspended in this phase. 47 */ 48 KP_CHECK_EXCHANGES, 49 50 /** 51 * Return the prepared response to the client. 52 */ 53 KP_RETURN_RESPONSE, 54 55 /** 56 * Return MHD_YES to finish handling. 57 */ 58 KP_END_YES, 59 60 /** 61 * Return MHD_NO to close the connection. 62 */ 63 KP_END_NO 64 }; 65 66 67 struct ExchangeAccount 68 { 69 70 /** 71 * Payto URI of the exchange, saved from /keys. 72 */ 73 struct TALER_FullPayto payto; 74 75 /** 76 * Pending bank gateway registration handle, or NULL. 77 */ 78 struct TALER_BANK_RegistrationHandle *brh; 79 80 /** 81 * Context we are operating in. 82 */ 83 struct KycAuthContext *kac; 84 85 /** 86 * Transfer subjects to use for the account. 87 */ 88 struct TALER_BANK_TransferSubject *subjects; 89 90 /** 91 * Length of the @e subjects array. 92 */ 93 unsigned int num_subjects; 94 95 /** 96 * Expiration time for the given @e ts. 97 */ 98 struct GNUNET_TIME_Timestamp expiration; 99 }; 100 101 102 /** 103 * Per-request context for POST /private/accounts/$H_WIRE/kycauth. 104 */ 105 struct KycAuthContext 106 { 107 108 /** 109 * Kept in doubly-linked list. 110 */ 111 struct KycAuthContext *next; 112 113 /** 114 * Kept in doubly-linked list. 115 */ 116 struct KycAuthContext *prev; 117 118 /** 119 * MHD connection we are handling. 120 */ 121 struct MHD_Connection *connection; 122 123 /** 124 * Handler context for this request. 125 */ 126 struct TMH_HandlerContext *hc; 127 128 /** 129 * Prepared HTTP response to return, or NULL. 130 */ 131 struct MHD_Response *response; 132 133 /** 134 * Exchange URL from the request body. 135 */ 136 char *exchange_url; 137 138 /** 139 * Payto URI of our merchant bank account (from DB). 140 */ 141 struct TALER_FullPayto payto_uri; 142 143 /** 144 * Pending /keys lookup operation with the exchange. 145 */ 146 struct TMH_EXCHANGES_KeysOperation *fo; 147 148 /** 149 * Tiny amount from exchange /keys, used as the transfer amount. 150 */ 151 struct TALER_Amount tiny_amount; 152 153 /** 154 * Array of exchange wire account payto URIs, saved from /keys. 155 * Length is @e exchange_accounts_len. 156 */ 157 struct ExchangeAccount *exchange_accounts; 158 159 /** 160 * Number of entries in @e exchange_accounts. 161 */ 162 unsigned int exchange_accounts_len; 163 164 /** 165 * Number of active registrations in @e exchange_accounts. 166 */ 167 unsigned int active_registrations; 168 169 /** 170 * HTTP status code to send with @e response. 171 * UINT_MAX indicates a hard error (return MHD_NO). 172 */ 173 unsigned int response_code; 174 175 /** 176 * Current processing phase. 177 */ 178 enum KycAuthPhase phase; 179 180 /** 181 * #GNUNET_NO if the connection is not suspended, 182 * #GNUNET_YES if the connection is suspended, 183 * #GNUNET_SYSERR if suspended and being force-resumed during shutdown. 184 */ 185 enum GNUNET_GenericReturnValue suspended; 186 187 }; 188 189 190 /** 191 * Head of the doubly-linked list of active kycauth contexts. 192 */ 193 static struct KycAuthContext *kac_head; 194 195 /** 196 * Tail of the doubly-linked list of active kycauth contexts. 197 */ 198 static struct KycAuthContext *kac_tail; 199 200 201 void 202 TMH_force_kac_resume (void) 203 { 204 for (struct KycAuthContext *kac = kac_head; 205 NULL != kac; 206 kac = kac->next) 207 { 208 if (NULL != kac->fo) 209 { 210 TMH_EXCHANGES_keys4exchange_cancel (kac->fo); 211 kac->fo = NULL; 212 } 213 if (GNUNET_YES == kac->suspended) 214 { 215 kac->suspended = GNUNET_SYSERR; 216 MHD_resume_connection (kac->connection); 217 } 218 } 219 } 220 221 222 /** 223 * Resume processing of @a kac after an async operation completed. 224 * 225 * @param[in,out] kac context to resume 226 */ 227 static void 228 kac_resume (struct KycAuthContext *kac) 229 { 230 GNUNET_assert (GNUNET_YES == kac->suspended); 231 kac->suspended = GNUNET_NO; 232 MHD_resume_connection (kac->connection); 233 TALER_MHD_daemon_trigger (); 234 } 235 236 237 /** 238 * Clean up a kycauth context, freeing all resources. 239 * 240 * @param cls a `struct KycAuthContext` to clean up 241 */ 242 static void 243 kac_cleanup (void *cls) 244 { 245 struct KycAuthContext *kac = cls; 246 247 if (NULL != kac->fo) 248 { 249 TMH_EXCHANGES_keys4exchange_cancel (kac->fo); 250 kac->fo = NULL; 251 } 252 if (NULL != kac->response) 253 { 254 MHD_destroy_response (kac->response); 255 kac->response = NULL; 256 } 257 GNUNET_free (kac->exchange_url); 258 GNUNET_free (kac->payto_uri.full_payto); 259 for (unsigned int i = 0; i < kac->exchange_accounts_len; i++) 260 { 261 struct ExchangeAccount *acc = &kac->exchange_accounts[i]; 262 263 if (NULL != acc->brh) 264 { 265 TALER_BANK_registration_cancel (acc->brh); 266 acc->brh = NULL; 267 } 268 for (unsigned int j = 0; j<acc->num_subjects; j++) 269 TALER_BANK_transfer_subject_free (&acc->subjects[j]); 270 GNUNET_free (acc->subjects); 271 GNUNET_free (acc->payto.full_payto); 272 } 273 GNUNET_free (kac->exchange_accounts); 274 GNUNET_CONTAINER_DLL_remove (kac_head, 275 kac_tail, 276 kac); 277 GNUNET_free (kac); 278 } 279 280 281 /** 282 * Convert a @a ts (transfer subject) to a JSON object. 283 * The resulting object has a "type" field discriminating the format, 284 * matching the JSON format returned by the bank gateway's /registration. 285 * 286 * @param acc general account details 287 * @param ts the specific transfer subject to convert 288 * @return freshly allocated JSON object, or NULL on error 289 */ 290 static json_t * 291 transfer_subject_to_json (const struct ExchangeAccount *acc, 292 const struct TALER_BANK_TransferSubject *ts) 293 { 294 struct KycAuthContext *kac = acc->kac; 295 296 switch (ts->format) 297 { 298 case TALER_BANK_SUBJECT_FORMAT_SIMPLE: 299 return GNUNET_JSON_PACK ( 300 GNUNET_JSON_pack_string ("type", 301 "SIMPLE"), 302 TALER_JSON_pack_amount ("credit_amount", 303 &kac->tiny_amount), 304 GNUNET_JSON_pack_data_auto ("subject", 305 &kac->hc->instance->merchant_pub)); 306 case TALER_BANK_SUBJECT_FORMAT_URI: 307 return GNUNET_JSON_PACK ( 308 GNUNET_JSON_pack_string ("type", 309 "URI"), 310 TALER_JSON_pack_amount ("credit_amount", 311 &kac->tiny_amount), 312 GNUNET_JSON_pack_string ("uri", 313 ts->details.uri.uri)); 314 case TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL: 315 return GNUNET_JSON_PACK ( 316 GNUNET_JSON_pack_string ("type", 317 "CH_QR_BILL"), 318 TALER_JSON_pack_amount ("credit_amount", 319 &ts->details.ch_qr_bill.credit_amount), 320 GNUNET_JSON_pack_string ("qr_reference_number", 321 ts->details.ch_qr_bill.qr_reference_number)); 322 } 323 GNUNET_break (0); 324 return NULL; 325 } 326 327 328 /** 329 * Generate the final "OK" response for the request in @a kac. 330 * Builds the wire_instructions JSON array from the saved exchange accounts 331 * and the given transfer subject. 332 * 333 * @param[in,out] kac request with all the details ready to build a response 334 */ 335 static void 336 generate_ok_response (struct KycAuthContext *kac) 337 { 338 json_t *arr; 339 340 arr = json_array (); 341 GNUNET_assert (NULL != arr); 342 for (unsigned int i = 0; i < kac->exchange_accounts_len; i++) 343 { 344 const struct ExchangeAccount *acc = &kac->exchange_accounts[i]; 345 json_t *subj; 346 json_t *entry; 347 348 for (unsigned int j = 0; j < acc->num_subjects; j++) 349 { 350 subj = transfer_subject_to_json (acc, 351 &acc->subjects[j]); 352 GNUNET_assert (NULL != subj); 353 entry = GNUNET_JSON_PACK ( 354 TALER_JSON_pack_amount ("amount", 355 &kac->tiny_amount), 356 TALER_JSON_pack_full_payto ("target_payto", 357 acc->payto), 358 GNUNET_JSON_pack_object_steal ("subject", 359 subj), 360 GNUNET_JSON_pack_timestamp ("expiration", 361 acc->expiration)); 362 GNUNET_assert (NULL != entry); 363 GNUNET_assert (0 == 364 json_array_append_new (arr, 365 entry)); 366 } 367 } 368 kac->response_code = MHD_HTTP_OK; 369 kac->response = TALER_MHD_MAKE_JSON_PACK ( 370 GNUNET_JSON_pack_array_steal ("wire_instructions", 371 arr)); 372 kac->phase = KP_RETURN_RESPONSE; 373 kac_resume (kac); 374 } 375 376 377 /** 378 * Callback invoked with the result of the bank gateway /registration request. 379 * 380 * @param cls our `struct KycAuthContext` 381 * @param rr response details 382 */ 383 static void 384 registration_cb (void *cls, 385 const struct TALER_BANK_RegistrationResponse *rr) 386 { 387 struct ExchangeAccount *acc = cls; 388 struct KycAuthContext *kac = acc->kac; 389 390 acc->brh = NULL; 391 kac->active_registrations--; 392 if (MHD_HTTP_OK != rr->http_status) 393 { 394 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 395 "Bank gateway registration failed with HTTP status %u\n", 396 rr->http_status); 397 kac->response_code = MHD_HTTP_BAD_GATEWAY; 398 kac->response = TALER_MHD_make_error ( 399 TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_BANK_GATEWAY_UNREACHABLE, 400 "bank gateway /registration failed"); 401 } 402 GNUNET_free (acc->subjects); 403 acc->num_subjects = rr->details.ok.num_subjects; 404 acc->subjects = GNUNET_new_array (acc->num_subjects, 405 struct TALER_BANK_TransferSubject); 406 for (unsigned int i = 0; i<acc->num_subjects; i++) 407 { 408 TALER_BANK_transfer_subject_copy (&acc->subjects[i], 409 &rr->details.ok.subjects[i]); 410 } 411 acc->expiration = rr->details.ok.expiration; 412 if (0 != kac->active_registrations) 413 return; /* wait for more replies */ 414 generate_ok_response (kac); 415 } 416 417 418 /** 419 * Callback invoked with the /keys of the selected exchange. 420 * 421 * Saves tiny_amount and exchange account payto URIs from the keys, then 422 * either calls the bank gateway /registration (if wire_transfer_gateway is 423 * present) or directly constructs the SIMPLE-format response. 424 * 425 * @param cls our `struct KycAuthContext` 426 * @param keys the exchange's key material, or NULL on failure 427 * @param exchange internal exchange handle (unused) 428 */ 429 static void 430 process_keys_cb (void *cls, 431 struct TALER_EXCHANGE_Keys *keys, 432 struct TMH_Exchange *exchange) 433 { 434 struct KycAuthContext *kac = cls; 435 struct TMH_MerchantInstance *mi = kac->hc->instance; 436 437 (void) exchange; 438 kac->fo = NULL; 439 if (NULL == keys) 440 { 441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 442 "Could not get /keys from exchange `%s'\n", 443 kac->exchange_url); 444 kac->response_code = MHD_HTTP_BAD_GATEWAY; 445 kac->response = TALER_MHD_make_error ( 446 TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_EXCHANGE_UNREACHABLE, 447 kac->exchange_url); 448 kac->phase = KP_RETURN_RESPONSE; 449 kac_resume (kac); 450 return; 451 } 452 if (! keys->tiny_amount_available) 453 { 454 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 455 "Exchange `%s' did not provide tiny_amount in /keys\n", 456 kac->exchange_url); 457 kac->response_code = MHD_HTTP_BAD_GATEWAY; 458 kac->response = TALER_MHD_make_error ( 459 TALER_EC_MERCHANT_POST_ACCOUNTS_EXCHANGE_TOO_OLD, 460 "exchange /keys missing tiny_amount"); 461 kac->phase = KP_RETURN_RESPONSE; 462 kac_resume (kac); 463 return; 464 } 465 466 /* Save tiny_amount and exchange wire accounts from keys */ 467 kac->tiny_amount = keys->tiny_amount; 468 kac->exchange_accounts_len = keys->accounts_len; 469 if (0 == keys->accounts_len) 470 { 471 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 472 "Exchange `%s' did not provide any wire accounts in /keys\n", 473 kac->exchange_url); 474 kac->response_code = MHD_HTTP_BAD_GATEWAY; 475 kac->response = TALER_MHD_make_error ( 476 TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS, 477 "exchange /keys missing wire accounts"); 478 kac->phase = KP_RETURN_RESPONSE; 479 kac_resume (kac); 480 return; 481 } 482 483 kac->exchange_accounts 484 = GNUNET_new_array (keys->accounts_len, 485 struct ExchangeAccount); 486 for (unsigned int i = 0; i < keys->accounts_len; i++) 487 { 488 struct ExchangeAccount *acc = &kac->exchange_accounts[i]; 489 struct TALER_ReserveMapAuthorizationPrivateKeyP apk; 490 491 GNUNET_CRYPTO_eddsa_key_create (&apk.eddsa_priv); 492 acc->kac = kac; 493 acc->payto.full_payto 494 = GNUNET_strdup (keys->accounts[i].fpayto_uri.full_payto); 495 acc->subjects = GNUNET_new (struct TALER_BANK_TransferSubject); 496 acc->num_subjects = 1; 497 acc->subjects[0].format = TALER_BANK_SUBJECT_FORMAT_SIMPLE; 498 acc->expiration = GNUNET_TIME_UNIT_FOREVER_TS; 499 if (NULL == keys->accounts[i].wire_transfer_gateway) 500 continue; 501 acc->brh = TALER_BANK_registration ( 502 TMH_curl_ctx, 503 keys->accounts[i].wire_transfer_gateway, 504 &kac->tiny_amount, 505 TALER_BANK_REGISTRATION_TYPE_KYC, 506 (const union TALER_AccountPublicKeyP *) &mi->merchant_pub, 507 // FIXME: turn this into an option when modernizing the BANK API... 508 &apk, 509 false, /* recurrent */ 510 ®istration_cb, 511 acc); 512 if (NULL == acc->brh) 513 { 514 GNUNET_break (0); 515 kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 516 kac->response = TALER_MHD_make_error ( 517 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 518 "could not start bank gateway registration"); 519 kac->phase = KP_RETURN_RESPONSE; 520 kac_resume (kac); 521 return; 522 } 523 kac->active_registrations++; 524 } 525 if (0 != kac->active_registrations) 526 return; 527 generate_ok_response (kac); 528 } 529 530 531 /** 532 * Process the PARSE phase: validate the URL path, parse the request body, 533 * and look up the merchant account in the database. 534 * 535 * @param[in,out] kac context to process 536 */ 537 static void 538 phase_parse (struct KycAuthContext *kac) 539 { 540 const char *h_wire_s = kac->hc->infix; 541 struct TALER_MerchantWireHashP h_wire; 542 const char *exchange_url; 543 enum GNUNET_GenericReturnValue res; 544 struct GNUNET_JSON_Specification spec[] = { 545 GNUNET_JSON_spec_string ("exchange_url", 546 &exchange_url), 547 GNUNET_JSON_spec_end () 548 }; 549 550 GNUNET_assert (NULL != h_wire_s); 551 if (GNUNET_OK != 552 GNUNET_STRINGS_string_to_data (h_wire_s, 553 strlen (h_wire_s), 554 &h_wire, 555 sizeof (h_wire))) 556 { 557 GNUNET_break_op (0); 558 kac->response_code = MHD_HTTP_BAD_REQUEST; 559 kac->response = TALER_MHD_make_error ( 560 TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED, 561 h_wire_s); 562 kac->phase = KP_RETURN_RESPONSE; 563 return; 564 } 565 566 res = TALER_MHD_parse_json_data (kac->connection, 567 kac->hc->request_body, 568 spec); 569 if (GNUNET_OK != res) 570 { 571 kac->phase = (GNUNET_NO == res) ? KP_END_YES : KP_END_NO; 572 return; 573 } 574 575 /* Look up the merchant account in the database */ 576 { 577 struct TALER_MERCHANTDB_AccountDetails ad = { 0 }; 578 enum GNUNET_DB_QueryStatus qs; 579 580 qs = TALER_MERCHANTDB_select_account (TMH_db, 581 kac->hc->instance->settings.id, 582 &h_wire, 583 &ad); 584 if (0 > qs) 585 { 586 GNUNET_break (0); 587 GNUNET_JSON_parse_free (spec); 588 kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 589 kac->response = TALER_MHD_make_error ( 590 TALER_EC_GENERIC_DB_FETCH_FAILED, 591 "select_account"); 592 kac->phase = KP_RETURN_RESPONSE; 593 return; 594 } 595 if (0 == qs) 596 { 597 GNUNET_JSON_parse_free (spec); 598 kac->response_code = MHD_HTTP_NOT_FOUND; 599 kac->response = TALER_MHD_make_error ( 600 TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN, 601 h_wire_s); 602 kac->phase = KP_RETURN_RESPONSE; 603 return; 604 } 605 kac->payto_uri.full_payto = GNUNET_strdup (ad.payto_uri.full_payto); 606 /* Free fields we do not need */ 607 GNUNET_free (ad.payto_uri.full_payto); 608 GNUNET_free (ad.credit_facade_url); 609 GNUNET_free (ad.extra_wire_subject_metadata); 610 json_decref (ad.credit_facade_credentials); 611 } 612 613 kac->exchange_url = GNUNET_strdup (exchange_url); 614 GNUNET_JSON_parse_free (spec); 615 kac->phase = KP_CHECK_EXCHANGES; 616 } 617 618 619 /** 620 * Process the CHECK_EXCHANGES phase: start an async /keys fetch from 621 * the requested exchange and suspend the connection. 622 * 623 * @param[in,out] kac context to process 624 */ 625 static void 626 phase_check_exchanges (struct KycAuthContext *kac) 627 { 628 kac->fo = TMH_EXCHANGES_keys4exchange (kac->exchange_url, 629 false, 630 &process_keys_cb, 631 kac); 632 if (NULL == kac->fo) 633 { 634 GNUNET_break_op (0); 635 kac->response_code = MHD_HTTP_BAD_REQUEST; 636 kac->response = TALER_MHD_make_error ( 637 TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED, 638 kac->exchange_url); 639 kac->phase = KP_RETURN_RESPONSE; 640 return; 641 } 642 MHD_suspend_connection (kac->connection); 643 kac->suspended = GNUNET_YES; 644 } 645 646 647 /** 648 * Process the RETURN_RESPONSE phase: queue the prepared response into MHD. 649 * 650 * @param[in,out] kac context to process 651 */ 652 static void 653 phase_return_response (struct KycAuthContext *kac) 654 { 655 enum MHD_Result ret; 656 657 if (UINT_MAX == kac->response_code) 658 { 659 GNUNET_break (0); 660 kac->phase = KP_END_NO; 661 return; 662 } 663 GNUNET_assert (0 != kac->response_code); 664 ret = MHD_queue_response (kac->connection, 665 kac->response_code, 666 kac->response); 667 kac->phase = (MHD_YES == ret) ? KP_END_YES : KP_END_NO; 668 } 669 670 671 enum MHD_Result 672 TMH_private_post_accounts_H_WIRE_kycauth ( 673 const struct TMH_RequestHandler *rh, 674 struct MHD_Connection *connection, 675 struct TMH_HandlerContext *hc) 676 { 677 struct KycAuthContext *kac = hc->ctx; 678 679 (void) rh; 680 GNUNET_assert (NULL != hc->infix); 681 if (NULL == kac) 682 { 683 kac = GNUNET_new (struct KycAuthContext); 684 kac->connection = connection; 685 kac->hc = hc; 686 hc->ctx = kac; 687 hc->cc = &kac_cleanup; 688 GNUNET_CONTAINER_DLL_insert (kac_head, 689 kac_tail, 690 kac); 691 } 692 693 while (1) 694 { 695 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 696 "Processing POST /private/accounts/$H_WIRE/kycauth" 697 " in phase %d\n", 698 (int) kac->phase); 699 switch (kac->phase) 700 { 701 case KP_PARSE: 702 phase_parse (kac); 703 break; 704 case KP_CHECK_EXCHANGES: 705 phase_check_exchanges (kac); 706 break; 707 case KP_RETURN_RESPONSE: 708 phase_return_response (kac); 709 break; 710 case KP_END_YES: 711 return MHD_YES; 712 case KP_END_NO: 713 return MHD_NO; 714 default: 715 GNUNET_assert (0); 716 return MHD_NO; 717 } 718 719 switch (kac->suspended) 720 { 721 case GNUNET_SYSERR: 722 /* Shutdown in progress */ 723 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 724 "Processing POST /private/accounts/$H_WIRE/kycauth" 725 " ends due to shutdown in phase %d\n", 726 (int) kac->phase); 727 return MHD_NO; 728 case GNUNET_NO: 729 /* Not suspended, continue with next phase */ 730 break; 731 case GNUNET_YES: 732 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 733 "Processing POST /private/accounts/$H_WIRE/kycauth" 734 " suspended in phase %d\n", 735 (int) kac->phase); 736 return MHD_YES; 737 } 738 } 739 GNUNET_assert (0); 740 return MHD_YES; 741 } 742 743 744 /* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c */