LCOV - code coverage report
Current view: top level - backend - taler-merchant-kyccheck.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 308 502 61.4 %
Date: 2025-06-23 16:22:09 Functions: 19 21 90.5 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 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 taler-merchant-kyccheck.c
      18             :  * @brief Process that check the KYC status of our bank accounts at all exchanges
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include "microhttpd.h"
      23             : #include <gnunet/gnunet_util_lib.h>
      24             : #include <jansson.h>
      25             : #include <pthread.h>
      26             : #include <regex.h>
      27             : #include <taler/taler_dbevents.h>
      28             : #include <taler/taler_json_lib.h>
      29             : #include <taler/taler_exchange_service.h>
      30             : #include "taler_merchant_util.h"
      31             : #include "taler_merchant_bank_lib.h"
      32             : #include "taler_merchantdb_lib.h"
      33             : #include "taler_merchantdb_plugin.h"
      34             : 
      35             : /**
      36             :  * Timeout for the exchange interaction.  Rather long as we should do
      37             :  * long-polling and do not want to wake up too often.
      38             :  */
      39             : #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
      40             :           GNUNET_TIME_UNIT_MINUTES, \
      41             :           30)
      42             : 
      43             : /**
      44             :  * How long do we wait between requests if all we wait
      45             :  * for is a change in the AML investigation status?
      46             :  * Default value.
      47             :  */
      48             : #define AML_FREQ GNUNET_TIME_relative_multiply ( \
      49             :           GNUNET_TIME_UNIT_HOURS, \
      50             :           6)
      51             : 
      52             : /**
      53             :  * How long do we wait between requests if all we wait
      54             :  * for is a change in the AML investigation status?
      55             :  */
      56             : static struct GNUNET_TIME_Relative aml_freq;
      57             : 
      58             : /**
      59             :  * How frequently do we check for updates to our KYC status
      60             :  * if there is no actual reason to check? Set to a very low
      61             :  * frequency, just to ensure we eventually notice.
      62             :  * Default value.
      63             :  */
      64             : #define AML_LOW_FREQ GNUNET_TIME_relative_multiply ( \
      65             :           GNUNET_TIME_UNIT_DAYS, \
      66             :           7)
      67             : 
      68             : /**
      69             :  * How frequently do we check for updates to our KYC status
      70             :  * if there is no actual reason to check? Set to a very low
      71             :  * frequency, just to ensure we eventually notice.
      72             :  */
      73             : static struct GNUNET_TIME_Relative aml_low_freq;
      74             : 
      75             : 
      76             : /**
      77             :  * How many inquiries do we process concurrently at most.
      78             :  */
      79             : #define OPEN_INQUIRY_LIMIT 1024
      80             : 
      81             : 
      82             : /**
      83             :  * Information about an exchange.
      84             :  */
      85             : struct Exchange
      86             : {
      87             :   /**
      88             :    * Kept in a DLL.
      89             :    */
      90             :   struct Exchange *next;
      91             : 
      92             :   /**
      93             :    * Kept in a DLL.
      94             :    */
      95             :   struct Exchange *prev;
      96             : 
      97             :   /**
      98             :    * The keys of this exchange
      99             :    */
     100             :   struct TALER_EXCHANGE_Keys *keys;
     101             : 
     102             : };
     103             : 
     104             : 
     105             : /**
     106             :  * Information about an Account.
     107             :  */
     108             : struct Account
     109             : {
     110             :   /**
     111             :    * Kept in a DLL.
     112             :    */
     113             :   struct Account *next;
     114             : 
     115             :   /**
     116             :    * Kept in a DLL.
     117             :    */
     118             :   struct Account *prev;
     119             : 
     120             :   /**
     121             :    * Head of inquiries for this account.
     122             :    */
     123             :   struct Inquiry *i_head;
     124             : 
     125             :   /**
     126             :    * Tail of inquiries for this account.
     127             :    */
     128             :   struct Inquiry *i_tail;
     129             : 
     130             :   /**
     131             :    * Merchant instance this account belongs to.
     132             :    */
     133             :   char *instance_id;
     134             : 
     135             :   /**
     136             :    * The payto-URI of this account.
     137             :    */
     138             :   struct TALER_FullPayto merchant_account_uri;
     139             : 
     140             :   /**
     141             :    * Wire hash of the merchant bank account (with the
     142             :    * respective salt).
     143             :    */
     144             :   struct TALER_MerchantWireHashP h_wire;
     145             : 
     146             :   /**
     147             :    * Private key of the instance.
     148             :    */
     149             :   union TALER_AccountPrivateKeyP ap;
     150             : 
     151             :   /**
     152             :    * Hash of the @e merchant_account_uri.
     153             :    */
     154             :   struct TALER_NormalizedPaytoHashP h_payto;
     155             : 
     156             :   /**
     157             :    * Database generation when this account
     158             :    * was last active.
     159             :    */
     160             :   uint64_t account_gen;
     161             : 
     162             : };
     163             : 
     164             : 
     165             : /**
     166             :  * Information about an inquiry job.
     167             :  */
     168             : struct Inquiry
     169             : {
     170             :   /**
     171             :    * Kept in a DLL.
     172             :    */
     173             :   struct Inquiry *next;
     174             : 
     175             :   /**
     176             :    * Kept in a DLL.
     177             :    */
     178             :   struct Inquiry *prev;
     179             : 
     180             :   /**
     181             :    * Main task for this inquiry.
     182             :    */
     183             :   struct GNUNET_SCHEDULER_Task *task;
     184             : 
     185             :   /**
     186             :    * Which exchange is this inquiry about.
     187             :    */
     188             :   struct Exchange *e;
     189             : 
     190             :   /**
     191             :    * Which account is this inquiry about.
     192             :    */
     193             :   struct Account *a;
     194             : 
     195             :   /**
     196             :    * AccountLimits that apply to the account, NULL
     197             :    * if unknown.
     198             :    */
     199             :   json_t *jlimits;
     200             : 
     201             :   /**
     202             :    * Handle for the actual HTTP request to the exchange.
     203             :    */
     204             :   struct TALER_EXCHANGE_KycCheckHandle *kyc;
     205             : 
     206             :   /**
     207             :    * Access token for the /kyc-info API.
     208             :    */
     209             :   struct TALER_AccountAccessTokenP access_token;
     210             : 
     211             :   /**
     212             :    * Last time we called the /kyc-check endpoint.
     213             :    */
     214             :   struct GNUNET_TIME_Timestamp last_kyc_check;
     215             : 
     216             :   /**
     217             :    * When is the next KYC check due?
     218             :    */
     219             :   struct GNUNET_TIME_Absolute due;
     220             : 
     221             :   /**
     222             :    * When should the current KYC time out?
     223             :    */
     224             :   struct GNUNET_TIME_Absolute timeout;
     225             : 
     226             :   /**
     227             :    * Current exponential backoff.
     228             :    */
     229             :   struct GNUNET_TIME_Relative backoff;
     230             : 
     231             :   /**
     232             :    * Rule generation known to the client, 0 for none.
     233             :    * Corresponds to the decision row in the exchange.
     234             :    */
     235             :   uint64_t rule_gen;
     236             : 
     237             :   /**
     238             :    * Last HTTP status returned by the exchange from
     239             :    * the /kyc-check endpoint.
     240             :    */
     241             :   unsigned int last_http_status;
     242             : 
     243             :   /**
     244             :    * Last Taler error code returned by the exchange from
     245             :    * the /kyc-check endpoint.
     246             :    */
     247             :   enum TALER_ErrorCode last_ec;
     248             : 
     249             :   /**
     250             :    * True if this is not our first time we make this request.
     251             :    */
     252             :   bool not_first_time;
     253             : 
     254             :   /**
     255             :    * Do soft limits on transactions apply to this merchant for operations
     256             :    * merchants care about? If so, we should increase our request frequency
     257             :    * and ask more often to see if they were lifted.
     258             :    */
     259             :   bool zero_limited;
     260             : 
     261             :   /**
     262             :    * Did we not run this inquiry due to limits?
     263             :    */
     264             :   bool limited;
     265             : 
     266             :   /**
     267             :    * Do we believe this account's KYC is in good shape?
     268             :    */
     269             :   bool kyc_ok;
     270             : 
     271             :   /**
     272             :    * True if merchant did perform this account's KYC AUTH transfer and @e access_token is set.
     273             :    */
     274             :   bool auth_ok;
     275             : 
     276             :   /**
     277             :    * True if the account is known to be currently under
     278             :    * investigation by AML staff.
     279             :    */
     280             :   bool aml_review;
     281             : 
     282             : };
     283             : 
     284             : 
     285             : /**
     286             :  * Head of known exchanges.
     287             :  */
     288             : static struct Exchange *e_head;
     289             : 
     290             : /**
     291             :  * Tail of known exchanges.
     292             :  */
     293             : static struct Exchange *e_tail;
     294             : 
     295             : /**
     296             :  * Head of accounts.
     297             :  */
     298             : static struct Account *a_head;
     299             : 
     300             : /**
     301             :  * Tail of accounts.
     302             :  */
     303             : static struct Account *a_tail;
     304             : 
     305             : /**
     306             :  * The merchant's configuration.
     307             :  */
     308             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     309             : 
     310             : /**
     311             :  * Our database plugin.
     312             :  */
     313             : static struct TALER_MERCHANTDB_Plugin *db_plugin;
     314             : 
     315             : /**
     316             :  * Handle to the context for interacting with the bank.
     317             :  */
     318             : static struct GNUNET_CURL_Context *ctx;
     319             : 
     320             : /**
     321             :  * Scheduler context for running the @e ctx.
     322             :  */
     323             : static struct GNUNET_CURL_RescheduleContext *rc;
     324             : 
     325             : /**
     326             :  * Event handler to learn that there may be new bank
     327             :  * accounts to check.
     328             :  */
     329             : static struct GNUNET_DB_EventHandler *eh_accounts;
     330             : 
     331             : /**
     332             :  * Event handler to learn that there may be new exchange
     333             :  * keys to check.
     334             :  */
     335             : static struct GNUNET_DB_EventHandler *eh_keys;
     336             : 
     337             : /**
     338             :  * Event handler to learn that there was a KYC
     339             :  * rule triggered and we need to check the KYC
     340             :  * status for an account.
     341             :  */
     342             : static struct GNUNET_DB_EventHandler *eh_rule;
     343             : 
     344             : /**
     345             :  * Main task to discover (new) accounts.
     346             :  */
     347             : static struct GNUNET_SCHEDULER_Task *account_task;
     348             : 
     349             : /**
     350             :  * Counter determining how often we have called
     351             :  * "select_accounts" on the database.
     352             :  */
     353             : static uint64_t database_gen;
     354             : 
     355             : /**
     356             :  * How many active inquiries do we have right now.
     357             :  */
     358             : static unsigned int active_inquiries;
     359             : 
     360             : /**
     361             :  * Value to return from main(). 0 on success, non-zero on errors.
     362             :  */
     363             : static int global_ret;
     364             : 
     365             : /**
     366             :  * #GNUNET_YES if we are in test mode and should exit when idle.
     367             :  */
     368             : static int test_mode;
     369             : 
     370             : /**
     371             :  * True if the last DB query was limited by the
     372             :  * #OPEN_INQUIRY_LIMIT and we thus should check again
     373             :  * as soon as we are substantially below that limit,
     374             :  * and not only when we get a DB notification.
     375             :  */
     376             : static bool at_limit;
     377             : 
     378             : 
     379             : /**
     380             :  * Check about performing a /kyc-check request with the
     381             :  * exchange for the given inquiry.
     382             :  *
     383             :  * @param cls a `struct Inquiry` to process
     384             :  */
     385             : static void
     386             : inquiry_work (void *cls);
     387             : 
     388             : 
     389             : /**
     390             :  * An inquiry finished, check if we should resume
     391             :  * others.
     392             :  */
     393             : static void
     394          14 : end_inquiry (void)
     395             : {
     396          14 :   GNUNET_assert (active_inquiries > 0);
     397          14 :   active_inquiries--;
     398          14 :   if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
     399             :        (at_limit) )
     400             :   {
     401           0 :     at_limit = false;
     402           0 :     for (struct Account *a = a_head;
     403           0 :          NULL != a;
     404           0 :          a = a->next)
     405             :     {
     406           0 :       for (struct Inquiry *i = a->i_head;
     407           0 :            NULL != i;
     408           0 :            i = i->next)
     409             :       {
     410           0 :         if (! i->limited)
     411           0 :           continue;
     412           0 :         GNUNET_assert (NULL == i->task);
     413             :         /* done synchronously so that the active_inquiries
     414             :            is updated immediately */
     415           0 :         inquiry_work (i);
     416           0 :         if (at_limit)
     417           0 :           break;
     418             :       }
     419           0 :       if (at_limit)
     420           0 :         break;
     421             :     }
     422             :   }
     423          14 :   if ( (! at_limit) &&
     424          14 :        (0 == active_inquiries) &&
     425             :        (test_mode) )
     426             :   {
     427           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     428             :                 "No more open inquiries and in test mode. Existing.\n");
     429           0 :     GNUNET_SCHEDULER_shutdown ();
     430           0 :     return;
     431             :   }
     432             : }
     433             : 
     434             : 
     435             : /**
     436             :  * Pack the given @a limit into the JSON @a limits array.
     437             :  *
     438             :  * @param limit account limit to pack
     439             :  * @param[in,out] limits JSON array to extend
     440             :  */
     441             : static void
     442           2 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
     443             :             json_t *limits)
     444             : {
     445             :   json_t *jl;
     446             : 
     447           2 :   jl = GNUNET_JSON_PACK (
     448             :     TALER_JSON_pack_kycte ("operation_type",
     449             :                            limit->operation_type),
     450             :     GNUNET_JSON_pack_time_rel ("timeframe",
     451             :                                limit->timeframe),
     452             :     TALER_JSON_pack_amount ("threshold",
     453             :                             &limit->threshold),
     454             :     GNUNET_JSON_pack_bool ("soft_limit",
     455             :                            limit->soft_limit)
     456             :     );
     457           2 :   GNUNET_assert (0 ==
     458             :                  json_array_append_new (limits,
     459             :                                         jl));
     460           2 : }
     461             : 
     462             : 
     463             : /**
     464             :  * Update KYC status for @a i based on
     465             :  * @a account_kyc_status
     466             :  *
     467             :  * @param[in,out] i inquiry context, jlimits is updated
     468             :  * @param account_kyc_status account KYC status details
     469             :  */
     470             : static void
     471           2 : store_kyc_status (
     472             :   struct Inquiry *i,
     473             :   const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
     474             : {
     475             :   json_t *jlimits;
     476             : 
     477           2 :   json_decref (i->jlimits);
     478           2 :   jlimits = json_array ();
     479           2 :   GNUNET_assert (NULL != jlimits);
     480           2 :   i->zero_limited = false;
     481           4 :   for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
     482             :   {
     483           2 :     const struct TALER_EXCHANGE_AccountLimit *limit
     484           2 :       = &account_kyc_status->limits[j];
     485             : 
     486           2 :     pack_limit (limit,
     487             :                 jlimits);
     488           2 :     if (TALER_amount_is_zero (&limit->threshold) &&
     489           2 :         limit->soft_limit &&
     490           2 :         ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) ||
     491           2 :           (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) ||
     492           0 :           (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) )
     493             :     {
     494           2 :       i->zero_limited = true;
     495             :     }
     496             :   }
     497           2 :   i->jlimits = jlimits;
     498           2 :   GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token));
     499           2 :   i->access_token = account_kyc_status->access_token;
     500           2 :   i->auth_ok = true;
     501           2 :   i->aml_review = account_kyc_status->aml_review;
     502           2 :   i->kyc_ok = (MHD_HTTP_OK == i->last_http_status);
     503           2 : }
     504             : 
     505             : 
     506             : /**
     507             :  * Function called with the result of a KYC check.
     508             :  *
     509             :  * @param cls a `struct Inquiry *`
     510             :  * @param ks the account's KYC status details
     511             :  */
     512             : static void
     513          14 : exchange_check_cb (
     514             :   void *cls,
     515             :   const struct TALER_EXCHANGE_KycStatus *ks)
     516             : {
     517          14 :   struct Inquiry *i = cls;
     518          14 :   bool progress = false;
     519             : 
     520          14 :   if (! i->not_first_time)
     521          12 :     progress = true;
     522          14 :   i->kyc = NULL;
     523          14 :   i->last_http_status = ks->hr.http_status;
     524          14 :   i->last_ec = ks->hr.ec;
     525          14 :   i->rule_gen = 0;
     526          14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     527             :               "Checking KYC status of `%s' at `%s' is %u\n",
     528             :               i->a->merchant_account_uri.full_payto,
     529             :               i->e->keys->exchange_url,
     530             :               ks->hr.http_status);
     531          14 :   switch (ks->hr.http_status)
     532             :   {
     533           2 :   case MHD_HTTP_OK:
     534           2 :     if (! i->kyc_ok)
     535           1 :       progress = true;
     536           2 :     i->rule_gen = ks->details.ok.rule_gen;
     537           2 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     538             :     /* exchange says KYC is OK, gives status information */
     539           2 :     store_kyc_status (i,
     540             :                       &ks->details.ok);
     541           2 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     542           2 :     if (i->aml_review || i->zero_limited)
     543             :     {
     544           2 :       if (! progress)
     545           1 :         i->due = GNUNET_TIME_relative_to_absolute (
     546             :           GNUNET_TIME_randomize (aml_freq));
     547             :     }
     548             :     else
     549             :     {
     550             :       /* KYC is OK, only check again if triggered */
     551           0 :       i->due = GNUNET_TIME_relative_to_absolute (
     552             :         GNUNET_TIME_randomize (
     553             :           aml_low_freq));
     554             :     }
     555           2 :     break;
     556           0 :   case MHD_HTTP_ACCEPTED:
     557           0 :     progress = ! i->auth_ok;
     558           0 :     i->rule_gen = ks->details.ok.rule_gen;
     559           0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     560             : 
     561             :     /* exchange says KYC is required */
     562           0 :     store_kyc_status (i,
     563             :                       &ks->details.accepted);
     564           0 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     565             :     /* Start immediately with long-polling */
     566           0 :     if (! progress)
     567           0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     568             :                                          i->timeout);
     569           0 :     break;
     570          11 :   case MHD_HTTP_NO_CONTENT:
     571          11 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     572          11 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     573             :     /* exchange claims KYC is off! */
     574          11 :     i->kyc_ok = true;
     575          11 :     i->aml_review = false;
     576             :     /* Clear limits, in case exchange had KYC on previously */
     577          11 :     json_decref (i->jlimits);
     578          11 :     i->jlimits = NULL;
     579             :     /* KYC is OK, only check again if triggered */
     580          11 :     i->due = GNUNET_TIME_relative_to_absolute (
     581             :       GNUNET_TIME_randomize (aml_low_freq));
     582          11 :     break;
     583           0 :   case MHD_HTTP_FORBIDDEN: /* bad signature */
     584           0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     585             :     /* Forbidden => KYC auth must be wrong */
     586           0 :     i->auth_ok = false;
     587             :     /* Start with long-polling */
     588           0 :     if (! progress)
     589           0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     590             :                                          i->timeout);
     591           0 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     592           0 :     break;
     593           1 :   case MHD_HTTP_NOT_FOUND: /* account unknown */
     594           1 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     595             :     /* Account unknown => no KYC auth yet */
     596           1 :     i->auth_ok = false;
     597             :     /* unknown account => wire transfer required! */
     598           1 :     i->kyc_ok = false;
     599             :     /* There should not be any limits yet, but clear them
     600             :        just in case the exchange has amnesia */
     601           1 :     json_decref (i->jlimits);
     602           1 :     i->jlimits = NULL;
     603             :     /* Start immediately with Long-polling */
     604           1 :     if (! progress)
     605           0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     606             :                                          i->timeout);
     607           1 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     608           1 :     break;
     609           0 :   case MHD_HTTP_CONFLICT: /* no account_pub known */
     610           0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     611             :     /* Conflict => KYC auth wire transfer missing! */
     612           0 :     i->auth_ok = false;
     613             :     /* Start immediately with Long-polling */
     614           0 :     if (! progress)
     615           0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     616             :                                          i->timeout);
     617           0 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     618           0 :     break;
     619           0 :   default:
     620           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     621             :                 "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
     622             :                 ks->hr.http_status,
     623             :                 ks->hr.ec);
     624             :     i->backoff
     625           0 :       = GNUNET_TIME_randomized_backoff (i->backoff,
     626             :                                         EXCHANGE_TIMEOUT);
     627           0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     628           0 :     i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
     629           0 :     i->auth_ok = false;
     630           0 :     break;
     631             :   }
     632             : 
     633             :   {
     634             :     enum GNUNET_DB_QueryStatus qs;
     635             : 
     636          14 :     qs = db_plugin->account_kyc_set_status (
     637          14 :       db_plugin->cls,
     638          14 :       i->a->instance_id,
     639          14 :       &i->a->h_wire,
     640          14 :       i->e->keys->exchange_url,
     641             :       i->last_kyc_check,
     642             :       i->last_http_status,
     643             :       i->last_ec,
     644             :       i->rule_gen,
     645          14 :       (i->auth_ok)
     646             :       ? &i->access_token
     647             :       : NULL,
     648          14 :       i->jlimits,
     649          14 :       i->aml_review,
     650          14 :       i->kyc_ok);
     651          14 :     if (qs < 0)
     652             :     {
     653           0 :       GNUNET_break (0);
     654           0 :       global_ret = EXIT_FAILURE;
     655           0 :       GNUNET_SCHEDULER_shutdown ();
     656           0 :       return;
     657             :     }
     658          14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     659             :                 "account_set_kyc_status (%s, %u, %s, %s) returned %d\n",
     660             :                 i->e->keys->exchange_url,
     661             :                 i->last_http_status,
     662             :                 i->auth_ok ? "auth OK" : "auth needed",
     663             :                 NULL == i->jlimits ? "default limits" : "custom limits",
     664             :                 (int) qs);
     665          14 :     i->not_first_time = true;
     666             :   }
     667          14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     668             :               "Will repeat inquiry in %s\n",
     669             :               GNUNET_TIME_relative2s (
     670             :                 GNUNET_TIME_absolute_get_remaining (i->due),
     671             :                 true));
     672          14 :   if (! GNUNET_TIME_absolute_is_never (i->due))
     673          14 :     i->task = GNUNET_SCHEDULER_add_at (i->due,
     674             :                                        &inquiry_work,
     675             :                                        i);
     676          14 :   end_inquiry ();
     677             : }
     678             : 
     679             : 
     680             : static void
     681          14 : inquiry_work (void *cls)
     682             : {
     683          14 :   struct Inquiry *i = cls;
     684             :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     685             : 
     686          14 :   i->task = NULL;
     687          14 :   if (! GNUNET_TIME_absolute_is_past (i->due))
     688             :   {
     689           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     690             :                 "Will start inquiry on %s for %s in %s\n",
     691             :                 i->a->merchant_account_uri.full_payto,
     692             :                 i->e->keys->exchange_url,
     693             :                 GNUNET_TIME_relative2s (
     694             :                   GNUNET_TIME_absolute_get_remaining (i->due),
     695             :                   true));
     696             :     i->task
     697           0 :       = GNUNET_SCHEDULER_add_at (i->due,
     698             :                                  &inquiry_work,
     699             :                                  i);
     700           0 :     goto finish;
     701             :   }
     702             : 
     703          14 :   GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
     704          14 :   if (OPEN_INQUIRY_LIMIT <= active_inquiries)
     705             :   {
     706           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     707             :                 "Not looking for work: at limit\n");
     708           0 :     i->limited = true;
     709           0 :     at_limit = true;
     710           0 :     return;
     711             :   }
     712          14 :   at_limit = false;
     713          14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     714             :               "Checking KYC status of `%s' at `%s'\n",
     715             :               i->a->merchant_account_uri.full_payto,
     716             :               i->e->keys->exchange_url);
     717             :   i->timeout
     718          14 :     = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
     719          14 :   lpt = TALER_EXCHANGE_KLPT_NONE;
     720          14 :   if (! i->auth_ok)
     721          13 :     lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER;
     722           1 :   else if (! i->kyc_ok)
     723           0 :     lpt = TALER_EXCHANGE_KLPT_KYC_OK;
     724           1 :   else if (i->aml_review)
     725           0 :     lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
     726          14 :   if (! i->not_first_time)
     727          12 :     lpt = TALER_EXCHANGE_KLPT_NONE;
     728          12 :   i->kyc = TALER_EXCHANGE_kyc_check (
     729             :     ctx,
     730          14 :     i->e->keys->exchange_url,
     731          14 :     &i->a->h_payto,
     732          14 :     &i->a->ap,
     733             :     i->rule_gen,
     734             :     lpt,
     735          14 :     i->not_first_time && (! test_mode)
     736           2 :     ? EXCHANGE_TIMEOUT
     737             :     : GNUNET_TIME_UNIT_ZERO,
     738             :     &exchange_check_cb,
     739             :     i);
     740          14 :   if (NULL == i->kyc)
     741             :   {
     742           0 :     GNUNET_break (0);
     743           0 :     i->due = i->timeout;
     744             :     i->task
     745           0 :       = GNUNET_SCHEDULER_add_at (i->due,
     746             :                                  &inquiry_work,
     747             :                                  i);
     748           0 :     goto finish;
     749             :   }
     750          14 :   active_inquiries++;
     751          14 : finish:
     752          14 :   if ( (0 == active_inquiries) &&
     753             :        (test_mode) )
     754             :   {
     755           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     756             :                 "No more open inquiries and in test mode. Existing.\n");
     757           0 :     GNUNET_SCHEDULER_shutdown ();
     758           0 :     return;
     759             :   }
     760             : }
     761             : 
     762             : 
     763             : /**
     764             :  * Check if the account @a could work with exchange that
     765             :  * has keys @a keys.
     766             :  *
     767             :  * @param keys the keys of an exchange
     768             :  * @param a an account
     769             :  */
     770             : static bool
     771          27 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
     772             :              const struct Account *a)
     773             : {
     774             :   struct TALER_NormalizedPayto np;
     775             :   bool ret;
     776             : 
     777          27 :   np = TALER_payto_normalize (a->merchant_account_uri);
     778          27 :   ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
     779             :                                                   true,
     780             :                                                   np);
     781          27 :   GNUNET_free (np.normalized_payto);
     782          27 :   return ret;
     783             : }
     784             : 
     785             : 
     786             : /**
     787             :  * Start the KYC checking for account @a at exchange @a e.
     788             :  *
     789             :  * @param e an exchange
     790             :  * @param a an account
     791             :  */
     792             : static void
     793          12 : start_inquiry (struct Exchange *e,
     794             :                struct Account *a)
     795             : {
     796             :   struct Inquiry *i;
     797             :   enum GNUNET_DB_QueryStatus qs;
     798             : 
     799          12 :   i = GNUNET_new (struct Inquiry);
     800          12 :   i->e = e;
     801          12 :   i->a = a;
     802          12 :   GNUNET_CONTAINER_DLL_insert (a->i_head,
     803             :                                a->i_tail,
     804             :                                i);
     805          12 :   qs = db_plugin->get_kyc_status (db_plugin->cls,
     806             :                                   a->merchant_account_uri,
     807          12 :                                   a->instance_id,
     808          12 :                                   e->keys->exchange_url,
     809             :                                   &i->auth_ok,
     810             :                                   &i->access_token,
     811             :                                   &i->kyc_ok,
     812             :                                   &i->last_http_status,
     813             :                                   &i->last_ec,
     814             :                                   &i->rule_gen,
     815             :                                   &i->last_kyc_check,
     816             :                                   &i->aml_review,
     817             :                                   &i->jlimits);
     818          12 :   if (qs < 0)
     819             :   {
     820           0 :     GNUNET_break (0);
     821           0 :     global_ret = EXIT_FAILURE;
     822           0 :     GNUNET_SCHEDULER_shutdown ();
     823           0 :     return;
     824             :   }
     825          12 :   if (qs > 0)
     826           0 :     i->not_first_time = true;
     827          12 :   switch (i->last_http_status)
     828             :   {
     829           0 :   case MHD_HTTP_OK:
     830             :     /* KYC is OK, but we could have missed some triggers,
     831             :        so let's check, but slowly within the next minute
     832             :        so that we do not overdo it if this process happens
     833             :        to be restarted a lot. */
     834           0 :     if (GNUNET_YES != test_mode)
     835             :     {
     836           0 :       i->due = GNUNET_TIME_relative_to_absolute (
     837             :         GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES));
     838           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     839             :                   "Previous KYC status is OK, randomizing inquiry to start at %s\n",
     840             :                   GNUNET_TIME_absolute2s (i->due));
     841             :     }
     842           0 :     break;
     843           0 :   case MHD_HTTP_ACCEPTED:
     844             :     /* KYC required, due immediately */
     845           0 :     break;
     846           0 :   case MHD_HTTP_NO_CONTENT:
     847             :     /* KYC is OFF, only check again if triggered */
     848           0 :     if (GNUNET_YES != test_mode)
     849             :     {
     850           0 :       i->due = GNUNET_TIME_relative_to_absolute (
     851             :         GNUNET_TIME_randomize (aml_low_freq));
     852           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     853             :                   "KYC was disabled, randomizing inquiry to start at %s\n",
     854             :                   GNUNET_TIME_absolute2s (i->due));
     855             :     }
     856           0 :     break;
     857           0 :   case MHD_HTTP_FORBIDDEN: /* bad signature */
     858             :   case MHD_HTTP_NOT_FOUND: /* account unknown */
     859             :   case MHD_HTTP_CONFLICT: /* no account_pub known */
     860             :     /* go immediately into long-polling */
     861           0 :     break;
     862          12 :   default:
     863             :     /* start with decent back-off after hard failure */
     864          12 :     if (GNUNET_YES != test_mode)
     865             :     {
     866             :       i->backoff
     867          12 :         = GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES);
     868          12 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     869             :                   "Last KYC check failed, starting with backoff %s\n",
     870             :                   GNUNET_TIME_relative2s (i->backoff,
     871             :                                           true));
     872             :     }
     873          12 :     break;
     874             :   }
     875          12 :   inquiry_work (i);
     876             : }
     877             : 
     878             : 
     879             : /**
     880             :  * Stop KYC inquiry @a i.
     881             :  *
     882             :  * @param[in] i the inquiry to stop
     883             :  */
     884             : static void
     885          12 : stop_inquiry (struct Inquiry *i)
     886             : {
     887          12 :   struct Account *a = i->a;
     888             : 
     889          12 :   GNUNET_CONTAINER_DLL_remove (a->i_head,
     890             :                                a->i_tail,
     891             :                                i);
     892          12 :   if (NULL != i->task)
     893             :   {
     894          12 :     GNUNET_SCHEDULER_cancel (i->task);
     895          12 :     i->task = NULL;
     896             :   }
     897          12 :   if (NULL != i->kyc)
     898             :   {
     899           0 :     TALER_EXCHANGE_kyc_check_cancel (i->kyc);
     900           0 :     i->kyc = NULL;
     901             :   }
     902          12 :   if (NULL != i->jlimits)
     903             :   {
     904           1 :     json_decref (i->jlimits);
     905           1 :     i->jlimits = NULL;
     906             :   }
     907          12 :   GNUNET_free (i);
     908          12 : }
     909             : 
     910             : 
     911             : /**
     912             :  * Stop KYC inquiry for account @a at exchange @a e.
     913             :  *
     914             :  * @param e an exchange
     915             :  * @param a an account
     916             :  */
     917             : static void
     918           0 : stop_inquiry_at (struct Exchange *e,
     919             :                  struct Account *a)
     920             : {
     921           0 :   for (struct Inquiry *i = a->i_head;
     922           0 :        NULL != i;
     923           0 :        i = i->next)
     924             :   {
     925           0 :     if (e == i->e)
     926             :     {
     927           0 :       stop_inquiry (i);
     928           0 :       return;
     929             :     }
     930             :   }
     931             :   /* strange, there should have been a match! */
     932           0 :   GNUNET_break (0);
     933             : }
     934             : 
     935             : 
     936             : /**
     937             :  * Start inquries for all exchanges on account @a a.
     938             :  *
     939             :  * @param a an account
     940             :  */
     941             : static void
     942          14 : start_inquiries (struct Account *a)
     943             : {
     944          14 :   for (struct Exchange *e = e_head;
     945          38 :        NULL != e;
     946          24 :        e = e->next)
     947             :   {
     948          24 :     if (is_eligible (e->keys,
     949             :                      a))
     950           9 :       start_inquiry (e,
     951             :                      a);
     952             :     else
     953          15 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     954             :                   "Account %s not eligible at exchange %s\n",
     955             :                   a->merchant_account_uri.full_payto,
     956             :                   e->keys->exchange_url);
     957             :   }
     958          14 : }
     959             : 
     960             : 
     961             : /**
     962             :  * Stop all inquries involving account @a a.
     963             :  *
     964             :  * @param a an account
     965             :  */
     966             : static void
     967          19 : stop_inquiries (struct Account *a)
     968             : {
     969             :   struct Inquiry *i;
     970             : 
     971          31 :   while (NULL != (i = a->i_head))
     972          12 :     stop_inquiry (i);
     973          19 : }
     974             : 
     975             : 
     976             : /**
     977             :  * Callback invoked with information about a bank account.
     978             :  *
     979             :  * @param cls closure
     980             :  * @param merchant_priv private key of the merchant instance
     981             :  * @param ad details about the account
     982             :  */
     983             : static void
     984          39 : account_cb (
     985             :   void *cls,
     986             :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
     987             :   const struct TALER_MERCHANTDB_AccountDetails *ad)
     988             : {
     989          39 :   struct TALER_FullPayto payto_uri = ad->payto_uri;
     990             : 
     991          39 :   if (! ad->active)
     992          25 :     return;
     993          38 :   if (NULL == merchant_priv)
     994           0 :     return; /* instance was deleted */
     995          38 :   for (struct Account *a = a_head;
     996          45 :        NULL != a;
     997           7 :        a = a->next)
     998             :   {
     999          31 :     if (0 ==
    1000          31 :         TALER_full_payto_cmp (payto_uri,
    1001             :                               a->merchant_account_uri))
    1002             :     {
    1003          24 :       a->account_gen = database_gen;
    1004          24 :       return;
    1005             :     }
    1006             :   }
    1007             :   {
    1008          14 :     struct Account *a = GNUNET_new (struct Account);
    1009             : 
    1010          14 :     a->account_gen = database_gen;
    1011             :     a->merchant_account_uri.full_payto
    1012          14 :       = GNUNET_strdup (ad->payto_uri.full_payto);
    1013             :     a->instance_id
    1014          14 :       = GNUNET_strdup (ad->instance_id);
    1015             :     a->h_wire
    1016          14 :       = ad->h_wire;
    1017             :     a->ap.merchant_priv
    1018          14 :       = *merchant_priv;
    1019          14 :     TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
    1020             :                                          &a->h_payto);
    1021          14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1022             :                 "Found account %s of instance %s with H_PAYTO %s\n",
    1023             :                 ad->payto_uri.full_payto,
    1024             :                 ad->instance_id,
    1025             :                 GNUNET_sh2s (&a->h_payto.hash));
    1026          14 :     GNUNET_CONTAINER_DLL_insert (a_head,
    1027             :                                  a_tail,
    1028             :                                  a);
    1029          14 :     start_inquiries (a);
    1030             :   }
    1031             : }
    1032             : 
    1033             : 
    1034             : /**
    1035             :  * The set of bank accounts has changed, update our
    1036             :  * list of active inquiries.
    1037             :  *
    1038             :  * @param cls unused
    1039             :  */
    1040             : static void
    1041          51 : find_accounts (void *cls)
    1042             : {
    1043             :   enum GNUNET_DB_QueryStatus qs;
    1044             : 
    1045             :   (void) cls;
    1046          51 :   account_task = NULL;
    1047          51 :   database_gen++;
    1048          51 :   qs = db_plugin->select_accounts (db_plugin->cls,
    1049             :                                    NULL, /* all instances */
    1050             :                                    &account_cb,
    1051             :                                    NULL);
    1052          51 :   if (qs < 0)
    1053             :   {
    1054           0 :     GNUNET_break (0);
    1055           0 :     return;
    1056             :   }
    1057          51 :   for (struct Account *a = a_head;
    1058          92 :        NULL != a;
    1059          41 :        a = a->next)
    1060             :   {
    1061          41 :     if (a->account_gen < database_gen)
    1062           5 :       stop_inquiries (a);
    1063             :   }
    1064             : }
    1065             : 
    1066             : 
    1067             : /**
    1068             :  * Function called when transfers are added to the merchant database.  We look
    1069             :  * for more work.
    1070             :  *
    1071             :  * @param cls closure (NULL)
    1072             :  * @param extra additional event data provided
    1073             :  * @param extra_size number of bytes in @a extra
    1074             :  */
    1075             : static void
    1076          38 : account_changed (void *cls,
    1077             :                  const void *extra,
    1078             :                  size_t extra_size)
    1079             : {
    1080             :   (void) cls;
    1081             :   (void) extra;
    1082             :   (void) extra_size;
    1083          38 :   if (NULL != account_task)
    1084           0 :     return;
    1085             :   account_task
    1086          38 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    1087             :                                 NULL);
    1088             : }
    1089             : 
    1090             : 
    1091             : /**
    1092             :  * Interact with the database to get the current set
    1093             :  * of exchange keys known to us.
    1094             :  *
    1095             :  * @param exchange_url the exchange URL to check
    1096             :  */
    1097             : static void
    1098          48 : find_keys (const char *exchange_url)
    1099             : {
    1100             :   enum GNUNET_DB_QueryStatus qs;
    1101             :   struct TALER_EXCHANGE_Keys *keys;
    1102             :   struct Exchange *e;
    1103             :   struct GNUNET_TIME_Absolute first_retry;
    1104             : 
    1105          48 :   qs = db_plugin->select_exchange_keys (db_plugin->cls,
    1106             :                                         exchange_url,
    1107             :                                         &first_retry,
    1108             :                                         &keys);
    1109          48 :   if (qs < 0)
    1110             :   {
    1111           0 :     GNUNET_break (0);
    1112          26 :     return;
    1113             :   }
    1114          48 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1115             :   {
    1116          26 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1117             :                 "No %s/keys yet!\n",
    1118             :                 exchange_url);
    1119          26 :     return;
    1120             :   }
    1121          31 :   for (e = e_head; NULL != e; e = e->next)
    1122             :   {
    1123           9 :     if (0 == strcmp (e->keys->exchange_url,
    1124           9 :                      keys->exchange_url))
    1125             :     {
    1126           0 :       struct TALER_EXCHANGE_Keys *old_keys = e->keys;
    1127             : 
    1128           0 :       e->keys = keys;
    1129           0 :       for (struct Account *a = a_head;
    1130           0 :            NULL != a;
    1131           0 :            a = a->next)
    1132             :       {
    1133           0 :         bool was_eligible = is_eligible (old_keys,
    1134             :                                          a);
    1135           0 :         bool now_eligible = is_eligible (keys,
    1136             :                                          a);
    1137             : 
    1138           0 :         if (was_eligible == now_eligible)
    1139           0 :           continue; /* no change, do nothing */
    1140           0 :         if (was_eligible)
    1141           0 :           stop_inquiry_at (e,
    1142             :                            a);
    1143             :         else /* is_eligible */
    1144           0 :           start_inquiry (e,
    1145             :                          a);
    1146             :       }
    1147           0 :       TALER_EXCHANGE_keys_decref (old_keys);
    1148           0 :       return;
    1149             :     }
    1150             :   }
    1151          22 :   e = GNUNET_new (struct Exchange);
    1152          22 :   e->keys = keys;
    1153          22 :   GNUNET_CONTAINER_DLL_insert (e_head,
    1154             :                                e_tail,
    1155             :                                e);
    1156          22 :   for (struct Account *a = a_head;
    1157          25 :        NULL != a;
    1158           3 :        a = a->next)
    1159             :   {
    1160           6 :     if ( (a->account_gen == database_gen) &&
    1161           3 :          (is_eligible (e->keys,
    1162             :                        a)) )
    1163           3 :       start_inquiry (e,
    1164             :                      a);
    1165             :   }
    1166             : }
    1167             : 
    1168             : 
    1169             : /**
    1170             :  * Function called when keys were changed in the
    1171             :  * merchant database. Updates ours.
    1172             :  *
    1173             :  * @param cls closure (NULL)
    1174             :  * @param extra additional event data provided
    1175             :  * @param extra_size number of bytes in @a extra
    1176             :  */
    1177             : static void
    1178          22 : keys_changed (void *cls,
    1179             :               const void *extra,
    1180             :               size_t extra_size)
    1181             : {
    1182          22 :   const char *url = extra;
    1183             : 
    1184             :   (void) cls;
    1185          22 :   if ( (NULL == extra) ||
    1186             :        (0 == extra_size) )
    1187             :   {
    1188           0 :     GNUNET_break (0);
    1189           0 :     return;
    1190             :   }
    1191          22 :   if ('\0' != url[extra_size - 1])
    1192             :   {
    1193           0 :     GNUNET_break (0);
    1194           0 :     return;
    1195             :   }
    1196          22 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1197             :               "Received keys change notification: reload `%s'\n",
    1198             :               url);
    1199          22 :   find_keys (url);
    1200             : }
    1201             : 
    1202             : 
    1203             : /**
    1204             :  * Function called when a KYC rule was triggered by
    1205             :  * a transaction and we need to get the latest KYC
    1206             :  * status immediately.
    1207             :  *
    1208             :  * @param cls closure (NULL)
    1209             :  * @param extra additional event data provided
    1210             :  * @param extra_size number of bytes in @a extra
    1211             :  */
    1212             : static void
    1213           0 : rule_triggered (void *cls,
    1214             :                 const void *extra,
    1215             :                 size_t extra_size)
    1216             : {
    1217           0 :   const char *text = extra;
    1218             :   const char *space;
    1219             :   struct TALER_MerchantWireHashP h_wire;
    1220             :   const char *exchange_url;
    1221             : 
    1222             :   (void) cls;
    1223           0 :   if ( (NULL == extra) ||
    1224             :        (0 == extra_size) )
    1225             :   {
    1226           0 :     GNUNET_break (0);
    1227           0 :     return;
    1228             :   }
    1229           0 :   if ('\0' != text[extra_size - 1])
    1230             :   {
    1231           0 :     GNUNET_break (0);
    1232           0 :     return;
    1233             :   }
    1234           0 :   space = memchr (extra,
    1235             :                   ' ',
    1236             :                   extra_size);
    1237           0 :   if (NULL == space)
    1238             :   {
    1239           0 :     GNUNET_break (0);
    1240           0 :     return;
    1241             :   }
    1242           0 :   if (GNUNET_OK !=
    1243           0 :       GNUNET_STRINGS_string_to_data (extra,
    1244           0 :                                      space - text,
    1245             :                                      &h_wire,
    1246             :                                      sizeof (h_wire)))
    1247             :   {
    1248           0 :     GNUNET_break (0);
    1249           0 :     return;
    1250             :   }
    1251           0 :   exchange_url = &space[1];
    1252           0 :   if (! TALER_is_web_url (exchange_url))
    1253             :   {
    1254           0 :     GNUNET_break (0);
    1255           0 :     return;
    1256             :   }
    1257             : 
    1258           0 :   for (struct Account *a = a_head;
    1259           0 :        NULL != a;
    1260           0 :        a = a->next)
    1261             :   {
    1262           0 :     if (0 !=
    1263           0 :         GNUNET_memcmp (&h_wire,
    1264             :                        &a->h_wire))
    1265           0 :       continue;
    1266           0 :     for (struct Inquiry *i = a->i_head;
    1267           0 :          NULL != i;
    1268           0 :          i = i->next)
    1269             :     {
    1270           0 :       if (0 != strcmp (exchange_url,
    1271           0 :                        i->e->keys->exchange_url))
    1272           0 :         continue;
    1273           0 :       i->kyc_ok = false;
    1274           0 :       i->due = GNUNET_TIME_UNIT_ZERO_ABS;
    1275           0 :       if (NULL != i->task)
    1276             :       {
    1277           0 :         GNUNET_SCHEDULER_cancel (i->task);
    1278           0 :         i->task = NULL;
    1279             :       }
    1280           0 :       if (NULL != i->kyc)
    1281             :       {
    1282           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1283             :                     "/kyc-check already running for %s\n",
    1284             :                     text);
    1285           0 :         return;
    1286             :       }
    1287           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1288             :                   "Starting %skyc-check for `%s' due to KYC rule trigger\n",
    1289             :                   exchange_url,
    1290             :                   i->a->merchant_account_uri.full_payto);
    1291           0 :       i->task = GNUNET_SCHEDULER_add_at (i->due,
    1292             :                                          &inquiry_work,
    1293             :                                          i);
    1294           0 :       return;
    1295             :     }
    1296             :   }
    1297           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1298             :               "KYC rule trigger notification `%s' matches none of our accounts\n",
    1299             :               text);
    1300             : }
    1301             : 
    1302             : 
    1303             : /**
    1304             :  * Function called on each configuration section. Finds sections
    1305             :  * about exchanges, parses the entries.
    1306             :  *
    1307             :  * @param cls NULL
    1308             :  * @param section name of the section
    1309             :  */
    1310             : static void
    1311         506 : accept_exchanges (void *cls,
    1312             :                   const char *section)
    1313             : {
    1314             :   char *url;
    1315             : 
    1316             :   (void) cls;
    1317         506 :   if (0 !=
    1318         506 :       strncasecmp (section,
    1319             :                    "merchant-exchange-",
    1320             :                    strlen ("merchant-exchange-")))
    1321         480 :     return;
    1322          39 :   if (GNUNET_YES ==
    1323          39 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    1324             :                                             section,
    1325             :                                             "DISABLED"))
    1326          13 :     return;
    1327          26 :   if (GNUNET_OK !=
    1328          26 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1329             :                                              section,
    1330             :                                              "EXCHANGE_BASE_URL",
    1331             :                                              &url))
    1332             :   {
    1333           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1334             :                                section,
    1335             :                                "EXCHANGE_BASE_URL");
    1336           0 :     global_ret = EXIT_NOTCONFIGURED;
    1337           0 :     GNUNET_SCHEDULER_shutdown ();
    1338           0 :     return;
    1339             :   }
    1340          26 :   find_keys (url);
    1341          26 :   GNUNET_free (url);
    1342             : }
    1343             : 
    1344             : 
    1345             : /**
    1346             :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
    1347             :  *
    1348             :  * @param cls closure (NULL)
    1349             :  */
    1350             : static void
    1351          13 : shutdown_task (void *cls)
    1352             : {
    1353             :   (void) cls;
    1354          13 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1355             :               "Running shutdown\n");
    1356          35 :   while (NULL != e_head)
    1357             :   {
    1358          22 :     struct Exchange *e = e_head;
    1359             : 
    1360          22 :     if (NULL != e->keys)
    1361             :     {
    1362          22 :       TALER_EXCHANGE_keys_decref (e->keys);
    1363          22 :       e->keys = NULL;
    1364             :     }
    1365          22 :     GNUNET_CONTAINER_DLL_remove (e_head,
    1366             :                                  e_tail,
    1367             :                                  e);
    1368          22 :     GNUNET_free (e);
    1369             :   }
    1370          27 :   while (NULL != a_head)
    1371             :   {
    1372          14 :     struct Account *a = a_head;
    1373             : 
    1374          14 :     stop_inquiries (a);
    1375          14 :     GNUNET_CONTAINER_DLL_remove (a_head,
    1376             :                                  a_tail,
    1377             :                                  a);
    1378          14 :     GNUNET_free (a->merchant_account_uri.full_payto);
    1379          14 :     GNUNET_free (a->instance_id);
    1380          14 :     GNUNET_free (a);
    1381             :   }
    1382          13 :   if (NULL != eh_accounts)
    1383             :   {
    1384          13 :     db_plugin->event_listen_cancel (eh_accounts);
    1385          13 :     eh_accounts = NULL;
    1386             :   }
    1387          13 :   if (NULL != account_task)
    1388             :   {
    1389           0 :     GNUNET_SCHEDULER_cancel (account_task);
    1390           0 :     account_task = NULL;
    1391             :   }
    1392          13 :   if (NULL != eh_keys)
    1393             :   {
    1394          13 :     db_plugin->event_listen_cancel (eh_keys);
    1395          13 :     eh_keys = NULL;
    1396             :   }
    1397          13 :   if (NULL != eh_rule)
    1398             :   {
    1399          13 :     db_plugin->event_listen_cancel (eh_rule);
    1400          13 :     eh_rule = NULL;
    1401             :   }
    1402          13 :   TALER_MERCHANTDB_plugin_unload (db_plugin);
    1403          13 :   db_plugin = NULL;
    1404          13 :   cfg = NULL;
    1405          13 :   if (NULL != ctx)
    1406             :   {
    1407          13 :     GNUNET_CURL_fini (ctx);
    1408          13 :     ctx = NULL;
    1409             :   }
    1410          13 :   if (NULL != rc)
    1411             :   {
    1412          13 :     GNUNET_CURL_gnunet_rc_destroy (rc);
    1413          13 :     rc = NULL;
    1414             :   }
    1415          13 : }
    1416             : 
    1417             : 
    1418             : /**
    1419             :  * First task.
    1420             :  *
    1421             :  * @param cls closure, NULL
    1422             :  * @param args remaining command-line arguments
    1423             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1424             :  * @param c configuration
    1425             :  */
    1426             : static void
    1427          13 : run (void *cls,
    1428             :      char *const *args,
    1429             :      const char *cfgfile,
    1430             :      const struct GNUNET_CONFIGURATION_Handle *c)
    1431             : {
    1432             :   (void) args;
    1433             :   (void) cfgfile;
    1434             : 
    1435          13 :   cfg = c;
    1436          13 :   if (GNUNET_OK !=
    1437          13 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1438             :                                            "merchant-kyccheck",
    1439             :                                            "AML_FREQ",
    1440             :                                            &aml_freq))
    1441             :   {
    1442           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    1443             :                                "merchant-kyccheck",
    1444             :                                "AML_FREQ");
    1445             :     /* use default */
    1446           0 :     aml_freq = AML_FREQ;
    1447             :   }
    1448          13 :   if (GNUNET_OK !=
    1449          13 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1450             :                                            "merchant-kyccheck",
    1451             :                                            "AML_LOW_FREQ",
    1452             :                                            &aml_low_freq))
    1453             :   {
    1454           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    1455             :                                "merchant-kyccheck",
    1456             :                                "AML_LOW_FREQ");
    1457             :     /* use default */
    1458           0 :     aml_low_freq = AML_LOW_FREQ;
    1459             :   }
    1460          13 :   if (GNUNET_TIME_relative_cmp (aml_low_freq,
    1461             :                                 <,
    1462             :                                 aml_freq))
    1463             :   {
    1464           0 :     aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq,
    1465             :                                                   10);
    1466           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1467             :                 "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n",
    1468             :                 GNUNET_TIME_relative2s (aml_low_freq,
    1469             :                                         true));
    1470             :   }
    1471          13 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    1472             :                                  NULL);
    1473          13 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1474             :                           &rc);
    1475          13 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    1476          13 :   if (NULL == ctx)
    1477             :   {
    1478           0 :     GNUNET_break (0);
    1479           0 :     GNUNET_SCHEDULER_shutdown ();
    1480           0 :     global_ret = EXIT_FAILURE;
    1481           0 :     return;
    1482             :   }
    1483          13 :   if (NULL ==
    1484          13 :       (db_plugin = TALER_MERCHANTDB_plugin_load (cfg)))
    1485             :   {
    1486           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1487             :                 "Failed to initialize DB subsystem\n");
    1488           0 :     GNUNET_SCHEDULER_shutdown ();
    1489           0 :     global_ret = EXIT_NOTCONFIGURED;
    1490           0 :     return;
    1491             :   }
    1492          13 :   if (GNUNET_OK !=
    1493          13 :       db_plugin->connect (db_plugin->cls))
    1494             :   {
    1495           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1496             :                 "Failed to connect to database. Consider running taler-merchant-dbinit.\n");
    1497           0 :     GNUNET_SCHEDULER_shutdown ();
    1498           0 :     global_ret = EXIT_FAILURE;
    1499           0 :     return;
    1500             :   }
    1501             :   {
    1502          13 :     struct GNUNET_DB_EventHeaderP es = {
    1503          13 :       .size = htons (sizeof (es)),
    1504          13 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    1505             :     };
    1506             : 
    1507             :     eh_keys
    1508          26 :       = db_plugin->event_listen (db_plugin->cls,
    1509             :                                  &es,
    1510          13 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1511             :                                  &keys_changed,
    1512             :                                  NULL);
    1513             :   }
    1514             :   {
    1515          13 :     struct GNUNET_DB_EventHeaderP es = {
    1516          13 :       .size = htons (sizeof (es)),
    1517          13 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
    1518             :     };
    1519             : 
    1520             :     eh_rule
    1521          26 :       = db_plugin->event_listen (db_plugin->cls,
    1522             :                                  &es,
    1523          13 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1524             :                                  &rule_triggered,
    1525             :                                  NULL);
    1526             :   }
    1527          13 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1528             :                                          &accept_exchanges,
    1529             :                                          NULL);
    1530             :   {
    1531          13 :     struct GNUNET_DB_EventHeaderP es = {
    1532          13 :       .size = htons (sizeof (es)),
    1533          13 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
    1534             :     };
    1535             : 
    1536             :     eh_accounts
    1537          26 :       = db_plugin->event_listen (db_plugin->cls,
    1538             :                                  &es,
    1539          13 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1540             :                                  &account_changed,
    1541             :                                  NULL);
    1542             :   }
    1543          13 :   GNUNET_assert (NULL == account_task);
    1544             :   account_task
    1545          13 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    1546             :                                 NULL);
    1547             : }
    1548             : 
    1549             : 
    1550             : /**
    1551             :  * The main function of taler-merchant-kyccheck
    1552             :  *
    1553             :  * @param argc number of arguments from the command line
    1554             :  * @param argv command line arguments
    1555             :  * @return 0 ok, 1 on error
    1556             :  */
    1557             : int
    1558          13 : main (int argc,
    1559             :       char *const *argv)
    1560             : {
    1561          13 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    1562          13 :     GNUNET_GETOPT_option_timetravel ('T',
    1563             :                                      "timetravel"),
    1564          13 :     GNUNET_GETOPT_option_flag ('t',
    1565             :                                "test",
    1566             :                                "run in test mode and exit when idle",
    1567             :                                &test_mode),
    1568          13 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
    1569             :     GNUNET_GETOPT_OPTION_END
    1570             :   };
    1571             :   enum GNUNET_GenericReturnValue ret;
    1572             : 
    1573          13 :   ret = GNUNET_PROGRAM_run (
    1574             :     TALER_MERCHANT_project_data (),
    1575             :     argc, argv,
    1576             :     "taler-merchant-kyccheck",
    1577             :     gettext_noop (
    1578             :       "background process that checks the KYC state of our bank accounts at various exchanges"),
    1579             :     options,
    1580             :     &run, NULL);
    1581          13 :   if (GNUNET_SYSERR == ret)
    1582           0 :     return EXIT_INVALIDARGUMENT;
    1583          13 :   if (GNUNET_NO == ret)
    1584           0 :     return EXIT_SUCCESS;
    1585          13 :   return global_ret;
    1586             : }
    1587             : 
    1588             : 
    1589             : /* end of taler-merchant-kyccheck.c */

Generated by: LCOV version 1.16