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