Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2023, 2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero 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 Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file exchangedb_aml.c
18 : * @brief helper function to handle AML programs
19 : * @author Christian Grothoff
20 : */
21 : #include "taler/taler_exchangedb_plugin.h"
22 : #include "taler/taler_exchangedb_lib.h"
23 : #include "taler/taler_kyclogic_lib.h"
24 : #include "taler/taler_json_lib.h"
25 : #include "taler/taler_dbevents.h"
26 : #include <gnunet/gnunet_common.h>
27 :
28 : /**
29 : * Maximum recursion depth we allow for AML programs.
30 : * Basically, after this number of "skip" processes
31 : * we forcefully terminate the recursion and fail hard.
32 : */
33 : #define MAX_DEPTH 16
34 :
35 : enum GNUNET_DB_QueryStatus
36 10 : TALER_EXCHANGEDB_persist_aml_program_result (
37 : struct TALER_EXCHANGEDB_Plugin *plugin,
38 : uint64_t process_row,
39 : const struct TALER_NormalizedPaytoHashP *account_id,
40 : const struct TALER_KYCLOGIC_AmlProgramResult *apr,
41 : enum TALER_EXCHANGEDB_PersistProgramResultStatus *ret_pprs)
42 : {
43 : enum GNUNET_DB_QueryStatus qs;
44 10 : json_t *jmeasures = NULL;
45 10 : struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
46 :
47 10 : GNUNET_assert (NULL != ret_pprs);
48 :
49 10 : *ret_pprs = TALER_EXCHANGEDB_PPRS_OK;
50 :
51 10 : if ( (TALER_KYCLOGIC_AMLR_SUCCESS == apr->status) &&
52 10 : (NULL != apr->details.success.new_measures) )
53 : {
54 0 : lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
55 0 : if (NULL == lrs)
56 : {
57 0 : qs = plugin->insert_aml_program_failure (
58 : plugin->cls,
59 : process_row,
60 : account_id,
61 : "Failed to parse AML program output",
62 : TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
63 0 : GNUNET_break (qs > 0);
64 0 : return qs;
65 : }
66 0 : jmeasures = TALER_KYCLOGIC_get_jmeasures (
67 : lrs,
68 0 : apr->details.success.new_measures);
69 0 : if (NULL == jmeasures)
70 : {
71 : char *err;
72 :
73 0 : GNUNET_break (0);
74 0 : GNUNET_asprintf (&err,
75 : "Failed to find measures `%s' specified in AML program output",
76 0 : apr->details.success.new_measures);
77 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
78 : "AML program specified invalid measures `%s'\n",
79 : apr->details.success.new_measures);
80 0 : qs = plugin->insert_aml_program_failure (
81 : plugin->cls,
82 : process_row,
83 : account_id,
84 : err,
85 : TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
86 0 : *ret_pprs = TALER_EXCHANGEDB_PPRS_BAD_OUTCOME;
87 0 : TALER_KYCLOGIC_rules_free (lrs);
88 0 : GNUNET_free (err);
89 0 : GNUNET_break (qs > 0);
90 0 : return qs;
91 : }
92 : }
93 :
94 10 : qs = plugin->clear_aml_lock (
95 : plugin->cls,
96 : account_id);
97 10 : switch (apr->status)
98 : {
99 0 : case TALER_KYCLOGIC_AMLR_FAILURE:
100 0 : qs = plugin->insert_aml_program_failure (
101 : plugin->cls,
102 : process_row,
103 : account_id,
104 0 : apr->details.failure.error_message,
105 0 : apr->details.failure.ec);
106 0 : GNUNET_break (qs > 0);
107 0 : goto cleanup;
108 10 : case TALER_KYCLOGIC_AMLR_SUCCESS:
109 : {
110 10 : struct TALER_FullPayto null_payto_uri = { 0 };
111 : bool invalid_officer;
112 : bool unknown_account;
113 : struct GNUNET_TIME_Timestamp last_date;
114 : uint64_t legitimization_measure_serial_id;
115 : bool is_wallet;
116 :
117 10 : qs = plugin->insert_aml_decision (
118 : plugin->cls,
119 : null_payto_uri,
120 : account_id,
121 : GNUNET_TIME_timestamp_get (),
122 : apr->details.success.expiration_time,
123 10 : apr->details.success.account_properties,
124 10 : apr->details.success.new_rules,
125 10 : apr->details.success.to_investigate,
126 10 : apr->details.success.new_measures,
127 : jmeasures,
128 : NULL, /* justification */
129 : NULL, /* decider_pub */
130 : NULL, /* decider_sig */
131 10 : apr->details.success.num_events,
132 10 : apr->details.success.events,
133 : NULL, /* form ID */
134 : 0, /* enc_attributes_size*/
135 : NULL, /* enc_attributes*/
136 : NULL, /* attributes_hash */
137 10 : GNUNET_TIME_UNIT_ZERO_TS, /* attributes_expiration_time */
138 : &invalid_officer,
139 : &unknown_account,
140 : &last_date,
141 : &legitimization_measure_serial_id,
142 : &is_wallet);
143 10 : GNUNET_break (qs > 0);
144 10 : goto cleanup;
145 : }
146 : }
147 0 : GNUNET_break (0);
148 0 : qs = GNUNET_DB_STATUS_HARD_ERROR;
149 10 : cleanup:
150 10 : TALER_KYCLOGIC_rules_free (lrs);
151 10 : json_decref (jmeasures);
152 10 : return qs;
153 : }
154 :
155 :
156 : struct TALER_EXCHANGEDB_RuleUpdater
157 : {
158 : /**
159 : * database plugin to use
160 : */
161 : struct TALER_EXCHANGEDB_Plugin *plugin;
162 :
163 : /**
164 : * key to use to decrypt attributes
165 : */
166 : struct TALER_AttributeEncryptionKeyP attribute_key;
167 :
168 : /**
169 : * account to get the rule set for
170 : */
171 : struct TALER_NormalizedPaytoHashP account;
172 :
173 : /**
174 : * function to call with the result
175 : */
176 : TALER_EXCHANGEDB_CurrentRulesCallback cb;
177 :
178 : /**
179 : * Closure for @e cb.
180 : */
181 : void *cb_cls;
182 :
183 : /**
184 : * Current rule set we are working on.
185 : */
186 : struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
187 :
188 : /**
189 : * Task for asynchronous continuations.
190 : */
191 : struct GNUNET_SCHEDULER_Task *t;
192 :
193 : /**
194 : * Handler waiting notification that (previous) AML program
195 : * finished.
196 : */
197 : struct GNUNET_DB_EventHandler *eh;
198 :
199 : /**
200 : * Handle to running AML program.
201 : */
202 : struct TALER_KYCLOGIC_AmlProgramRunnerHandle *amlh;
203 :
204 : /**
205 : * Name of the AML program we were running asynchronously,
206 : * for diagnostics.
207 : */
208 : char *aml_program_name;
209 :
210 : /**
211 : * Error hint to return with @e ec.
212 : */
213 : const char *hint;
214 :
215 : /**
216 : * Row the rule set in @a lrs is based on.
217 : */
218 : uint64_t legitimization_outcome_last_row;
219 :
220 : /**
221 : * Taler error code to return.
222 : */
223 : enum TALER_ErrorCode ec;
224 :
225 : /**
226 : * Counter used to limit recursion depth.
227 : */
228 : unsigned int depth;
229 :
230 : /**
231 : * True if @e account is for a wallet.
232 : */
233 : bool is_wallet;
234 : };
235 :
236 :
237 : /**
238 : * Function that finally returns the result to the application and cleans
239 : * up. Called with an open database transaction on success; on failure, the
240 : * transaction will have already been rolled back.
241 : *
242 : * @param[in,out] ru rule updater to return result for
243 : */
244 : static void
245 28 : return_result (struct TALER_EXCHANGEDB_RuleUpdater *ru)
246 : {
247 28 : struct TALER_EXCHANGEDB_RuleUpdaterResult rur = {
248 28 : .legitimization_outcome_last_row = ru->legitimization_outcome_last_row,
249 28 : .lrs = ru->lrs,
250 28 : .ec = ru->ec,
251 : };
252 :
253 28 : ru->cb (ru->cb_cls,
254 : &rur);
255 28 : ru->lrs = NULL;
256 28 : TALER_EXCHANGEDB_update_rules_cancel (ru);
257 28 : }
258 :
259 :
260 : /**
261 : * Fail the update with the given @a ec and @a hint.
262 : * Called with an open database transaction, which will
263 : * be rolled back (!).
264 : *
265 : * @param[in,out] ru account we are processing
266 : * @param ec error code to fail with
267 : * @param hint hint to return, can be NULL
268 : */
269 : static void
270 0 : fail_update (struct TALER_EXCHANGEDB_RuleUpdater *ru,
271 : enum TALER_ErrorCode ec,
272 : const char *hint)
273 : {
274 0 : GNUNET_assert (NULL == ru->t);
275 0 : ru->plugin->rollback (ru->plugin->cls);
276 0 : ru->ec = ec;
277 0 : ru->hint = hint;
278 0 : return_result (ru);
279 0 : }
280 :
281 :
282 : /**
283 : * Check the rules in @a ru to see if they are current, and
284 : * if not begin the updating process. Called with an open
285 : * database transaction.
286 : *
287 : * @param[in] ru rule updater context
288 : */
289 : static void
290 : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru);
291 :
292 :
293 : /**
294 : * Run the measure @a m in the context of the legitimisation rules
295 : * of @a ru. Called with an open database transaction.
296 : *
297 : * @param ru updating context we are using
298 : * @param m measure we need to run next
299 : */
300 : static void
301 : run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
302 : const struct TALER_KYCLOGIC_Measure *m);
303 :
304 :
305 : /**
306 : * Function called after AML program was run. Called
307 : * without an open database transaction, will start one!
308 : *
309 : * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *`
310 : * @param apr result of the AML program.
311 : */
312 : static void
313 0 : aml_result_callback (
314 : void *cls,
315 : const struct TALER_KYCLOGIC_AmlProgramResult *apr)
316 : {
317 0 : struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
318 : enum GNUNET_DB_QueryStatus qs;
319 : enum GNUNET_GenericReturnValue res;
320 : enum TALER_EXCHANGEDB_PersistProgramResultStatus pprs;
321 :
322 0 : ru->amlh = NULL;
323 0 : res = ru->plugin->start (ru->plugin->cls,
324 : "aml-persist-aml-program-result");
325 0 : if (GNUNET_OK != res)
326 : {
327 0 : GNUNET_break (0);
328 0 : fail_update (ru,
329 : TALER_EC_GENERIC_DB_START_FAILED,
330 : "aml-persist-aml-program-result");
331 0 : return;
332 : }
333 : /* Update database update based on result */
334 0 : qs = TALER_EXCHANGEDB_persist_aml_program_result (
335 : ru->plugin,
336 : 0LLU, /* 0: no existing legitimization process, creates new row */
337 0 : &ru->account,
338 : apr,
339 : &pprs);
340 0 : switch (qs)
341 : {
342 0 : case GNUNET_DB_STATUS_HARD_ERROR:
343 0 : GNUNET_break (0);
344 0 : fail_update (ru,
345 : TALER_EC_GENERIC_DB_STORE_FAILED,
346 : "persist_aml_program_result");
347 0 : return;
348 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
349 : /* Bad, couldn't persist AML result. Try again... */
350 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
351 : "Serialization issue persisting result of AML program. Restarting.\n");
352 0 : fail_update (ru,
353 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
354 : "persist_aml_program_result");
355 0 : return;
356 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
357 : /* Strange, but let's just continue */
358 0 : break;
359 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
360 : /* normal case */
361 0 : break;
362 : }
363 0 : switch (pprs)
364 : {
365 0 : case TALER_EXCHANGEDB_PPRS_OK:
366 0 : break;
367 0 : case TALER_EXCHANGEDB_PPRS_BAD_OUTCOME:
368 0 : fail_update (ru,
369 : TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT,
370 : "persist_aml_program_result");
371 0 : return;
372 : }
373 0 : switch (apr->status)
374 : {
375 0 : case TALER_KYCLOGIC_AMLR_SUCCESS:
376 0 : TALER_KYCLOGIC_rules_free (ru->lrs);
377 0 : ru->lrs = NULL;
378 0 : ru->lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
379 : /* Fall back to default rules on parse error! */
380 0 : GNUNET_break (NULL != ru->lrs);
381 0 : check_rules (ru);
382 0 : return;
383 0 : case TALER_KYCLOGIC_AMLR_FAILURE:
384 : {
385 0 : const char *fmn = apr->details.failure.fallback_measure;
386 : const struct TALER_KYCLOGIC_Measure *m;
387 :
388 0 : m = TALER_KYCLOGIC_get_measure (ru->lrs,
389 : fmn);
390 0 : if (NULL == m)
391 : {
392 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 : "Fallback measure `%s' does not exist (anymore?).\n",
394 : fmn);
395 0 : TALER_KYCLOGIC_rules_free (ru->lrs);
396 0 : ru->lrs = NULL;
397 0 : return_result (ru);
398 0 : return;
399 : }
400 0 : run_measure (ru,
401 : m);
402 0 : return;
403 : }
404 : }
405 : /* This should be impossible */
406 0 : GNUNET_assert (0);
407 : }
408 :
409 :
410 : /**
411 : * Entrypoint that fetches the latest rules from the database
412 : * and starts processing them. Called without an open database
413 : * transaction, will start one.
414 : *
415 : * @param[in] cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to run
416 : */
417 : static void
418 : fetch_latest_rules (void *cls);
419 :
420 :
421 : /**
422 : * Notification called when we either timeout on the AML program lock
423 : * or when the (previous) AML program finished and we can thus try again.
424 : *
425 : * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to continue
426 : * @param extra additional event data provided (unused)
427 : * @param extra_size number of bytes in @a extra (unused)
428 : */
429 : static void
430 0 : trigger_fetch_latest_rules (void *cls,
431 : const void *extra,
432 : size_t extra_size)
433 : {
434 0 : struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
435 :
436 : (void) extra;
437 : (void) extra_size;
438 0 : if (NULL != ru->t)
439 0 : return; /* multiple events triggered us, ignore */
440 0 : ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
441 : ru);
442 : }
443 :
444 :
445 : static void
446 0 : run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
447 : const struct TALER_KYCLOGIC_Measure *m)
448 : {
449 0 : if (NULL == m)
450 : {
451 : /* fall back to default rules */
452 0 : TALER_KYCLOGIC_rules_free (ru->lrs);
453 0 : ru->lrs = NULL;
454 0 : return_result (ru);
455 0 : return;
456 : }
457 0 : ru->depth++;
458 0 : if (ru->depth > MAX_DEPTH)
459 : {
460 0 : fail_update (ru,
461 : TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
462 : NULL);
463 0 : return;
464 : }
465 0 : if ( (NULL == m->check_name) ||
466 : (0 ==
467 0 : strcasecmp ("SKIP",
468 0 : m->check_name)) )
469 : {
470 0 : struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
471 0 : .account = &ru->account,
472 0 : .is_wallet = ru->is_wallet,
473 0 : .db_plugin = ru->plugin,
474 0 : .attribute_key = &ru->attribute_key
475 : };
476 : enum GNUNET_DB_QueryStatus qs;
477 : struct GNUNET_TIME_Absolute xlock;
478 :
479 : /* Free previous one, in case we are iterating... */
480 0 : GNUNET_free (ru->aml_program_name);
481 0 : if (NULL != m->prog_name)
482 : {
483 0 : ru->aml_program_name = GNUNET_strdup (m->prog_name);
484 : }
485 : else
486 : {
487 : /* How do we get to run a measure if the check type
488 : is INFO (which is the only case where prog_name
489 : is allowed to be NULL?) */
490 0 : GNUNET_break (0);
491 0 : ru->aml_program_name = NULL;
492 : }
493 0 : qs = ru->plugin->set_aml_lock (
494 0 : ru->plugin->cls,
495 0 : &ru->account,
496 0 : GNUNET_TIME_relative_multiply (ru->plugin->max_aml_program_runtime,
497 : 2),
498 : &xlock);
499 0 : if (GNUNET_TIME_absolute_is_future (xlock))
500 : {
501 0 : struct TALER_KycCompletedEventP eh = {
502 0 : .header.size = htons (sizeof (eh)),
503 0 : .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
504 : .h_payto = ru->account
505 : };
506 : /* Wait for either timeout or notification */
507 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
508 : "AML program already running, waiting for it to finish\n");
509 0 : ru->plugin->rollback (ru->plugin->cls);
510 : ru->eh
511 0 : = ru->plugin->event_listen (
512 0 : ru->plugin->cls,
513 : GNUNET_TIME_absolute_get_remaining (xlock),
514 : &eh.header,
515 : &trigger_fetch_latest_rules,
516 : ru);
517 0 : return;
518 : }
519 0 : qs = ru->plugin->commit (ru->plugin->cls);
520 0 : if (qs < 0)
521 : {
522 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
523 0 : fail_update (ru,
524 : GNUNET_DB_STATUS_SOFT_ERROR == qs
525 : ? TALER_EC_GENERIC_DB_SOFT_FAILURE
526 : : TALER_EC_GENERIC_DB_COMMIT_FAILED,
527 : "current-aml-rule-fetch");
528 0 : return;
529 : }
530 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
531 : "Check is of type 'SKIP', running AML program %s.\n",
532 : m->prog_name);
533 0 : GNUNET_assert (NULL == ru->t);
534 0 : ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
535 0 : ru->is_wallet,
536 : m,
537 : &TALER_EXCHANGEDB_current_attributes_builder,
538 : &hbc,
539 : &TALER_EXCHANGEDB_current_rule_builder,
540 : &hbc,
541 : &TALER_EXCHANGEDB_aml_history_builder,
542 : &hbc,
543 : &TALER_EXCHANGEDB_kyc_history_builder,
544 : &hbc,
545 0 : ru->plugin->max_aml_program_runtime,
546 : &aml_result_callback,
547 : ru);
548 0 : return;
549 : }
550 :
551 : /* User MUST pass interactive check */
552 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
553 : "Measure %s involves check %s\n",
554 : m->measure_name,
555 : m->check_name);
556 : {
557 : /* activate the measure/check */
558 : json_t *succ_jmeasures
559 0 : = TALER_KYCLOGIC_get_jmeasures (
560 0 : ru->lrs,
561 0 : m->measure_name);
562 : bool unknown_account;
563 : struct GNUNET_TIME_Timestamp last_date;
564 : enum GNUNET_DB_QueryStatus qs;
565 :
566 0 : qs = ru->plugin->insert_successor_measure (
567 0 : ru->plugin->cls,
568 0 : &ru->account,
569 : GNUNET_TIME_timestamp_get (),
570 0 : m->measure_name,
571 : succ_jmeasures,
572 : &unknown_account,
573 : &last_date);
574 0 : json_decref (succ_jmeasures);
575 0 : switch (qs)
576 : {
577 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
578 0 : GNUNET_log (
579 : GNUNET_ERROR_TYPE_INFO,
580 : "Serialization issue!\n");
581 0 : fail_update (ru,
582 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
583 : "insert_successor_measure");
584 0 : return;
585 0 : case GNUNET_DB_STATUS_HARD_ERROR:
586 0 : GNUNET_break (0);
587 0 : fail_update (ru,
588 : TALER_EC_GENERIC_DB_STORE_FAILED,
589 : "insert_successor_measure");
590 0 : return;
591 0 : default:
592 0 : break;
593 : }
594 0 : if (unknown_account)
595 : {
596 0 : fail_update (ru,
597 : TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN,
598 : NULL);
599 0 : return;
600 : }
601 : }
602 : /* The rules remain these rules until the user passes the check */
603 0 : return_result (ru);
604 : }
605 :
606 :
607 : /**
608 : * Update the expired legitimization rules in @a ru, checking for expiration
609 : * first. Called with an open database transaction.
610 : *
611 : * @param[in,out] ru account we are processing
612 : */
613 : static void
614 0 : update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
615 : {
616 : const struct TALER_KYCLOGIC_Measure *m;
617 :
618 0 : GNUNET_assert (NULL != ru->lrs);
619 0 : GNUNET_assert (GNUNET_TIME_absolute_is_past (
620 : TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time));
621 0 : m = TALER_KYCLOGIC_rules_get_successor (ru->lrs);
622 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
623 : "Successor measure is %s.\n",
624 : (NULL != m) ? m->measure_name : "(null)");
625 0 : run_measure (ru,
626 : m);
627 0 : }
628 :
629 :
630 : static void
631 28 : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
632 : {
633 28 : ru->depth++;
634 28 : if (ru->depth > MAX_DEPTH)
635 : {
636 0 : fail_update (ru,
637 : TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
638 : NULL);
639 0 : return;
640 : }
641 28 : if (NULL == ru->lrs)
642 : {
643 : /* return NULL, aka default rules */
644 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
645 : "Default rules apply\n");
646 11 : return_result (ru);
647 11 : return;
648 : }
649 17 : if (! GNUNET_TIME_absolute_is_past
650 17 : (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) )
651 : {
652 : /* Rules did not expire, return them! */
653 17 : return_result (ru);
654 17 : return;
655 : }
656 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
657 : "Custom rules expired, updating...\n");
658 0 : update_rules (ru);
659 : }
660 :
661 :
662 : static void
663 28 : fetch_latest_rules (void *cls)
664 : {
665 28 : struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
666 : enum GNUNET_DB_QueryStatus qs;
667 : json_t *jnew_rules;
668 : enum GNUNET_GenericReturnValue res;
669 :
670 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
671 : "Fetching latest rules.");
672 :
673 28 : ru->t = NULL;
674 28 : if (NULL != ru->eh)
675 : {
676 : /* cancel event listener, if we have one */
677 0 : ru->plugin->event_listen_cancel (ru->plugin->cls,
678 : ru->eh);
679 0 : ru->eh = NULL;
680 : }
681 28 : GNUNET_break (NULL == ru->lrs);
682 28 : res = ru->plugin->start (ru->plugin->cls,
683 : "aml-begin-lookup-rules-by-access-token");
684 28 : if (GNUNET_OK != res)
685 : {
686 0 : GNUNET_break (0);
687 0 : fail_update (ru,
688 : TALER_EC_GENERIC_DB_START_FAILED,
689 : "aml-begin-lookup-rules-by-access-token");
690 0 : return;
691 : }
692 28 : qs = ru->plugin->lookup_rules_by_access_token (
693 28 : ru->plugin->cls,
694 28 : &ru->account,
695 : &jnew_rules,
696 : &ru->legitimization_outcome_last_row);
697 28 : if (qs < 0)
698 : {
699 0 : GNUNET_break (0);
700 0 : fail_update (ru,
701 : TALER_EC_GENERIC_DB_FETCH_FAILED,
702 : "lookup_rules_by_access_token");
703 0 : return;
704 : }
705 28 : if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
706 17 : (NULL != jnew_rules) )
707 : {
708 17 : ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules);
709 17 : GNUNET_break (NULL != ru->lrs);
710 17 : json_decref (jnew_rules);
711 : }
712 28 : check_rules (ru);
713 : }
714 :
715 :
716 : struct TALER_EXCHANGEDB_RuleUpdater *
717 28 : TALER_EXCHANGEDB_update_rules (
718 : struct TALER_EXCHANGEDB_Plugin *plugin,
719 : const struct TALER_AttributeEncryptionKeyP *attribute_key,
720 : const struct TALER_NormalizedPaytoHashP *account,
721 : bool is_wallet,
722 : TALER_EXCHANGEDB_CurrentRulesCallback cb,
723 : void *cb_cls)
724 : {
725 : struct TALER_EXCHANGEDB_RuleUpdater *ru;
726 :
727 28 : ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater);
728 28 : ru->plugin = plugin;
729 28 : ru->attribute_key = *attribute_key;
730 28 : ru->account = *account;
731 28 : ru->is_wallet = is_wallet;
732 28 : ru->cb = cb;
733 28 : ru->cb_cls = cb_cls;
734 28 : ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
735 : ru);
736 28 : return ru;
737 : }
738 :
739 :
740 : void
741 28 : TALER_EXCHANGEDB_update_rules_cancel (
742 : struct TALER_EXCHANGEDB_RuleUpdater *ru)
743 : {
744 28 : if (NULL != ru->t)
745 : {
746 0 : GNUNET_SCHEDULER_cancel (ru->t);
747 0 : ru->t = NULL;
748 : }
749 28 : if (NULL != ru->amlh)
750 : {
751 0 : TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
752 0 : ru->amlh = NULL;
753 : }
754 28 : if (NULL != ru->lrs)
755 : {
756 0 : TALER_KYCLOGIC_rules_free (ru->lrs);
757 0 : ru->lrs = NULL;
758 : }
759 28 : if (NULL != ru->eh)
760 : {
761 0 : ru->plugin->event_listen_cancel (ru->plugin->cls,
762 : ru->eh);
763 0 : ru->eh = NULL;
764 : }
765 28 : GNUNET_free (ru->aml_program_name);
766 28 : GNUNET_free (ru);
767 28 : }
|