exchange_api_get-aml-OFFICER_PUB-decisions.c (21933B)
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 if (lr.details.ok.records_length > 395 GNUNET_MAX_MALLOC_CHECKED 396 / sizeof (struct TALER_EXCHANGE_GetAmlDecisionsDecision)) 397 { 398 GNUNET_break_op (0); 399 return GNUNET_SYSERR; 400 } 401 402 /* First pass: count total rules and measures across all records */ 403 { 404 size_t total_rules = 0; 405 size_t total_measures = 0; 406 const json_t *obj; 407 size_t idx; 408 409 json_array_foreach ((json_t *) jrecords, idx, obj) 410 { 411 const json_t *jlimits = json_object_get (obj, "limits"); 412 const json_t *jrules; 413 414 if (NULL == jlimits) 415 continue; 416 jrules = json_object_get (jlimits, "rules"); 417 if (NULL == jrules) 418 continue; 419 total_rules += json_array_size (jrules); 420 421 { 422 const json_t *jrule; 423 size_t ridx; 424 425 json_array_foreach ((json_t *) jrules, ridx, jrule) 426 { 427 const json_t *jmeasures = json_object_get (jrule, "measures"); 428 429 if (NULL != jmeasures) 430 total_measures += json_array_size (jmeasures); 431 } 432 } 433 } 434 435 adgh->all_rules = GNUNET_new_array ( 436 GNUNET_NZL (total_rules), 437 struct TALER_EXCHANGE_GetAmlDecisionsKycRule); 438 adgh->all_mp = GNUNET_new_array ( 439 GNUNET_NZL (total_measures), 440 const char *); 441 } 442 443 { 444 struct TALER_EXCHANGE_GetAmlDecisionsDecision *records; 445 enum GNUNET_GenericReturnValue ret; 446 447 records = GNUNET_new_array ( 448 GNUNET_NZL (lr.details.ok.records_length), 449 struct TALER_EXCHANGE_GetAmlDecisionsDecision); 450 lr.details.ok.records = records; 451 ret = parse_aml_decisions (adgh, 452 jrecords, 453 lr.details.ok.records_length, 454 records); 455 if (GNUNET_OK == ret) 456 { 457 adgh->cb (adgh->cb_cls, 458 &lr); 459 adgh->cb = NULL; 460 } 461 GNUNET_free (records); 462 GNUNET_free (adgh->all_rules); 463 adgh->all_rules = NULL; 464 GNUNET_free (adgh->all_mp); 465 adgh->all_mp = NULL; 466 return ret; 467 } 468 } 469 470 471 /** 472 * Function called when we're done processing the 473 * HTTP /aml/$OFFICER_PUB/decisions request. 474 * 475 * @param cls the `struct TALER_EXCHANGE_GetAmlDecisionsHandle` 476 * @param response_code HTTP response code, 0 on error 477 * @param response parsed JSON result, NULL on error 478 */ 479 static void 480 handle_get_aml_decisions_finished (void *cls, 481 long response_code, 482 const void *response) 483 { 484 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh = cls; 485 const json_t *j = response; 486 struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = { 487 .hr.reply = j, 488 .hr.http_status = (unsigned int) response_code 489 }; 490 491 adgh->job = NULL; 492 switch (response_code) 493 { 494 case 0: 495 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 496 break; 497 case MHD_HTTP_OK: 498 if (GNUNET_OK != 499 parse_get_aml_decisions_ok (adgh, 500 j)) 501 { 502 GNUNET_break_op (0); 503 lr.hr.http_status = 0; 504 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 505 break; 506 } 507 GNUNET_assert (NULL == adgh->cb); 508 TALER_EXCHANGE_get_aml_decisions_cancel (adgh); 509 return; 510 case MHD_HTTP_NO_CONTENT: 511 break; 512 case MHD_HTTP_BAD_REQUEST: 513 #if DEBUG 514 json_dumpf (j, 515 stderr, 516 JSON_INDENT (2)); 517 #endif 518 lr.hr.ec = TALER_JSON_get_error_code (j); 519 lr.hr.hint = TALER_JSON_get_error_hint (j); 520 break; 521 case MHD_HTTP_FORBIDDEN: 522 lr.hr.ec = TALER_JSON_get_error_code (j); 523 lr.hr.hint = TALER_JSON_get_error_hint (j); 524 break; 525 case MHD_HTTP_NOT_FOUND: 526 lr.hr.ec = TALER_JSON_get_error_code (j); 527 lr.hr.hint = TALER_JSON_get_error_hint (j); 528 break; 529 case MHD_HTTP_INTERNAL_SERVER_ERROR: 530 lr.hr.ec = TALER_JSON_get_error_code (j); 531 lr.hr.hint = TALER_JSON_get_error_hint (j); 532 break; 533 default: 534 /* unexpected response code */ 535 GNUNET_break_op (0); 536 lr.hr.ec = TALER_JSON_get_error_code (j); 537 lr.hr.hint = TALER_JSON_get_error_hint (j); 538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 539 "Unexpected response code %u/%d for GET AML decisions\n", 540 (unsigned int) response_code, 541 (int) lr.hr.ec); 542 break; 543 } 544 if (NULL != adgh->cb) 545 adgh->cb (adgh->cb_cls, 546 &lr); 547 TALER_EXCHANGE_get_aml_decisions_cancel (adgh); 548 } 549 550 551 struct TALER_EXCHANGE_GetAmlDecisionsHandle * 552 TALER_EXCHANGE_get_aml_decisions_create ( 553 struct GNUNET_CURL_Context *ctx, 554 const char *url, 555 const struct TALER_AmlOfficerPrivateKeyP *officer_priv) 556 { 557 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh; 558 559 adgh = GNUNET_new (struct TALER_EXCHANGE_GetAmlDecisionsHandle); 560 adgh->ctx = ctx; 561 adgh->base_url = GNUNET_strdup (url); 562 adgh->officer_priv = *officer_priv; 563 GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, 564 &adgh->officer_pub.eddsa_pub); 565 adgh->options.limit = -20; 566 adgh->options.offset = INT64_MAX; 567 adgh->options.active = TALER_EXCHANGE_YNA_ALL; 568 adgh->options.investigation = TALER_EXCHANGE_YNA_ALL; 569 return adgh; 570 } 571 572 573 enum GNUNET_GenericReturnValue 574 TALER_EXCHANGE_get_aml_decisions_set_options_ ( 575 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 576 unsigned int num_options, 577 const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue options[]) 578 { 579 for (unsigned int i = 0; i < num_options; i++) 580 { 581 const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i]; 582 583 switch (opt->option) 584 { 585 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END: 586 return GNUNET_OK; 587 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT: 588 adgh->options.limit = opt->details.limit; 589 break; 590 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET: 591 if (opt->details.offset > INT64_MAX) 592 { 593 GNUNET_break (0); 594 return GNUNET_NO; 595 } 596 adgh->options.offset = opt->details.offset; 597 break; 598 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO: 599 adgh->options.h_payto = opt->details.h_payto; 600 break; 601 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE: 602 adgh->options.active = opt->details.active; 603 break; 604 case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION: 605 adgh->options.investigation = opt->details.investigation; 606 break; 607 default: 608 GNUNET_break (0); 609 return GNUNET_SYSERR; 610 } 611 } 612 return GNUNET_OK; 613 } 614 615 616 enum TALER_ErrorCode 617 TALER_EXCHANGE_get_aml_decisions_start ( 618 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh, 619 TALER_EXCHANGE_GetAmlDecisionsCallback cb, 620 TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls) 621 { 622 CURL *eh; 623 char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32]; 624 struct curl_slist *job_headers = NULL; 625 626 adgh->cb = cb; 627 adgh->cb_cls = cb_cls; 628 629 /* Build AML officer signature */ 630 TALER_officer_aml_query_sign (&adgh->officer_priv, 631 &adgh->officer_sig); 632 633 /* Build the path component: aml/{officer_pub}/decisions */ 634 { 635 char pub_str[sizeof (adgh->officer_pub) * 2]; 636 char *end; 637 638 end = GNUNET_STRINGS_data_to_string ( 639 &adgh->officer_pub, 640 sizeof (adgh->officer_pub), 641 pub_str, 642 sizeof (pub_str)); 643 *end = '\0'; 644 GNUNET_snprintf (arg_str, 645 sizeof (arg_str), 646 "aml/%s/decisions", 647 pub_str); 648 } 649 650 /* Build URL with optional query parameters */ 651 { 652 char limit_s[24]; 653 char offset_s[24]; 654 char payto_s[sizeof (*adgh->options.h_payto) * 2 + 1]; 655 int64_t limit = adgh->options.limit; 656 uint64_t offset = adgh->options.offset; 657 bool omit_limit = (-20 == limit); 658 bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) || 659 ( (limit > 0) && (0 == offset) ) ); 660 661 GNUNET_snprintf (limit_s, 662 sizeof (limit_s), 663 "%lld", 664 (long long) limit); 665 GNUNET_snprintf (offset_s, 666 sizeof (offset_s), 667 "%llu", 668 (unsigned long long) offset); 669 670 if (NULL != adgh->options.h_payto) 671 { 672 char *end; 673 674 end = GNUNET_STRINGS_data_to_string ( 675 adgh->options.h_payto, 676 sizeof (*adgh->options.h_payto), 677 payto_s, 678 sizeof (payto_s) - 1); 679 *end = '\0'; 680 } 681 682 adgh->url = TALER_url_join ( 683 adgh->base_url, 684 arg_str, 685 "limit", 686 omit_limit ? NULL : limit_s, 687 "offset", 688 omit_offset ? NULL : offset_s, 689 "h_payto", 690 (NULL != adgh->options.h_payto) ? payto_s : NULL, 691 "active", 692 (TALER_EXCHANGE_YNA_ALL != adgh->options.active) 693 ? TALER_yna_to_string (adgh->options.active) 694 : NULL, 695 "investigation", 696 (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation) 697 ? TALER_yna_to_string (adgh->options.investigation) 698 : NULL, 699 NULL); 700 } 701 702 if (NULL == adgh->url) 703 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 704 705 eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url); 706 if (NULL == eh) 707 { 708 GNUNET_break (0); 709 GNUNET_free (adgh->url); 710 adgh->url = NULL; 711 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 712 } 713 714 /* Build job headers with AML officer signature */ 715 { 716 char *hdr; 717 char sig_str[sizeof (adgh->officer_sig) * 2]; 718 char *end; 719 720 end = GNUNET_STRINGS_data_to_string ( 721 &adgh->officer_sig, 722 sizeof (adgh->officer_sig), 723 sig_str, 724 sizeof (sig_str)); 725 *end = '\0'; 726 727 GNUNET_asprintf (&hdr, 728 "%s: %s", 729 TALER_AML_OFFICER_SIGNATURE_HEADER, 730 sig_str); 731 job_headers = curl_slist_append (NULL, 732 hdr); 733 GNUNET_free (hdr); 734 } 735 736 adgh->job = GNUNET_CURL_job_add2 (adgh->ctx, 737 eh, 738 job_headers, 739 &handle_get_aml_decisions_finished, 740 adgh); 741 curl_slist_free_all (job_headers); 742 743 if (NULL == adgh->job) 744 { 745 GNUNET_free (adgh->url); 746 adgh->url = NULL; 747 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 748 } 749 return TALER_EC_NONE; 750 } 751 752 753 void 754 TALER_EXCHANGE_get_aml_decisions_cancel ( 755 struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh) 756 { 757 if (NULL != adgh->job) 758 { 759 GNUNET_CURL_job_cancel (adgh->job); 760 adgh->job = NULL; 761 } 762 GNUNET_free (adgh->all_rules); 763 GNUNET_free (adgh->all_mp); 764 GNUNET_free (adgh->url); 765 GNUNET_free (adgh->base_url); 766 GNUNET_free (adgh); 767 } 768 769 770 /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */