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 6 : 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 6 : GNUNET_JSON_spec_timestamp ("expiration_time",
151 : &limits->expiration_time),
152 6 : GNUNET_JSON_spec_mark_optional (
153 : GNUNET_JSON_spec_string ("successor_measure",
154 : &limits->successor_measure),
155 : NULL),
156 6 : GNUNET_JSON_spec_array_const ("rules",
157 : &jrules),
158 6 : GNUNET_JSON_spec_mark_optional (
159 : GNUNET_JSON_spec_object_const ("custom_measures",
160 : &jcustom_measures),
161 : NULL),
162 6 : GNUNET_JSON_spec_end ()
163 : };
164 :
165 6 : if (GNUNET_OK !=
166 6 : GNUNET_JSON_parse (jlimits,
167 : spec,
168 : NULL,
169 : NULL))
170 : {
171 0 : GNUNET_break_op (0);
172 0 : return GNUNET_SYSERR;
173 : }
174 6 : limits->custom_measures = jcustom_measures;
175 :
176 : {
177 6 : size_t rule_count = json_array_size (jrules);
178 6 : size_t rule_start = *rule_off;
179 :
180 6 : limits->rules = &adgh->all_rules[rule_start];
181 6 : limits->rules_length = rule_count;
182 :
183 : {
184 : const json_t *jrule;
185 : size_t ridx;
186 :
187 37 : json_array_foreach ((json_t *) jrules, ridx, jrule)
188 : {
189 31 : struct TALER_EXCHANGE_GetAmlDecisionsKycRule *r
190 31 : = &adgh->all_rules[*rule_off];
191 : const json_t *jsmeasures;
192 : struct GNUNET_JSON_Specification rspec[] = {
193 31 : TALER_JSON_spec_kycte ("operation_type",
194 : &r->operation_type),
195 31 : GNUNET_JSON_spec_mark_optional (
196 : GNUNET_JSON_spec_string ("rule_name",
197 : &r->rule_name),
198 : NULL),
199 31 : TALER_JSON_spec_amount_any ("threshold",
200 : &r->threshold),
201 31 : GNUNET_JSON_spec_relative_time ("timeframe",
202 : &r->timeframe),
203 31 : GNUNET_JSON_spec_array_const ("measures",
204 : &jsmeasures),
205 31 : GNUNET_JSON_spec_mark_optional (
206 : GNUNET_JSON_spec_bool ("exposed",
207 : &r->exposed),
208 : NULL),
209 31 : GNUNET_JSON_spec_mark_optional (
210 : GNUNET_JSON_spec_bool ("is_and_combinator",
211 : &r->is_and_combinator),
212 : NULL),
213 31 : GNUNET_JSON_spec_int64 ("display_priority",
214 : &r->display_priority),
215 31 : GNUNET_JSON_spec_end ()
216 : };
217 :
218 31 : if (GNUNET_OK !=
219 31 : 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 31 : size_t mlen = json_array_size (jsmeasures);
230 31 : size_t mp_start = *mp_off;
231 :
232 31 : r->measures = &adgh->all_mp[mp_start];
233 31 : r->measures_length = mlen;
234 :
235 : {
236 : size_t midx;
237 : const json_t *jm;
238 :
239 61 : json_array_foreach (jsmeasures, midx, jm)
240 : {
241 30 : const char *sval = json_string_value (jm);
242 :
243 30 : if (NULL == sval)
244 : {
245 0 : GNUNET_break_op (0);
246 0 : return GNUNET_SYSERR;
247 : }
248 30 : adgh->all_mp[*mp_off] = sval;
249 30 : (*mp_off)++;
250 : }
251 : }
252 : }
253 :
254 31 : (*rule_off)++;
255 : }
256 : }
257 : }
258 :
259 6 : 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 8 : json_array_foreach ((json_t *) jrecords, idx, obj)
285 : {
286 6 : struct TALER_EXCHANGE_GetAmlDecisionsDecision *decision = &records_ar[idx];
287 : const json_t *jlimits;
288 : struct GNUNET_JSON_Specification spec[] = {
289 6 : GNUNET_JSON_spec_fixed_auto ("h_payto",
290 : &decision->h_payto),
291 6 : GNUNET_JSON_spec_mark_optional (
292 : GNUNET_JSON_spec_string ("full_payto",
293 : &decision->full_payto),
294 : NULL),
295 6 : GNUNET_JSON_spec_mark_optional (
296 : GNUNET_JSON_spec_bool ("is_wallet",
297 : &decision->is_wallet),
298 : NULL),
299 6 : GNUNET_JSON_spec_uint64 ("rowid",
300 : &decision->rowid),
301 6 : GNUNET_JSON_spec_mark_optional (
302 : GNUNET_JSON_spec_string ("justification",
303 : &decision->justification),
304 : NULL),
305 6 : GNUNET_JSON_spec_timestamp ("decision_time",
306 : &decision->decision_time),
307 6 : GNUNET_JSON_spec_mark_optional (
308 : GNUNET_JSON_spec_object_const ("properties",
309 : &decision->properties),
310 : NULL),
311 6 : GNUNET_JSON_spec_object_const ("limits",
312 : &jlimits),
313 6 : GNUNET_JSON_spec_bool ("to_investigate",
314 : &decision->to_investigate),
315 6 : GNUNET_JSON_spec_bool ("is_active",
316 : &decision->is_active),
317 6 : GNUNET_JSON_spec_end ()
318 : };
319 :
320 6 : GNUNET_assert (idx < records_ar_length);
321 6 : if (GNUNET_OK !=
322 6 : 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 6 : if (GNUNET_OK !=
332 6 : 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 8 : json_array_foreach ((json_t *) jrecords, idx, obj)
388 : {
389 6 : const json_t *jlimits = json_object_get (obj, "limits");
390 : const json_t *jrules;
391 :
392 6 : if (NULL == jlimits)
393 0 : continue;
394 6 : jrules = json_object_get (jlimits, "rules");
395 6 : if (NULL == jrules)
396 0 : continue;
397 6 : total_rules += json_array_size (jrules);
398 :
399 : {
400 : const json_t *jrule;
401 : size_t ridx;
402 :
403 37 : json_array_foreach ((json_t *) jrules, ridx, jrule)
404 : {
405 31 : const json_t *jmeasures = json_object_get (jrule, "measures");
406 :
407 31 : if (NULL != jmeasures)
408 31 : 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 10 : for (unsigned int i = 0; i < num_options; i++)
556 : {
557 10 : const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i];
558 :
559 10 : switch (opt->option)
560 : {
561 3 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END:
562 3 : return GNUNET_OK;
563 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT:
564 0 : adgh->options.limit = opt->details.limit;
565 0 : break;
566 4 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET:
567 4 : if (opt->details.offset > INT64_MAX)
568 : {
569 4 : GNUNET_break (0);
570 4 : return GNUNET_NO;
571 : }
572 0 : adgh->options.offset = opt->details.offset;
573 0 : break;
574 3 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO:
575 3 : adgh->options.h_payto = opt->details.h_payto;
576 3 : break;
577 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE:
578 0 : adgh->options.active = opt->details.active;
579 0 : break;
580 0 : case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION:
581 0 : adgh->options.investigation = opt->details.investigation;
582 0 : break;
583 0 : default:
584 0 : GNUNET_break (0);
585 0 : return GNUNET_SYSERR;
586 : }
587 : }
588 0 : return GNUNET_OK;
589 : }
590 :
591 :
592 : enum TALER_ErrorCode
593 4 : TALER_EXCHANGE_get_aml_decisions_start (
594 : struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
595 : TALER_EXCHANGE_GetAmlDecisionsCallback cb,
596 : TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls)
597 : {
598 : CURL *eh;
599 : char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32];
600 4 : struct curl_slist *job_headers = NULL;
601 :
602 4 : adgh->cb = cb;
603 4 : adgh->cb_cls = cb_cls;
604 :
605 : /* Build AML officer signature */
606 4 : TALER_officer_aml_query_sign (&adgh->officer_priv,
607 : &adgh->officer_sig);
608 :
609 : /* Build the path component: aml/{officer_pub}/decisions */
610 : {
611 : char pub_str[sizeof (adgh->officer_pub) * 2];
612 : char *end;
613 :
614 4 : end = GNUNET_STRINGS_data_to_string (
615 4 : &adgh->officer_pub,
616 : sizeof (adgh->officer_pub),
617 : pub_str,
618 : sizeof (pub_str));
619 4 : *end = '\0';
620 4 : GNUNET_snprintf (arg_str,
621 : sizeof (arg_str),
622 : "aml/%s/decisions",
623 : pub_str);
624 : }
625 :
626 : /* Build URL with optional query parameters */
627 : {
628 : char limit_s[24];
629 : char offset_s[24];
630 : char payto_s[sizeof (*adgh->options.h_payto) * 2 + 1];
631 4 : int64_t limit = adgh->options.limit;
632 4 : uint64_t offset = adgh->options.offset;
633 4 : bool omit_limit = (-20 == limit);
634 4 : bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) ||
635 0 : ( (limit > 0) && (0 == offset) ) );
636 :
637 4 : GNUNET_snprintf (limit_s,
638 : sizeof (limit_s),
639 : "%lld",
640 : (long long) limit);
641 4 : GNUNET_snprintf (offset_s,
642 : sizeof (offset_s),
643 : "%llu",
644 : (unsigned long long) offset);
645 :
646 4 : if (NULL != adgh->options.h_payto)
647 : {
648 : char *end;
649 :
650 3 : end = GNUNET_STRINGS_data_to_string (
651 3 : adgh->options.h_payto,
652 : sizeof (*adgh->options.h_payto),
653 : payto_s,
654 : sizeof (payto_s) - 1);
655 3 : *end = '\0';
656 : }
657 :
658 12 : adgh->url = TALER_url_join (
659 4 : adgh->base_url,
660 : arg_str,
661 : "limit",
662 : omit_limit ? NULL : limit_s,
663 : "offset",
664 : omit_offset ? NULL : offset_s,
665 : "h_payto",
666 4 : (NULL != adgh->options.h_payto) ? payto_s : NULL,
667 : "active",
668 4 : (TALER_EXCHANGE_YNA_ALL != adgh->options.active)
669 0 : ? TALER_yna_to_string (adgh->options.active)
670 : : NULL,
671 : "investigation",
672 4 : (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation)
673 0 : ? TALER_yna_to_string (adgh->options.investigation)
674 : : NULL,
675 : NULL);
676 : }
677 :
678 4 : if (NULL == adgh->url)
679 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
680 :
681 4 : eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url);
682 4 : if (NULL == eh)
683 : {
684 0 : GNUNET_break (0);
685 0 : GNUNET_free (adgh->url);
686 0 : adgh->url = NULL;
687 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
688 : }
689 :
690 : /* Build job headers with AML officer signature */
691 : {
692 : char *hdr;
693 : char sig_str[sizeof (adgh->officer_sig) * 2];
694 : char *end;
695 :
696 4 : end = GNUNET_STRINGS_data_to_string (
697 4 : &adgh->officer_sig,
698 : sizeof (adgh->officer_sig),
699 : sig_str,
700 : sizeof (sig_str));
701 4 : *end = '\0';
702 :
703 4 : GNUNET_asprintf (&hdr,
704 : "%s: %s",
705 : TALER_AML_OFFICER_SIGNATURE_HEADER,
706 : sig_str);
707 4 : job_headers = curl_slist_append (NULL,
708 : hdr);
709 4 : GNUNET_free (hdr);
710 : }
711 :
712 4 : adgh->job = GNUNET_CURL_job_add2 (adgh->ctx,
713 : eh,
714 : job_headers,
715 : &handle_get_aml_decisions_finished,
716 : adgh);
717 4 : curl_slist_free_all (job_headers);
718 :
719 4 : if (NULL == adgh->job)
720 : {
721 0 : GNUNET_free (adgh->url);
722 0 : adgh->url = NULL;
723 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
724 : }
725 4 : return TALER_EC_NONE;
726 : }
727 :
728 :
729 : void
730 4 : TALER_EXCHANGE_get_aml_decisions_cancel (
731 : struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh)
732 : {
733 4 : if (NULL != adgh->job)
734 : {
735 0 : GNUNET_CURL_job_cancel (adgh->job);
736 0 : adgh->job = NULL;
737 : }
738 4 : GNUNET_free (adgh->all_rules);
739 4 : GNUNET_free (adgh->all_mp);
740 4 : GNUNET_free (adgh->url);
741 4 : GNUNET_free (adgh->base_url);
742 4 : GNUNET_free (adgh);
743 4 : }
744 :
745 :
746 : /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */
|