exchangedb_aml.c (20812B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023, 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 exchangedb_aml.c 18 * @brief helper function to handle AML programs 19 * @author Christian Grothoff 20 */ 21 #include "taler/taler_exchangedb_plugin.h" 22 #include "taler/taler_exchangedb_lib.h" 23 #include "taler/taler_kyclogic_lib.h" 24 #include "taler/taler_json_lib.h" 25 #include "taler/taler_dbevents.h" 26 #include <gnunet/gnunet_common.h> 27 28 /** 29 * Maximum recursion depth we allow for AML programs. 30 * Basically, after this number of "skip" processes 31 * we forcefully terminate the recursion and fail hard. 32 */ 33 #define MAX_DEPTH 16 34 35 enum GNUNET_DB_QueryStatus 36 TALER_EXCHANGEDB_persist_aml_program_result ( 37 struct TALER_EXCHANGEDB_Plugin *plugin, 38 uint64_t process_row, 39 const struct TALER_NormalizedPaytoHashP *account_id, 40 const struct TALER_KYCLOGIC_AmlProgramResult *apr, 41 enum TALER_EXCHANGEDB_PersistProgramResultStatus *ret_pprs) 42 { 43 enum GNUNET_DB_QueryStatus qs; 44 json_t *jmeasures = NULL; 45 struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL; 46 47 GNUNET_assert (NULL != ret_pprs); 48 49 *ret_pprs = TALER_EXCHANGEDB_PPRS_OK; 50 51 if ( (TALER_KYCLOGIC_AMLR_SUCCESS == apr->status) && 52 (NULL != apr->details.success.new_measures) ) 53 { 54 lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules); 55 if (NULL == lrs) 56 { 57 qs = plugin->insert_aml_program_failure ( 58 plugin->cls, 59 process_row, 60 account_id, 61 "Failed to parse AML program output", 62 TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT); 63 GNUNET_break (qs > 0); 64 return qs; 65 } 66 jmeasures = TALER_KYCLOGIC_get_jmeasures ( 67 lrs, 68 apr->details.success.new_measures); 69 if (NULL == jmeasures) 70 { 71 char *err; 72 73 GNUNET_break (0); 74 GNUNET_asprintf (&err, 75 "Failed to find measures `%s' specified in AML program output", 76 apr->details.success.new_measures); 77 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 78 "AML program specified invalid measures `%s'\n", 79 apr->details.success.new_measures); 80 qs = plugin->insert_aml_program_failure ( 81 plugin->cls, 82 process_row, 83 account_id, 84 err, 85 TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT); 86 *ret_pprs = TALER_EXCHANGEDB_PPRS_BAD_OUTCOME; 87 TALER_KYCLOGIC_rules_free (lrs); 88 GNUNET_free (err); 89 GNUNET_break (qs > 0); 90 return qs; 91 } 92 } 93 94 qs = plugin->clear_aml_lock ( 95 plugin->cls, 96 account_id); 97 switch (apr->status) 98 { 99 case TALER_KYCLOGIC_AMLR_FAILURE: 100 qs = plugin->insert_aml_program_failure ( 101 plugin->cls, 102 process_row, 103 account_id, 104 apr->details.failure.error_message, 105 apr->details.failure.ec); 106 GNUNET_break (qs > 0); 107 goto cleanup; 108 case TALER_KYCLOGIC_AMLR_SUCCESS: 109 { 110 struct TALER_FullPayto null_payto_uri = { 0 }; 111 bool invalid_officer; 112 bool unknown_account; 113 struct GNUNET_TIME_Timestamp last_date; 114 uint64_t legitimization_measure_serial_id; 115 bool is_wallet; 116 117 qs = plugin->insert_aml_decision ( 118 plugin->cls, 119 null_payto_uri, 120 account_id, 121 GNUNET_TIME_timestamp_get (), 122 apr->details.success.expiration_time, 123 apr->details.success.account_properties, 124 apr->details.success.new_rules, 125 apr->details.success.to_investigate, 126 apr->details.success.new_measures, 127 jmeasures, 128 NULL, /* justification */ 129 NULL, /* decider_pub */ 130 NULL, /* decider_sig */ 131 apr->details.success.num_events, 132 apr->details.success.events, 133 NULL, /* form ID */ 134 0, /* enc_attributes_size*/ 135 NULL, /* enc_attributes*/ 136 NULL, /* attributes_hash */ 137 GNUNET_TIME_UNIT_ZERO_TS, /* attributes_expiration_time */ 138 &invalid_officer, 139 &unknown_account, 140 &last_date, 141 &legitimization_measure_serial_id, 142 &is_wallet); 143 GNUNET_break (qs > 0); 144 goto cleanup; 145 } 146 } 147 GNUNET_break (0); 148 qs = GNUNET_DB_STATUS_HARD_ERROR; 149 cleanup: 150 TALER_KYCLOGIC_rules_free (lrs); 151 json_decref (jmeasures); 152 return qs; 153 } 154 155 156 struct TALER_EXCHANGEDB_RuleUpdater 157 { 158 /** 159 * database plugin to use 160 */ 161 struct TALER_EXCHANGEDB_Plugin *plugin; 162 163 /** 164 * key to use to decrypt attributes 165 */ 166 struct TALER_AttributeEncryptionKeyP attribute_key; 167 168 /** 169 * account to get the rule set for 170 */ 171 struct TALER_NormalizedPaytoHashP account; 172 173 /** 174 * function to call with the result 175 */ 176 TALER_EXCHANGEDB_CurrentRulesCallback cb; 177 178 /** 179 * Closure for @e cb. 180 */ 181 void *cb_cls; 182 183 /** 184 * Current rule set we are working on. 185 */ 186 struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; 187 188 /** 189 * Task for asynchronous continuations. 190 */ 191 struct GNUNET_SCHEDULER_Task *t; 192 193 /** 194 * Handler waiting notification that (previous) AML program 195 * finished. 196 */ 197 struct GNUNET_DB_EventHandler *eh; 198 199 /** 200 * Handle to running AML program. 201 */ 202 struct TALER_KYCLOGIC_AmlProgramRunnerHandle *amlh; 203 204 /** 205 * Name of the AML program we were running asynchronously, 206 * for diagnostics. 207 */ 208 char *aml_program_name; 209 210 /** 211 * Error hint to return with @e ec. 212 */ 213 const char *hint; 214 215 /** 216 * Row the rule set in @a lrs is based on. 217 */ 218 uint64_t legitimization_outcome_last_row; 219 220 /** 221 * Taler error code to return. 222 */ 223 enum TALER_ErrorCode ec; 224 225 /** 226 * Counter used to limit recursion depth. 227 */ 228 unsigned int depth; 229 230 /** 231 * True if @e account is for a wallet. 232 */ 233 bool is_wallet; 234 }; 235 236 237 /** 238 * Function that finally returns the result to the application and cleans 239 * up. Called with an open database transaction on success; on failure, the 240 * transaction will have already been rolled back. 241 * 242 * @param[in,out] ru rule updater to return result for 243 */ 244 static void 245 return_result (struct TALER_EXCHANGEDB_RuleUpdater *ru) 246 { 247 struct TALER_EXCHANGEDB_RuleUpdaterResult rur = { 248 .legitimization_outcome_last_row = ru->legitimization_outcome_last_row, 249 .lrs = ru->lrs, 250 .ec = ru->ec, 251 }; 252 253 ru->cb (ru->cb_cls, 254 &rur); 255 ru->lrs = NULL; 256 TALER_EXCHANGEDB_update_rules_cancel (ru); 257 } 258 259 260 /** 261 * Fail the update with the given @a ec and @a hint. 262 * Called with an open database transaction, which will 263 * be rolled back (!). 264 * 265 * @param[in,out] ru account we are processing 266 * @param ec error code to fail with 267 * @param hint hint to return, can be NULL 268 */ 269 static void 270 fail_update (struct TALER_EXCHANGEDB_RuleUpdater *ru, 271 enum TALER_ErrorCode ec, 272 const char *hint) 273 { 274 GNUNET_assert (NULL == ru->t); 275 ru->plugin->rollback (ru->plugin->cls); 276 ru->ec = ec; 277 ru->hint = hint; 278 return_result (ru); 279 } 280 281 282 /** 283 * Check the rules in @a ru to see if they are current, and 284 * if not begin the updating process. Called with an open 285 * database transaction. 286 * 287 * @param[in] ru rule updater context 288 */ 289 static void 290 check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru); 291 292 293 /** 294 * Run the measure @a m in the context of the legitimisation rules 295 * of @a ru. Called with an open database transaction. 296 * 297 * @param ru updating context we are using 298 * @param m measure we need to run next 299 */ 300 static void 301 run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru, 302 const struct TALER_KYCLOGIC_Measure *m); 303 304 305 /** 306 * Function called after AML program was run. Called 307 * without an open database transaction, will start one! 308 * 309 * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *` 310 * @param apr result of the AML program. 311 */ 312 static void 313 aml_result_callback ( 314 void *cls, 315 const struct TALER_KYCLOGIC_AmlProgramResult *apr) 316 { 317 struct TALER_EXCHANGEDB_RuleUpdater *ru = cls; 318 enum GNUNET_DB_QueryStatus qs; 319 enum GNUNET_GenericReturnValue res; 320 enum TALER_EXCHANGEDB_PersistProgramResultStatus pprs; 321 322 ru->amlh = NULL; 323 res = ru->plugin->start (ru->plugin->cls, 324 "aml-persist-aml-program-result"); 325 if (GNUNET_OK != res) 326 { 327 GNUNET_break (0); 328 fail_update (ru, 329 TALER_EC_GENERIC_DB_START_FAILED, 330 "aml-persist-aml-program-result"); 331 return; 332 } 333 /* Update database update based on result */ 334 qs = TALER_EXCHANGEDB_persist_aml_program_result ( 335 ru->plugin, 336 0LLU, /* 0: no existing legitimization process, creates new row */ 337 &ru->account, 338 apr, 339 &pprs); 340 switch (qs) 341 { 342 case GNUNET_DB_STATUS_HARD_ERROR: 343 GNUNET_break (0); 344 fail_update (ru, 345 TALER_EC_GENERIC_DB_STORE_FAILED, 346 "persist_aml_program_result"); 347 return; 348 case GNUNET_DB_STATUS_SOFT_ERROR: 349 /* Bad, couldn't persist AML result. Try again... */ 350 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 351 "Serialization issue persisting result of AML program. Restarting.\n"); 352 fail_update (ru, 353 TALER_EC_GENERIC_DB_SOFT_FAILURE, 354 "persist_aml_program_result"); 355 return; 356 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 357 /* Strange, but let's just continue */ 358 break; 359 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 360 /* normal case */ 361 break; 362 } 363 switch (pprs) 364 { 365 case TALER_EXCHANGEDB_PPRS_OK: 366 break; 367 case TALER_EXCHANGEDB_PPRS_BAD_OUTCOME: 368 fail_update (ru, 369 TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT, 370 "persist_aml_program_result"); 371 return; 372 } 373 switch (apr->status) 374 { 375 case TALER_KYCLOGIC_AMLR_SUCCESS: 376 TALER_KYCLOGIC_rules_free (ru->lrs); 377 ru->lrs = NULL; 378 ru->lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules); 379 /* Fall back to default rules on parse error! */ 380 GNUNET_break (NULL != ru->lrs); 381 check_rules (ru); 382 return; 383 case TALER_KYCLOGIC_AMLR_FAILURE: 384 { 385 const char *fmn = apr->details.failure.fallback_measure; 386 const struct TALER_KYCLOGIC_Measure *m; 387 388 m = TALER_KYCLOGIC_get_measure (ru->lrs, 389 fmn); 390 if (NULL == m) 391 { 392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 393 "Fallback measure `%s' does not exist (anymore?).\n", 394 fmn); 395 TALER_KYCLOGIC_rules_free (ru->lrs); 396 ru->lrs = NULL; 397 return_result (ru); 398 return; 399 } 400 run_measure (ru, 401 m); 402 return; 403 } 404 } 405 /* This should be impossible */ 406 GNUNET_assert (0); 407 } 408 409 410 /** 411 * Entrypoint that fetches the latest rules from the database 412 * and starts processing them. Called without an open database 413 * transaction, will start one. 414 * 415 * @param[in] cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to run 416 */ 417 static void 418 fetch_latest_rules (void *cls); 419 420 421 /** 422 * Notification called when we either timeout on the AML program lock 423 * or when the (previous) AML program finished and we can thus try again. 424 * 425 * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to continue 426 * @param extra additional event data provided (unused) 427 * @param extra_size number of bytes in @a extra (unused) 428 */ 429 static void 430 trigger_fetch_latest_rules (void *cls, 431 const void *extra, 432 size_t extra_size) 433 { 434 struct TALER_EXCHANGEDB_RuleUpdater *ru = cls; 435 436 (void) extra; 437 (void) extra_size; 438 if (NULL != ru->t) 439 return; /* multiple events triggered us, ignore */ 440 ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules, 441 ru); 442 } 443 444 445 static void 446 run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru, 447 const struct TALER_KYCLOGIC_Measure *m) 448 { 449 if (NULL == m) 450 { 451 /* fall back to default rules */ 452 TALER_KYCLOGIC_rules_free (ru->lrs); 453 ru->lrs = NULL; 454 return_result (ru); 455 return; 456 } 457 ru->depth++; 458 if (ru->depth > MAX_DEPTH) 459 { 460 fail_update (ru, 461 TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED, 462 NULL); 463 return; 464 } 465 if ( (NULL == m->check_name) || 466 (0 == 467 strcasecmp ("SKIP", 468 m->check_name)) ) 469 { 470 struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = { 471 .account = &ru->account, 472 .is_wallet = ru->is_wallet, 473 .db_plugin = ru->plugin, 474 .attribute_key = &ru->attribute_key 475 }; 476 enum GNUNET_DB_QueryStatus qs; 477 struct GNUNET_TIME_Absolute xlock; 478 479 /* Free previous one, in case we are iterating... */ 480 GNUNET_free (ru->aml_program_name); 481 if (NULL != m->prog_name) 482 { 483 ru->aml_program_name = GNUNET_strdup (m->prog_name); 484 } 485 else 486 { 487 /* How do we get to run a measure if the check type 488 is INFO (which is the only case where prog_name 489 is allowed to be NULL?) */ 490 GNUNET_break (0); 491 ru->aml_program_name = NULL; 492 } 493 qs = ru->plugin->set_aml_lock ( 494 ru->plugin->cls, 495 &ru->account, 496 GNUNET_TIME_relative_multiply (ru->plugin->max_aml_program_runtime, 497 2), 498 &xlock); 499 if (GNUNET_TIME_absolute_is_future (xlock)) 500 { 501 struct TALER_KycCompletedEventP eh = { 502 .header.size = htons (sizeof (eh)), 503 .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), 504 .h_payto = ru->account 505 }; 506 /* Wait for either timeout or notification */ 507 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 508 "AML program already running, waiting for it to finish\n"); 509 ru->plugin->rollback (ru->plugin->cls); 510 ru->eh 511 = ru->plugin->event_listen ( 512 ru->plugin->cls, 513 GNUNET_TIME_absolute_get_remaining (xlock), 514 &eh.header, 515 &trigger_fetch_latest_rules, 516 ru); 517 return; 518 } 519 qs = ru->plugin->commit (ru->plugin->cls); 520 if (qs < 0) 521 { 522 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 523 fail_update (ru, 524 GNUNET_DB_STATUS_SOFT_ERROR == qs 525 ? TALER_EC_GENERIC_DB_SOFT_FAILURE 526 : TALER_EC_GENERIC_DB_COMMIT_FAILED, 527 "current-aml-rule-fetch"); 528 return; 529 } 530 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 531 "Check is of type 'SKIP', running AML program %s.\n", 532 m->prog_name); 533 GNUNET_assert (NULL == ru->t); 534 ru->amlh = TALER_KYCLOGIC_run_aml_program3 ( 535 ru->is_wallet, 536 m, 537 &TALER_EXCHANGEDB_current_attributes_builder, 538 &hbc, 539 &TALER_EXCHANGEDB_current_rule_builder, 540 &hbc, 541 &TALER_EXCHANGEDB_aml_history_builder, 542 &hbc, 543 &TALER_EXCHANGEDB_kyc_history_builder, 544 &hbc, 545 ru->plugin->max_aml_program_runtime, 546 &aml_result_callback, 547 ru); 548 return; 549 } 550 551 /* User MUST pass interactive check */ 552 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 553 "Measure %s involves check %s\n", 554 m->measure_name, 555 m->check_name); 556 { 557 /* activate the measure/check */ 558 json_t *succ_jmeasures 559 = TALER_KYCLOGIC_get_jmeasures ( 560 ru->lrs, 561 m->measure_name); 562 bool unknown_account; 563 struct GNUNET_TIME_Timestamp last_date; 564 enum GNUNET_DB_QueryStatus qs; 565 566 qs = ru->plugin->insert_successor_measure ( 567 ru->plugin->cls, 568 &ru->account, 569 GNUNET_TIME_timestamp_get (), 570 m->measure_name, 571 succ_jmeasures, 572 &unknown_account, 573 &last_date); 574 json_decref (succ_jmeasures); 575 switch (qs) 576 { 577 case GNUNET_DB_STATUS_SOFT_ERROR: 578 GNUNET_log ( 579 GNUNET_ERROR_TYPE_INFO, 580 "Serialization issue!\n"); 581 fail_update (ru, 582 TALER_EC_GENERIC_DB_SOFT_FAILURE, 583 "insert_successor_measure"); 584 return; 585 case GNUNET_DB_STATUS_HARD_ERROR: 586 GNUNET_break (0); 587 fail_update (ru, 588 TALER_EC_GENERIC_DB_STORE_FAILED, 589 "insert_successor_measure"); 590 return; 591 default: 592 break; 593 } 594 if (unknown_account) 595 { 596 fail_update (ru, 597 TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN, 598 NULL); 599 return; 600 } 601 } 602 /* The rules remain these rules until the user passes the check */ 603 return_result (ru); 604 } 605 606 607 /** 608 * Update the expired legitimization rules in @a ru, checking for expiration 609 * first. Called with an open database transaction. 610 * 611 * @param[in,out] ru account we are processing 612 */ 613 static void 614 update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru) 615 { 616 const struct TALER_KYCLOGIC_Measure *m; 617 618 GNUNET_assert (NULL != ru->lrs); 619 GNUNET_assert (GNUNET_TIME_absolute_is_past ( 620 TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time)); 621 m = TALER_KYCLOGIC_rules_get_successor (ru->lrs); 622 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 623 "Successor measure is %s.\n", 624 (NULL != m) ? m->measure_name : "(null)"); 625 run_measure (ru, 626 m); 627 } 628 629 630 static void 631 check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru) 632 { 633 ru->depth++; 634 if (ru->depth > MAX_DEPTH) 635 { 636 fail_update (ru, 637 TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED, 638 NULL); 639 return; 640 } 641 if (NULL == ru->lrs) 642 { 643 /* return NULL, aka default rules */ 644 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 645 "Default rules apply\n"); 646 return_result (ru); 647 return; 648 } 649 if (! GNUNET_TIME_absolute_is_past 650 (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) ) 651 { 652 /* Rules did not expire, return them! */ 653 return_result (ru); 654 return; 655 } 656 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 657 "Custom rules expired, updating...\n"); 658 update_rules (ru); 659 } 660 661 662 static void 663 fetch_latest_rules (void *cls) 664 { 665 struct TALER_EXCHANGEDB_RuleUpdater *ru = cls; 666 enum GNUNET_DB_QueryStatus qs; 667 json_t *jnew_rules; 668 enum GNUNET_GenericReturnValue res; 669 670 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 671 "Fetching latest rules."); 672 673 ru->t = NULL; 674 if (NULL != ru->eh) 675 { 676 /* cancel event listener, if we have one */ 677 ru->plugin->event_listen_cancel (ru->plugin->cls, 678 ru->eh); 679 ru->eh = NULL; 680 } 681 GNUNET_break (NULL == ru->lrs); 682 res = ru->plugin->start (ru->plugin->cls, 683 "aml-begin-lookup-rules-by-access-token"); 684 if (GNUNET_OK != res) 685 { 686 GNUNET_break (0); 687 fail_update (ru, 688 TALER_EC_GENERIC_DB_START_FAILED, 689 "aml-begin-lookup-rules-by-access-token"); 690 return; 691 } 692 qs = ru->plugin->lookup_rules_by_access_token ( 693 ru->plugin->cls, 694 &ru->account, 695 &jnew_rules, 696 &ru->legitimization_outcome_last_row); 697 if (qs < 0) 698 { 699 GNUNET_break (0); 700 fail_update (ru, 701 TALER_EC_GENERIC_DB_FETCH_FAILED, 702 "lookup_rules_by_access_token"); 703 return; 704 } 705 if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && 706 (NULL != jnew_rules) ) 707 { 708 ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules); 709 GNUNET_break (NULL != ru->lrs); 710 json_decref (jnew_rules); 711 } 712 check_rules (ru); 713 } 714 715 716 struct TALER_EXCHANGEDB_RuleUpdater * 717 TALER_EXCHANGEDB_update_rules ( 718 struct TALER_EXCHANGEDB_Plugin *plugin, 719 const struct TALER_AttributeEncryptionKeyP *attribute_key, 720 const struct TALER_NormalizedPaytoHashP *account, 721 bool is_wallet, 722 TALER_EXCHANGEDB_CurrentRulesCallback cb, 723 void *cb_cls) 724 { 725 struct TALER_EXCHANGEDB_RuleUpdater *ru; 726 727 ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater); 728 ru->plugin = plugin; 729 ru->attribute_key = *attribute_key; 730 ru->account = *account; 731 ru->is_wallet = is_wallet; 732 ru->cb = cb; 733 ru->cb_cls = cb_cls; 734 ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules, 735 ru); 736 return ru; 737 } 738 739 740 void 741 TALER_EXCHANGEDB_update_rules_cancel ( 742 struct TALER_EXCHANGEDB_RuleUpdater *ru) 743 { 744 if (NULL != ru->t) 745 { 746 GNUNET_SCHEDULER_cancel (ru->t); 747 ru->t = NULL; 748 } 749 if (NULL != ru->amlh) 750 { 751 TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh); 752 ru->amlh = NULL; 753 } 754 if (NULL != ru->lrs) 755 { 756 TALER_KYCLOGIC_rules_free (ru->lrs); 757 ru->lrs = NULL; 758 } 759 if (NULL != ru->eh) 760 { 761 ru->plugin->event_listen_cancel (ru->plugin->cls, 762 ru->eh); 763 ru->eh = NULL; 764 } 765 GNUNET_free (ru->aml_program_name); 766 GNUNET_free (ru); 767 }