taler-exchange-kyc-tester.c (51844B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-kyc-tester.c 18 * @brief tool to test KYC integrations 19 * @author Christian Grothoff 20 * @defgroup request Request handling routines 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include <sched.h> 27 #include <sys/resource.h> 28 #include <limits.h> 29 #include "taler/taler_mhd_lib.h" 30 #include "taler/taler_json_lib.h" 31 #include "taler/taler_templating_lib.h" 32 #include "taler/taler_util.h" 33 #include "taler/taler_kyclogic_lib.h" 34 #include "taler/taler_kyclogic_plugin.h" 35 #include <gnunet/gnunet_mhd_compat.h> 36 37 38 /** 39 * @brief Context in which the exchange is processing 40 * all requests 41 */ 42 struct TEKT_RequestContext 43 { 44 45 /** 46 * Opaque parsing context. 47 */ 48 void *opaque_post_parsing_context; 49 50 /** 51 * Request handler responsible for this request. 52 */ 53 const struct TEKT_RequestHandler *rh; 54 55 /** 56 * Request URL (for logging). 57 */ 58 const char *url; 59 60 /** 61 * Connection we are processing. 62 */ 63 struct MHD_Connection *connection; 64 65 /** 66 * HTTP response to return (or NULL). 67 */ 68 struct MHD_Response *response; 69 70 /** 71 * @e rh-specific cleanup routine. Function called 72 * upon completion of the request that should 73 * clean up @a rh_ctx. Can be NULL. 74 */ 75 void 76 (*rh_cleaner)(struct TEKT_RequestContext *rc); 77 78 /** 79 * @e rh-specific context. Place where the request 80 * handler can associate state with this request. 81 * Can be NULL. 82 */ 83 void *rh_ctx; 84 85 /** 86 * Uploaded JSON body, if any. 87 */ 88 json_t *root; 89 90 /** 91 * HTTP status to return upon resume if @e response 92 * is non-NULL. 93 */ 94 unsigned int http_status; 95 96 }; 97 98 99 /** 100 * @brief Struct describing an URL and the handler for it. 101 */ 102 struct TEKT_RequestHandler 103 { 104 105 /** 106 * URL the handler is for (first part only). 107 */ 108 const char *url; 109 110 /** 111 * Method the handler is for. 112 */ 113 const char *method; 114 115 /** 116 * Callbacks for handling of the request. Which one is used 117 * depends on @e method. 118 */ 119 union 120 { 121 /** 122 * Function to call to handle a GET requests (and those 123 * with @e method NULL). 124 * 125 * @param rc context for the request 126 * @param mime_type the @e mime_type for the reply (hint, can be NULL) 127 * @param args array of arguments, needs to be of length @e args_expected 128 * @return MHD result code 129 */ 130 enum MHD_Result 131 (*get)(struct TEKT_RequestContext *rc, 132 const char *const args[]); 133 134 135 /** 136 * Function to call to handle a POST request. 137 * 138 * @param rc context for the request 139 * @param json uploaded JSON data 140 * @param args array of arguments, needs to be of length @e args_expected 141 * @return MHD result code 142 */ 143 enum MHD_Result 144 (*post)(struct TEKT_RequestContext *rc, 145 const json_t *root, 146 const char *const args[]); 147 148 } handler; 149 150 /** 151 * Number of arguments this handler expects in the @a args array. 152 */ 153 unsigned int nargs; 154 155 /** 156 * Is the number of arguments given in @e nargs only an upper bound, 157 * and calling with fewer arguments could be OK? 158 */ 159 bool nargs_is_upper_bound; 160 161 /** 162 * Mime type to use in reply (hint, can be NULL). 163 */ 164 const char *mime_type; 165 166 /** 167 * Raw data for the @e handler, can be NULL for none provided. 168 */ 169 const void *data; 170 171 /** 172 * Number of bytes in @e data, 0 for data is 0-terminated (!). 173 */ 174 size_t data_size; 175 176 /** 177 * Default response code. 0 for none provided. 178 */ 179 unsigned int response_code; 180 }; 181 182 183 /** 184 * Information we track per ongoing kyc-proof request. 185 */ 186 struct ProofRequestState 187 { 188 /** 189 * Kept in a DLL. 190 */ 191 struct ProofRequestState *next; 192 193 /** 194 * Kept in a DLL. 195 */ 196 struct ProofRequestState *prev; 197 198 /** 199 * Handle for operation with the plugin. 200 */ 201 struct TALER_KYCLOGIC_ProofHandle *ph; 202 203 /** 204 * Logic plugin we are using. 205 */ 206 struct TALER_KYCLOGIC_Plugin *logic; 207 208 /** 209 * HTTP request details. 210 */ 211 struct TEKT_RequestContext *rc; 212 213 }; 214 215 /** 216 * Head of DLL. 217 */ 218 static struct ProofRequestState *rs_head; 219 220 /** 221 * Tail of DLL. 222 */ 223 static struct ProofRequestState *rs_tail; 224 225 /** 226 * The exchange's configuration (global) 227 */ 228 static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg; 229 230 /** 231 * Our base URL. 232 */ 233 static char *TEKT_base_url; 234 235 /** 236 * Payto set via command-line (or otherwise random). 237 */ 238 static struct TALER_NormalizedPaytoHashP cmd_line_h_payto; 239 240 /** 241 * Provider user ID to use. 242 */ 243 static char *cmd_provider_user_id; 244 245 /** 246 * Provider legitimization ID to use. 247 */ 248 static char *cmd_provider_legitimization_id; 249 250 /** 251 * Custom legitimization rule in JSON given as 252 * a string. 253 */ 254 static char *lrs_s; 255 256 /** 257 * Type of the operation that triggers legitimization. 258 */ 259 static char *operation_s; 260 261 /** 262 * Amount threshold crossed that triggers some rule. 263 */ 264 static struct TALER_Amount trigger_amount; 265 266 /** 267 * Row ID to use, override with '-r' 268 */ 269 static unsigned int kyc_row_id = 42; 270 271 /** 272 * -P command-line option. 273 */ 274 static int print_h_payto; 275 276 /** 277 * -W command-line option. 278 */ 279 static int cmd_line_is_wallet; 280 281 /** 282 * -w command-line option. 283 */ 284 static int run_webservice; 285 286 /** 287 * -M command-line option. 288 */ 289 static int list_measures; 290 291 /** 292 * Value to return from main() 293 */ 294 static int global_ret; 295 296 /** 297 * -m command-line flag. 298 */ 299 static char *measure; 300 301 /** 302 * Legitimization rule set parsed from the command-line, 303 * or NULL if none was given. 304 */ 305 static struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; 306 307 /** 308 * Handle for ongoing initiation operation. 309 */ 310 static struct TALER_KYCLOGIC_InitiateHandle *ih; 311 312 /** 313 * KYC logic running for @e ih. 314 */ 315 static struct TALER_KYCLOGIC_Plugin *ih_logic; 316 317 /** 318 * True if we started any daemon. 319 */ 320 static bool have_daemons; 321 322 /** 323 * Context for all CURL operations (useful to the event loop) 324 */ 325 static struct GNUNET_CURL_Context *TEKT_curl_ctx; 326 327 /** 328 * Context for integrating #TEKT_curl_ctx with the 329 * GNUnet event loop. 330 */ 331 static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; 332 333 334 /** 335 * Context for the webhook. 336 */ 337 struct KycWebhookContext 338 { 339 340 /** 341 * Kept in a DLL while suspended. 342 */ 343 struct KycWebhookContext *next; 344 345 /** 346 * Kept in a DLL while suspended. 347 */ 348 struct KycWebhookContext *prev; 349 350 /** 351 * Details about the connection we are processing. 352 */ 353 struct TEKT_RequestContext *rc; 354 355 /** 356 * Plugin responsible for the webhook. 357 */ 358 struct TALER_KYCLOGIC_Plugin *plugin; 359 360 /** 361 * Configuration for the specific action. 362 */ 363 struct TALER_KYCLOGIC_ProviderDetails *pd; 364 365 /** 366 * Webhook activity. 367 */ 368 struct TALER_KYCLOGIC_WebhookHandle *wh; 369 370 /** 371 * HTTP response to return. 372 */ 373 struct MHD_Response *response; 374 375 /** 376 * Name of the configuration 377 * section defining the KYC logic. 378 */ 379 const char *section_name; 380 381 /** 382 * HTTP response code to return. 383 */ 384 unsigned int response_code; 385 386 /** 387 * #GNUNET_YES if we are suspended, 388 * #GNUNET_NO if not. 389 * #GNUNET_SYSERR if we had some error. 390 */ 391 enum GNUNET_GenericReturnValue suspended; 392 393 }; 394 395 396 /** 397 * Contexts are kept in a DLL while suspended. 398 */ 399 static struct KycWebhookContext *kwh_head; 400 401 /** 402 * Contexts are kept in a DLL while suspended. 403 */ 404 static struct KycWebhookContext *kwh_tail; 405 406 407 /** 408 * Resume processing the @a kwh request. 409 * 410 * @param kwh request to resume 411 */ 412 static void 413 kwh_resume (struct KycWebhookContext *kwh) 414 { 415 GNUNET_assert (GNUNET_YES == kwh->suspended); 416 kwh->suspended = GNUNET_NO; 417 GNUNET_CONTAINER_DLL_remove (kwh_head, 418 kwh_tail, 419 kwh); 420 MHD_resume_connection (kwh->rc->connection); 421 } 422 423 424 static void 425 kyc_webhook_cleanup (void) 426 { 427 struct KycWebhookContext *kwh; 428 429 while (NULL != (kwh = kwh_head)) 430 { 431 if (NULL != kwh->wh) 432 { 433 kwh->plugin->webhook_cancel (kwh->wh); 434 kwh->wh = NULL; 435 } 436 kwh_resume (kwh); 437 } 438 } 439 440 441 /** 442 * Function called with the result of a webhook operation. 443 * 444 * @param cls closure 445 * @param process_row legitimization process request the webhook was about 446 * @param account_id account the webhook was about 447 * @param is_wallet true if @a account_id is for a wallet 448 * @param provider_section configuration section of the logic 449 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 450 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 451 * @param status KYC status 452 * @param expiration until when is the KYC check valid 453 * @param attributes user attributes returned by the provider 454 * @param http_status HTTP status code of @a response 455 * @param[in] response to return to the HTTP client 456 */ 457 static void 458 webhook_finished_cb ( 459 void *cls, 460 uint64_t process_row, 461 const struct TALER_NormalizedPaytoHashP *account_id, 462 bool is_wallet, 463 const char *provider_section, 464 const char *provider_user_id, 465 const char *provider_legitimization_id, 466 enum TALER_KYCLOGIC_KycStatus status, 467 struct GNUNET_TIME_Absolute expiration, 468 const json_t *attributes, 469 unsigned int http_status, 470 struct MHD_Response *response) 471 { 472 struct KycWebhookContext *kwh = cls; 473 474 (void) expiration; 475 (void) provider_section; 476 (void) is_wallet; 477 kwh->wh = NULL; 478 if ( (NULL != account_id) && 479 (0 != GNUNET_memcmp (account_id, 480 &cmd_line_h_payto)) ) 481 { 482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 483 "Received webhook for unexpected account\n"); 484 } 485 if ( (NULL != provider_user_id) && 486 (NULL != cmd_provider_user_id) && 487 (0 != strcmp (provider_user_id, 488 cmd_provider_user_id)) ) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 491 "Received webhook for unexpected provider user ID (%s)\n", 492 provider_user_id); 493 } 494 if ( (NULL != provider_legitimization_id) && 495 (NULL != cmd_provider_legitimization_id) && 496 (0 != strcmp (provider_legitimization_id, 497 cmd_provider_legitimization_id)) ) 498 { 499 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 500 "Received webhook for unexpected provider legitimization ID (%s)\n", 501 provider_legitimization_id); 502 } 503 switch (status) 504 { 505 case TALER_KYCLOGIC_STATUS_SUCCESS: 506 /* _successfully_ resumed case */ 507 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 508 "KYC successful for user `%s' (legi: %s)\n", 509 provider_user_id, 510 provider_legitimization_id); 511 GNUNET_break (NULL != attributes); 512 fprintf (stderr, 513 "Extracted attributes:\n"); 514 json_dumpf (attributes, 515 stderr, 516 JSON_INDENT (2)); 517 break; 518 default: 519 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 520 "KYC status of %s/%s (process #%llu) is %d\n", 521 provider_user_id, 522 provider_legitimization_id, 523 (unsigned long long) process_row, 524 status); 525 break; 526 } 527 kwh->response = response; 528 kwh->response_code = http_status; 529 kwh_resume (kwh); 530 TALER_MHD_daemon_trigger (); 531 } 532 533 534 /** 535 * Function called to clean up a context. 536 * 537 * @param rc request context 538 */ 539 static void 540 clean_kwh (struct TEKT_RequestContext *rc) 541 { 542 struct KycWebhookContext *kwh = rc->rh_ctx; 543 544 if (NULL != kwh->wh) 545 { 546 kwh->plugin->webhook_cancel (kwh->wh); 547 kwh->wh = NULL; 548 } 549 if (NULL != kwh->response) 550 { 551 MHD_destroy_response (kwh->response); 552 kwh->response = NULL; 553 } 554 GNUNET_free (kwh); 555 } 556 557 558 /** 559 * Function the plugin can use to lookup an 560 * @a h_payto by @a provider_legitimization_id. 561 * 562 * @param pg database connection, NULL 563 * @param provider_section 564 * @param provider_legitimization_id legi to look up 565 * @param[out] h_payto where to write the result 566 * @param[out] is_wallet set to true if @a h_payto is for a wallet 567 * @param[out] legi_row where to write the row ID for the legitimization ID 568 * @return database transaction status 569 */ 570 static enum GNUNET_DB_QueryStatus 571 kyc_provider_account_lookup ( 572 struct TALER_EXCHANGEDB_PostgresContext *pg, 573 const char *provider_section, 574 const char *provider_legitimization_id, 575 struct TALER_NormalizedPaytoHashP *h_payto, 576 bool *is_wallet, 577 uint64_t *legi_row) 578 { 579 (void) pg; 580 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 581 "Simulated account lookup using `%s/%s'\n", 582 provider_section, 583 provider_legitimization_id); 584 *h_payto = cmd_line_h_payto; 585 *legi_row = kyc_row_id; 586 *is_wallet = (0 != cmd_line_is_wallet); 587 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 588 } 589 590 591 /** 592 * Handle a (GET or POST) "/kyc-webhook" request. 593 * 594 * @param rc request to handle 595 * @param method HTTP request method used by the client 596 * @param root uploaded JSON body (can be NULL) 597 * @param args one argument with the legitimization_uuid 598 * @return MHD result code 599 */ 600 static enum MHD_Result 601 handler_kyc_webhook_generic ( 602 struct TEKT_RequestContext *rc, 603 const char *method, 604 const json_t *root, 605 const char *const args[]) 606 { 607 struct KycWebhookContext *kwh = rc->rh_ctx; 608 609 if (NULL == kwh) 610 { /* first time */ 611 kwh = GNUNET_new (struct KycWebhookContext); 612 kwh->rc = rc; 613 rc->rh_ctx = kwh; 614 rc->rh_cleaner = &clean_kwh; 615 616 if ( (NULL == args[0]) || 617 (GNUNET_OK != 618 TALER_KYCLOGIC_lookup_logic (args[0], 619 &kwh->plugin, 620 &kwh->pd, 621 &kwh->section_name)) ) 622 { 623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 624 "KYC logic `%s' unknown (check KYC provider configuration)\n", 625 args[0]); 626 return TALER_MHD_reply_with_error (rc->connection, 627 MHD_HTTP_NOT_FOUND, 628 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 629 args[0]); 630 } 631 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 632 "Calling KYC provider specific webhook\n"); 633 kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, 634 kwh->pd, 635 &kyc_provider_account_lookup, 636 NULL, 637 method, 638 &args[1], 639 rc->connection, 640 root, 641 &webhook_finished_cb, 642 kwh); 643 if (NULL == kwh->wh) 644 { 645 GNUNET_break_op (0); 646 return TALER_MHD_reply_with_error (rc->connection, 647 MHD_HTTP_INTERNAL_SERVER_ERROR, 648 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 649 "failed to run webhook logic"); 650 } 651 kwh->suspended = GNUNET_YES; 652 GNUNET_CONTAINER_DLL_insert (kwh_head, 653 kwh_tail, 654 kwh); 655 MHD_suspend_connection (rc->connection); 656 return MHD_YES; 657 } 658 659 if (NULL != kwh->response) 660 { 661 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 662 "Returning queued reply for KWH\n"); 663 /* handle _failed_ resumed cases */ 664 return MHD_queue_response (rc->connection, 665 kwh->response_code, 666 kwh->response); 667 } 668 669 /* We resumed, but got no response? This should 670 not happen. */ 671 GNUNET_assert (0); 672 return TALER_MHD_reply_with_error (rc->connection, 673 MHD_HTTP_INTERNAL_SERVER_ERROR, 674 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 675 "resumed without response"); 676 } 677 678 679 /** 680 * Handle a GET "/kyc-webhook" request. 681 * 682 * @param rc request to handle 683 * @param args one argument with the legitimization_uuid 684 * @return MHD result code 685 */ 686 static enum MHD_Result 687 handler_kyc_webhook_get ( 688 struct TEKT_RequestContext *rc, 689 const char *const args[]) 690 { 691 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 692 "Webhook GET triggered\n"); 693 return handler_kyc_webhook_generic (rc, 694 MHD_HTTP_METHOD_GET, 695 NULL, 696 args); 697 } 698 699 700 /** 701 * Handle a POST "/kyc-webhook" request. 702 * 703 * @param rc request to handle 704 * @param root uploaded JSON body (can be NULL) 705 * @param args one argument with the legitimization_uuid 706 * @return MHD result code 707 */ 708 static enum MHD_Result 709 handler_kyc_webhook_post ( 710 struct TEKT_RequestContext *rc, 711 const json_t *root, 712 const char *const args[]) 713 { 714 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 715 "Webhook POST triggered\n"); 716 return handler_kyc_webhook_generic (rc, 717 MHD_HTTP_METHOD_POST, 718 root, 719 args); 720 } 721 722 723 /** 724 * Function called with the result of a proof check operation. 725 * 726 * Note that the "decref" for the @a response 727 * will be done by the callee and MUST NOT be done by the plugin. 728 * 729 * @param cls closure with the `struct ProofRequestState` 730 * @param status KYC status 731 * @param provider_name name of the KYC provider 732 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 733 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 734 * @param expiration until when is the KYC check valid 735 * @param attributes attributes about the user 736 * @param http_status HTTP status code of @a response 737 * @param[in] response to return to the HTTP client 738 */ 739 static void 740 proof_cb ( 741 void *cls, 742 enum TALER_KYCLOGIC_KycStatus status, 743 const char *provider_name, 744 const char *provider_user_id, 745 const char *provider_legitimization_id, 746 struct GNUNET_TIME_Absolute expiration, 747 const json_t *attributes, 748 unsigned int http_status, 749 struct MHD_Response *response) 750 { 751 struct ProofRequestState *rs = cls; 752 753 (void) expiration; 754 (void) provider_name; 755 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 756 "KYC legitimization %s completed with status %d (%u) for %s\n", 757 provider_legitimization_id, 758 status, 759 http_status, 760 provider_user_id); 761 if (TALER_KYCLOGIC_STATUS_SUCCESS == status) 762 { 763 GNUNET_break (NULL != attributes); 764 fprintf (stderr, 765 "Extracted attributes:\n"); 766 json_dumpf (attributes, 767 stderr, 768 JSON_INDENT (2)); 769 } 770 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 771 "Returning response %p with status %u\n", 772 response, 773 http_status); 774 rs->rc->response = response; 775 rs->rc->http_status = http_status; 776 GNUNET_CONTAINER_DLL_remove (rs_head, 777 rs_tail, 778 rs); 779 MHD_resume_connection (rs->rc->connection); 780 TALER_MHD_daemon_trigger (); 781 GNUNET_free (rs); 782 } 783 784 785 /** 786 * Function called when we receive a 'GET' to the 787 * '/kyc-proof' endpoint. 788 * 789 * @param rc request context 790 * @param args remaining URL arguments; 791 * args[0] should be the logic plugin name 792 */ 793 static enum MHD_Result 794 handler_kyc_proof_get ( 795 struct TEKT_RequestContext *rc, 796 const char *const args[1]) 797 { 798 struct TALER_NormalizedPaytoHashP h_payto; 799 struct TALER_KYCLOGIC_ProviderDetails *pd; 800 struct TALER_KYCLOGIC_Plugin *logic; 801 struct ProofRequestState *rs; 802 const char *section_name; 803 const char *h_paytos; 804 805 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 806 "GET /kyc-proof triggered\n"); 807 if (NULL == args[0]) 808 { 809 GNUNET_break_op (0); 810 return TALER_MHD_reply_with_error (rc->connection, 811 MHD_HTTP_NOT_FOUND, 812 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 813 "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required"); 814 } 815 h_paytos = MHD_lookup_connection_value (rc->connection, 816 MHD_GET_ARGUMENT_KIND, 817 "state"); 818 if (NULL == h_paytos) 819 { 820 GNUNET_break_op (0); 821 return TALER_MHD_reply_with_error (rc->connection, 822 MHD_HTTP_BAD_REQUEST, 823 TALER_EC_GENERIC_PARAMETER_MISSING, 824 "h_payto"); 825 } 826 if (GNUNET_OK != 827 GNUNET_STRINGS_string_to_data (h_paytos, 828 strcspn (h_paytos, 829 "-"), 830 &h_payto, 831 sizeof (h_payto))) 832 { 833 GNUNET_break_op (0); 834 return TALER_MHD_reply_with_error (rc->connection, 835 MHD_HTTP_BAD_REQUEST, 836 TALER_EC_GENERIC_PARAMETER_MALFORMED, 837 "h_payto"); 838 } 839 if (0 != 840 GNUNET_memcmp (&h_payto, 841 &cmd_line_h_payto)) 842 { 843 GNUNET_break_op (0); 844 return TALER_MHD_reply_with_error (rc->connection, 845 MHD_HTTP_NOT_FOUND, 846 TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, 847 "h_payto"); 848 } 849 850 if (GNUNET_OK != 851 TALER_KYCLOGIC_lookup_logic (args[0], 852 &logic, 853 &pd, 854 §ion_name)) 855 { 856 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 857 "Could not initiate KYC with provider `%s' (configuration error?)\n", 858 args[0]); 859 return TALER_MHD_reply_with_error (rc->connection, 860 MHD_HTTP_NOT_FOUND, 861 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 862 args[0]); 863 } 864 rs = GNUNET_new (struct ProofRequestState); 865 rs->rc = rc; 866 rs->logic = logic; 867 MHD_suspend_connection (rc->connection); 868 GNUNET_CONTAINER_DLL_insert (rs_head, 869 rs_tail, 870 rs); 871 rs->ph = logic->proof (logic->cls, 872 pd, 873 rc->connection, 874 &h_payto, 875 kyc_row_id, 876 cmd_provider_user_id, 877 cmd_provider_legitimization_id, 878 &proof_cb, 879 rs); 880 GNUNET_assert (NULL != rs->ph); 881 return MHD_YES; 882 } 883 884 885 /** 886 * Function called whenever MHD is done with a request. If the 887 * request was a POST, we may have stored a `struct Buffer *` in the 888 * @a con_cls that might still need to be cleaned up. Call the 889 * respective function to free the memory. 890 * 891 * @param cls client-defined closure 892 * @param connection connection handle 893 * @param con_cls value as set by the last call to 894 * the #MHD_AccessHandlerCallback 895 * @param toe reason for request termination 896 * @see #MHD_OPTION_NOTIFY_COMPLETED 897 * @ingroup request 898 */ 899 static void 900 handle_mhd_completion_callback (void *cls, 901 struct MHD_Connection *connection, 902 void **con_cls, 903 enum MHD_RequestTerminationCode toe) 904 { 905 struct TEKT_RequestContext *rc = *con_cls; 906 907 (void) cls; 908 if (NULL == rc) 909 return; 910 if (NULL != rc->rh_cleaner) 911 rc->rh_cleaner (rc); 912 { 913 #if MHD_VERSION >= 0x00097304 914 const union MHD_ConnectionInfo *ci; 915 unsigned int http_status = 0; 916 917 ci = MHD_get_connection_info (connection, 918 MHD_CONNECTION_INFO_HTTP_STATUS); 919 if (NULL != ci) 920 http_status = ci->http_status; 921 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 922 "Request for `%s' completed with HTTP status %u (%d)\n", 923 rc->url, 924 http_status, 925 toe); 926 #else 927 (void) connection; 928 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 929 "Request for `%s' completed (%d)\n", 930 rc->url, 931 toe); 932 #endif 933 } 934 935 TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context); 936 /* Sanity-check that we didn't leave any transactions hanging */ 937 if (NULL != rc->root) 938 json_decref (rc->root); 939 GNUNET_free (rc); 940 *con_cls = NULL; 941 } 942 943 944 /** 945 * We found a request handler responsible for handling a request. Parse the 946 * @a upload_data (if applicable) and the @a url and call the 947 * handler. 948 * 949 * @param rc request context 950 * @param url rest of the URL to parse 951 * @param upload_data upload data to parse (if available) 952 * @param[in,out] upload_data_size number of bytes in @a upload_data 953 * @return MHD result code 954 */ 955 static enum MHD_Result 956 proceed_with_handler (struct TEKT_RequestContext *rc, 957 const char *url, 958 const char *upload_data, 959 size_t *upload_data_size) 960 { 961 const struct TEKT_RequestHandler *rh = rc->rh; 962 const char *args[rh->nargs + 2]; 963 size_t ulen = strlen (url) + 1; 964 enum MHD_Result ret; 965 966 /* We do check for "ulen" here, because we'll later stack-allocate a buffer 967 of that size and don't want to enable malicious clients to cause us 968 huge stack allocations. */ 969 if (ulen > 512) 970 { 971 /* 512 is simply "big enough", as it is bigger than "6 * 54", 972 which is the longest URL format we ever get (for 973 /deposits/). The value should be adjusted if we ever define protocol 974 endpoints with plausibly longer inputs. */ 975 GNUNET_break_op (0); 976 return TALER_MHD_reply_with_error ( 977 rc->connection, 978 MHD_HTTP_URI_TOO_LONG, 979 TALER_EC_GENERIC_URI_TOO_LONG, 980 url); 981 } 982 983 /* All POST endpoints come with a body in JSON format. So we parse 984 the JSON here. */ 985 if ( (NULL == rc->root) && 986 (0 == strcasecmp (rh->method, 987 MHD_HTTP_METHOD_POST)) ) 988 { 989 enum GNUNET_GenericReturnValue res; 990 991 res = TALER_MHD_parse_post_json ( 992 rc->connection, 993 &rc->opaque_post_parsing_context, 994 upload_data, 995 upload_data_size, 996 &rc->root); 997 if (GNUNET_SYSERR == res) 998 { 999 GNUNET_assert (NULL == rc->root); 1000 GNUNET_break (0); 1001 return MHD_NO; /* bad upload, could not even generate error */ 1002 } 1003 if ( (GNUNET_NO == res) || 1004 (NULL == rc->root) ) 1005 { 1006 GNUNET_assert (NULL == rc->root); 1007 return MHD_YES; /* so far incomplete upload or parser error */ 1008 } 1009 } 1010 1011 { 1012 char d[ulen]; 1013 unsigned int i; 1014 char *sp; 1015 1016 /* Parse command-line arguments */ 1017 /* make a copy of 'url' because 'strtok_r()' will modify */ 1018 GNUNET_memcpy (d, 1019 url, 1020 ulen); 1021 i = 0; 1022 args[i++] = strtok_r (d, "/", &sp); 1023 while ( (NULL != args[i - 1]) && 1024 (i <= rh->nargs + 1) ) 1025 args[i++] = strtok_r (NULL, "/", &sp); 1026 /* make sure above loop ran nicely until completion, and also 1027 that there is no excess data in 'd' afterwards */ 1028 if ( ( (rh->nargs_is_upper_bound) && 1029 (i - 1 > rh->nargs) ) || 1030 ( (! rh->nargs_is_upper_bound) && 1031 (i - 1 != rh->nargs) ) ) 1032 { 1033 char emsg[128 + 512]; 1034 1035 GNUNET_snprintf (emsg, 1036 sizeof (emsg), 1037 "Got %u+/%u segments for `%s' request (`%s')", 1038 i - 1, 1039 rh->nargs, 1040 rh->url, 1041 url); 1042 GNUNET_break_op (0); 1043 return TALER_MHD_reply_with_error ( 1044 rc->connection, 1045 MHD_HTTP_NOT_FOUND, 1046 TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, 1047 emsg); 1048 } 1049 GNUNET_assert (NULL == args[i - 1]); 1050 1051 /* Above logic ensures that 'root' is exactly non-NULL for POST operations, 1052 so we test for 'root' to decide which handler to invoke. */ 1053 if (NULL != rc->root) 1054 ret = rh->handler.post (rc, 1055 rc->root, 1056 args); 1057 else /* We also only have "POST" or "GET" in the API for at this point 1058 (OPTIONS/HEAD are taken care of earlier) */ 1059 ret = rh->handler.get (rc, 1060 args); 1061 } 1062 return ret; 1063 } 1064 1065 1066 static void 1067 rh_cleaner_cb (struct TEKT_RequestContext *rc) 1068 { 1069 if (NULL != rc->response) 1070 { 1071 MHD_destroy_response (rc->response); 1072 rc->response = NULL; 1073 } 1074 if (NULL != rc->root) 1075 { 1076 json_decref (rc->root); 1077 rc->root = NULL; 1078 } 1079 } 1080 1081 1082 /** 1083 * Handle incoming HTTP request. 1084 * 1085 * @param cls closure for MHD daemon (unused) 1086 * @param connection the connection 1087 * @param url the requested url 1088 * @param method the method (POST, GET, ...) 1089 * @param version HTTP version (ignored) 1090 * @param upload_data request data 1091 * @param upload_data_size size of @a upload_data in bytes 1092 * @param con_cls closure for request (a `struct TEKT_RequestContext *`) 1093 * @return MHD result code 1094 */ 1095 static enum MHD_Result 1096 handle_mhd_request (void *cls, 1097 struct MHD_Connection *connection, 1098 const char *url, 1099 const char *method, 1100 const char *version, 1101 const char *upload_data, 1102 size_t *upload_data_size, 1103 void **con_cls) 1104 { 1105 static struct TEKT_RequestHandler handlers[] = { 1106 /* simulated KYC endpoints */ 1107 { 1108 .url = "kyc-proof", 1109 .method = MHD_HTTP_METHOD_GET, 1110 .handler.get = &handler_kyc_proof_get, 1111 .nargs = 1 1112 }, 1113 { 1114 .url = "kyc-webhook", 1115 .method = MHD_HTTP_METHOD_POST, 1116 .handler.post = &handler_kyc_webhook_post, 1117 .nargs = 128, 1118 .nargs_is_upper_bound = true 1119 }, 1120 { 1121 .url = "kyc-webhook", 1122 .method = MHD_HTTP_METHOD_GET, 1123 .handler.get = &handler_kyc_webhook_get, 1124 .nargs = 128, 1125 .nargs_is_upper_bound = true 1126 }, 1127 /* mark end of list */ 1128 { 1129 .url = NULL 1130 } 1131 }; 1132 struct TEKT_RequestContext *rc = *con_cls; 1133 1134 (void) cls; 1135 (void) version; 1136 if (NULL == rc) 1137 { 1138 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1139 "Handling new request\n"); 1140 /* We're in a new async scope! */ 1141 rc = *con_cls = GNUNET_new (struct TEKT_RequestContext); 1142 rc->url = url; 1143 rc->connection = connection; 1144 rc->rh_cleaner = &rh_cleaner_cb; 1145 } 1146 if (NULL != rc->response) 1147 { 1148 return MHD_queue_response (rc->connection, 1149 rc->http_status, 1150 rc->response); 1151 } 1152 1153 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1154 "Handling request (%s) for URL '%s'\n", 1155 method, 1156 url); 1157 /* on repeated requests, check our cache first */ 1158 if (NULL != rc->rh) 1159 { 1160 const char *start; 1161 1162 if ('\0' == url[0]) 1163 /* strange, should start with '/', treat as just "/" */ 1164 url = "/"; 1165 start = strchr (url + 1, '/'); 1166 if (NULL == start) 1167 start = ""; 1168 return proceed_with_handler (rc, 1169 start, 1170 upload_data, 1171 upload_data_size); 1172 } 1173 if (0 == strcasecmp (method, 1174 MHD_HTTP_METHOD_HEAD)) 1175 method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ 1176 1177 /* parse first part of URL */ 1178 { 1179 bool found = false; 1180 size_t tok_size; 1181 const char *tok; 1182 const char *rest; 1183 1184 if ('\0' == url[0]) 1185 /* strange, should start with '/', treat as just "/" */ 1186 url = "/"; 1187 tok = url + 1; 1188 rest = strchr (tok, '/'); 1189 if (NULL == rest) 1190 { 1191 tok_size = strlen (tok); 1192 } 1193 else 1194 { 1195 tok_size = rest - tok; 1196 rest++; /* skip over '/' */ 1197 } 1198 for (unsigned int i = 0; NULL != handlers[i].url; i++) 1199 { 1200 struct TEKT_RequestHandler *rh = &handlers[i]; 1201 1202 if ( (0 != strncmp (tok, 1203 rh->url, 1204 tok_size)) || 1205 (tok_size != strlen (rh->url) ) ) 1206 continue; 1207 found = true; 1208 /* The URL is a match! What we now do depends on the method. */ 1209 if (0 == strcasecmp (method, 1210 MHD_HTTP_METHOD_OPTIONS)) 1211 { 1212 return TALER_MHD_reply_cors_preflight (connection); 1213 } 1214 GNUNET_assert (NULL != rh->method); 1215 if (0 != strcasecmp (method, 1216 rh->method)) 1217 { 1218 found = true; 1219 continue; 1220 } 1221 /* cache to avoid the loop next time */ 1222 rc->rh = rh; 1223 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1224 "Handler found for %s '%s'\n", 1225 method, 1226 url); 1227 return MHD_YES; 1228 } 1229 1230 if (found) 1231 { 1232 /* we found a matching address, but the method is wrong */ 1233 struct MHD_Response *reply; 1234 enum MHD_Result ret; 1235 char *allowed = NULL; 1236 1237 GNUNET_break_op (0); 1238 for (unsigned int i = 0; NULL != handlers[i].url; i++) 1239 { 1240 struct TEKT_RequestHandler *rh = &handlers[i]; 1241 1242 if ( (0 != strncmp (tok, 1243 rh->url, 1244 tok_size)) || 1245 (tok_size != strlen (rh->url) ) ) 1246 continue; 1247 if (NULL == allowed) 1248 { 1249 allowed = GNUNET_strdup (rh->method); 1250 } 1251 else 1252 { 1253 char *tmp; 1254 1255 GNUNET_asprintf (&tmp, 1256 "%s, %s", 1257 allowed, 1258 rh->method); 1259 GNUNET_free (allowed); 1260 allowed = tmp; 1261 } 1262 if (0 == strcasecmp (rh->method, 1263 MHD_HTTP_METHOD_GET)) 1264 { 1265 char *tmp; 1266 1267 GNUNET_asprintf (&tmp, 1268 "%s, %s", 1269 allowed, 1270 MHD_HTTP_METHOD_HEAD); 1271 GNUNET_free (allowed); 1272 allowed = tmp; 1273 } 1274 } 1275 reply = TALER_MHD_make_error ( 1276 TALER_EC_GENERIC_METHOD_INVALID, 1277 method); 1278 GNUNET_break ( 1279 MHD_YES == 1280 MHD_add_response_header (reply, 1281 MHD_HTTP_HEADER_ALLOW, 1282 allowed)); 1283 GNUNET_free (allowed); 1284 ret = MHD_queue_response (connection, 1285 MHD_HTTP_METHOD_NOT_ALLOWED, 1286 reply); 1287 MHD_destroy_response (reply); 1288 return ret; 1289 } 1290 } 1291 1292 /* No handler matches, generate not found */ 1293 return TALER_MHD_reply_with_error (connection, 1294 MHD_HTTP_NOT_FOUND, 1295 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 1296 url); 1297 } 1298 1299 1300 /** 1301 * Load configuration parameters for the exchange 1302 * server into the corresponding global variables. 1303 * 1304 * @return #GNUNET_OK on success 1305 */ 1306 static enum GNUNET_GenericReturnValue 1307 exchange_serve_process_config (void) 1308 { 1309 if (GNUNET_OK != 1310 GNUNET_CONFIGURATION_get_value_string (TEKT_cfg, 1311 "exchange", 1312 "BASE_URL", 1313 &TEKT_base_url)) 1314 { 1315 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1316 "exchange", 1317 "BASE_URL"); 1318 return GNUNET_SYSERR; 1319 } 1320 if (! TALER_url_valid_charset (TEKT_base_url)) 1321 { 1322 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1323 "exchange", 1324 "BASE_URL", 1325 "invalid URL"); 1326 return GNUNET_SYSERR; 1327 } 1328 1329 return GNUNET_OK; 1330 } 1331 1332 1333 /** 1334 * Function run on shutdown. 1335 * 1336 * @param cls NULL 1337 */ 1338 static void 1339 do_shutdown (void *cls) 1340 { 1341 struct ProofRequestState *rs; 1342 1343 (void) cls; 1344 while (NULL != (rs = rs_head)) 1345 { 1346 GNUNET_CONTAINER_DLL_remove (rs_head, 1347 rs_tail, 1348 rs); 1349 rs->logic->proof_cancel (rs->ph); 1350 MHD_resume_connection (rs->rc->connection); 1351 GNUNET_free (rs); 1352 } 1353 if (NULL != ih) 1354 { 1355 ih_logic->initiate_cancel (ih); 1356 ih = NULL; 1357 } 1358 kyc_webhook_cleanup (); 1359 TALER_KYCLOGIC_rules_free (lrs); 1360 lrs = NULL; 1361 TALER_KYCLOGIC_kyc_done (); 1362 TALER_MHD_daemons_halt (); 1363 TALER_MHD_daemons_destroy (); 1364 if (NULL != TEKT_curl_ctx) 1365 { 1366 GNUNET_CURL_fini (TEKT_curl_ctx); 1367 TEKT_curl_ctx = NULL; 1368 } 1369 if (NULL != exchange_curl_rc) 1370 { 1371 GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); 1372 exchange_curl_rc = NULL; 1373 } 1374 TALER_TEMPLATING_done (); 1375 } 1376 1377 1378 /** 1379 * Function called with the result of a KYC initiation 1380 * operation. 1381 * 1382 * @param cls closure 1383 * @param ec #TALER_EC_NONE on success 1384 * @param redirect_url set to where to redirect the user on success, NULL on failure 1385 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 1386 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 1387 * @param error_msg_hint set to additional details to return to user, NULL on success 1388 */ 1389 static void 1390 initiate_cb ( 1391 void *cls, 1392 enum TALER_ErrorCode ec, 1393 const char *redirect_url, 1394 const char *provider_user_id, 1395 const char *provider_legitimization_id, 1396 const char *error_msg_hint) 1397 { 1398 (void) cls; 1399 ih = NULL; 1400 if (TALER_EC_NONE != ec) 1401 { 1402 fprintf (stderr, 1403 "Failed to start KYC process: %s (#%d)\n", 1404 error_msg_hint, 1405 ec); 1406 global_ret = EXIT_FAILURE; 1407 GNUNET_SCHEDULER_shutdown (); 1408 return; 1409 } 1410 { 1411 char *s; 1412 1413 s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto, 1414 sizeof (cmd_line_h_payto)); 1415 if (NULL != provider_user_id) 1416 { 1417 fprintf (stdout, 1418 "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -u '%s' -U '%s' -p %s\n", 1419 redirect_url, 1420 provider_user_id, 1421 provider_legitimization_id, 1422 s); 1423 } 1424 else 1425 { 1426 fprintf (stdout, 1427 "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -U '%s' -p %s\n", 1428 redirect_url, 1429 provider_legitimization_id, 1430 s); 1431 } 1432 GNUNET_free (s); 1433 } 1434 GNUNET_free (cmd_provider_user_id); 1435 GNUNET_free (cmd_provider_legitimization_id); 1436 if (NULL != provider_user_id) 1437 cmd_provider_user_id = GNUNET_strdup (provider_user_id); 1438 if (NULL != provider_legitimization_id) 1439 cmd_provider_legitimization_id = GNUNET_strdup (provider_legitimization_id); 1440 if (! run_webservice) 1441 GNUNET_SCHEDULER_shutdown (); 1442 } 1443 1444 1445 /** 1446 * Function called to iterate over KYC-relevant 1447 * transaction amounts for a particular time range. 1448 * Called within a database transaction, so must 1449 * not start a new one. 1450 * 1451 * @param cls closure, identifies the event type and 1452 * account to iterate over events for 1453 * @param limit maximum time-range for which events 1454 * should be fetched (timestamp in the past) 1455 * @param cb function to call on each event found, 1456 * events must be returned in reverse chronological 1457 * order 1458 * @param cb_cls closure for @a cb 1459 * @return transaction status 1460 */ 1461 static enum GNUNET_DB_QueryStatus 1462 amount_iterator ( 1463 void *cls, 1464 struct GNUNET_TIME_Absolute limit, 1465 TALER_KYCLOGIC_KycAmountCallback cb, 1466 void *cb_cls) 1467 { 1468 const struct TALER_Amount *amount = cls; 1469 struct GNUNET_TIME_Absolute date; 1470 enum GNUNET_GenericReturnValue ret; 1471 1472 date = GNUNET_TIME_absolute_subtract (limit, 1473 GNUNET_TIME_UNIT_SECONDS); 1474 1475 ret = cb (cb_cls, 1476 amount, 1477 date); 1478 GNUNET_break (GNUNET_SYSERR != ret); 1479 if (GNUNET_OK != ret) 1480 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1481 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 1482 } 1483 1484 1485 /** 1486 * Callback invoked on every listen socket to start the 1487 * respective MHD HTTP daemon. 1488 * 1489 * @param cls unused 1490 * @param lsock the listen socket 1491 */ 1492 static void 1493 start_daemon (void *cls, 1494 int lsock) 1495 { 1496 struct MHD_Daemon *mhd; 1497 1498 (void) cls; 1499 GNUNET_assert (-1 != lsock); 1500 mhd = MHD_start_daemon ( 1501 MHD_USE_SUSPEND_RESUME 1502 | MHD_USE_PIPE_FOR_SHUTDOWN 1503 | MHD_USE_DEBUG | MHD_USE_DUAL_STACK 1504 | MHD_USE_TCP_FASTOPEN, 1505 0, 1506 NULL, NULL, 1507 &handle_mhd_request, NULL, 1508 MHD_OPTION_LISTEN_SOCKET, 1509 lsock, 1510 MHD_OPTION_EXTERNAL_LOGGER, 1511 &TALER_MHD_handle_logs, 1512 NULL, 1513 MHD_OPTION_NOTIFY_COMPLETED, 1514 &handle_mhd_completion_callback, 1515 NULL, 1516 MHD_OPTION_END); 1517 if (NULL == mhd) 1518 { 1519 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1520 "Failed to launch HTTP service. Is the port in use?\n"); 1521 GNUNET_SCHEDULER_shutdown (); 1522 return; 1523 } 1524 have_daemons = true; 1525 TALER_MHD_daemon_start (mhd); 1526 } 1527 1528 1529 /** 1530 * Main function that will be run by the scheduler. 1531 * 1532 * @param cls closure 1533 * @param args remaining command-line arguments 1534 * @param cfgfile name of the configuration file used (for saving, can be 1535 * NULL!) 1536 * @param config configuration 1537 */ 1538 static void 1539 run (void *cls, 1540 char *const *args, 1541 const char *cfgfile, 1542 const struct GNUNET_CONFIGURATION_Handle *config) 1543 { 1544 enum TALER_KYCLOGIC_KycTriggerEvent event; 1545 const struct TALER_KYCLOGIC_KycRule *rule = NULL; 1546 1547 (void) cls; 1548 (void) args; 1549 (void ) cfgfile; 1550 if (GNUNET_OK != 1551 TALER_TEMPLATING_init (TALER_EXCHANGE_project_data ())) 1552 { 1553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1554 "Could not load templates. Installation broken.\n"); 1555 global_ret = EXIT_FAILURE; 1556 return; 1557 } 1558 if (NULL != operation_s) 1559 { 1560 if (GNUNET_OK != 1561 TALER_KYCLOGIC_kyc_trigger_from_string (operation_s, 1562 &event)) 1563 { 1564 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1565 "Malformed operation type `%s'\n", 1566 operation_s); 1567 global_ret = EXIT_FAILURE; 1568 return; 1569 } 1570 } 1571 1572 if (NULL != lrs_s) 1573 { 1574 json_t *jlrs; 1575 json_error_t err; 1576 1577 jlrs = json_loads (lrs_s, 1578 JSON_REJECT_DUPLICATES, 1579 &err); 1580 if (NULL == jlrs) 1581 { 1582 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1583 "Malformed JSON for legitimization rule set: %s at %d\n", 1584 err.text, 1585 err.position); 1586 global_ret = EXIT_INVALIDARGUMENT; 1587 return; 1588 } 1589 lrs = TALER_KYCLOGIC_rules_parse (jlrs); 1590 json_decref (jlrs); 1591 if (NULL == lrs) 1592 { 1593 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1594 "Malformed legitimization rule set `%s'\n", 1595 lrs_s); 1596 global_ret = EXIT_INVALIDARGUMENT; 1597 return; 1598 } 1599 } 1600 1601 if (print_h_payto) 1602 { 1603 char *s; 1604 1605 s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto, 1606 sizeof (cmd_line_h_payto)); 1607 fprintf (stdout, 1608 "%s\n", 1609 s); 1610 GNUNET_free (s); 1611 } 1612 TALER_MHD_setup (TALER_MHD_GO_NONE); 1613 TEKT_cfg = config; 1614 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1615 NULL); 1616 if (GNUNET_OK != 1617 TALER_KYCLOGIC_kyc_init (config, 1618 cfgfile)) 1619 { 1620 global_ret = EXIT_NOTCONFIGURED; 1621 GNUNET_SCHEDULER_shutdown (); 1622 return; 1623 } 1624 if (GNUNET_OK != 1625 exchange_serve_process_config ()) 1626 { 1627 global_ret = EXIT_NOTCONFIGURED; 1628 GNUNET_SCHEDULER_shutdown (); 1629 return; 1630 } 1631 global_ret = EXIT_SUCCESS; 1632 if (NULL != operation_s) 1633 { 1634 enum GNUNET_DB_QueryStatus qs; 1635 1636 if (GNUNET_OK != 1637 TALER_amount_is_valid (&trigger_amount)) 1638 { 1639 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1640 "Trigger amount command-line option (-t) required\n"); 1641 global_ret = EXIT_INVALIDARGUMENT; 1642 GNUNET_SCHEDULER_shutdown (); 1643 return; 1644 } 1645 { 1646 struct TALER_Amount next_threshold; 1647 1648 qs = TALER_KYCLOGIC_kyc_test_required ( 1649 event, 1650 lrs, 1651 &amount_iterator, 1652 &trigger_amount, 1653 &rule, 1654 &next_threshold); 1655 } 1656 switch (qs) 1657 { 1658 case GNUNET_DB_STATUS_HARD_ERROR: 1659 GNUNET_break (0); 1660 global_ret = EXIT_NOTCONFIGURED; 1661 GNUNET_SCHEDULER_shutdown (); 1662 return; 1663 case GNUNET_DB_STATUS_SOFT_ERROR: 1664 GNUNET_break (0); 1665 global_ret = EXIT_NOTCONFIGURED; 1666 GNUNET_SCHEDULER_shutdown (); 1667 return; 1668 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1669 fprintf (stdout, 1670 "KYC not required for the given operation type and amount\n"); 1671 global_ret = EXIT_SUCCESS; 1672 GNUNET_SCHEDULER_shutdown (); 1673 return; 1674 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1675 break; 1676 } 1677 } 1678 1679 if (NULL != rule) 1680 { 1681 struct TALER_KYCLOGIC_KycCheckContext kcc; 1682 1683 if (0 != list_measures) 1684 { 1685 // FIXME: print rule with possible measures! 1686 GNUNET_break (0); 1687 global_ret = EXIT_SUCCESS; 1688 GNUNET_SCHEDULER_shutdown (); 1689 return; 1690 } 1691 1692 if (GNUNET_OK != 1693 TALER_KYCLOGIC_requirements_to_check (lrs, 1694 rule, 1695 measure, 1696 &kcc)) 1697 { 1698 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1699 "Could not initiate KYC for measure `%s' (configuration error?)\n", 1700 measure); 1701 global_ret = EXIT_NOTCONFIGURED; 1702 GNUNET_SCHEDULER_shutdown (); 1703 return; 1704 } 1705 if (NULL == kcc.check) 1706 { 1707 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1708 "SKIP check selected, nothing to do here\n"); 1709 global_ret = EXIT_SUCCESS; 1710 GNUNET_SCHEDULER_shutdown (); 1711 return; 1712 } 1713 switch (kcc.check->type) 1714 { 1715 case TALER_KYCLOGIC_CT_INFO: 1716 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1717 "KYC information is `%s'\n", 1718 kcc.check->description); 1719 break; 1720 case TALER_KYCLOGIC_CT_FORM: 1721 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1722 "Would initiate KYC check `%s' with form `%s'\n", 1723 kcc.check->check_name, 1724 kcc.check->details.form.name); 1725 break; 1726 case TALER_KYCLOGIC_CT_LINK: 1727 { 1728 struct TALER_KYCLOGIC_ProviderDetails *pd; 1729 const char *provider_name; 1730 1731 TALER_KYCLOGIC_provider_to_logic ( 1732 kcc.check->details.link.provider, 1733 &ih_logic, 1734 &pd, 1735 &provider_name); 1736 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1737 "Initiating KYC check `%s' at provider `%s'\n", 1738 kcc.check->check_name, 1739 provider_name); 1740 ih = ih_logic->initiate (ih_logic->cls, 1741 pd, 1742 &cmd_line_h_payto, 1743 kyc_row_id, 1744 NULL, /* FIXME: support passing context*/ 1745 &initiate_cb, 1746 NULL); 1747 GNUNET_break (NULL != ih); 1748 break; 1749 } 1750 } 1751 } 1752 if (run_webservice) 1753 { 1754 enum GNUNET_GenericReturnValue ret; 1755 1756 TEKT_curl_ctx 1757 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1758 &exchange_curl_rc); 1759 if (NULL == TEKT_curl_ctx) 1760 { 1761 GNUNET_break (0); 1762 global_ret = EXIT_FAILURE; 1763 GNUNET_SCHEDULER_shutdown (); 1764 return; 1765 } 1766 exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx); 1767 ret = TALER_MHD_listen_bind (TEKT_cfg, 1768 "exchange", 1769 &start_daemon, 1770 NULL); 1771 switch (ret) 1772 { 1773 case GNUNET_SYSERR: 1774 global_ret = EXIT_NOTCONFIGURED; 1775 GNUNET_SCHEDULER_shutdown (); 1776 return; 1777 case GNUNET_NO: 1778 if (! have_daemons) 1779 { 1780 global_ret = EXIT_NOTCONFIGURED; 1781 GNUNET_SCHEDULER_shutdown (); 1782 return; 1783 } 1784 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1785 "Could not open all configured listen sockets\n"); 1786 break; 1787 case GNUNET_OK: 1788 break; 1789 } 1790 } 1791 } 1792 1793 1794 /** 1795 * The main function of the taler-exchange-kyc-tester, a tool for 1796 * testing KYC processes. 1797 * 1798 * @param argc number of arguments from the command line 1799 * @param argv command line arguments 1800 * @return 0 ok, non-zero on error 1801 */ 1802 int 1803 main (int argc, 1804 char *const *argv) 1805 { 1806 const struct GNUNET_GETOPT_CommandLineOption options[] = { 1807 GNUNET_GETOPT_option_help ( 1808 TALER_EXCHANGE_project_data (), 1809 "tool to test KYC provider integrations"), 1810 GNUNET_GETOPT_option_flag ( 1811 'M', 1812 "list-measures", 1813 "list available measures", 1814 &list_measures), 1815 GNUNET_GETOPT_option_string ( 1816 'm', 1817 "measure", 1818 "MEASURE_NAME", 1819 "initiate KYC check for the selected measure", 1820 &measure), 1821 GNUNET_GETOPT_option_string ( 1822 'o', 1823 "operation", 1824 "OPERATION_TYPE", 1825 "name of the operation that triggers legitimization (WITHDRAW, DEPOSIT, etc.)", 1826 &operation_s), 1827 GNUNET_GETOPT_option_flag ( 1828 'P', 1829 "print-payto-hash", 1830 "output the hash of the (normalized) payto://-URI", 1831 &print_h_payto), 1832 GNUNET_GETOPT_option_base32_fixed_size ( 1833 'p', 1834 "payto-hash", 1835 "HASH", 1836 "base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)", 1837 &cmd_line_h_payto, 1838 sizeof (cmd_line_h_payto)), 1839 GNUNET_GETOPT_option_string ( 1840 'R', 1841 "ruleset", 1842 "JSON", 1843 "use the given legitimization rule set (otherwise defaults from configuration are used)", 1844 &lrs_s), 1845 GNUNET_GETOPT_option_uint ( 1846 'r', 1847 "rowid", 1848 "NUMBER", 1849 "override row ID to use in simulation (default: 42)", 1850 &kyc_row_id), 1851 TALER_getopt_get_amount ( 1852 't', 1853 "trigger", 1854 "AMOUNT", 1855 "threshold crossed that would trigger some legitimization rule", 1856 &trigger_amount), 1857 GNUNET_GETOPT_option_string ( 1858 'U', 1859 "legitimization", 1860 "ID", 1861 "use the given provider legitimization ID (overridden if -i is also used)", 1862 &cmd_provider_legitimization_id), 1863 GNUNET_GETOPT_option_string ( 1864 'u', 1865 "user", 1866 "ID", 1867 "use the given provider user ID (overridden if -i is also used)", 1868 &cmd_provider_user_id), 1869 GNUNET_GETOPT_option_flag ( 1870 'w', 1871 "run-webservice", 1872 "run the integrated HTTP service", 1873 &run_webservice), 1874 GNUNET_GETOPT_option_flag ( 1875 'W', 1876 "wallet-payto", 1877 "simulate that the address the KYC process is about is a wallet", 1878 &cmd_line_is_wallet), 1879 GNUNET_GETOPT_OPTION_END 1880 }; 1881 enum GNUNET_GenericReturnValue ret; 1882 1883 GNUNET_CRYPTO_random_block (&cmd_line_h_payto, 1884 sizeof (cmd_line_h_payto)); 1885 ret = GNUNET_PROGRAM_run (TALER_EXCHANGE_project_data (), 1886 argc, argv, 1887 "taler-exchange-kyc-tester", 1888 "tool to test KYC provider integrations", 1889 options, 1890 &run, NULL); 1891 if (GNUNET_SYSERR == ret) 1892 return EXIT_INVALIDARGUMENT; 1893 if (GNUNET_NO == ret) 1894 return EXIT_SUCCESS; 1895 return global_ret; 1896 } 1897 1898 1899 /* end of taler-exchange-kyc-tester.c */