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