taler-exchange-httpd_kyc-info.c (23315B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021-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_kyc-info.c 18 * @brief Handle request for generic KYC info. 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include <gnunet/gnunet_json_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include <pthread.h> 27 #include "taler/taler_json_lib.h" 28 #include "taler/taler_exchangedb_lib.h" 29 #include "taler/taler_kyclogic_lib.h" 30 #include "taler/taler_mhd_lib.h" 31 #include "taler/taler_signatures.h" 32 #include "taler/taler_dbevents.h" 33 #include "taler-exchange-httpd_keys.h" 34 #include "taler-exchange-httpd_kyc-info.h" 35 #include "taler-exchange-httpd_responses.h" 36 #include "taler-exchange-httpd_common_kyc.h" 37 38 39 /** 40 * Context for the GET /kyc-info request. 41 * 42 * Used for long-polling and other asynchronous waiting. 43 */ 44 struct KycPoller 45 { 46 /** 47 * Kept in a DLL. 48 */ 49 struct KycPoller *next; 50 51 /** 52 * Kept in a DLL. 53 */ 54 struct KycPoller *prev; 55 56 /** 57 * Connection we are handling. 58 */ 59 struct MHD_Connection *connection; 60 61 /** 62 * Subscription for the database event we are 63 * waiting for. 64 */ 65 struct GNUNET_DB_EventHandler *eh; 66 67 /** 68 * Handle to async activity to get the latest legitimization 69 * rule set. 70 */ 71 struct TALER_EXCHANGEDB_RuleUpdater *ru; 72 73 /** 74 * Current legitimization rule set, owned by callee. Will be NULL on error 75 * or for default rules. Will not contain skip rules and not be expired. 76 */ 77 struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; 78 79 /** 80 * Handle for async KYC processing. 81 */ 82 struct TEH_KycMeasureRunContext *kat; 83 84 /** 85 * Response to return, NULL if none yet. 86 */ 87 struct MHD_Response *response; 88 89 /** 90 * Set to access token for a KYC process by the account, 91 * if @e have_token is true. 92 */ 93 struct TALER_AccountAccessTokenP access_token; 94 95 /** 96 * Payto hash of the account matching @a access_token. 97 */ 98 struct TALER_NormalizedPaytoHashP h_payto; 99 100 /** 101 * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client. 0 for none 102 * (or malformed). 103 */ 104 uint64_t etag_outcome_in; 105 106 /** 107 * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client. 0 for none 108 * (or malformed). 109 */ 110 uint64_t etag_measure_in; 111 112 /** 113 * When will this request time out? 114 */ 115 struct GNUNET_TIME_Absolute timeout; 116 117 /** 118 * 119 */ 120 uint64_t legitimization_measure_last_row; 121 122 /** 123 * Row in the legitimization outcomes table that @e lrs matches. 124 */ 125 uint64_t legitimization_outcome_last_row; 126 127 /** 128 * HTTP status code to use with @e response. 129 */ 130 unsigned int response_code; 131 132 /** 133 * #GNUNET_YES if our @e h_payto is for a wallet, 134 * #GNUNET_NO if it is for an account, 135 * #GNUNET_SYSERR if we do not know. 136 */ 137 enum GNUNET_GenericReturnValue is_wallet; 138 139 /** 140 * True if we are still suspended. 141 */ 142 bool suspended; 143 144 }; 145 146 147 /** 148 * Head of list of requests in long polling. 149 */ 150 static struct KycPoller *kyp_head; 151 152 /** 153 * Tail of list of requests in long polling. 154 */ 155 static struct KycPoller *kyp_tail; 156 157 158 void 159 TEH_kyc_info_cleanup () 160 { 161 struct KycPoller *kyp; 162 163 while (NULL != (kyp = kyp_head)) 164 { 165 GNUNET_CONTAINER_DLL_remove (kyp_head, 166 kyp_tail, 167 kyp); 168 if (kyp->suspended) 169 { 170 kyp->suspended = false; 171 MHD_resume_connection (kyp->connection); 172 } 173 } 174 } 175 176 177 /** 178 * Function called once a connection is done to 179 * clean up the `struct ReservePoller` state. 180 * 181 * @param rc context to clean up for 182 */ 183 static void 184 kyp_cleanup (struct TEH_RequestContext *rc) 185 { 186 struct KycPoller *kyp = rc->rh_ctx; 187 188 GNUNET_assert (! kyp->suspended); 189 if (NULL != kyp->eh) 190 { 191 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 192 "Cancelling DB event listening\n"); 193 TEH_plugin->event_listen_cancel (TEH_plugin->cls, 194 kyp->eh); 195 kyp->eh = NULL; 196 } 197 if (NULL != kyp->ru) 198 { 199 TALER_EXCHANGEDB_update_rules_cancel (kyp->ru); 200 kyp->ru = NULL; 201 } 202 if (NULL != kyp->response) 203 { 204 MHD_destroy_response (kyp->response); 205 kyp->response = NULL; 206 } 207 if (NULL != kyp->lrs) 208 { 209 TALER_KYCLOGIC_rules_free (kyp->lrs); 210 kyp->lrs = NULL; 211 } 212 if (NULL != kyp->kat) 213 { 214 TEH_kyc_run_measure_cancel (kyp->kat); 215 kyp->kat = NULL; 216 } 217 GNUNET_free (kyp); 218 } 219 220 221 /** 222 * Function called on events received from Postgres. 223 * Wakes up long pollers. 224 * 225 * @param cls the `struct TEH_RequestContext *` 226 * @param extra additional event data provided 227 * @param extra_size number of bytes in @a extra 228 */ 229 static void 230 db_event_cb (void *cls, 231 const void *extra, 232 size_t extra_size) 233 { 234 struct TEH_RequestContext *rc = cls; 235 struct KycPoller *kyp = rc->rh_ctx; 236 struct GNUNET_AsyncScopeSave old_scope; 237 238 (void) extra; 239 (void) extra_size; 240 if (! kyp->suspended) 241 return; /* event triggered while main transaction 242 was still running, or got multiple wake-up events */ 243 GNUNET_async_scope_enter (&rc->async_scope_id, 244 &old_scope); 245 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 246 "Resuming from long-polling on KYC status\n"); 247 GNUNET_CONTAINER_DLL_remove (kyp_head, 248 kyp_tail, 249 kyp); 250 kyp->suspended = false; 251 MHD_resume_connection (kyp->connection); 252 TALER_MHD_daemon_trigger (); 253 GNUNET_async_scope_restore (&old_scope); 254 } 255 256 257 /** 258 * Add the headers we want to set for every response. 259 * 260 * @param[in,out] response the response to modify 261 */ 262 static void 263 add_nocache_header (struct MHD_Response *response) 264 { 265 GNUNET_break (MHD_YES == 266 MHD_add_response_header (response, 267 MHD_HTTP_HEADER_CACHE_CONTROL, 268 "no-cache")); 269 } 270 271 272 /** 273 * Resume processing the @a kyp request with the @a response. 274 * 275 * @param kyp request to resume and respond to 276 * @param http_status HTTP status for @a response 277 * @param response HTTP response to return 278 */ 279 static void 280 resume_with_response (struct KycPoller *kyp, 281 unsigned int http_status, 282 struct MHD_Response *response) 283 { 284 kyp->response_code = http_status; 285 kyp->response = response; 286 GNUNET_CONTAINER_DLL_remove (kyp_head, 287 kyp_tail, 288 kyp); 289 kyp->suspended = false; 290 MHD_resume_connection (kyp->connection); 291 TALER_MHD_daemon_trigger (); 292 } 293 294 295 /** 296 * Function called after a measure has been run. 297 * 298 * @param kyp request to fail with the error code 299 * @param ec error code or 0 on success 300 * @param hint detail error message or NULL on success / no info 301 */ 302 static void 303 fail_with_ec ( 304 struct KycPoller *kyp, 305 enum TALER_ErrorCode ec, 306 const char *hint) 307 { 308 resume_with_response (kyp, 309 TALER_ErrorCode_get_http_status (ec), 310 TALER_MHD_make_error (ec, 311 hint)); 312 } 313 314 315 /** 316 * Generate a reply with the KycProcessClientInformation from 317 * the LegitimizationMeasures. 318 * 319 * @param[in,out] kyp request to reply on 320 * @param legitimization_measure_row_id part of etag to set for the response 321 * @param legitimization_outcome_row_id part of etag to set for the response 322 * @param jmeasures a `LegitimizationMeasures` object to encode 323 * @param jvoluntary array of voluntary measures to encode, can be NULL 324 */ 325 static void 326 resume_with_reply (struct KycPoller *kyp, 327 uint64_t legitimization_measure_row_id, 328 uint64_t legitimization_outcome_row_id, 329 const json_t *jmeasures, 330 const json_t *jvoluntary) 331 { 332 const json_t *measures; /* array of MeasureInformation */ 333 bool is_and_combinator = false; 334 bool verboten; 335 struct GNUNET_JSON_Specification spec[] = { 336 GNUNET_JSON_spec_mark_optional ( 337 GNUNET_JSON_spec_bool ("is_and_combinator", 338 &is_and_combinator), 339 NULL), 340 GNUNET_JSON_spec_bool ("verboten", 341 &verboten), 342 GNUNET_JSON_spec_array_const ("measures", 343 &measures), 344 GNUNET_JSON_spec_end () 345 }; 346 enum GNUNET_GenericReturnValue ret; 347 const char *ename; 348 unsigned int eline; 349 json_t *kris; 350 size_t i; 351 json_t *mi; /* a MeasureInformation object */ 352 353 ret = GNUNET_JSON_parse (jmeasures, 354 spec, 355 &ename, 356 &eline); 357 if (GNUNET_OK != ret) 358 { 359 GNUNET_break (0); 360 fail_with_ec (kyp, 361 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 362 ename); 363 return; 364 } 365 kris = json_array (); 366 GNUNET_assert (NULL != kris); 367 json_array_foreach ((json_t *) measures, i, mi) 368 { 369 const char *check_name; 370 const json_t *context = NULL; 371 struct GNUNET_JSON_Specification ispec[] = { 372 GNUNET_JSON_spec_string ("check_name", 373 &check_name), 374 GNUNET_JSON_spec_mark_optional ( 375 GNUNET_JSON_spec_object_const ("context", 376 &context), 377 NULL), 378 GNUNET_JSON_spec_end () 379 }; 380 json_t *kri; 381 382 ret = GNUNET_JSON_parse (mi, 383 ispec, 384 &ename, 385 &eline); 386 if (GNUNET_OK != ret) 387 { 388 GNUNET_break (0); 389 json_decref (kris); 390 fail_with_ec ( 391 kyp, 392 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 393 ename); 394 return; 395 } 396 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 397 "Found required check `%s'\n", 398 check_name); 399 if (NULL != context) 400 { 401 json_dumpf (context, 402 stderr, 403 JSON_INDENT (2)); 404 fprintf (stderr, 405 "\n"); 406 } 407 /* Check if requirement is a duplicate, and in that case do 408 not return it */ 409 { 410 bool duplicate = false; 411 412 for (size_t off = 0; off < i; off++) 413 { 414 json_t *have = json_array_get (measures, 415 off); 416 if ( (1 == 417 json_equal (json_object_get (have, 418 "check_name"), 419 json_object_get (mi, 420 "check_name")) ) && 421 (1 == 422 json_equal (json_object_get (have, 423 "check_name"), 424 json_object_get (mi, 425 "check_name")) ) && 426 (1 == 427 json_equal (json_object_get (have, 428 "check_name"), 429 json_object_get (mi, 430 "check_name")) ) ) 431 { 432 /* Duplicate requirement, do not return again */ 433 duplicate = true; 434 break; 435 } 436 } 437 if (duplicate) 438 { 439 continue; 440 } 441 } 442 kri = TALER_KYCLOGIC_measure_to_requirement ( 443 check_name, 444 context, 445 &kyp->access_token, 446 i, 447 legitimization_measure_row_id); 448 if (NULL == kri) 449 { 450 GNUNET_break (0); 451 json_decref (kris); 452 fail_with_ec ( 453 kyp, 454 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 455 "could not convert measure to requirement"); 456 return; 457 } 458 GNUNET_assert (0 == 459 json_array_append_new (kris, 460 kri)); 461 } 462 463 { 464 char etags[128]; 465 struct MHD_Response *resp; 466 467 GNUNET_snprintf (etags, 468 sizeof (etags), 469 "\"%llu-%llu\"", 470 (unsigned long long) legitimization_measure_row_id, 471 (unsigned long long) legitimization_outcome_row_id); 472 resp = TALER_MHD_MAKE_JSON_PACK ( 473 GNUNET_JSON_pack_array_steal ("requirements", 474 kris), 475 GNUNET_JSON_pack_bool ("is_and_combinator", 476 is_and_combinator), 477 GNUNET_JSON_pack_allow_null ( 478 GNUNET_JSON_pack_array_incref ( 479 "voluntary_measures", 480 (json_t *) jvoluntary))); 481 GNUNET_break (MHD_YES == 482 MHD_add_response_header (resp, 483 MHD_HTTP_HEADER_ETAG, 484 etags)); 485 add_nocache_header (resp); 486 resume_with_response (kyp, 487 MHD_HTTP_OK, 488 resp); 489 } 490 } 491 492 493 /** 494 * Function called with the current rule set. 495 * 496 * @param cls closure with a `struct KycPoller *` 497 * @param rur includes legitimziation rule set that applies to the account 498 * (owned by callee, callee must free the lrs!) 499 */ 500 static void 501 current_rules_cb ( 502 void *cls, 503 struct TALER_EXCHANGEDB_RuleUpdaterResult *rur) 504 { 505 struct KycPoller *kyp = cls; 506 enum GNUNET_DB_QueryStatus qs; 507 uint64_t legitimization_measure_last_row; 508 json_t *jmeasures; 509 json_t *vmeasures; 510 511 kyp->ru = NULL; 512 if (TALER_EC_NONE != rur->ec) 513 { 514 /* Rollback should not be needed, just to be sure */ 515 TEH_plugin->rollback (TEH_plugin->cls); 516 fail_with_ec (kyp, 517 rur->ec, 518 rur->hint); 519 return; 520 } 521 GNUNET_assert (NULL == kyp->lrs); 522 kyp->lrs 523 = rur->lrs; 524 kyp->legitimization_outcome_last_row 525 = rur->legitimization_outcome_last_row; 526 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 527 "LRS is for the account uses %s\n", 528 NULL == kyp->lrs 529 ? "default rules" 530 : "custom rules"); 531 532 /* Check if there is an unfinished legitimization measure */ 533 qs = TEH_plugin->lookup_kyc_status_by_token ( 534 TEH_plugin->cls, 535 &kyp->access_token, 536 &legitimization_measure_last_row, 537 &jmeasures); 538 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 539 "lookup_kyc_status_by_token returned %d\n", 540 (int) qs); 541 if (qs < 0) 542 { 543 GNUNET_break (0); 544 TEH_plugin->rollback (TEH_plugin->cls); 545 fail_with_ec ( 546 kyp, 547 TALER_EC_GENERIC_DB_FETCH_FAILED, 548 "lookup_kyc_status_by_token"); 549 return; 550 } 551 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 552 { 553 jmeasures 554 = TALER_KYCLOGIC_zero_measures (kyp->lrs, 555 kyp->is_wallet); 556 if (NULL == jmeasures) 557 { 558 qs = TEH_plugin->commit (TEH_plugin->cls); 559 if (qs < 0) 560 { 561 TEH_plugin->rollback (TEH_plugin->cls); 562 fail_with_ec ( 563 kyp, 564 TALER_EC_GENERIC_DB_COMMIT_FAILED, 565 "kyc-info"); 566 return; 567 } 568 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 569 "No KYC requirement open\n"); 570 resume_with_response (kyp, 571 MHD_HTTP_OK, 572 TALER_MHD_MAKE_JSON_PACK ( 573 GNUNET_JSON_pack_allow_null ( 574 GNUNET_JSON_pack_array_steal ( 575 "voluntary_measures", 576 TALER_KYCLOGIC_voluntary_measures ( 577 kyp->lrs)) 578 ))); 579 return; 580 } 581 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 582 "Making applicable zero-measures for %s under current rules active\n", 583 (GNUNET_SYSERR == kyp->is_wallet) 584 ? "unknown account type" 585 : ( (GNUNET_YES == kyp->is_wallet) 586 ? "wallets" 587 : "accounts")); 588 json_dumpf (jmeasures, 589 stderr, 590 JSON_INDENT (2)); 591 qs = TEH_plugin->insert_active_legitimization_measure ( 592 TEH_plugin->cls, 593 &kyp->access_token, 594 jmeasures, 595 &legitimization_measure_last_row); 596 if (qs < 0) 597 { 598 GNUNET_break (0); 599 TEH_plugin->rollback (TEH_plugin->cls); 600 fail_with_ec (kyp, 601 TALER_EC_GENERIC_DB_STORE_FAILED, 602 "insert_active_legitimization_measure"); 603 return; 604 } 605 } 606 if ( (legitimization_measure_last_row == kyp->etag_measure_in) && 607 (kyp->legitimization_outcome_last_row == kyp->etag_outcome_in) && 608 GNUNET_TIME_absolute_is_future (kyp->timeout) ) 609 { 610 /* Note: in practice this commit should do nothing, but we cannot 611 trust that the client provided correct etags, and so we must 612 commit anyway just in case the client lied about the etags. */ 613 qs = TEH_plugin->commit (TEH_plugin->cls); 614 if (qs < 0) 615 { 616 TEH_plugin->rollback (TEH_plugin->cls); 617 fail_with_ec ( 618 kyp, 619 TALER_EC_GENERIC_DB_COMMIT_FAILED, 620 "kyc-info"); 621 return; 622 } 623 if (NULL != kyp->lrs) 624 { 625 TALER_KYCLOGIC_rules_free (kyp->lrs); 626 kyp->lrs = NULL; 627 } 628 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 629 "Suspending HTTP request on timeout (%s)\n", 630 GNUNET_TIME_relative2s ( 631 GNUNET_TIME_absolute_get_remaining ( 632 kyp->timeout), 633 true)); 634 GNUNET_assert (NULL != kyp->eh); 635 GNUNET_break (kyp->suspended); 636 return; 637 } 638 qs = TEH_plugin->commit (TEH_plugin->cls); 639 if (qs < 0) 640 { 641 TEH_plugin->rollback (TEH_plugin->cls); 642 fail_with_ec ( 643 kyp, 644 TALER_EC_GENERIC_DB_COMMIT_FAILED, 645 "kyc-info"); 646 return; 647 } 648 if ( (legitimization_measure_last_row == 649 kyp->etag_measure_in) && 650 (kyp->legitimization_outcome_last_row == 651 kyp->etag_outcome_in) ) 652 { 653 char etags[128]; 654 struct MHD_Response *resp; 655 656 GNUNET_snprintf (etags, 657 sizeof (etags), 658 "\"%llu-%llu\"", 659 (unsigned long long) legitimization_measure_last_row, 660 (unsigned long long) kyp->legitimization_outcome_last_row); 661 resp = MHD_create_response_from_buffer (0, 662 NULL, 663 MHD_RESPMEM_PERSISTENT); 664 add_nocache_header (resp); 665 TALER_MHD_add_global_headers (resp, 666 false); 667 GNUNET_break (MHD_YES == 668 MHD_add_response_header (resp, 669 MHD_HTTP_HEADER_ETAG, 670 etags)); 671 resume_with_response (kyp, 672 MHD_HTTP_NOT_MODIFIED, 673 resp); 674 json_decref (jmeasures); 675 return; 676 } 677 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 678 "Generating success reply to kyc-info query\n"); 679 vmeasures = TALER_KYCLOGIC_voluntary_measures (kyp->lrs); 680 resume_with_reply (kyp, 681 legitimization_measure_last_row, 682 kyp->legitimization_outcome_last_row, 683 jmeasures, 684 vmeasures); 685 json_decref (vmeasures); 686 json_decref (jmeasures); 687 } 688 689 690 MHD_RESULT 691 TEH_handler_kyc_info ( 692 struct TEH_RequestContext *rc, 693 const char *const args[1]) 694 { 695 struct KycPoller *kyp = rc->rh_ctx; 696 enum GNUNET_DB_QueryStatus qs; 697 698 if (NULL == kyp) 699 { 700 bool bis_wallet; 701 702 kyp = GNUNET_new (struct KycPoller); 703 kyp->connection = rc->connection; 704 rc->rh_ctx = kyp; 705 rc->rh_cleaner = &kyp_cleanup; 706 707 if (GNUNET_OK != 708 GNUNET_STRINGS_string_to_data ( 709 args[0], 710 strlen (args[0]), 711 &kyp->access_token, 712 sizeof (kyp->access_token))) 713 { 714 GNUNET_break_op (0); 715 return TALER_MHD_reply_with_error ( 716 rc->connection, 717 MHD_HTTP_BAD_REQUEST, 718 TALER_EC_GENERIC_PARAMETER_MALFORMED, 719 "access token"); 720 } 721 TALER_MHD_parse_request_timeout (rc->connection, 722 &kyp->timeout); 723 724 /* Get etag */ 725 { 726 const char *etags; 727 728 etags = MHD_lookup_connection_value ( 729 rc->connection, 730 MHD_HEADER_KIND, 731 MHD_HTTP_HEADER_IF_NONE_MATCH); 732 if (NULL != etags) 733 { 734 char dummy; 735 unsigned long long ev1; 736 unsigned long long ev2; 737 738 if (2 != sscanf (etags, 739 "\"%llu-%llu\"%c", 740 &ev1, 741 &ev2, 742 &dummy)) 743 { 744 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 745 "Client send malformed `%s' header `%s'\n", 746 MHD_HTTP_HEADER_IF_NONE_MATCH, 747 etags); 748 } 749 else 750 { 751 kyp->etag_measure_in = (uint64_t) ev1; 752 kyp->etag_outcome_in = (uint64_t) ev2; 753 } 754 } 755 } /* etag */ 756 757 /* Check access token */ 758 kyp->is_wallet = GNUNET_SYSERR; 759 qs = TEH_plugin->lookup_h_payto_by_access_token ( 760 TEH_plugin->cls, 761 &kyp->access_token, 762 &kyp->h_payto, 763 &bis_wallet); 764 if (qs < 0) 765 { 766 GNUNET_break (0); 767 return TALER_MHD_reply_with_ec ( 768 rc->connection, 769 TALER_EC_GENERIC_DB_FETCH_FAILED, 770 "lookup_h_payto_by_access_token"); 771 } 772 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 773 { 774 GNUNET_break_op (0); 775 return TALER_MHD_REPLY_JSON_PACK ( 776 rc->connection, 777 MHD_HTTP_FORBIDDEN, 778 TALER_JSON_pack_ec ( 779 TALER_EC_EXCHANGE_KYC_INFO_AUTHORIZATION_FAILED)); 780 } 781 kyp->is_wallet = (bis_wallet) ? GNUNET_YES : GNUNET_NO; 782 783 if (GNUNET_TIME_absolute_is_future (kyp->timeout)) 784 { 785 struct TALER_KycCompletedEventP rep = { 786 .header.size = htons (sizeof (rep)), 787 .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), 788 .h_payto = kyp->h_payto 789 }; 790 791 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 792 "Starting DB event listening\n"); 793 kyp->eh = TEH_plugin->event_listen ( 794 TEH_plugin->cls, 795 GNUNET_TIME_absolute_get_remaining (kyp->timeout), 796 &rep.header, 797 &db_event_cb, 798 rc); 799 } 800 } /* end of one-time initialization */ 801 802 if (NULL != kyp->response) 803 { 804 return MHD_queue_response (rc->connection, 805 kyp->response_code, 806 kyp->response); 807 } 808 809 kyp->ru = TALER_EXCHANGEDB_update_rules (TEH_plugin, 810 &TEH_attribute_key, 811 &kyp->h_payto, 812 kyp->is_wallet, 813 ¤t_rules_cb, 814 kyp); 815 kyp->suspended = true; 816 GNUNET_CONTAINER_DLL_insert (kyp_head, 817 kyp_tail, 818 kyp); 819 MHD_suspend_connection (rc->connection); 820 return MHD_YES; 821 822 } 823 824 825 /* end of taler-exchange-httpd_kyc-info.c */