exchange_api_get-aml-OFFICER_PUB-measures.c (20911B)
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-measures.c 19 * @brief Implementation of the GET /aml/$OFFICER_PUB/measures 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-measures.h" 27 #include "exchange_api_handle.h" 28 #include "taler/taler_signatures.h" 29 #include "exchange_api_curl_defaults.h" 30 31 32 /** 33 * Scrap buffer of temporary arrays. 34 */ 35 struct Scrap 36 { 37 /** 38 * Kept in DLL. 39 */ 40 struct Scrap *next; 41 42 /** 43 * Kept in DLL. 44 */ 45 struct Scrap *prev; 46 47 /** 48 * Pointer to our allocation. 49 */ 50 const char **ptr; 51 }; 52 53 54 /** 55 * @brief A GET /aml/$OFFICER_PUB/measures Handle 56 */ 57 struct TALER_EXCHANGE_GetAmlMeasuresHandle 58 { 59 60 /** 61 * The base URL of the exchange. 62 */ 63 char *base_url; 64 65 /** 66 * The full URL for this request, set during _start. 67 */ 68 char *url; 69 70 /** 71 * Handle for the request. 72 */ 73 struct GNUNET_CURL_Job *job; 74 75 /** 76 * Function to call with the result. 77 */ 78 TALER_EXCHANGE_GetAmlMeasuresCallback cb; 79 80 /** 81 * Closure for @e cb. 82 */ 83 TALER_EXCHANGE_GET_AML_MEASURES_RESULT_CLOSURE *cb_cls; 84 85 /** 86 * Reference to the execution context. 87 */ 88 struct GNUNET_CURL_Context *ctx; 89 90 /** 91 * Public key of the AML officer. 92 */ 93 struct TALER_AmlOfficerPublicKeyP officer_pub; 94 95 /** 96 * Private key of the AML officer (for signing). 97 */ 98 struct TALER_AmlOfficerPrivateKeyP officer_priv; 99 100 /** 101 * Head of scrap list. 102 */ 103 struct Scrap *scrap_head; 104 105 /** 106 * Tail of scrap list. 107 */ 108 struct Scrap *scrap_tail; 109 }; 110 111 112 /** 113 * Create array of length @a len in scrap book. 114 * 115 * @param[in,out] amh context for allocations 116 * @param len length of array 117 * @return scrap array 118 */ 119 static const char ** 120 make_scrap ( 121 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 122 unsigned int len) 123 { 124 struct Scrap *s = GNUNET_new (struct Scrap); 125 126 s->ptr = GNUNET_new_array (len, 127 const char *); 128 GNUNET_CONTAINER_DLL_insert (amh->scrap_head, 129 amh->scrap_tail, 130 s); 131 return s->ptr; 132 } 133 134 135 /** 136 * Free all scrap space. 137 * 138 * @param[in,out] amh scrap context 139 */ 140 static void 141 free_scrap (struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh) 142 { 143 struct Scrap *s; 144 145 while (NULL != (s = amh->scrap_head)) 146 { 147 GNUNET_CONTAINER_DLL_remove (amh->scrap_head, 148 amh->scrap_tail, 149 s); 150 GNUNET_free (s->ptr); 151 GNUNET_free (s); 152 } 153 } 154 155 156 /** 157 * Convert JSON array of strings to string array. 158 * 159 * @param j JSON array to convert 160 * @param[out] a array to initialize 161 * @return true on success 162 */ 163 static bool 164 j_to_a (const json_t *j, 165 const char **a) 166 { 167 const json_t *e; 168 size_t idx; 169 170 json_array_foreach ((json_t *) j, idx, e) 171 { 172 if (NULL == (a[idx] = json_string_value (e))) 173 return false; 174 } 175 return true; 176 } 177 178 179 /** 180 * Parse AML root measures. 181 * 182 * @param jroots JSON object with measure data 183 * @param[out] roots where to write the result 184 * @return #GNUNET_OK on success 185 */ 186 static enum GNUNET_GenericReturnValue 187 parse_aml_roots ( 188 const json_t *jroots, 189 struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo *roots) 190 { 191 const json_t *obj; 192 const char *name; 193 size_t idx = 0; 194 195 json_object_foreach ((json_t *) jroots, name, obj) 196 { 197 struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo *root = &roots[idx++]; 198 struct GNUNET_JSON_Specification spec[] = { 199 GNUNET_JSON_spec_string ("check_name", 200 &root->check_name), 201 GNUNET_JSON_spec_mark_optional ( 202 GNUNET_JSON_spec_string ("prog_name", 203 &root->prog_name), 204 NULL), 205 GNUNET_JSON_spec_mark_optional ( 206 GNUNET_JSON_spec_object_const ("context", 207 &root->context), 208 NULL), 209 GNUNET_JSON_spec_mark_optional ( 210 GNUNET_JSON_spec_string ("operation_type", 211 &root->operation_type), 212 NULL), 213 GNUNET_JSON_spec_mark_optional ( 214 GNUNET_JSON_spec_bool ("voluntary", 215 &root->voluntary), 216 NULL), 217 GNUNET_JSON_spec_end () 218 }; 219 220 if (GNUNET_OK != 221 GNUNET_JSON_parse (obj, 222 spec, 223 NULL, 224 NULL)) 225 { 226 GNUNET_break_op (0); 227 return GNUNET_SYSERR; 228 } 229 root->measure_name = name; 230 } 231 return GNUNET_OK; 232 } 233 234 235 /** 236 * Parse AML programs. 237 * 238 * @param[in,out] amh context for allocations 239 * @param jprogs JSON object with program data 240 * @param[out] progs where to write the result 241 * @return #GNUNET_OK on success 242 */ 243 static enum GNUNET_GenericReturnValue 244 parse_aml_programs ( 245 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 246 const json_t *jprogs, 247 struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement *progs) 248 { 249 const json_t *obj; 250 const char *name; 251 size_t idx = 0; 252 253 json_object_foreach ((json_t *) jprogs, name, obj) 254 { 255 struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement *prog = &progs[idx++] 256 ; 257 const json_t *jcontext; 258 const json_t *jinputs; 259 struct GNUNET_JSON_Specification spec[] = { 260 GNUNET_JSON_spec_string ("description", 261 &prog->description), 262 GNUNET_JSON_spec_array_const ("context", 263 &jcontext), 264 GNUNET_JSON_spec_array_const ("inputs", 265 &jinputs), 266 GNUNET_JSON_spec_end () 267 }; 268 unsigned int len; 269 const char **ptr; 270 271 if (GNUNET_OK != 272 GNUNET_JSON_parse (obj, 273 spec, 274 NULL, 275 NULL)) 276 { 277 GNUNET_break_op (0); 278 return GNUNET_SYSERR; 279 } 280 prog->prog_name = name; 281 prog->contexts_length = json_array_size (jcontext); 282 prog->inputs_length = json_array_size (jinputs); 283 len = (unsigned int) (prog->contexts_length + prog->inputs_length); 284 if ( ((size_t) len) != prog->contexts_length + prog->inputs_length) 285 { 286 GNUNET_break_op (0); 287 return GNUNET_SYSERR; 288 } 289 ptr = make_scrap (amh, len); 290 prog->contexts = ptr; 291 if (! j_to_a (jcontext, prog->contexts)) 292 { 293 GNUNET_break_op (0); 294 return GNUNET_SYSERR; 295 } 296 prog->inputs = &ptr[prog->contexts_length]; 297 if (! j_to_a (jinputs, prog->inputs)) 298 { 299 GNUNET_break_op (0); 300 return GNUNET_SYSERR; 301 } 302 } 303 return GNUNET_OK; 304 } 305 306 307 /** 308 * Parse KYC checks. 309 * 310 * @param[in,out] amh context for allocations 311 * @param jchecks JSON object with check data 312 * @param[out] checks where to write the result 313 * @return #GNUNET_OK on success 314 */ 315 static enum GNUNET_GenericReturnValue 316 parse_aml_checks ( 317 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 318 const json_t *jchecks, 319 struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo *checks) 320 { 321 const json_t *obj; 322 const char *name; 323 size_t idx = 0; 324 325 json_object_foreach ((json_t *) jchecks, name, obj) 326 { 327 struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo *check = &checks[idx++]; 328 const json_t *jrequires; 329 const json_t *joutputs; 330 struct GNUNET_JSON_Specification spec[] = { 331 GNUNET_JSON_spec_string ("description", 332 &check->description), 333 GNUNET_JSON_spec_mark_optional ( 334 GNUNET_JSON_spec_object_const ("description_i18n", 335 &check->description_i18n), 336 NULL), 337 GNUNET_JSON_spec_array_const ("requires", 338 &jrequires), 339 GNUNET_JSON_spec_array_const ("outputs", 340 &joutputs), 341 GNUNET_JSON_spec_string ("fallback", 342 &check->fallback), 343 GNUNET_JSON_spec_end () 344 }; 345 unsigned int len; 346 const char **ptr; 347 348 if (GNUNET_OK != 349 GNUNET_JSON_parse (obj, 350 spec, 351 NULL, 352 NULL)) 353 { 354 GNUNET_break_op (0); 355 return GNUNET_SYSERR; 356 } 357 check->check_name = name; 358 check->requires_length = json_array_size (jrequires); 359 check->outputs_length = json_array_size (joutputs); 360 len = (unsigned int) (check->requires_length + check->outputs_length); 361 if ( ((size_t) len) != check->requires_length + check->outputs_length) 362 { 363 GNUNET_break_op (0); 364 return GNUNET_SYSERR; 365 } 366 ptr = make_scrap (amh, len); 367 check->requires = ptr; 368 if (! j_to_a (jrequires, check->requires)) 369 { 370 GNUNET_break_op (0); 371 return GNUNET_SYSERR; 372 } 373 check->outputs = &ptr[check->requires_length]; 374 if (! j_to_a (joutputs, check->outputs)) 375 { 376 GNUNET_break_op (0); 377 return GNUNET_SYSERR; 378 } 379 } 380 return GNUNET_OK; 381 } 382 383 384 /** 385 * Parse default KYC rules from the default_rules array. 386 * 387 * @param[in,out] amh context for allocations 388 * @param jrules JSON array with rule data 389 * @param[out] rules where to write the result 390 * @return #GNUNET_OK on success 391 */ 392 static enum GNUNET_GenericReturnValue 393 parse_default_rules ( 394 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 395 const json_t *jrules, 396 struct TALER_EXCHANGE_GetAmlMeasuresKycRule *rules) 397 { 398 const json_t *obj; 399 size_t idx; 400 401 json_array_foreach ((json_t *) jrules, idx, obj) 402 { 403 struct TALER_EXCHANGE_GetAmlMeasuresKycRule *rule = &rules[idx]; 404 const json_t *jmeasures; 405 struct GNUNET_JSON_Specification spec[] = { 406 TALER_JSON_spec_kycte ("operation_type", 407 &rule->operation_type), 408 GNUNET_JSON_spec_mark_optional ( 409 GNUNET_JSON_spec_string ("rule_name", 410 &rule->rule_name), 411 NULL), 412 TALER_JSON_spec_amount_any ("threshold", 413 &rule->threshold), 414 GNUNET_JSON_spec_relative_time ("timeframe", 415 &rule->timeframe), 416 GNUNET_JSON_spec_array_const ("measures", 417 &jmeasures), 418 GNUNET_JSON_spec_mark_optional ( 419 GNUNET_JSON_spec_bool ("exposed", 420 &rule->exposed), 421 NULL), 422 GNUNET_JSON_spec_mark_optional ( 423 GNUNET_JSON_spec_bool ("is_and_combinator", 424 &rule->is_and_combinator), 425 NULL), 426 GNUNET_JSON_spec_int64 ("display_priority", 427 &rule->display_priority), 428 GNUNET_JSON_spec_end () 429 }; 430 431 if (GNUNET_OK != 432 GNUNET_JSON_parse (obj, 433 spec, 434 NULL, 435 NULL)) 436 { 437 GNUNET_break_op (0); 438 return GNUNET_SYSERR; 439 } 440 rule->measures_length = json_array_size (jmeasures); 441 { 442 const char **ptr = make_scrap (amh, 443 (unsigned int) rule->measures_length); 444 445 rule->measures = ptr; 446 if (! j_to_a (jmeasures, 447 ptr)) 448 { 449 GNUNET_break_op (0); 450 return GNUNET_SYSERR; 451 } 452 } 453 } 454 return GNUNET_OK; 455 } 456 457 458 /** 459 * Parse the provided measures data from the "200 OK" response. 460 * 461 * @param[in,out] amh handle (callback may be zero'ed out) 462 * @param json json reply with the data 463 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 464 */ 465 static enum GNUNET_GenericReturnValue 466 parse_get_aml_measures_ok (struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 467 const json_t *json) 468 { 469 struct TALER_EXCHANGE_GetAmlMeasuresResponse lr = { 470 .hr.reply = json, 471 .hr.http_status = MHD_HTTP_OK 472 }; 473 const json_t *jroots; 474 const json_t *jprograms; 475 const json_t *jchecks; 476 const json_t *jdefault_rules = NULL; 477 struct GNUNET_JSON_Specification spec[] = { 478 GNUNET_JSON_spec_object_const ("roots", 479 &jroots), 480 GNUNET_JSON_spec_object_const ("programs", 481 &jprograms), 482 GNUNET_JSON_spec_object_const ("checks", 483 &jchecks), 484 GNUNET_JSON_spec_mark_optional ( 485 GNUNET_JSON_spec_array_const ("default_rules", 486 &jdefault_rules), 487 NULL), 488 GNUNET_JSON_spec_end () 489 }; 490 491 if (GNUNET_OK != 492 GNUNET_JSON_parse (json, 493 spec, 494 NULL, 495 NULL)) 496 { 497 GNUNET_break_op (0); 498 return GNUNET_SYSERR; 499 } 500 501 lr.details.ok.roots_length = json_object_size (jroots); 502 lr.details.ok.programs_length = json_object_size (jprograms); 503 lr.details.ok.checks_length = json_object_size (jchecks); 504 lr.details.ok.default_rules_length = 505 (NULL != jdefault_rules) ? json_array_size (jdefault_rules) : 0; 506 507 { 508 struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo *roots; 509 struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement *progs; 510 struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo *checks; 511 struct TALER_EXCHANGE_GetAmlMeasuresKycRule *drules; 512 enum GNUNET_GenericReturnValue ret; 513 514 if ( (lr.details.ok.roots_length > 515 GNUNET_MAX_MALLOC_CHECKED / sizeof (*roots)) || 516 (lr.details.ok.programs_length > 517 GNUNET_MAX_MALLOC_CHECKED / sizeof (*progs)) || 518 (lr.details.ok.checks_length > 519 GNUNET_MAX_MALLOC_CHECKED / sizeof (*checks)) || 520 (lr.details.ok.default_rules_length > 521 GNUNET_MAX_MALLOC_CHECKED / sizeof (*drules)) ) 522 { 523 GNUNET_break_op (0); 524 return GNUNET_SYSERR; 525 } 526 roots = GNUNET_new_array ( 527 GNUNET_NZL (lr.details.ok.roots_length), 528 struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo); 529 progs = GNUNET_new_array ( 530 GNUNET_NZL (lr.details.ok.programs_length), 531 struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement); 532 checks = GNUNET_new_array ( 533 GNUNET_NZL (lr.details.ok.checks_length), 534 struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo); 535 drules = GNUNET_new_array ( 536 GNUNET_NZL (lr.details.ok.default_rules_length), 537 struct TALER_EXCHANGE_GetAmlMeasuresKycRule); 538 lr.details.ok.roots = roots; 539 lr.details.ok.programs = progs; 540 lr.details.ok.checks = checks; 541 lr.details.ok.default_rules = drules; 542 543 ret = parse_aml_roots (jroots, roots); 544 if (GNUNET_OK == ret) 545 ret = parse_aml_programs (amh, jprograms, progs); 546 if (GNUNET_OK == ret) 547 ret = parse_aml_checks (amh, jchecks, checks); 548 if ( (GNUNET_OK == ret) && (NULL != jdefault_rules) ) 549 ret = parse_default_rules (amh, jdefault_rules, drules); 550 if (GNUNET_OK == ret) 551 { 552 amh->cb (amh->cb_cls, 553 &lr); 554 amh->cb = NULL; 555 } 556 GNUNET_free (roots); 557 GNUNET_free (progs); 558 GNUNET_free (checks); 559 GNUNET_free (drules); 560 free_scrap (amh); 561 return ret; 562 } 563 } 564 565 566 /** 567 * Function called when we're done processing the 568 * HTTP GET /aml/$OFFICER_PUB/measures request. 569 * 570 * @param cls the `struct TALER_EXCHANGE_GetAmlMeasuresHandle` 571 * @param response_code HTTP response code, 0 on error 572 * @param response parsed JSON result, NULL on error 573 */ 574 static void 575 handle_get_aml_measures_finished (void *cls, 576 long response_code, 577 const void *response) 578 { 579 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh = cls; 580 const json_t *j = response; 581 struct TALER_EXCHANGE_GetAmlMeasuresResponse lr = { 582 .hr.reply = j, 583 .hr.http_status = (unsigned int) response_code 584 }; 585 586 amh->job = NULL; 587 switch (response_code) 588 { 589 case 0: 590 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 591 break; 592 case MHD_HTTP_OK: 593 if (GNUNET_OK != 594 parse_get_aml_measures_ok (amh, 595 j)) 596 { 597 GNUNET_break_op (0); 598 lr.hr.http_status = 0; 599 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 600 break; 601 } 602 GNUNET_assert (NULL == amh->cb); 603 TALER_EXCHANGE_get_aml_measures_cancel (amh); 604 return; 605 case MHD_HTTP_NO_CONTENT: 606 break; 607 case MHD_HTTP_BAD_REQUEST: 608 lr.hr.ec = TALER_JSON_get_error_code (j); 609 lr.hr.hint = TALER_JSON_get_error_hint (j); 610 break; 611 case MHD_HTTP_FORBIDDEN: 612 lr.hr.ec = TALER_JSON_get_error_code (j); 613 lr.hr.hint = TALER_JSON_get_error_hint (j); 614 break; 615 case MHD_HTTP_INTERNAL_SERVER_ERROR: 616 lr.hr.ec = TALER_JSON_get_error_code (j); 617 lr.hr.hint = TALER_JSON_get_error_hint (j); 618 break; 619 default: 620 /* unexpected response code */ 621 GNUNET_break_op (0); 622 lr.hr.ec = TALER_JSON_get_error_code (j); 623 lr.hr.hint = TALER_JSON_get_error_hint (j); 624 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 625 "Unexpected response code %u/%d for GET AML measures\n", 626 (unsigned int) response_code, 627 (int) lr.hr.ec); 628 break; 629 } 630 if (NULL != amh->cb) 631 amh->cb (amh->cb_cls, 632 &lr); 633 TALER_EXCHANGE_get_aml_measures_cancel (amh); 634 } 635 636 637 struct TALER_EXCHANGE_GetAmlMeasuresHandle * 638 TALER_EXCHANGE_get_aml_measures_create ( 639 struct GNUNET_CURL_Context *ctx, 640 const char *url, 641 const struct TALER_AmlOfficerPrivateKeyP *officer_priv) 642 { 643 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh; 644 645 amh = GNUNET_new (struct TALER_EXCHANGE_GetAmlMeasuresHandle); 646 amh->ctx = ctx; 647 amh->base_url = GNUNET_strdup (url); 648 amh->officer_priv = *officer_priv; 649 GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, 650 &amh->officer_pub.eddsa_pub); 651 return amh; 652 } 653 654 655 enum TALER_ErrorCode 656 TALER_EXCHANGE_get_aml_measures_start ( 657 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh, 658 TALER_EXCHANGE_GetAmlMeasuresCallback cb, 659 TALER_EXCHANGE_GET_AML_MEASURES_RESULT_CLOSURE *cb_cls) 660 { 661 CURL *eh; 662 struct TALER_AmlOfficerSignatureP officer_sig; 663 char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32]; 664 struct curl_slist *job_headers = NULL; 665 666 amh->cb = cb; 667 amh->cb_cls = cb_cls; 668 669 /* Build AML officer signature */ 670 TALER_officer_aml_query_sign (&amh->officer_priv, 671 &officer_sig); 672 673 /* Build the path component: aml/{officer_pub}/measures */ 674 { 675 char pub_str[sizeof (amh->officer_pub) * 2]; 676 char *end; 677 678 end = GNUNET_STRINGS_data_to_string ( 679 &amh->officer_pub, 680 sizeof (amh->officer_pub), 681 pub_str, 682 sizeof (pub_str)); 683 *end = '\0'; 684 GNUNET_snprintf (arg_str, 685 sizeof (arg_str), 686 "aml/%s/measures", 687 pub_str); 688 } 689 690 amh->url = TALER_url_join (amh->base_url, 691 arg_str, 692 NULL); 693 if (NULL == amh->url) 694 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 695 696 eh = TALER_EXCHANGE_curl_easy_get_ (amh->url); 697 if (NULL == eh) 698 { 699 GNUNET_break (0); 700 GNUNET_free (amh->url); 701 amh->url = NULL; 702 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 703 } 704 705 /* Build job headers with AML officer signature */ 706 { 707 char *hdr; 708 char sig_str[sizeof (officer_sig) * 2]; 709 char *end; 710 711 end = GNUNET_STRINGS_data_to_string ( 712 &officer_sig, 713 sizeof (officer_sig), 714 sig_str, 715 sizeof (sig_str)); 716 *end = '\0'; 717 718 GNUNET_asprintf (&hdr, 719 "%s: %s", 720 TALER_AML_OFFICER_SIGNATURE_HEADER, 721 sig_str); 722 job_headers = curl_slist_append (NULL, 723 hdr); 724 GNUNET_free (hdr); 725 } 726 727 amh->job = GNUNET_CURL_job_add2 (amh->ctx, 728 eh, 729 job_headers, 730 &handle_get_aml_measures_finished, 731 amh); 732 curl_slist_free_all (job_headers); 733 734 if (NULL == amh->job) 735 { 736 GNUNET_free (amh->url); 737 amh->url = NULL; 738 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 739 } 740 return TALER_EC_NONE; 741 } 742 743 744 void 745 TALER_EXCHANGE_get_aml_measures_cancel ( 746 struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh) 747 { 748 if (NULL != amh->job) 749 { 750 GNUNET_CURL_job_cancel (amh->job); 751 amh->job = NULL; 752 } 753 free_scrap (amh); 754 GNUNET_free (amh->url); 755 GNUNET_free (amh->base_url); 756 GNUNET_free (amh); 757 } 758 759 760 /* end of exchange_api_get-aml-OFFICER_PUB-measures.c */