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 begin_rule_update.c
18 : * @brief helper function to handle AML programs
19 : * @author Christian Grothoff
20 : */
21 : #include "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/update_to_aml_locked.h"
28 : #include "exchange-database/update_to_aml_unlocked.h"
29 : #include "exchange-database/do_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/get_rules_by_access_token.h"
35 : #include "exchange-database/begin_rule_update.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_begin_rule_update_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_do_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_update_to_aml_locked (
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 (qs <= 0)
392 : {
393 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
394 0 : fail_update (ru,
395 : GNUNET_DB_STATUS_SOFT_ERROR == qs
396 : ? TALER_EC_GENERIC_DB_SOFT_FAILURE
397 : : TALER_EC_GENERIC_DB_STORE_FAILED,
398 : "set_aml_lock");
399 0 : return;
400 : }
401 0 : if (GNUNET_TIME_absolute_is_future (xlock))
402 : {
403 0 : struct TALER_EXCHANGEDB_KycCompletedEventP eh = {
404 0 : .header.size = htons (sizeof (eh)),
405 0 : .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
406 : .h_payto = ru->account
407 : };
408 : /* Wait for either timeout or notification */
409 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
410 : "AML program already running, waiting for it to finish\n");
411 0 : TALER_EXCHANGEDB_rollback (ru->pg);
412 : ru->eh
413 0 : = TALER_EXCHANGEDB_event_listen (
414 : ru->pg,
415 : GNUNET_TIME_absolute_get_remaining (xlock),
416 : &eh.header,
417 : &trigger_fetch_latest_rules,
418 : ru);
419 0 : return;
420 : }
421 0 : qs = TALER_EXCHANGEDB_commit (ru->pg);
422 0 : if (qs < 0)
423 : {
424 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
425 0 : fail_update (ru,
426 : GNUNET_DB_STATUS_SOFT_ERROR == qs
427 : ? TALER_EC_GENERIC_DB_SOFT_FAILURE
428 : : TALER_EC_GENERIC_DB_COMMIT_FAILED,
429 : "current-aml-rule-fetch");
430 0 : return;
431 : }
432 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
433 : "Check is of type 'SKIP', running AML program %s.\n",
434 : m->prog_name);
435 0 : GNUNET_assert (NULL == ru->t);
436 0 : ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
437 0 : ru->is_wallet,
438 : m,
439 : &TALER_EXCHANGEDB_current_attributes_builder,
440 : &hbc,
441 : &TALER_EXCHANGEDB_current_rule_builder,
442 : &hbc,
443 : &TALER_EXCHANGEDB_aml_history_builder,
444 : &hbc,
445 : &TALER_EXCHANGEDB_kyc_history_builder,
446 : &hbc,
447 0 : ru->pg->max_aml_program_runtime,
448 : &aml_result_callback,
449 : ru);
450 0 : return;
451 : }
452 :
453 : /* User MUST pass interactive check */
454 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
455 : "Measure %s involves check %s\n",
456 : m->measure_name,
457 : m->check_name);
458 : {
459 : /* activate the measure/check */
460 : json_t *succ_jmeasures
461 0 : = TALER_KYCLOGIC_get_jmeasures (
462 0 : ru->lrs,
463 0 : m->measure_name);
464 : bool unknown_account;
465 : struct GNUNET_TIME_Timestamp last_date;
466 : enum GNUNET_DB_QueryStatus qs;
467 :
468 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
469 : "Inserting LEGI OUTCOME as successor measure\n");
470 0 : qs = TALER_EXCHANGEDB_insert_successor_measure (
471 : ru->pg,
472 0 : &ru->account,
473 : GNUNET_TIME_timestamp_get (),
474 0 : m->measure_name,
475 : succ_jmeasures,
476 : &unknown_account,
477 : &last_date);
478 0 : json_decref (succ_jmeasures);
479 0 : switch (qs)
480 : {
481 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
482 0 : GNUNET_log (
483 : GNUNET_ERROR_TYPE_INFO,
484 : "Serialization issue!\n");
485 0 : fail_update (ru,
486 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
487 : "insert_successor_measure");
488 0 : return;
489 0 : case GNUNET_DB_STATUS_HARD_ERROR:
490 0 : GNUNET_break (0);
491 0 : fail_update (ru,
492 : TALER_EC_GENERIC_DB_STORE_FAILED,
493 : "insert_successor_measure");
494 0 : return;
495 0 : default:
496 0 : break;
497 : }
498 :
499 0 : if (unknown_account)
500 : {
501 0 : fail_update (ru,
502 : TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN,
503 : NULL);
504 0 : return;
505 : }
506 : }
507 : /* The rules remain these rules until the user passes the check */
508 0 : return_result (ru);
509 : }
510 :
511 :
512 : /**
513 : * Update the expired legitimization rules in @a ru, checking for expiration
514 : * first. Called with an open database transaction.
515 : *
516 : * @param[in,out] ru account we are processing
517 : */
518 : static void
519 0 : update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
520 : {
521 : const struct TALER_KYCLOGIC_Measure *m;
522 :
523 0 : GNUNET_assert (NULL != ru->lrs);
524 0 : GNUNET_assert (GNUNET_TIME_absolute_is_past (
525 : TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time));
526 0 : m = TALER_KYCLOGIC_rules_get_successor (ru->lrs);
527 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
528 : "Successor measure is %s.\n",
529 : (NULL != m) ? m->measure_name : "(null)");
530 0 : run_measure (ru,
531 : m);
532 0 : }
533 :
534 :
535 : static void
536 28 : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
537 : {
538 28 : ru->depth++;
539 28 : if (ru->depth > MAX_DEPTH)
540 : {
541 0 : fail_update (ru,
542 : TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
543 : NULL);
544 0 : return;
545 : }
546 28 : if (NULL == ru->lrs)
547 : {
548 : /* return NULL, aka default rules */
549 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
550 : "Default rules apply\n");
551 11 : return_result (ru);
552 11 : return;
553 : }
554 17 : if (! GNUNET_TIME_absolute_is_past
555 17 : (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) )
556 : {
557 : /* Rules did not expire, return them! */
558 17 : return_result (ru);
559 17 : return;
560 : }
561 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
562 : "Custom rules expired, updating...\n");
563 0 : update_rules (ru);
564 : }
565 :
566 :
567 : static void
568 28 : fetch_latest_rules (void *cls)
569 : {
570 28 : struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
571 : enum GNUNET_DB_QueryStatus qs;
572 : json_t *jnew_rules;
573 : enum GNUNET_GenericReturnValue res;
574 :
575 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
576 : "Fetching latest rules.");
577 :
578 28 : ru->t = NULL;
579 28 : if (NULL != ru->eh)
580 : {
581 : /* cancel event listener, if we have one */
582 0 : TALER_EXCHANGEDB_event_listen_cancel (ru->pg,
583 : ru->eh);
584 0 : ru->eh = NULL;
585 : }
586 28 : GNUNET_break (NULL == ru->lrs);
587 28 : res = TALER_EXCHANGEDB_start (ru->pg,
588 : "aml-begin-lookup-rules-by-access-token");
589 28 : if (GNUNET_OK != res)
590 : {
591 0 : GNUNET_break (0);
592 0 : fail_update (ru,
593 : TALER_EC_GENERIC_DB_START_FAILED,
594 : "aml-begin-lookup-rules-by-access-token");
595 0 : return;
596 : }
597 28 : qs = TALER_EXCHANGEDB_get_rules_by_access_token (
598 : ru->pg,
599 28 : &ru->account,
600 : &jnew_rules,
601 : &ru->legitimization_outcome_last_row);
602 28 : if (qs < 0)
603 : {
604 0 : GNUNET_break (0);
605 0 : fail_update (ru,
606 : TALER_EC_GENERIC_DB_FETCH_FAILED,
607 : "lookup_rules_by_access_token");
608 0 : return;
609 : }
610 28 : if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
611 17 : (NULL != jnew_rules) )
612 : {
613 17 : ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules);
614 17 : GNUNET_break (NULL != ru->lrs);
615 17 : json_decref (jnew_rules);
616 : }
617 28 : check_rules (ru);
618 : }
619 :
620 :
621 : struct TALER_EXCHANGEDB_RuleUpdater *
622 29 : TALER_EXCHANGEDB_begin_rule_update (
623 : struct TALER_EXCHANGEDB_PostgresContext *pg,
624 : const struct TALER_AttributeEncryptionKeyP *attribute_key,
625 : const struct TALER_NormalizedPaytoHashP *account,
626 : bool is_wallet,
627 : TALER_EXCHANGEDB_CurrentRulesCallback cb,
628 : void *cb_cls)
629 : {
630 : struct TALER_EXCHANGEDB_RuleUpdater *ru;
631 :
632 29 : ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater);
633 29 : ru->pg = pg;
634 29 : ru->attribute_key = *attribute_key;
635 29 : ru->account = *account;
636 29 : ru->is_wallet = is_wallet;
637 29 : ru->cb = cb;
638 29 : ru->cb_cls = cb_cls;
639 29 : ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
640 : ru);
641 29 : return ru;
642 : }
643 :
644 :
645 : void
646 29 : TALER_EXCHANGEDB_begin_rule_update_cancel (
647 : struct TALER_EXCHANGEDB_RuleUpdater *ru)
648 : {
649 29 : if (NULL != ru->t)
650 : {
651 1 : GNUNET_SCHEDULER_cancel (ru->t);
652 1 : ru->t = NULL;
653 : }
654 29 : if (NULL != ru->amlh)
655 : {
656 0 : TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
657 0 : ru->amlh = NULL;
658 : }
659 29 : if (NULL != ru->lrs)
660 : {
661 0 : TALER_KYCLOGIC_rules_free (ru->lrs);
662 0 : ru->lrs = NULL;
663 : }
664 29 : if (NULL != ru->eh)
665 : {
666 0 : TALER_EXCHANGEDB_event_listen_cancel (ru->pg,
667 : ru->eh);
668 0 : ru->eh = NULL;
669 : }
670 29 : GNUNET_free (ru->aml_program_name);
671 29 : GNUNET_free (ru);
672 29 : }
|