exchange_api_get-aml-OFFICER_PUB-decisions.c (21694B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023, 2024, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_get-aml-OFFICER_PUB-decisions.c 19 * @brief Implementation of the /aml/$OFFICER_PUB/decisions request 20 * @author Christian Grothoff 21 */ 22 #include <microhttpd.h> /* just for HTTP status codes */ 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_curl_lib.h> 25 #include "taler/taler_json_lib.h" 26 #include "taler/exchange/get-aml-OFFICER_PUB-decisions.h" 27 #include "exchange_api_handle.h" 28 #include "taler/taler_signatures.h" 29 #include "exchange_api_curl_defaults.h" 30 31 #define DEBUG 0 32 33 /** 34 * @brief A GET /aml/$OFFICER_PUB/decisions Handle 35 */ 36 struct TALER_EXCHANGE_GetAmlDecisionsHandle 37 { 38 39 /** 40 * The base URL of the exchange. 41 */ 42 char *base_url; 43 44 /** 45 * The full URL for this request, set during _start. 46 */ 47 char *url; 48 49 /** 50 * Handle for the request. 51 */ 52 struct GNUNET_CURL_Job *job; 53 54 /** 55 * Function to call with the result. 56 */ 57 TALER_EXCHANGE_GetAmlDecisionsCallback cb; 58 59 /** 60 * Closure for @e cb. 61 */ 62 TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls; 63 64 /** 65 * Reference to the execution context. 66 */ 67 struct GNUNET_CURL_Context *ctx; 68 69 /** 70 * Public key of the AML officer. 71 */ 72 struct TALER_AmlOfficerPublicKeyP officer_pub; 73 74 /** 75 * Private key of the AML officer (for signing). 76 */ 77 struct TALER_AmlOfficerPrivateKeyP officer_priv; 78 79 /** 80 * Signature of the AML officer. 81 */ 82 struct TALER_AmlOfficerSignatureP officer_sig; 83 84 /** 85 * Options for the request. 86 */ 87 struct 88 { 89 /** 90 * Limit on number of results (-20 by default). 91 */ 92 int64_t limit; 93 94 /** 95 * Row offset threshold (INT64_MAX by default). 96 */ 97 uint64_t offset; 98 99 /** 100 * Optional account filter; NULL if not set. 101 */ 102 const struct TALER_NormalizedPaytoHashP *h_payto; 103 104 /** 105 * Filter for active decisions (YNA_ALL by default). 106 */ 107 enum TALER_EXCHANGE_YesNoAll active; 108 109 /** 110 * Filter for investigation status (YNA_ALL by default). 111 */ 112 enum TALER_EXCHANGE_YesNoAll investigation; 113 } options; 114 115 /** 116 * Flat array of all KYC rules across all decisions (allocated during parse). 117 */ 118 struct TALER_EXCHANGE_GetAmlDecisionsKycRule *all_rules; 119 120 /** 121 * Flat array of all measure string pointers across all rules (allocated during parse). 122 */ 123 const char **all_mp; 124 125 }; 126 127 128 /** 129 * Parse the limits/rules object. 130 * 131 * @param[in,out] adgh handle (used for allocation tracking) 132 * @param jlimits JSON object with legitimization rule set data 133 * @param[out] limits where to write the parsed rule set 134 * @param[in,out] rule_off current offset into adgh->all_rules (advanced) 135 * @param[in,out] mp_off current offset into adgh->all_mp (advanced) 136 * @return #GNUNET_OK on success 137 */ 138 static enum GNUNET_GenericReturnValue 139 parse_limits ( 140 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 141 const json_t *jlimits, 142 struct TALER_EXCHANGE_GetAmlDecisionsLegitimizationRuleSet *limits, 143 size_t *rule_off, 144 size_t *mp_off) 145 { 146 const json_t *jrules; 147 const json_t *jcustom_measures; 148 struct GNUNET_JSON_Specification spec[] = { 149 GNUNET_JSON_spec_timestamp ("expiration_time", 150 &limits->expiration_time), 151 GNUNET_JSON_spec_mark_optional ( 152 GNUNET_JSON_spec_string ("successor_measure", 153 &limits->successor_measure), 154 NULL), 155 GNUNET_JSON_spec_array_const ("rules", 156 &jrules), 157 GNUNET_JSON_spec_mark_optional ( 158 GNUNET_JSON_spec_object_const ("custom_measures", 159 &jcustom_measures), 160 NULL), 161 GNUNET_JSON_spec_end () 162 }; 163 164 if (GNUNET_OK != 165 GNUNET_JSON_parse (jlimits, 166 spec, 167 NULL, 168 NULL)) 169 { 170 GNUNET_break_op (0); 171 return GNUNET_SYSERR; 172 } 173 limits->custom_measures = jcustom_measures; 174 175 { 176 size_t rule_count = json_array_size (jrules); 177 size_t rule_start = *rule_off; 178 179 limits->rules = &adgh->all_rules[rule_start]; 180 limits->rules_length = rule_count; 181 182 { 183 const json_t *jrule; 184 size_t ridx; 185 186 json_array_foreach ((json_t *) jrules, ridx, jrule) 187 { 188 struct TALER_EXCHANGE_GetAmlDecisionsKycRule *r 189 = &adgh->all_rules[*rule_off]; 190 const json_t *jsmeasures; 191 struct GNUNET_JSON_Specification rspec[] = { 192 TALER_JSON_spec_kycte ("operation_type", 193 &r->operation_type), 194 GNUNET_JSON_spec_mark_optional ( 195 GNUNET_JSON_spec_string ("rule_name", 196 &r->rule_name), 197 NULL), 198 TALER_JSON_spec_amount_any ("threshold", 199 &r->threshold), 200 GNUNET_JSON_spec_relative_time ("timeframe", 201 &r->timeframe), 202 GNUNET_JSON_spec_array_const ("measures", 203 &jsmeasures), 204 GNUNET_JSON_spec_mark_optional ( 205 GNUNET_JSON_spec_bool ("exposed", 206 &r->exposed), 207 NULL), 208 GNUNET_JSON_spec_mark_optional ( 209 GNUNET_JSON_spec_bool ("is_and_combinator", 210 &r->is_and_combinator), 211 NULL), 212 GNUNET_JSON_spec_int64 ("display_priority", 213 &r->display_priority), 214 GNUNET_JSON_spec_end () 215 }; 216 217 if (GNUNET_OK != 218 GNUNET_JSON_parse (jrule, 219 rspec, 220 NULL, 221 NULL)) 222 { 223 GNUNET_break_op (0); 224 return GNUNET_SYSERR; 225 } 226 227 { 228 size_t mlen = json_array_size (jsmeasures); 229 size_t mp_start = *mp_off; 230 231 r->measures = &adgh->all_mp[mp_start]; 232 r->measures_length = mlen; 233 234 { 235 size_t midx; 236 const json_t *jm; 237 238 json_array_foreach (jsmeasures, midx, jm) 239 { 240 const char *sval = json_string_value (jm); 241 242 if (NULL == sval) 243 { 244 GNUNET_break_op (0); 245 return GNUNET_SYSERR; 246 } 247 adgh->all_mp[*mp_off] = sval; 248 (*mp_off)++; 249 } 250 } 251 } 252 253 (*rule_off)++; 254 } 255 } 256 } 257 258 return GNUNET_OK; 259 } 260 261 262 /** 263 * Parse AML decision records. 264 * 265 * @param[in,out] adgh handle (for allocations) 266 * @param jrecords JSON array of decision records 267 * @param records_ar_length length of @a records_ar 268 * @param[out] records_ar caller-allocated array to fill 269 * @return #GNUNET_OK on success 270 */ 271 static enum GNUNET_GenericReturnValue 272 parse_aml_decisions ( 273 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 274 const json_t *jrecords, 275 size_t records_ar_length, 276 struct TALER_EXCHANGE_GetAmlDecisionsDecision *records_ar) 277 { 278 size_t rule_off = 0; 279 size_t mp_off = 0; 280 const json_t *obj; 281 size_t idx; 282 283 json_array_foreach ((json_t *) jrecords, idx, obj) 284 { 285 struct TALER_EXCHANGE_GetAmlDecisionsDecision *decision = &records_ar[idx]; 286 const json_t *jlimits; 287 struct GNUNET_JSON_Specification spec[] = { 288 GNUNET_JSON_spec_fixed_auto ("h_payto", 289 &decision->h_payto), 290 GNUNET_JSON_spec_mark_optional ( 291 GNUNET_JSON_spec_string ("full_payto", 292 &decision->full_payto), 293 NULL), 294 GNUNET_JSON_spec_mark_optional ( 295 GNUNET_JSON_spec_bool ("is_wallet", 296 &decision->is_wallet), 297 NULL), 298 GNUNET_JSON_spec_uint64 ("rowid", 299 &decision->rowid), 300 GNUNET_JSON_spec_mark_optional ( 301 GNUNET_JSON_spec_string ("justification", 302 &decision->justification), 303 NULL), 304 GNUNET_JSON_spec_mark_optional ( 305 GNUNET_JSON_spec_string ("new_measures", 306 &decision->new_measures), 307 NULL), 308 GNUNET_JSON_spec_mark_optional ( 309 GNUNET_JSON_spec_fixed_auto ("decider_pub", 310 &decision->decider_pub), 311 NULL), 312 GNUNET_JSON_spec_mark_optional ( 313 GNUNET_JSON_spec_string ("decider_name", 314 &decision->decider_name), 315 NULL), 316 GNUNET_JSON_spec_mark_optional ( 317 GNUNET_JSON_spec_uint64 ("kyc_attributes_rowid", 318 &decision->kyc_attributes_rowid), 319 NULL), 320 GNUNET_JSON_spec_timestamp ("decision_time", 321 &decision->decision_time), 322 GNUNET_JSON_spec_mark_optional ( 323 GNUNET_JSON_spec_object_const ("properties", 324 &decision->properties), 325 NULL), 326 GNUNET_JSON_spec_object_const ("limits", 327 &jlimits), 328 GNUNET_JSON_spec_bool ("to_investigate", 329 &decision->to_investigate), 330 GNUNET_JSON_spec_bool ("is_active", 331 &decision->is_active), 332 GNUNET_JSON_spec_end () 333 }; 334 335 GNUNET_assert (idx < records_ar_length); 336 if (GNUNET_OK != 337 GNUNET_JSON_parse (obj, 338 spec, 339 NULL, 340 NULL)) 341 { 342 GNUNET_break_op (0); 343 return GNUNET_SYSERR; 344 } 345 346 if (GNUNET_OK != 347 parse_limits (adgh, 348 jlimits, 349 &decision->limits, 350 &rule_off, 351 &mp_off)) 352 { 353 GNUNET_break_op (0); 354 return GNUNET_SYSERR; 355 } 356 } 357 return GNUNET_OK; 358 } 359 360 361 /** 362 * Parse the provided decision data from the "200 OK" response. 363 * 364 * @param[in,out] adgh handle (callback may be zero'ed out) 365 * @param json json reply with the data 366 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 367 */ 368 static enum GNUNET_GenericReturnValue 369 parse_get_aml_decisions_ok (struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 370 const json_t *json) 371 { 372 struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = { 373 .hr.reply = json, 374 .hr.http_status = MHD_HTTP_OK 375 }; 376 const json_t *jrecords; 377 struct GNUNET_JSON_Specification spec[] = { 378 GNUNET_JSON_spec_array_const ("records", 379 &jrecords), 380 GNUNET_JSON_spec_end () 381 }; 382 383 if (GNUNET_OK != 384 GNUNET_JSON_parse (json, 385 spec, 386 NULL, 387 NULL)) 388 { 389 GNUNET_break_op (0); 390 return GNUNET_SYSERR; 391 } 392 393 lr.details.ok.records_length = json_array_size (jrecords); 394 395 /* First pass: count total rules and measures across all records */ 396 { 397 size_t total_rules = 0; 398 size_t total_measures = 0; 399 const json_t *obj; 400 size_t idx; 401 402 json_array_foreach ((json_t *) jrecords, idx, obj) 403 { 404 const json_t *jlimits = json_object_get (obj, "limits"); 405 const json_t *jrules; 406 407 if (NULL == jlimits) 408 continue; 409 jrules = json_object_get (jlimits, "rules"); 410 if (NULL == jrules) 411 continue; 412 total_rules += json_array_size (jrules); 413 414 { 415 const json_t *jrule; 416 size_t ridx; 417 418 json_array_foreach ((json_t *) jrules, ridx, jrule) 419 { 420 const json_t *jmeasures = json_object_get (jrule, "measures"); 421 422 if (NULL != jmeasures) 423 total_measures += json_array_size (jmeasures); 424 } 425 } 426 } 427 428 adgh->all_rules = GNUNET_new_array ( 429 GNUNET_NZL (total_rules), 430 struct TALER_EXCHANGE_GetAmlDecisionsKycRule); 431 adgh->all_mp = GNUNET_new_array ( 432 GNUNET_NZL (total_measures), 433 const char *); 434 } 435 436 { 437 struct TALER_EXCHANGE_GetAmlDecisionsDecision records[ 438 GNUNET_NZL (lr.details.ok.records_length)]; 439 enum GNUNET_GenericReturnValue ret; 440 441 memset (records, 442 0, 443 sizeof (records)); 444 lr.details.ok.records = records; 445 ret = parse_aml_decisions (adgh, 446 jrecords, 447 lr.details.ok.records_length, 448 records); 449 if (GNUNET_OK == ret) 450 { 451 adgh->cb (adgh->cb_cls, 452 &lr); 453 adgh->cb = NULL; 454 } 455 GNUNET_free (adgh->all_rules); 456 adgh->all_rules = NULL; 457 GNUNET_free (adgh->all_mp); 458 adgh->all_mp = NULL; 459 return ret; 460 } 461 } 462 463 464 /** 465 * Function called when we're done processing the 466 * HTTP /aml/$OFFICER_PUB/decisions request. 467 * 468 * @param cls the `struct TALER_EXCHANGE_GetAmlDecisionsHandle` 469 * @param response_code HTTP response code, 0 on error 470 * @param response parsed JSON result, NULL on error 471 */ 472 static void 473 handle_get_aml_decisions_finished (void *cls, 474 long response_code, 475 const void *response) 476 { 477 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh = cls; 478 const json_t *j = response; 479 struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = { 480 .hr.reply = j, 481 .hr.http_status = (unsigned int) response_code 482 }; 483 484 adgh->job = NULL; 485 switch (response_code) 486 { 487 case 0: 488 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 489 break; 490 case MHD_HTTP_OK: 491 if (GNUNET_OK != 492 parse_get_aml_decisions_ok (adgh, 493 j)) 494 { 495 GNUNET_break_op (0); 496 lr.hr.http_status = 0; 497 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 498 break; 499 } 500 GNUNET_assert (NULL == adgh->cb); 501 TALER_EXCHANGE_get_aml_decisions_cancel (adgh); 502 return; 503 case MHD_HTTP_NO_CONTENT: 504 break; 505 case MHD_HTTP_BAD_REQUEST: 506 #if DEBUG 507 json_dumpf (j, 508 stderr, 509 JSON_INDENT (2)); 510 #endif 511 lr.hr.ec = TALER_JSON_get_error_code (j); 512 lr.hr.hint = TALER_JSON_get_error_hint (j); 513 break; 514 case MHD_HTTP_FORBIDDEN: 515 lr.hr.ec = TALER_JSON_get_error_code (j); 516 lr.hr.hint = TALER_JSON_get_error_hint (j); 517 break; 518 case MHD_HTTP_NOT_FOUND: 519 lr.hr.ec = TALER_JSON_get_error_code (j); 520 lr.hr.hint = TALER_JSON_get_error_hint (j); 521 break; 522 case MHD_HTTP_INTERNAL_SERVER_ERROR: 523 lr.hr.ec = TALER_JSON_get_error_code (j); 524 lr.hr.hint = TALER_JSON_get_error_hint (j); 525 break; 526 default: 527 /* unexpected response code */ 528 GNUNET_break_op (0); 529 lr.hr.ec = TALER_JSON_get_error_code (j); 530 lr.hr.hint = TALER_JSON_get_error_hint (j); 531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 532 "Unexpected response code %u/%d for GET AML decisions\n", 533 (unsigned int) response_code, 534 (int) lr.hr.ec); 535 break; 536 } 537 if (NULL != adgh->cb) 538 adgh->cb (adgh->cb_cls, 539 &lr); 540 TALER_EXCHANGE_get_aml_decisions_cancel (adgh); 541 } 542 543 544 struct TALER_EXCHANGE_GetAmlDecisionsHandle * 545 TALER_EXCHANGE_get_aml_decisions_create ( 546 struct GNUNET_CURL_Context *ctx, 547 const char *url, 548 const struct TALER_AmlOfficerPrivateKeyP *officer_priv) 549 { 550 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh; 551 552 adgh = GNUNET_new (struct TALER_EXCHANGE_GetAmlDecisionsHandle); 553 adgh->ctx = ctx; 554 adgh->base_url = GNUNET_strdup (url); 555 adgh->officer_priv = *officer_priv; 556 GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, 557 &adgh->officer_pub.eddsa_pub); 558 adgh->options.limit = -20; 559 adgh->options.offset = INT64_MAX; 560 adgh->options.active = TALER_EXCHANGE_YNA_ALL; 561 adgh->options.investigation = TALER_EXCHANGE_YNA_ALL; 562 return adgh; 563 } 564 565 566 enum GNUNET_GenericReturnValue 567 TALER_EXCHANGE_get_aml_decisions_set_options_ ( 568 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 569 unsigned int num_options, 570 const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue options[]) 571 { 572 for (unsigned int i = 0; i < num_options; i++) 573 { 574 const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i]; 575 576 switch (opt->option) 577 { 578 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END: 579 return GNUNET_OK; 580 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT: 581 adgh->options.limit = opt->details.limit; 582 break; 583 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET: 584 if (opt->details.offset > INT64_MAX) 585 { 586 GNUNET_break (0); 587 return GNUNET_NO; 588 } 589 adgh->options.offset = opt->details.offset; 590 break; 591 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO: 592 adgh->options.h_payto = opt->details.h_payto; 593 break; 594 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE: 595 adgh->options.active = opt->details.active; 596 break; 597 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION: 598 adgh->options.investigation = opt->details.investigation; 599 break; 600 default: 601 GNUNET_break (0); 602 return GNUNET_SYSERR; 603 } 604 } 605 return GNUNET_OK; 606 } 607 608 609 enum TALER_ErrorCode 610 TALER_EXCHANGE_get_aml_decisions_start ( 611 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 612 TALER_EXCHANGE_GetAmlDecisionsCallback cb, 613 TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls) 614 { 615 CURL *eh; 616 char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32]; 617 struct curl_slist *job_headers = NULL; 618 619 adgh->cb = cb; 620 adgh->cb_cls = cb_cls; 621 622 /* Build AML officer signature */ 623 TALER_officer_aml_query_sign (&adgh->officer_priv, 624 &adgh->officer_sig); 625 626 /* Build the path component: aml/{officer_pub}/decisions */ 627 { 628 char pub_str[sizeof (adgh->officer_pub) * 2]; 629 char *end; 630 631 end = GNUNET_STRINGS_data_to_string ( 632 &adgh->officer_pub, 633 sizeof (adgh->officer_pub), 634 pub_str, 635 sizeof (pub_str)); 636 *end = '\0'; 637 GNUNET_snprintf (arg_str, 638 sizeof (arg_str), 639 "aml/%s/decisions", 640 pub_str); 641 } 642 643 /* Build URL with optional query parameters */ 644 { 645 char limit_s[24]; 646 char offset_s[24]; 647 char payto_s[sizeof (*adgh->options.h_payto) * 2 + 1]; 648 int64_t limit = adgh->options.limit; 649 uint64_t offset = adgh->options.offset; 650 bool omit_limit = (-20 == limit); 651 bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) || 652 ( (limit > 0) && (0 == offset) ) ); 653 654 GNUNET_snprintf (limit_s, 655 sizeof (limit_s), 656 "%lld", 657 (long long) limit); 658 GNUNET_snprintf (offset_s, 659 sizeof (offset_s), 660 "%llu", 661 (unsigned long long) offset); 662 663 if (NULL != adgh->options.h_payto) 664 { 665 char *end; 666 667 end = GNUNET_STRINGS_data_to_string ( 668 adgh->options.h_payto, 669 sizeof (*adgh->options.h_payto), 670 payto_s, 671 sizeof (payto_s) - 1); 672 *end = '\0'; 673 } 674 675 adgh->url = TALER_url_join ( 676 adgh->base_url, 677 arg_str, 678 "limit", 679 omit_limit ? NULL : limit_s, 680 "offset", 681 omit_offset ? NULL : offset_s, 682 "h_payto", 683 (NULL != adgh->options.h_payto) ? payto_s : NULL, 684 "active", 685 (TALER_EXCHANGE_YNA_ALL != adgh->options.active) 686 ? TALER_yna_to_string (adgh->options.active) 687 : NULL, 688 "investigation", 689 (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation) 690 ? TALER_yna_to_string (adgh->options.investigation) 691 : NULL, 692 NULL); 693 } 694 695 if (NULL == adgh->url) 696 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 697 698 eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url); 699 if (NULL == eh) 700 { 701 GNUNET_break (0); 702 GNUNET_free (adgh->url); 703 adgh->url = NULL; 704 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 705 } 706 707 /* Build job headers with AML officer signature */ 708 { 709 char *hdr; 710 char sig_str[sizeof (adgh->officer_sig) * 2]; 711 char *end; 712 713 end = GNUNET_STRINGS_data_to_string ( 714 &adgh->officer_sig, 715 sizeof (adgh->officer_sig), 716 sig_str, 717 sizeof (sig_str)); 718 *end = '\0'; 719 720 GNUNET_asprintf (&hdr, 721 "%s: %s", 722 TALER_AML_OFFICER_SIGNATURE_HEADER, 723 sig_str); 724 job_headers = curl_slist_append (NULL, 725 hdr); 726 GNUNET_free (hdr); 727 } 728 729 adgh->job = GNUNET_CURL_job_add2 (adgh->ctx, 730 eh, 731 job_headers, 732 &handle_get_aml_decisions_finished, 733 adgh); 734 curl_slist_free_all (job_headers); 735 736 if (NULL == adgh->job) 737 { 738 GNUNET_free (adgh->url); 739 adgh->url = NULL; 740 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 741 } 742 return TALER_EC_NONE; 743 } 744 745 746 void 747 TALER_EXCHANGE_get_aml_decisions_cancel ( 748 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh) 749 { 750 if (NULL != adgh->job) 751 { 752 GNUNET_CURL_job_cancel (adgh->job); 753 adgh->job = NULL; 754 } 755 GNUNET_free (adgh->all_rules); 756 GNUNET_free (adgh->all_mp); 757 GNUNET_free (adgh->url); 758 GNUNET_free (adgh->base_url); 759 GNUNET_free (adgh); 760 } 761 762 763 /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */