LCOV - code coverage report
Current view: top level - backend - taler-merchant-kyccheck.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 61.0 % 502 306
Test Date: 2025-11-28 21:09:21 Functions: 90.5 % 21 19

            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 others.
     391              :  */
     392              : static void
     393           15 : end_inquiry (void)
     394              : {
     395           15 :   GNUNET_assert (active_inquiries > 0);
     396           15 :   active_inquiries--;
     397           15 :   if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
     398              :        (at_limit) )
     399              :   {
     400            0 :     at_limit = false;
     401            0 :     for (struct Account *a = a_head;
     402            0 :          NULL != a;
     403            0 :          a = a->next)
     404              :     {
     405            0 :       for (struct Inquiry *i = a->i_head;
     406            0 :            NULL != i;
     407            0 :            i = i->next)
     408              :       {
     409            0 :         if (! i->limited)
     410            0 :           continue;
     411            0 :         GNUNET_assert (NULL == i->task);
     412              :         /* done synchronously so that the active_inquiries
     413              :            is updated immediately */
     414            0 :         inquiry_work (i);
     415            0 :         if (at_limit)
     416            0 :           break;
     417              :       }
     418            0 :       if (at_limit)
     419            0 :         break;
     420              :     }
     421              :   }
     422           15 :   if ( (! at_limit) &&
     423           15 :        (0 == active_inquiries) &&
     424              :        (test_mode) )
     425              :   {
     426            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     427              :                 "No more open inquiries and in test mode. Existing.\n");
     428            0 :     GNUNET_SCHEDULER_shutdown ();
     429            0 :     return;
     430              :   }
     431              : }
     432              : 
     433              : 
     434              : /**
     435              :  * Pack the given @a limit into the JSON @a limits array.
     436              :  *
     437              :  * @param limit account limit to pack
     438              :  * @param[in,out] limits JSON array to extend
     439              :  */
     440              : static void
     441            2 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
     442              :             json_t *limits)
     443              : {
     444              :   json_t *jl;
     445              : 
     446            2 :   jl = GNUNET_JSON_PACK (
     447              :     TALER_JSON_pack_kycte ("operation_type",
     448              :                            limit->operation_type),
     449              :     GNUNET_JSON_pack_time_rel ("timeframe",
     450              :                                limit->timeframe),
     451              :     TALER_JSON_pack_amount ("threshold",
     452              :                             &limit->threshold),
     453              :     GNUNET_JSON_pack_bool ("soft_limit",
     454              :                            limit->soft_limit)
     455              :     );
     456            2 :   GNUNET_assert (0 ==
     457              :                  json_array_append_new (limits,
     458              :                                         jl));
     459            2 : }
     460              : 
     461              : 
     462              : /**
     463              :  * Update KYC status for @a i based on
     464              :  * @a account_kyc_status
     465              :  *
     466              :  * @param[in,out] i inquiry context, jlimits is updated
     467              :  * @param account_kyc_status account KYC status details
     468              :  */
     469              : static void
     470            2 : store_kyc_status (
     471              :   struct Inquiry *i,
     472              :   const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
     473              : {
     474              :   json_t *jlimits;
     475              : 
     476            2 :   json_decref (i->jlimits);
     477            2 :   jlimits = json_array ();
     478            2 :   GNUNET_assert (NULL != jlimits);
     479            2 :   i->zero_limited = false;
     480            4 :   for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
     481              :   {
     482            2 :     const struct TALER_EXCHANGE_AccountLimit *limit
     483            2 :       = &account_kyc_status->limits[j];
     484              : 
     485            2 :     pack_limit (limit,
     486              :                 jlimits);
     487            2 :     if (TALER_amount_is_zero (&limit->threshold) &&
     488            2 :         limit->soft_limit &&
     489            2 :         ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) ||
     490            2 :           (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) ||
     491            0 :           (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) )
     492              :     {
     493            2 :       i->zero_limited = true;
     494              :     }
     495              :   }
     496            2 :   i->jlimits = jlimits;
     497            2 :   GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token));
     498            2 :   i->access_token = account_kyc_status->access_token;
     499            2 :   i->auth_ok = true;
     500            2 :   i->aml_review = account_kyc_status->aml_review;
     501            2 :   i->kyc_ok = (MHD_HTTP_OK == i->last_http_status);
     502            2 : }
     503              : 
     504              : 
     505              : /**
     506              :  * Function called with the result of a KYC check.
     507              :  *
     508              :  * @param cls a `struct Inquiry *`
     509              :  * @param ks the account's KYC status details
     510              :  */
     511              : static void
     512           15 : exchange_check_cb (
     513              :   void *cls,
     514              :   const struct TALER_EXCHANGE_KycStatus *ks)
     515              : {
     516           15 :   struct Inquiry *i = cls;
     517           15 :   bool progress = false;
     518              : 
     519           15 :   if (! i->not_first_time)
     520           13 :     progress = true;
     521           15 :   i->kyc = NULL;
     522           15 :   i->last_http_status = ks->hr.http_status;
     523           15 :   i->last_ec = ks->hr.ec;
     524           15 :   i->rule_gen = 0;
     525           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     526              :               "Checking KYC status of `%s' at `%s' is %u\n",
     527              :               i->a->merchant_account_uri.full_payto,
     528              :               i->e->keys->exchange_url,
     529              :               ks->hr.http_status);
     530           15 :   switch (ks->hr.http_status)
     531              :   {
     532            2 :   case MHD_HTTP_OK:
     533            2 :     if (! i->kyc_ok)
     534            1 :       progress = true;
     535            2 :     i->rule_gen = ks->details.ok.rule_gen;
     536            2 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     537              :     /* exchange says KYC is OK, gives status information */
     538            2 :     store_kyc_status (i,
     539              :                       &ks->details.ok);
     540            2 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
     541            2 :     if (i->aml_review || i->zero_limited)
     542              :     {
     543            2 :       if (! progress)
     544            1 :         i->due = GNUNET_TIME_relative_to_absolute (
     545              :           GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_freq,
     546              :                                                            i->backoff)));
     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 (GNUNET_TIME_relative_max (aml_low_freq,
     553              :                                                          i->backoff)));
     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_MINUTES;
     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           12 :   case MHD_HTTP_NO_CONTENT:
     571           12 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     572           12 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
     573              :     /* exchange claims KYC is off! */
     574           12 :     i->kyc_ok = true;
     575           12 :     i->aml_review = false;
     576              :     /* Clear limits, in case exchange had KYC on previously */
     577           12 :     json_decref (i->jlimits);
     578           12 :     i->jlimits = NULL;
     579              :     /* KYC is OK, only check again if triggered */
     580           12 :     i->due = GNUNET_TIME_relative_to_absolute (
     581              :       GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
     582              :                                                        i->backoff)));
     583           12 :     break;
     584            0 :   case MHD_HTTP_FORBIDDEN: /* bad signature */
     585            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     586              :     /* Forbidden => KYC auth must be wrong */
     587            0 :     i->auth_ok = false;
     588              :     /* Start with long-polling */
     589            0 :     if (! progress)
     590            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     591              :                                          i->timeout);
     592            0 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
     593            0 :     break;
     594            1 :   case MHD_HTTP_NOT_FOUND: /* account unknown */
     595            1 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     596              :     /* Account unknown => no KYC auth yet */
     597            1 :     i->auth_ok = false;
     598              :     /* unknown account => wire transfer required! */
     599            1 :     i->kyc_ok = false;
     600              :     /* There should not be any limits yet, but clear them
     601              :        just in case the exchange has amnesia */
     602            1 :     json_decref (i->jlimits);
     603            1 :     i->jlimits = NULL;
     604              :     /* Start immediately with Long-polling */
     605            1 :     if (! progress)
     606            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     607              :                                          i->timeout);
     608            1 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
     609            1 :     break;
     610            0 :   case MHD_HTTP_CONFLICT: /* no account_pub known */
     611            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     612              :     /* Conflict => KYC auth wire transfer missing! */
     613            0 :     i->auth_ok = false;
     614              :     /* Start immediately with Long-polling */
     615            0 :     if (! progress)
     616            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
     617              :                                          i->timeout);
     618            0 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
     619            0 :     break;
     620            0 :   default:
     621            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     622              :                 "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
     623              :                 ks->hr.http_status,
     624              :                 ks->hr.ec);
     625              :     i->backoff
     626            0 :       = GNUNET_TIME_randomized_backoff (i->backoff,
     627              :                                         EXCHANGE_TIMEOUT);
     628            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     629            0 :     i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
     630            0 :     i->auth_ok = false;
     631            0 :     break;
     632              :   }
     633              : 
     634              :   {
     635              :     enum GNUNET_DB_QueryStatus qs;
     636              : 
     637           15 :     qs = db_plugin->account_kyc_set_status (
     638           15 :       db_plugin->cls,
     639           15 :       i->a->instance_id,
     640           15 :       &i->a->h_wire,
     641           15 :       i->e->keys->exchange_url,
     642              :       i->last_kyc_check,
     643              :       i->last_http_status,
     644              :       i->last_ec,
     645              :       i->rule_gen,
     646           15 :       (i->auth_ok)
     647              :       ? &i->access_token
     648              :       : NULL,
     649           15 :       i->jlimits,
     650           15 :       i->aml_review,
     651           15 :       i->kyc_ok);
     652           15 :     if (qs < 0)
     653              :     {
     654            0 :       GNUNET_break (0);
     655            0 :       global_ret = EXIT_FAILURE;
     656            0 :       GNUNET_SCHEDULER_shutdown ();
     657            0 :       return;
     658              :     }
     659           15 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     660              :                 "account_set_kyc_status (%s, %u, %s, %s) returned %d\n",
     661              :                 i->e->keys->exchange_url,
     662              :                 i->last_http_status,
     663              :                 i->auth_ok ? "auth OK" : "auth needed",
     664              :                 NULL == i->jlimits ? "default limits" : "custom limits",
     665              :                 (int) qs);
     666           15 :     i->not_first_time = true;
     667              :   }
     668           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     669              :               "Will repeat inquiry in %s\n",
     670              :               GNUNET_TIME_relative2s (
     671              :                 GNUNET_TIME_absolute_get_remaining (i->due),
     672              :                 true));
     673           15 :   if (! GNUNET_TIME_absolute_is_never (i->due))
     674           15 :     i->task = GNUNET_SCHEDULER_add_at (i->due,
     675              :                                        &inquiry_work,
     676              :                                        i);
     677           15 :   end_inquiry ();
     678              : }
     679              : 
     680              : 
     681              : static void
     682           15 : inquiry_work (void *cls)
     683              : {
     684           15 :   struct Inquiry *i = cls;
     685              :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     686              : 
     687           15 :   i->task = NULL;
     688           15 :   if (! GNUNET_TIME_absolute_is_past (i->due))
     689              :   {
     690            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     691              :                 "Will start inquiry on %s for %s in %s\n",
     692              :                 i->a->merchant_account_uri.full_payto,
     693              :                 i->e->keys->exchange_url,
     694              :                 GNUNET_TIME_relative2s (
     695              :                   GNUNET_TIME_absolute_get_remaining (i->due),
     696              :                   true));
     697              :     i->task
     698            0 :       = GNUNET_SCHEDULER_add_at (i->due,
     699              :                                  &inquiry_work,
     700              :                                  i);
     701            0 :     goto finish;
     702              :   }
     703              : 
     704           15 :   GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
     705           15 :   if (OPEN_INQUIRY_LIMIT <= active_inquiries)
     706              :   {
     707            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     708              :                 "Not looking for work: at limit\n");
     709            0 :     i->limited = true;
     710            0 :     at_limit = true;
     711            0 :     return;
     712              :   }
     713           15 :   at_limit = false;
     714           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     715              :               "Checking KYC status of `%s' at `%s'\n",
     716              :               i->a->merchant_account_uri.full_payto,
     717              :               i->e->keys->exchange_url);
     718              :   i->timeout
     719           15 :     = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
     720           15 :   lpt = TALER_EXCHANGE_KLPT_NONE;
     721           15 :   if (! i->auth_ok)
     722           14 :     lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER;
     723            1 :   else if (! i->kyc_ok)
     724            0 :     lpt = TALER_EXCHANGE_KLPT_KYC_OK;
     725            1 :   else if (i->aml_review)
     726            0 :     lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
     727           15 :   if (! i->not_first_time)
     728           13 :     lpt = TALER_EXCHANGE_KLPT_NONE;
     729           13 :   i->kyc = TALER_EXCHANGE_kyc_check (
     730              :     ctx,
     731           15 :     i->e->keys->exchange_url,
     732           15 :     &i->a->h_payto,
     733           15 :     &i->a->ap,
     734              :     i->rule_gen,
     735              :     lpt,
     736           15 :     i->not_first_time && (! test_mode)
     737            2 :     ? EXCHANGE_TIMEOUT
     738              :     : GNUNET_TIME_UNIT_ZERO,
     739              :     &exchange_check_cb,
     740              :     i);
     741           15 :   if (NULL == i->kyc)
     742              :   {
     743            0 :     GNUNET_break (0);
     744            0 :     i->due = i->timeout;
     745              :     i->task
     746            0 :       = GNUNET_SCHEDULER_add_at (i->due,
     747              :                                  &inquiry_work,
     748              :                                  i);
     749            0 :     goto finish;
     750              :   }
     751           15 :   active_inquiries++;
     752           15 : finish:
     753           15 :   if ( (0 == active_inquiries) &&
     754              :        (test_mode) )
     755              :   {
     756            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     757              :                 "No more open inquiries and in test mode. Existing.\n");
     758            0 :     GNUNET_SCHEDULER_shutdown ();
     759            0 :     return;
     760              :   }
     761              : }
     762              : 
     763              : 
     764              : /**
     765              :  * Check if the account @a could work with exchange that
     766              :  * has keys @a keys.
     767              :  *
     768              :  * @param keys the keys of an exchange
     769              :  * @param a an account
     770              :  */
     771              : static bool
     772           14 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
     773              :              const struct Account *a)
     774              : {
     775              :   struct TALER_NormalizedPayto np;
     776              :   bool ret;
     777              : 
     778           14 :   np = TALER_payto_normalize (a->merchant_account_uri);
     779           14 :   ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
     780              :                                                   true,
     781              :                                                   np);
     782           14 :   GNUNET_free (np.normalized_payto);
     783           14 :   return ret;
     784              : }
     785              : 
     786              : 
     787              : /**
     788              :  * Start the KYC checking for account @a at exchange @a e.
     789              :  *
     790              :  * @param e an exchange
     791              :  * @param a an account
     792              :  */
     793              : static void
     794           13 : start_inquiry (struct Exchange *e,
     795              :                struct Account *a)
     796              : {
     797              :   struct Inquiry *i;
     798              :   enum GNUNET_DB_QueryStatus qs;
     799              : 
     800           13 :   i = GNUNET_new (struct Inquiry);
     801           13 :   i->e = e;
     802           13 :   i->a = a;
     803           13 :   GNUNET_CONTAINER_DLL_insert (a->i_head,
     804              :                                a->i_tail,
     805              :                                i);
     806           13 :   qs = db_plugin->get_kyc_status (db_plugin->cls,
     807              :                                   a->merchant_account_uri,
     808           13 :                                   a->instance_id,
     809           13 :                                   e->keys->exchange_url,
     810              :                                   &i->auth_ok,
     811              :                                   &i->access_token,
     812              :                                   &i->kyc_ok,
     813              :                                   &i->last_http_status,
     814              :                                   &i->last_ec,
     815              :                                   &i->rule_gen,
     816              :                                   &i->last_kyc_check,
     817              :                                   &i->aml_review,
     818              :                                   &i->jlimits);
     819           13 :   if (qs < 0)
     820              :   {
     821            0 :     GNUNET_break (0);
     822            0 :     global_ret = EXIT_FAILURE;
     823            0 :     GNUNET_SCHEDULER_shutdown ();
     824            0 :     return;
     825              :   }
     826           13 :   if (qs > 0)
     827            0 :     i->not_first_time = true;
     828           13 :   switch (i->last_http_status)
     829              :   {
     830            0 :   case MHD_HTTP_OK:
     831              :     /* KYC is OK, but we could have missed some triggers,
     832              :        so let's check, but slowly within the next minute
     833              :        so that we do not overdo it if this process happens
     834              :        to be restarted a lot. */
     835            0 :     if (GNUNET_YES != test_mode)
     836              :     {
     837            0 :       i->due = GNUNET_TIME_relative_to_absolute (
     838              :         GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES));
     839            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     840              :                   "Previous KYC status is OK, randomizing inquiry to start at %s\n",
     841              :                   GNUNET_TIME_absolute2s (i->due));
     842              :     }
     843            0 :     break;
     844            0 :   case MHD_HTTP_ACCEPTED:
     845              :     /* KYC required, due immediately */
     846            0 :     break;
     847            0 :   case MHD_HTTP_NO_CONTENT:
     848              :     /* KYC is OFF, only check again if triggered */
     849            0 :     if (GNUNET_YES != test_mode)
     850              :     {
     851            0 :       i->due = GNUNET_TIME_relative_to_absolute (
     852              :         GNUNET_TIME_randomize (aml_low_freq));
     853            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     854              :                   "KYC was disabled, randomizing inquiry to start at %s\n",
     855              :                   GNUNET_TIME_absolute2s (i->due));
     856              :     }
     857            0 :     break;
     858            0 :   case MHD_HTTP_FORBIDDEN: /* bad signature */
     859              :   case MHD_HTTP_NOT_FOUND: /* account unknown */
     860              :   case MHD_HTTP_CONFLICT: /* no account_pub known */
     861              :     /* go immediately into long-polling */
     862            0 :     break;
     863           13 :   default:
     864              :     /* start with decent back-off after hard failure */
     865           13 :     if (GNUNET_YES != test_mode)
     866              :     {
     867              :       i->backoff
     868           13 :         = GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES);
     869           13 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     870              :                   "Last KYC check failed, starting with backoff %s\n",
     871              :                   GNUNET_TIME_relative2s (i->backoff,
     872              :                                           true));
     873              :     }
     874           13 :     break;
     875              :   }
     876           13 :   inquiry_work (i);
     877              : }
     878              : 
     879              : 
     880              : /**
     881              :  * Stop KYC inquiry @a i.
     882              :  *
     883              :  * @param[in] i the inquiry to stop
     884              :  */
     885              : static void
     886           13 : stop_inquiry (struct Inquiry *i)
     887              : {
     888           13 :   struct Account *a = i->a;
     889              : 
     890           13 :   GNUNET_CONTAINER_DLL_remove (a->i_head,
     891              :                                a->i_tail,
     892              :                                i);
     893           13 :   if (NULL != i->task)
     894              :   {
     895           13 :     GNUNET_SCHEDULER_cancel (i->task);
     896           13 :     i->task = NULL;
     897              :   }
     898           13 :   if (NULL != i->kyc)
     899              :   {
     900            0 :     TALER_EXCHANGE_kyc_check_cancel (i->kyc);
     901            0 :     i->kyc = NULL;
     902              :   }
     903           13 :   if (NULL != i->jlimits)
     904              :   {
     905            1 :     json_decref (i->jlimits);
     906            1 :     i->jlimits = NULL;
     907              :   }
     908           13 :   GNUNET_free (i);
     909           13 : }
     910              : 
     911              : 
     912              : /**
     913              :  * Stop KYC inquiry for account @a at exchange @a e.
     914              :  *
     915              :  * @param e an exchange
     916              :  * @param a an account
     917              :  */
     918              : static void
     919            0 : stop_inquiry_at (struct Exchange *e,
     920              :                  struct Account *a)
     921              : {
     922            0 :   for (struct Inquiry *i = a->i_head;
     923            0 :        NULL != i;
     924            0 :        i = i->next)
     925              :   {
     926            0 :     if (e == i->e)
     927              :     {
     928            0 :       stop_inquiry (i);
     929            0 :       return;
     930              :     }
     931              :   }
     932              :   /* strange, there should have been a match! */
     933            0 :   GNUNET_break (0);
     934              : }
     935              : 
     936              : 
     937              : /**
     938              :  * Start inquries for all exchanges on account @a a.
     939              :  *
     940              :  * @param a an account
     941              :  */
     942              : static void
     943           15 : start_inquiries (struct Account *a)
     944              : {
     945           15 :   for (struct Exchange *e = e_head;
     946           25 :        NULL != e;
     947           10 :        e = e->next)
     948              :   {
     949           10 :     if (is_eligible (e->keys,
     950              :                      a))
     951            9 :       start_inquiry (e,
     952              :                      a);
     953              :     else
     954            1 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     955              :                   "Account %s not eligible at exchange %s\n",
     956              :                   a->merchant_account_uri.full_payto,
     957              :                   e->keys->exchange_url);
     958              :   }
     959           15 : }
     960              : 
     961              : 
     962              : /**
     963              :  * Stop all inquries involving account @a a.
     964              :  *
     965              :  * @param a an account
     966              :  */
     967              : static void
     968           20 : stop_inquiries (struct Account *a)
     969              : {
     970              :   struct Inquiry *i;
     971              : 
     972           33 :   while (NULL != (i = a->i_head))
     973           13 :     stop_inquiry (i);
     974           20 : }
     975              : 
     976              : 
     977              : /**
     978              :  * Callback invoked with information about a bank account.
     979              :  *
     980              :  * @param cls closure
     981              :  * @param merchant_priv private key of the merchant instance
     982              :  * @param ad details about the account
     983              :  */
     984              : static void
     985           40 : account_cb (
     986              :   void *cls,
     987              :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
     988              :   const struct TALER_MERCHANTDB_AccountDetails *ad)
     989              : {
     990           40 :   struct TALER_FullPayto payto_uri = ad->payto_uri;
     991              : 
     992           40 :   if (! ad->active)
     993           25 :     return;
     994           39 :   if (NULL == merchant_priv)
     995            0 :     return; /* instance was deleted */
     996           39 :   for (struct Account *a = a_head;
     997           46 :        NULL != a;
     998            7 :        a = a->next)
     999              :   {
    1000           31 :     if (0 ==
    1001           31 :         TALER_full_payto_cmp (payto_uri,
    1002              :                               a->merchant_account_uri))
    1003              :     {
    1004           24 :       a->account_gen = database_gen;
    1005           24 :       return;
    1006              :     }
    1007              :   }
    1008              :   {
    1009           15 :     struct Account *a = GNUNET_new (struct Account);
    1010              : 
    1011           15 :     a->account_gen = database_gen;
    1012              :     a->merchant_account_uri.full_payto
    1013           15 :       = GNUNET_strdup (ad->payto_uri.full_payto);
    1014              :     a->instance_id
    1015           15 :       = GNUNET_strdup (ad->instance_id);
    1016              :     a->h_wire
    1017           15 :       = ad->h_wire;
    1018              :     a->ap.merchant_priv
    1019           15 :       = *merchant_priv;
    1020           15 :     TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
    1021              :                                          &a->h_payto);
    1022           15 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1023              :                 "Found account %s of instance %s with H_PAYTO %s\n",
    1024              :                 ad->payto_uri.full_payto,
    1025              :                 ad->instance_id,
    1026              :                 GNUNET_sh2s (&a->h_payto.hash));
    1027           15 :     GNUNET_CONTAINER_DLL_insert (a_head,
    1028              :                                  a_tail,
    1029              :                                  a);
    1030           15 :     start_inquiries (a);
    1031              :   }
    1032              : }
    1033              : 
    1034              : 
    1035              : /**
    1036              :  * The set of bank accounts has changed, update our
    1037              :  * list of active inquiries.
    1038              :  *
    1039              :  * @param cls unused
    1040              :  */
    1041              : static void
    1042           53 : find_accounts (void *cls)
    1043              : {
    1044              :   enum GNUNET_DB_QueryStatus qs;
    1045              : 
    1046              :   (void) cls;
    1047           53 :   account_task = NULL;
    1048           53 :   database_gen++;
    1049           53 :   qs = db_plugin->select_accounts (db_plugin->cls,
    1050              :                                    NULL, /* all instances */
    1051              :                                    &account_cb,
    1052              :                                    NULL);
    1053           53 :   if (qs < 0)
    1054              :   {
    1055            0 :     GNUNET_break (0);
    1056            0 :     return;
    1057              :   }
    1058           53 :   for (struct Account *a = a_head;
    1059           95 :        NULL != a;
    1060           42 :        a = a->next)
    1061              :   {
    1062           42 :     if (a->account_gen < database_gen)
    1063            5 :       stop_inquiries (a);
    1064              :   }
    1065              : }
    1066              : 
    1067              : 
    1068              : /**
    1069              :  * Function called when transfers are added to the merchant database.  We look
    1070              :  * for more work.
    1071              :  *
    1072              :  * @param cls closure (NULL)
    1073              :  * @param extra additional event data provided
    1074              :  * @param extra_size number of bytes in @a extra
    1075              :  */
    1076              : static void
    1077           39 : account_changed (void *cls,
    1078              :                  const void *extra,
    1079              :                  size_t extra_size)
    1080              : {
    1081              :   (void) cls;
    1082              :   (void) extra;
    1083              :   (void) extra_size;
    1084           39 :   if (NULL != account_task)
    1085            0 :     return;
    1086              :   account_task
    1087           39 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    1088              :                                 NULL);
    1089              : }
    1090              : 
    1091              : 
    1092              : /**
    1093              :  * Interact with the database to get the current set
    1094              :  * of exchange keys known to us.
    1095              :  *
    1096              :  * @param exchange_url the exchange URL to check
    1097              :  */
    1098              : static void
    1099           24 : find_keys (const char *exchange_url)
    1100              : {
    1101              :   enum GNUNET_DB_QueryStatus qs;
    1102              :   struct TALER_EXCHANGE_Keys *keys;
    1103              :   struct Exchange *e;
    1104              :   struct GNUNET_TIME_Absolute first_retry;
    1105              : 
    1106           24 :   qs = db_plugin->select_exchange_keys (db_plugin->cls,
    1107              :                                         exchange_url,
    1108              :                                         &first_retry,
    1109              :                                         &keys);
    1110           24 :   if (qs < 0)
    1111              :   {
    1112            0 :     GNUNET_break (0);
    1113           14 :     return;
    1114              :   }
    1115           24 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1116              :   {
    1117           14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1118              :                 "No %s/keys yet!\n",
    1119              :                 exchange_url);
    1120           14 :     return;
    1121              :   }
    1122           10 :   for (e = e_head; NULL != e; e = e->next)
    1123              :   {
    1124            0 :     if (0 == strcmp (e->keys->exchange_url,
    1125            0 :                      keys->exchange_url))
    1126              :     {
    1127            0 :       struct TALER_EXCHANGE_Keys *old_keys = e->keys;
    1128              : 
    1129            0 :       e->keys = keys;
    1130            0 :       for (struct Account *a = a_head;
    1131            0 :            NULL != a;
    1132            0 :            a = a->next)
    1133              :       {
    1134            0 :         bool was_eligible = is_eligible (old_keys,
    1135              :                                          a);
    1136            0 :         bool now_eligible = is_eligible (keys,
    1137              :                                          a);
    1138              : 
    1139            0 :         if (was_eligible == now_eligible)
    1140            0 :           continue; /* no change, do nothing */
    1141            0 :         if (was_eligible)
    1142            0 :           stop_inquiry_at (e,
    1143              :                            a);
    1144              :         else /* is_eligible */
    1145            0 :           start_inquiry (e,
    1146              :                          a);
    1147              :       }
    1148            0 :       TALER_EXCHANGE_keys_decref (old_keys);
    1149            0 :       return;
    1150              :     }
    1151              :   }
    1152           10 :   e = GNUNET_new (struct Exchange);
    1153           10 :   e->keys = keys;
    1154           10 :   GNUNET_CONTAINER_DLL_insert (e_head,
    1155              :                                e_tail,
    1156              :                                e);
    1157           10 :   for (struct Account *a = a_head;
    1158           14 :        NULL != a;
    1159            4 :        a = a->next)
    1160              :   {
    1161            8 :     if ( (a->account_gen == database_gen) &&
    1162            4 :          (is_eligible (e->keys,
    1163              :                        a)) )
    1164            4 :       start_inquiry (e,
    1165              :                      a);
    1166              :   }
    1167              : }
    1168              : 
    1169              : 
    1170              : /**
    1171              :  * Function called when keys were changed in the
    1172              :  * merchant database. Updates ours.
    1173              :  *
    1174              :  * @param cls closure (NULL)
    1175              :  * @param extra additional event data provided
    1176              :  * @param extra_size number of bytes in @a extra
    1177              :  */
    1178              : static void
    1179           10 : keys_changed (void *cls,
    1180              :               const void *extra,
    1181              :               size_t extra_size)
    1182              : {
    1183           10 :   const char *url = extra;
    1184              : 
    1185              :   (void) cls;
    1186           10 :   if ( (NULL == extra) ||
    1187              :        (0 == extra_size) )
    1188              :   {
    1189            0 :     GNUNET_break (0);
    1190            0 :     return;
    1191              :   }
    1192           10 :   if ('\0' != url[extra_size - 1])
    1193              :   {
    1194            0 :     GNUNET_break (0);
    1195            0 :     return;
    1196              :   }
    1197           10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1198              :               "Received keys change notification: reload `%s'\n",
    1199              :               url);
    1200           10 :   find_keys (url);
    1201              : }
    1202              : 
    1203              : 
    1204              : /**
    1205              :  * Function called when a KYC rule was triggered by
    1206              :  * a transaction and we need to get the latest KYC
    1207              :  * status immediately.
    1208              :  *
    1209              :  * @param cls closure (NULL)
    1210              :  * @param extra additional event data provided
    1211              :  * @param extra_size number of bytes in @a extra
    1212              :  */
    1213              : static void
    1214            0 : rule_triggered (void *cls,
    1215              :                 const void *extra,
    1216              :                 size_t extra_size)
    1217              : {
    1218            0 :   const char *text = extra;
    1219              :   const char *space;
    1220              :   struct TALER_MerchantWireHashP h_wire;
    1221              :   const char *exchange_url;
    1222              : 
    1223              :   (void) cls;
    1224            0 :   if ( (NULL == extra) ||
    1225              :        (0 == extra_size) )
    1226              :   {
    1227            0 :     GNUNET_break (0);
    1228            0 :     return;
    1229              :   }
    1230            0 :   if ('\0' != text[extra_size - 1])
    1231              :   {
    1232            0 :     GNUNET_break (0);
    1233            0 :     return;
    1234              :   }
    1235            0 :   space = memchr (extra,
    1236              :                   ' ',
    1237              :                   extra_size);
    1238            0 :   if (NULL == space)
    1239              :   {
    1240            0 :     GNUNET_break (0);
    1241            0 :     return;
    1242              :   }
    1243            0 :   if (GNUNET_OK !=
    1244            0 :       GNUNET_STRINGS_string_to_data (extra,
    1245            0 :                                      space - text,
    1246              :                                      &h_wire,
    1247              :                                      sizeof (h_wire)))
    1248              :   {
    1249            0 :     GNUNET_break (0);
    1250            0 :     return;
    1251              :   }
    1252            0 :   exchange_url = &space[1];
    1253            0 :   if (! TALER_is_web_url (exchange_url))
    1254              :   {
    1255            0 :     GNUNET_break (0);
    1256            0 :     return;
    1257              :   }
    1258              : 
    1259            0 :   for (struct Account *a = a_head;
    1260            0 :        NULL != a;
    1261            0 :        a = a->next)
    1262              :   {
    1263            0 :     if (0 !=
    1264            0 :         GNUNET_memcmp (&h_wire,
    1265              :                        &a->h_wire))
    1266            0 :       continue;
    1267            0 :     for (struct Inquiry *i = a->i_head;
    1268            0 :          NULL != i;
    1269            0 :          i = i->next)
    1270              :     {
    1271            0 :       if (0 != strcmp (exchange_url,
    1272            0 :                        i->e->keys->exchange_url))
    1273            0 :         continue;
    1274            0 :       i->kyc_ok = false;
    1275            0 :       i->due = GNUNET_TIME_UNIT_ZERO_ABS;
    1276            0 :       if (NULL != i->task)
    1277              :       {
    1278            0 :         GNUNET_SCHEDULER_cancel (i->task);
    1279            0 :         i->task = NULL;
    1280              :       }
    1281            0 :       if (NULL != i->kyc)
    1282              :       {
    1283            0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1284              :                     "/kyc-check already running for %s\n",
    1285              :                     text);
    1286            0 :         return;
    1287              :       }
    1288            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1289              :                   "Starting %skyc-check for `%s' due to KYC rule trigger\n",
    1290              :                   exchange_url,
    1291              :                   i->a->merchant_account_uri.full_payto);
    1292            0 :       i->task = GNUNET_SCHEDULER_add_at (i->due,
    1293              :                                          &inquiry_work,
    1294              :                                          i);
    1295            0 :       return;
    1296              :     }
    1297              :   }
    1298            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1299              :               "KYC rule trigger notification `%s' matches none of our accounts\n",
    1300              :               text);
    1301              : }
    1302              : 
    1303              : 
    1304              : /**
    1305              :  * Function called on each configuration section. Finds sections
    1306              :  * about exchanges, parses the entries.
    1307              :  *
    1308              :  * @param cls NULL
    1309              :  * @param section name of the section
    1310              :  */
    1311              : static void
    1312          564 : accept_exchanges (void *cls,
    1313              :                   const char *section)
    1314              : {
    1315              :   char *url;
    1316              : 
    1317              :   (void) cls;
    1318          564 :   if (0 !=
    1319          564 :       strncasecmp (section,
    1320              :                    "merchant-exchange-",
    1321              :                    strlen ("merchant-exchange-")))
    1322          550 :     return;
    1323           42 :   if (GNUNET_YES ==
    1324           42 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    1325              :                                             section,
    1326              :                                             "DISABLED"))
    1327           28 :     return;
    1328           14 :   if (GNUNET_OK !=
    1329           14 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1330              :                                              section,
    1331              :                                              "EXCHANGE_BASE_URL",
    1332              :                                              &url))
    1333              :   {
    1334            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1335              :                                section,
    1336              :                                "EXCHANGE_BASE_URL");
    1337            0 :     global_ret = EXIT_NOTCONFIGURED;
    1338            0 :     GNUNET_SCHEDULER_shutdown ();
    1339            0 :     return;
    1340              :   }
    1341           14 :   find_keys (url);
    1342           14 :   GNUNET_free (url);
    1343              : }
    1344              : 
    1345              : 
    1346              : /**
    1347              :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
    1348              :  *
    1349              :  * @param cls closure (NULL)
    1350              :  */
    1351              : static void
    1352           14 : shutdown_task (void *cls)
    1353              : {
    1354              :   (void) cls;
    1355           14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1356              :               "Running shutdown\n");
    1357           24 :   while (NULL != e_head)
    1358              :   {
    1359           10 :     struct Exchange *e = e_head;
    1360              : 
    1361           10 :     if (NULL != e->keys)
    1362              :     {
    1363           10 :       TALER_EXCHANGE_keys_decref (e->keys);
    1364           10 :       e->keys = NULL;
    1365              :     }
    1366           10 :     GNUNET_CONTAINER_DLL_remove (e_head,
    1367              :                                  e_tail,
    1368              :                                  e);
    1369           10 :     GNUNET_free (e);
    1370              :   }
    1371           29 :   while (NULL != a_head)
    1372              :   {
    1373           15 :     struct Account *a = a_head;
    1374              : 
    1375           15 :     stop_inquiries (a);
    1376           15 :     GNUNET_CONTAINER_DLL_remove (a_head,
    1377              :                                  a_tail,
    1378              :                                  a);
    1379           15 :     GNUNET_free (a->merchant_account_uri.full_payto);
    1380           15 :     GNUNET_free (a->instance_id);
    1381           15 :     GNUNET_free (a);
    1382              :   }
    1383           14 :   if (NULL != eh_accounts)
    1384              :   {
    1385           14 :     db_plugin->event_listen_cancel (eh_accounts);
    1386           14 :     eh_accounts = NULL;
    1387              :   }
    1388           14 :   if (NULL != account_task)
    1389              :   {
    1390            0 :     GNUNET_SCHEDULER_cancel (account_task);
    1391            0 :     account_task = NULL;
    1392              :   }
    1393           14 :   if (NULL != eh_keys)
    1394              :   {
    1395           14 :     db_plugin->event_listen_cancel (eh_keys);
    1396           14 :     eh_keys = NULL;
    1397              :   }
    1398           14 :   if (NULL != eh_rule)
    1399              :   {
    1400           14 :     db_plugin->event_listen_cancel (eh_rule);
    1401           14 :     eh_rule = NULL;
    1402              :   }
    1403           14 :   TALER_MERCHANTDB_plugin_unload (db_plugin);
    1404           14 :   db_plugin = NULL;
    1405           14 :   cfg = NULL;
    1406           14 :   if (NULL != ctx)
    1407              :   {
    1408           14 :     GNUNET_CURL_fini (ctx);
    1409           14 :     ctx = NULL;
    1410              :   }
    1411           14 :   if (NULL != rc)
    1412              :   {
    1413           14 :     GNUNET_CURL_gnunet_rc_destroy (rc);
    1414           14 :     rc = NULL;
    1415              :   }
    1416           14 : }
    1417              : 
    1418              : 
    1419              : /**
    1420              :  * First task.
    1421              :  *
    1422              :  * @param cls closure, NULL
    1423              :  * @param args remaining command-line arguments
    1424              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1425              :  * @param c configuration
    1426              :  */
    1427              : static void
    1428           14 : run (void *cls,
    1429              :      char *const *args,
    1430              :      const char *cfgfile,
    1431              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1432              : {
    1433              :   (void) args;
    1434              :   (void) cfgfile;
    1435              : 
    1436           14 :   cfg = c;
    1437           14 :   if (GNUNET_OK !=
    1438           14 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1439              :                                            "merchant-kyccheck",
    1440              :                                            "AML_FREQ",
    1441              :                                            &aml_freq))
    1442              :   {
    1443            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    1444              :                                "merchant-kyccheck",
    1445              :                                "AML_FREQ");
    1446              :     /* use default */
    1447            0 :     aml_freq = AML_FREQ;
    1448              :   }
    1449           14 :   if (GNUNET_OK !=
    1450           14 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1451              :                                            "merchant-kyccheck",
    1452              :                                            "AML_LOW_FREQ",
    1453              :                                            &aml_low_freq))
    1454              :   {
    1455            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    1456              :                                "merchant-kyccheck",
    1457              :                                "AML_LOW_FREQ");
    1458              :     /* use default */
    1459            0 :     aml_low_freq = AML_LOW_FREQ;
    1460              :   }
    1461           14 :   if (GNUNET_TIME_relative_cmp (aml_low_freq,
    1462              :                                 <,
    1463              :                                 aml_freq))
    1464              :   {
    1465            0 :     aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq,
    1466              :                                                   10);
    1467            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1468              :                 "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n",
    1469              :                 GNUNET_TIME_relative2s (aml_low_freq,
    1470              :                                         true));
    1471              :   }
    1472           14 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    1473              :                                  NULL);
    1474           14 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1475              :                           &rc);
    1476           14 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    1477           14 :   if (NULL == ctx)
    1478              :   {
    1479            0 :     GNUNET_break (0);
    1480            0 :     GNUNET_SCHEDULER_shutdown ();
    1481            0 :     global_ret = EXIT_FAILURE;
    1482            0 :     return;
    1483              :   }
    1484           14 :   if (NULL ==
    1485           14 :       (db_plugin = TALER_MERCHANTDB_plugin_load (cfg)))
    1486              :   {
    1487            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1488              :                 "Failed to initialize DB subsystem\n");
    1489            0 :     GNUNET_SCHEDULER_shutdown ();
    1490            0 :     global_ret = EXIT_NOTCONFIGURED;
    1491            0 :     return;
    1492              :   }
    1493           14 :   if (GNUNET_OK !=
    1494           14 :       db_plugin->connect (db_plugin->cls))
    1495              :   {
    1496            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1497              :                 "Failed to connect to database. Consider running taler-merchant-dbinit.\n");
    1498            0 :     GNUNET_SCHEDULER_shutdown ();
    1499            0 :     global_ret = EXIT_FAILURE;
    1500            0 :     return;
    1501              :   }
    1502              :   {
    1503           14 :     struct GNUNET_DB_EventHeaderP es = {
    1504           14 :       .size = htons (sizeof (es)),
    1505           14 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    1506              :     };
    1507              : 
    1508              :     eh_keys
    1509           28 :       = db_plugin->event_listen (db_plugin->cls,
    1510              :                                  &es,
    1511           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1512              :                                  &keys_changed,
    1513              :                                  NULL);
    1514              :   }
    1515              :   {
    1516           14 :     struct GNUNET_DB_EventHeaderP es = {
    1517           14 :       .size = htons (sizeof (es)),
    1518           14 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
    1519              :     };
    1520              : 
    1521              :     eh_rule
    1522           28 :       = db_plugin->event_listen (db_plugin->cls,
    1523              :                                  &es,
    1524           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1525              :                                  &rule_triggered,
    1526              :                                  NULL);
    1527              :   }
    1528           14 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1529              :                                          &accept_exchanges,
    1530              :                                          NULL);
    1531              :   {
    1532           14 :     struct GNUNET_DB_EventHeaderP es = {
    1533           14 :       .size = htons (sizeof (es)),
    1534           14 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
    1535              :     };
    1536              : 
    1537              :     eh_accounts
    1538           28 :       = db_plugin->event_listen (db_plugin->cls,
    1539              :                                  &es,
    1540           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1541              :                                  &account_changed,
    1542              :                                  NULL);
    1543              :   }
    1544           14 :   GNUNET_assert (NULL == account_task);
    1545              :   account_task
    1546           14 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    1547              :                                 NULL);
    1548              : }
    1549              : 
    1550              : 
    1551              : /**
    1552              :  * The main function of taler-merchant-kyccheck
    1553              :  *
    1554              :  * @param argc number of arguments from the command line
    1555              :  * @param argv command line arguments
    1556              :  * @return 0 ok, 1 on error
    1557              :  */
    1558              : int
    1559           14 : main (int argc,
    1560              :       char *const *argv)
    1561              : {
    1562           14 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    1563           14 :     GNUNET_GETOPT_option_timetravel ('T',
    1564              :                                      "timetravel"),
    1565           14 :     GNUNET_GETOPT_option_flag ('t',
    1566              :                                "test",
    1567              :                                "run in test mode and exit when idle",
    1568              :                                &test_mode),
    1569           14 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
    1570              :     GNUNET_GETOPT_OPTION_END
    1571              :   };
    1572              :   enum GNUNET_GenericReturnValue ret;
    1573              : 
    1574           14 :   ret = GNUNET_PROGRAM_run (
    1575              :     TALER_MERCHANT_project_data (),
    1576              :     argc, argv,
    1577              :     "taler-merchant-kyccheck",
    1578              :     gettext_noop (
    1579              :       "background process that checks the KYC state of our bank accounts at various exchanges"),
    1580              :     options,
    1581              :     &run, NULL);
    1582           14 :   if (GNUNET_SYSERR == ret)
    1583            0 :     return EXIT_INVALIDARGUMENT;
    1584           14 :   if (GNUNET_NO == ret)
    1585            0 :     return EXIT_SUCCESS;
    1586           14 :   return global_ret;
    1587              : }
    1588              : 
    1589              : 
    1590              : /* end of taler-merchant-kyccheck.c */
        

Generated by: LCOV version 2.0-1