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 : #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 6 : 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 6 : GNUNET_JSON_spec_timestamp ("expiration_time",
150 : &limits->expiration_time),
151 6 : GNUNET_JSON_spec_mark_optional (
152 : GNUNET_JSON_spec_string ("successor_measure",
153 : &limits->successor_measure),
154 : NULL),
155 6 : GNUNET_JSON_spec_array_const ("rules",
156 : &jrules),
157 6 : GNUNET_JSON_spec_mark_optional (
158 : GNUNET_JSON_spec_object_const ("custom_measures",
159 : &jcustom_measures),
160 : NULL),
161 6 : GNUNET_JSON_spec_end ()
162 : };
163 :
164 6 : if (GNUNET_OK !=
165 6 : GNUNET_JSON_parse (jlimits,
166 : spec,
167 : NULL,
168 : NULL))
169 : {
170 0 : GNUNET_break_op (0);
171 0 : return GNUNET_SYSERR;
172 : }
173 6 : limits->custom_measures = jcustom_measures;
174 :
175 : {
176 6 : size_t rule_count = json_array_size (jrules);
177 6 : size_t rule_start = *rule_off;
178 :
179 6 : limits->rules = &adgh->all_rules[rule_start];
180 6 : limits->rules_length = rule_count;
181 :
182 : {
183 : const json_t *jrule;
184 : size_t ridx;
185 :
186 37 : json_array_foreach ((json_t *) jrules, ridx, jrule)
187 : {
188 31 : struct TALER_EXCHANGE_GetAmlDecisionsKycRule *r
189 31 : = &adgh->all_rules[*rule_off];
190 : const json_t *jsmeasures;
191 : struct GNUNET_JSON_Specification rspec[] = {
192 31 : TALER_JSON_spec_kycte ("operation_type",
193 : &r->operation_type),
194 31 : GNUNET_JSON_spec_mark_optional (
195 : GNUNET_JSON_spec_string ("rule_name",
196 : &r->rule_name),
197 : NULL),
198 31 : TALER_JSON_spec_amount_any ("threshold",
199 : &r->threshold),
200 31 : GNUNET_JSON_spec_relative_time ("timeframe",
201 : &r->timeframe),
202 31 : GNUNET_JSON_spec_array_const ("measures",
203 : &jsmeasures),
204 31 : GNUNET_JSON_spec_mark_optional (
205 : GNUNET_JSON_spec_bool ("exposed",
206 : &r->exposed),
207 : NULL),
208 31 : GNUNET_JSON_spec_mark_optional (
209 : GNUNET_JSON_spec_bool ("is_and_combinator",
210 : &r->is_and_combinator),
211 : NULL),
212 31 : GNUNET_JSON_spec_int64 ("display_priority",
213 : &r->display_priority),
214 31 : GNUNET_JSON_spec_end ()
215 : };
216 :
217 31 : if (GNUNET_OK !=
218 31 : GNUNET_JSON_parse (jrule,
219 : rspec,
220 : NULL,
221 : NULL))
222 : {
223 0 : GNUNET_break_op (0);
224 0 : return GNUNET_SYSERR;
225 : }
226 :
227 : {
228 31 : size_t mlen = json_array_size (jsmeasures);
229 31 : size_t mp_start = *mp_off;
230 :
231 31 : r->measures = &adgh->all_mp[mp_start];
232 31 : r->measures_length = mlen;
233 :
234 : {
235 : size_t midx;
236 : const json_t *jm;
237 :
238 61 : json_array_foreach (jsmeasures, midx, jm)
239 : {
240 30 : const char *sval = json_string_value (jm);
241 :
242 30 : if (NULL == sval)
243 : {
244 0 : GNUNET_break_op (0);
245 0 : return GNUNET_SYSERR;
246 : }
247 30 : adgh->all_mp[*mp_off] = sval;
248 30 : (*mp_off)++;
249 : }
250 : }
251 : }
252 :
253 31 : (*rule_off)++;
254 : }
255 : }
256 : }
257 :
258 6 : 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 2 : 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 2 : size_t rule_off = 0;
279 2 : size_t mp_off = 0;
280 : const json_t *obj;
281 : size_t idx;
282 :
283 8 : json_array_foreach ((json_t *) jrecords, idx, obj)
284 : {
285 6 : struct TALER_EXCHANGE_GetAmlDecisionsDecision *decision = &records_ar[idx];
286 : const json_t *jlimits;
287 : struct GNUNET_JSON_Specification spec[] = {
288 6 : GNUNET_JSON_spec_fixed_auto ("h_payto",
289 : &decision->h_payto),
290 6 : GNUNET_JSON_spec_mark_optional (
291 : GNUNET_JSON_spec_string ("full_payto",
292 : &decision->full_payto),
293 : NULL),
294 6 : GNUNET_JSON_spec_mark_optional (
295 : GNUNET_JSON_spec_bool ("is_wallet",
296 : &decision->is_wallet),
297 : NULL),
298 6 : GNUNET_JSON_spec_uint64 ("rowid",
299 : &decision->rowid),
300 6 : GNUNET_JSON_spec_mark_optional (
301 : GNUNET_JSON_spec_string ("justification",
302 : &decision->justification),
303 : NULL),
304 6 : GNUNET_JSON_spec_mark_optional (
305 : GNUNET_JSON_spec_string ("new_measures",
306 : &decision->new_measures),
307 : NULL),
308 6 : GNUNET_JSON_spec_mark_optional (
309 6 : GNUNET_JSON_spec_fixed_auto ("decider_pub",
310 : &decision->decider_pub),
311 : NULL),
312 6 : GNUNET_JSON_spec_mark_optional (
313 : GNUNET_JSON_spec_string ("decider_name",
314 : &decision->decider_name),
315 : NULL),
316 6 : GNUNET_JSON_spec_mark_optional (
317 : GNUNET_JSON_spec_uint64 ("kyc_attributes_rowid",
318 : &decision->kyc_attributes_rowid),
319 : NULL),
320 6 : GNUNET_JSON_spec_timestamp ("decision_time",
321 : &decision->decision_time),
322 6 : GNUNET_JSON_spec_mark_optional (
323 : GNUNET_JSON_spec_object_const ("properties",
324 : &decision->properties),
325 : NULL),
326 6 : GNUNET_JSON_spec_object_const ("limits",
327 : &jlimits),
328 6 : GNUNET_JSON_spec_bool ("to_investigate",
329 : &decision->to_investigate),
330 6 : GNUNET_JSON_spec_bool ("is_active",
331 : &decision->is_active),
332 6 : GNUNET_JSON_spec_end ()
333 : };
334 :
335 6 : GNUNET_assert (idx < records_ar_length);
336 6 : if (GNUNET_OK !=
337 6 : GNUNET_JSON_parse (obj,
338 : spec,
339 : NULL,
340 : NULL))
341 : {
342 0 : GNUNET_break_op (0);
343 0 : return GNUNET_SYSERR;
344 : }
345 :
346 6 : if (GNUNET_OK !=
347 6 : parse_limits (adgh,
348 : jlimits,
349 : &decision->limits,
350 : &rule_off,
351 : &mp_off))
352 : {
353 0 : GNUNET_break_op (0);
354 0 : return GNUNET_SYSERR;
355 : }
356 : }
357 2 : 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 2 : parse_get_aml_decisions_ok (struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
370 : const json_t *json)
371 : {
372 2 : 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 2 : GNUNET_JSON_spec_array_const ("records",
379 : &jrecords),
380 2 : GNUNET_JSON_spec_end ()
381 : };
382 :
383 2 : if (GNUNET_OK !=
384 2 : GNUNET_JSON_parse (json,
385 : spec,
386 : NULL,
387 : NULL))
388 : {
389 0 : GNUNET_break_op (0);
390 0 : return GNUNET_SYSERR;
391 : }
392 :
393 2 : lr.details.ok.records_length = json_array_size (jrecords);
394 :
395 : /* First pass: count total rules and measures across all records */
396 : {
397 2 : size_t total_rules = 0;
398 2 : size_t total_measures = 0;
399 : const json_t *obj;
400 : size_t idx;
401 :
402 8 : json_array_foreach ((json_t *) jrecords, idx, obj)
403 : {
404 6 : const json_t *jlimits = json_object_get (obj, "limits");
405 : const json_t *jrules;
406 :
407 6 : if (NULL == jlimits)
408 0 : continue;
409 6 : jrules = json_object_get (jlimits, "rules");
410 6 : if (NULL == jrules)
411 0 : continue;
412 6 : total_rules += json_array_size (jrules);
413 :
414 : {
415 : const json_t *jrule;
416 : size_t ridx;
417 :
418 37 : json_array_foreach ((json_t *) jrules, ridx, jrule)
419 : {
420 31 : const json_t *jmeasures = json_object_get (jrule, "measures");
421 :
422 31 : if (NULL != jmeasures)
423 31 : total_measures += json_array_size (jmeasures);
424 : }
425 : }
426 : }
427 :
428 2 : adgh->all_rules = GNUNET_new_array (
429 : GNUNET_NZL (total_rules),
430 : struct TALER_EXCHANGE_GetAmlDecisionsKycRule);
431 2 : adgh->all_mp = GNUNET_new_array (
432 : GNUNET_NZL (total_measures),
433 : const char *);
434 : }
435 :
436 2 : {
437 2 : struct TALER_EXCHANGE_GetAmlDecisionsDecision records[
438 2 : GNUNET_NZL (lr.details.ok.records_length)];
439 : enum GNUNET_GenericReturnValue ret;
440 :
441 2 : memset (records,
442 : 0,
443 : sizeof (records));
444 2 : lr.details.ok.records = records;
445 2 : ret = parse_aml_decisions (adgh,
446 : jrecords,
447 : lr.details.ok.records_length,
448 : records);
449 2 : if (GNUNET_OK == ret)
450 : {
451 2 : adgh->cb (adgh->cb_cls,
452 : &lr);
453 2 : adgh->cb = NULL;
454 : }
455 2 : GNUNET_free (adgh->all_rules);
456 2 : adgh->all_rules = NULL;
457 2 : GNUNET_free (adgh->all_mp);
458 2 : adgh->all_mp = NULL;
459 2 : 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 4 : handle_get_aml_decisions_finished (void *cls,
474 : long response_code,
475 : const void *response)
476 : {
477 4 : struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh = cls;
478 4 : const json_t *j = response;
479 4 : struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = {
480 : .hr.reply = j,
481 4 : .hr.http_status = (unsigned int) response_code
482 : };
483 :
484 4 : adgh->job = NULL;
485 4 : switch (response_code)
486 : {
487 0 : case 0:
488 0 : lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
489 0 : break;
490 2 : case MHD_HTTP_OK:
491 2 : if (GNUNET_OK !=
492 2 : parse_get_aml_decisions_ok (adgh,
493 : j))
494 : {
495 0 : GNUNET_break_op (0);
496 0 : lr.hr.http_status = 0;
497 0 : lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
498 0 : break;
499 : }
500 2 : GNUNET_assert (NULL == adgh->cb);
501 2 : TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
502 2 : return;
503 1 : case MHD_HTTP_NO_CONTENT:
504 1 : break;
505 0 : case MHD_HTTP_BAD_REQUEST:
506 : #if DEBUG
507 : json_dumpf (j,
508 : stderr,
509 : JSON_INDENT (2));
510 : #endif
511 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
512 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
513 0 : break;
514 1 : case MHD_HTTP_FORBIDDEN:
515 1 : lr.hr.ec = TALER_JSON_get_error_code (j);
516 1 : lr.hr.hint = TALER_JSON_get_error_hint (j);
517 1 : break;
518 0 : case MHD_HTTP_NOT_FOUND:
519 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
520 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
521 0 : break;
522 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
523 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
524 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
525 0 : break;
526 0 : default:
527 : /* unexpected response code */
528 0 : GNUNET_break_op (0);
529 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
530 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
531 0 : 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 0 : break;
536 : }
537 2 : if (NULL != adgh->cb)
538 2 : adgh->cb (adgh->cb_cls,
539 : &lr);
540 2 : TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
541 : }
542 :
543 :
544 : struct TALER_EXCHANGE_GetAmlDecisionsHandle *
545 4 : 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 4 : adgh = GNUNET_new (struct TALER_EXCHANGE_GetAmlDecisionsHandle);
553 4 : adgh->ctx = ctx;
554 4 : adgh->base_url = GNUNET_strdup (url);
555 4 : adgh->officer_priv = *officer_priv;
556 4 : GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
557 : &adgh->officer_pub.eddsa_pub);
558 4 : adgh->options.limit = -20;
559 4 : adgh->options.offset = INT64_MAX;
560 4 : adgh->options.active = TALER_EXCHANGE_YNA_ALL;
561 4 : adgh->options.investigation = TALER_EXCHANGE_YNA_ALL;
562 4 : return adgh;
563 : }
564 :
565 :
566 : enum GNUNET_GenericReturnValue
567 7 : 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 10 : for (unsigned int i = 0; i < num_options; i++)
573 : {
574 10 : const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i];
575 :
576 10 : switch (opt->option)
577 : {
578 3 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END:
579 3 : return GNUNET_OK;
580 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT:
581 0 : adgh->options.limit = opt->details.limit;
582 0 : break;
583 4 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET:
584 4 : if (opt->details.offset > INT64_MAX)
585 : {
586 4 : GNUNET_break (0);
587 4 : return GNUNET_NO;
588 : }
589 0 : adgh->options.offset = opt->details.offset;
590 0 : break;
591 3 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO:
592 3 : adgh->options.h_payto = opt->details.h_payto;
593 3 : break;
594 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE:
595 0 : adgh->options.active = opt->details.active;
596 0 : break;
597 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION:
598 0 : adgh->options.investigation = opt->details.investigation;
599 0 : break;
600 0 : default:
601 0 : GNUNET_break (0);
602 0 : return GNUNET_SYSERR;
603 : }
604 : }
605 0 : return GNUNET_OK;
606 : }
607 :
608 :
609 : enum TALER_ErrorCode
610 4 : 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 4 : struct curl_slist *job_headers = NULL;
618 :
619 4 : adgh->cb = cb;
620 4 : adgh->cb_cls = cb_cls;
621 :
622 : /* Build AML officer signature */
623 4 : 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 4 : end = GNUNET_STRINGS_data_to_string (
632 4 : &adgh->officer_pub,
633 : sizeof (adgh->officer_pub),
634 : pub_str,
635 : sizeof (pub_str));
636 4 : *end = '\0';
637 4 : 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 4 : int64_t limit = adgh->options.limit;
649 4 : uint64_t offset = adgh->options.offset;
650 4 : bool omit_limit = (-20 == limit);
651 4 : bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) ||
652 0 : ( (limit > 0) && (0 == offset) ) );
653 :
654 4 : GNUNET_snprintf (limit_s,
655 : sizeof (limit_s),
656 : "%lld",
657 : (long long) limit);
658 4 : GNUNET_snprintf (offset_s,
659 : sizeof (offset_s),
660 : "%llu",
661 : (unsigned long long) offset);
662 :
663 4 : if (NULL != adgh->options.h_payto)
664 : {
665 : char *end;
666 :
667 3 : end = GNUNET_STRINGS_data_to_string (
668 3 : adgh->options.h_payto,
669 : sizeof (*adgh->options.h_payto),
670 : payto_s,
671 : sizeof (payto_s) - 1);
672 3 : *end = '\0';
673 : }
674 :
675 12 : adgh->url = TALER_url_join (
676 4 : 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 4 : (NULL != adgh->options.h_payto) ? payto_s : NULL,
684 : "active",
685 4 : (TALER_EXCHANGE_YNA_ALL != adgh->options.active)
686 0 : ? TALER_yna_to_string (adgh->options.active)
687 : : NULL,
688 : "investigation",
689 4 : (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation)
690 0 : ? TALER_yna_to_string (adgh->options.investigation)
691 : : NULL,
692 : NULL);
693 : }
694 :
695 4 : if (NULL == adgh->url)
696 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
697 :
698 4 : eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url);
699 4 : if (NULL == eh)
700 : {
701 0 : GNUNET_break (0);
702 0 : GNUNET_free (adgh->url);
703 0 : adgh->url = NULL;
704 0 : 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 4 : end = GNUNET_STRINGS_data_to_string (
714 4 : &adgh->officer_sig,
715 : sizeof (adgh->officer_sig),
716 : sig_str,
717 : sizeof (sig_str));
718 4 : *end = '\0';
719 :
720 4 : GNUNET_asprintf (&hdr,
721 : "%s: %s",
722 : TALER_AML_OFFICER_SIGNATURE_HEADER,
723 : sig_str);
724 4 : job_headers = curl_slist_append (NULL,
725 : hdr);
726 4 : GNUNET_free (hdr);
727 : }
728 :
729 4 : adgh->job = GNUNET_CURL_job_add2 (adgh->ctx,
730 : eh,
731 : job_headers,
732 : &handle_get_aml_decisions_finished,
733 : adgh);
734 4 : curl_slist_free_all (job_headers);
735 :
736 4 : if (NULL == adgh->job)
737 : {
738 0 : GNUNET_free (adgh->url);
739 0 : adgh->url = NULL;
740 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
741 : }
742 4 : return TALER_EC_NONE;
743 : }
744 :
745 :
746 : void
747 4 : TALER_EXCHANGE_get_aml_decisions_cancel (
748 : struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh)
749 : {
750 4 : if (NULL != adgh->job)
751 : {
752 0 : GNUNET_CURL_job_cancel (adgh->job);
753 0 : adgh->job = NULL;
754 : }
755 4 : GNUNET_free (adgh->all_rules);
756 4 : GNUNET_free (adgh->all_mp);
757 4 : GNUNET_free (adgh->url);
758 4 : GNUNET_free (adgh->base_url);
759 4 : GNUNET_free (adgh);
760 4 : }
761 :
762 :
763 : /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */
|