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