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-06 19:31:41 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
     391              :  * others.
     392              :  */
     393              : static void
     394           15 : end_inquiry (void)
     395              : {
     396           15 :   GNUNET_assert (active_inquiries > 0);
     397           15 :   active_inquiries--;
     398           15 :   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           15 :   if ( (! at_limit) &&
     424           15 :        (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           15 : exchange_check_cb (
     514              :   void *cls,
     515              :   const struct TALER_EXCHANGE_KycStatus *ks)
     516              : {
     517           15 :   struct Inquiry *i = cls;
     518           15 :   bool progress = false;
     519              : 
     520           15 :   if (! i->not_first_time)
     521           13 :     progress = true;
     522           15 :   i->kyc = NULL;
     523           15 :   i->last_http_status = ks->hr.http_status;
     524           15 :   i->last_ec = ks->hr.ec;
     525           15 :   i->rule_gen = 0;
     526           15 :   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           15 :   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           12 :   case MHD_HTTP_NO_CONTENT:
     571           12 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
     572           12 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
     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 (aml_low_freq));
     582           12 :     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           15 :     qs = db_plugin->account_kyc_set_status (
     637           15 :       db_plugin->cls,
     638           15 :       i->a->instance_id,
     639           15 :       &i->a->h_wire,
     640           15 :       i->e->keys->exchange_url,
     641              :       i->last_kyc_check,
     642              :       i->last_http_status,
     643              :       i->last_ec,
     644              :       i->rule_gen,
     645           15 :       (i->auth_ok)
     646              :       ? &i->access_token
     647              :       : NULL,
     648           15 :       i->jlimits,
     649           15 :       i->aml_review,
     650           15 :       i->kyc_ok);
     651           15 :     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           15 :     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           15 :     i->not_first_time = true;
     666              :   }
     667           15 :   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           15 :   if (! GNUNET_TIME_absolute_is_never (i->due))
     673           15 :     i->task = GNUNET_SCHEDULER_add_at (i->due,
     674              :                                        &inquiry_work,
     675              :                                        i);
     676           15 :   end_inquiry ();
     677              : }
     678              : 
     679              : 
     680              : static void
     681           15 : inquiry_work (void *cls)
     682              : {
     683           15 :   struct Inquiry *i = cls;
     684              :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     685              : 
     686           15 :   i->task = NULL;
     687           15 :   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           15 :   GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
     704           15 :   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           15 :   at_limit = false;
     713           15 :   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           15 :     = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
     719           15 :   lpt = TALER_EXCHANGE_KLPT_NONE;
     720           15 :   if (! i->auth_ok)
     721           14 :     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           15 :   if (! i->not_first_time)
     727           13 :     lpt = TALER_EXCHANGE_KLPT_NONE;
     728           13 :   i->kyc = TALER_EXCHANGE_kyc_check (
     729              :     ctx,
     730           15 :     i->e->keys->exchange_url,
     731           15 :     &i->a->h_payto,
     732           15 :     &i->a->ap,
     733              :     i->rule_gen,
     734              :     lpt,
     735           15 :     i->not_first_time && (! test_mode)
     736            2 :     ? EXCHANGE_TIMEOUT
     737              :     : GNUNET_TIME_UNIT_ZERO,
     738              :     &exchange_check_cb,
     739              :     i);
     740           15 :   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           15 :   active_inquiries++;
     751           15 : finish:
     752           15 :   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           14 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
     772              :              const struct Account *a)
     773              : {
     774              :   struct TALER_NormalizedPayto np;
     775              :   bool ret;
     776              : 
     777           14 :   np = TALER_payto_normalize (a->merchant_account_uri);
     778           14 :   ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
     779              :                                                   true,
     780              :                                                   np);
     781           14 :   GNUNET_free (np.normalized_payto);
     782           14 :   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           13 : start_inquiry (struct Exchange *e,
     794              :                struct Account *a)
     795              : {
     796              :   struct Inquiry *i;
     797              :   enum GNUNET_DB_QueryStatus qs;
     798              : 
     799           13 :   i = GNUNET_new (struct Inquiry);
     800           13 :   i->e = e;
     801           13 :   i->a = a;
     802           13 :   GNUNET_CONTAINER_DLL_insert (a->i_head,
     803              :                                a->i_tail,
     804              :                                i);
     805           13 :   qs = db_plugin->get_kyc_status (db_plugin->cls,
     806              :                                   a->merchant_account_uri,
     807           13 :                                   a->instance_id,
     808           13 :                                   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           13 :   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           13 :   if (qs > 0)
     826            0 :     i->not_first_time = true;
     827           13 :   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           13 :   default:
     863              :     /* start with decent back-off after hard failure */
     864           13 :     if (GNUNET_YES != test_mode)
     865              :     {
     866              :       i->backoff
     867           13 :         = GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES);
     868           13 :       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           13 :     break;
     874              :   }
     875           13 :   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           13 : stop_inquiry (struct Inquiry *i)
     886              : {
     887           13 :   struct Account *a = i->a;
     888              : 
     889           13 :   GNUNET_CONTAINER_DLL_remove (a->i_head,
     890              :                                a->i_tail,
     891              :                                i);
     892           13 :   if (NULL != i->task)
     893              :   {
     894           13 :     GNUNET_SCHEDULER_cancel (i->task);
     895           13 :     i->task = NULL;
     896              :   }
     897           13 :   if (NULL != i->kyc)
     898              :   {
     899            0 :     TALER_EXCHANGE_kyc_check_cancel (i->kyc);
     900            0 :     i->kyc = NULL;
     901              :   }
     902           13 :   if (NULL != i->jlimits)
     903              :   {
     904            1 :     json_decref (i->jlimits);
     905            1 :     i->jlimits = NULL;
     906              :   }
     907           13 :   GNUNET_free (i);
     908           13 : }
     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           15 : start_inquiries (struct Account *a)
     943              : {
     944           15 :   for (struct Exchange *e = e_head;
     945           25 :        NULL != e;
     946           10 :        e = e->next)
     947              :   {
     948           10 :     if (is_eligible (e->keys,
     949              :                      a))
     950            9 :       start_inquiry (e,
     951              :                      a);
     952              :     else
     953            1 :       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           15 : }
     959              : 
     960              : 
     961              : /**
     962              :  * Stop all inquries involving account @a a.
     963              :  *
     964              :  * @param a an account
     965              :  */
     966              : static void
     967           20 : stop_inquiries (struct Account *a)
     968              : {
     969              :   struct Inquiry *i;
     970              : 
     971           33 :   while (NULL != (i = a->i_head))
     972           13 :     stop_inquiry (i);
     973           20 : }
     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           40 : account_cb (
     985              :   void *cls,
     986              :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
     987              :   const struct TALER_MERCHANTDB_AccountDetails *ad)
     988              : {
     989           40 :   struct TALER_FullPayto payto_uri = ad->payto_uri;
     990              : 
     991           40 :   if (! ad->active)
     992           25 :     return;
     993           39 :   if (NULL == merchant_priv)
     994            0 :     return; /* instance was deleted */
     995           39 :   for (struct Account *a = a_head;
     996           46 :        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           15 :     struct Account *a = GNUNET_new (struct Account);
    1009              : 
    1010           15 :     a->account_gen = database_gen;
    1011              :     a->merchant_account_uri.full_payto
    1012           15 :       = GNUNET_strdup (ad->payto_uri.full_payto);
    1013              :     a->instance_id
    1014           15 :       = GNUNET_strdup (ad->instance_id);
    1015              :     a->h_wire
    1016           15 :       = ad->h_wire;
    1017              :     a->ap.merchant_priv
    1018           15 :       = *merchant_priv;
    1019           15 :     TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
    1020              :                                          &a->h_payto);
    1021           15 :     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           15 :     GNUNET_CONTAINER_DLL_insert (a_head,
    1027              :                                  a_tail,
    1028              :                                  a);
    1029           15 :     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           53 : find_accounts (void *cls)
    1042              : {
    1043              :   enum GNUNET_DB_QueryStatus qs;
    1044              : 
    1045              :   (void) cls;
    1046           53 :   account_task = NULL;
    1047           53 :   database_gen++;
    1048           53 :   qs = db_plugin->select_accounts (db_plugin->cls,
    1049              :                                    NULL, /* all instances */
    1050              :                                    &account_cb,
    1051              :                                    NULL);
    1052           53 :   if (qs < 0)
    1053              :   {
    1054            0 :     GNUNET_break (0);
    1055            0 :     return;
    1056              :   }
    1057           53 :   for (struct Account *a = a_head;
    1058           95 :        NULL != a;
    1059           42 :        a = a->next)
    1060              :   {
    1061           42 :     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           39 : 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           39 :   if (NULL != account_task)
    1084            0 :     return;
    1085              :   account_task
    1086           39 :     = 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           24 : 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           24 :   qs = db_plugin->select_exchange_keys (db_plugin->cls,
    1106              :                                         exchange_url,
    1107              :                                         &first_retry,
    1108              :                                         &keys);
    1109           24 :   if (qs < 0)
    1110              :   {
    1111            0 :     GNUNET_break (0);
    1112           14 :     return;
    1113              :   }
    1114           24 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1115              :   {
    1116           14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1117              :                 "No %s/keys yet!\n",
    1118              :                 exchange_url);
    1119           14 :     return;
    1120              :   }
    1121           10 :   for (e = e_head; NULL != e; e = e->next)
    1122              :   {
    1123            0 :     if (0 == strcmp (e->keys->exchange_url,
    1124            0 :                      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           10 :   e = GNUNET_new (struct Exchange);
    1152           10 :   e->keys = keys;
    1153           10 :   GNUNET_CONTAINER_DLL_insert (e_head,
    1154              :                                e_tail,
    1155              :                                e);
    1156           10 :   for (struct Account *a = a_head;
    1157           14 :        NULL != a;
    1158            4 :        a = a->next)
    1159              :   {
    1160            8 :     if ( (a->account_gen == database_gen) &&
    1161            4 :          (is_eligible (e->keys,
    1162              :                        a)) )
    1163            4 :       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           10 : keys_changed (void *cls,
    1179              :               const void *extra,
    1180              :               size_t extra_size)
    1181              : {
    1182           10 :   const char *url = extra;
    1183              : 
    1184              :   (void) cls;
    1185           10 :   if ( (NULL == extra) ||
    1186              :        (0 == extra_size) )
    1187              :   {
    1188            0 :     GNUNET_break (0);
    1189            0 :     return;
    1190              :   }
    1191           10 :   if ('\0' != url[extra_size - 1])
    1192              :   {
    1193            0 :     GNUNET_break (0);
    1194            0 :     return;
    1195              :   }
    1196           10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1197              :               "Received keys change notification: reload `%s'\n",
    1198              :               url);
    1199           10 :   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          564 : accept_exchanges (void *cls,
    1312              :                   const char *section)
    1313              : {
    1314              :   char *url;
    1315              : 
    1316              :   (void) cls;
    1317          564 :   if (0 !=
    1318          564 :       strncasecmp (section,
    1319              :                    "merchant-exchange-",
    1320              :                    strlen ("merchant-exchange-")))
    1321          550 :     return;
    1322           42 :   if (GNUNET_YES ==
    1323           42 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    1324              :                                             section,
    1325              :                                             "DISABLED"))
    1326           28 :     return;
    1327           14 :   if (GNUNET_OK !=
    1328           14 :       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           14 :   find_keys (url);
    1341           14 :   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           14 : shutdown_task (void *cls)
    1352              : {
    1353              :   (void) cls;
    1354           14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1355              :               "Running shutdown\n");
    1356           24 :   while (NULL != e_head)
    1357              :   {
    1358           10 :     struct Exchange *e = e_head;
    1359              : 
    1360           10 :     if (NULL != e->keys)
    1361              :     {
    1362           10 :       TALER_EXCHANGE_keys_decref (e->keys);
    1363           10 :       e->keys = NULL;
    1364              :     }
    1365           10 :     GNUNET_CONTAINER_DLL_remove (e_head,
    1366              :                                  e_tail,
    1367              :                                  e);
    1368           10 :     GNUNET_free (e);
    1369              :   }
    1370           29 :   while (NULL != a_head)
    1371              :   {
    1372           15 :     struct Account *a = a_head;
    1373              : 
    1374           15 :     stop_inquiries (a);
    1375           15 :     GNUNET_CONTAINER_DLL_remove (a_head,
    1376              :                                  a_tail,
    1377              :                                  a);
    1378           15 :     GNUNET_free (a->merchant_account_uri.full_payto);
    1379           15 :     GNUNET_free (a->instance_id);
    1380           15 :     GNUNET_free (a);
    1381              :   }
    1382           14 :   if (NULL != eh_accounts)
    1383              :   {
    1384           14 :     db_plugin->event_listen_cancel (eh_accounts);
    1385           14 :     eh_accounts = NULL;
    1386              :   }
    1387           14 :   if (NULL != account_task)
    1388              :   {
    1389            0 :     GNUNET_SCHEDULER_cancel (account_task);
    1390            0 :     account_task = NULL;
    1391              :   }
    1392           14 :   if (NULL != eh_keys)
    1393              :   {
    1394           14 :     db_plugin->event_listen_cancel (eh_keys);
    1395           14 :     eh_keys = NULL;
    1396              :   }
    1397           14 :   if (NULL != eh_rule)
    1398              :   {
    1399           14 :     db_plugin->event_listen_cancel (eh_rule);
    1400           14 :     eh_rule = NULL;
    1401              :   }
    1402           14 :   TALER_MERCHANTDB_plugin_unload (db_plugin);
    1403           14 :   db_plugin = NULL;
    1404           14 :   cfg = NULL;
    1405           14 :   if (NULL != ctx)
    1406              :   {
    1407           14 :     GNUNET_CURL_fini (ctx);
    1408           14 :     ctx = NULL;
    1409              :   }
    1410           14 :   if (NULL != rc)
    1411              :   {
    1412           14 :     GNUNET_CURL_gnunet_rc_destroy (rc);
    1413           14 :     rc = NULL;
    1414              :   }
    1415           14 : }
    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           14 : 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           14 :   cfg = c;
    1436           14 :   if (GNUNET_OK !=
    1437           14 :       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           14 :   if (GNUNET_OK !=
    1449           14 :       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           14 :   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           14 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    1472              :                                  NULL);
    1473           14 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1474              :                           &rc);
    1475           14 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    1476           14 :   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           14 :   if (NULL ==
    1484           14 :       (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           14 :   if (GNUNET_OK !=
    1493           14 :       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           14 :     struct GNUNET_DB_EventHeaderP es = {
    1503           14 :       .size = htons (sizeof (es)),
    1504           14 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    1505              :     };
    1506              : 
    1507              :     eh_keys
    1508           28 :       = db_plugin->event_listen (db_plugin->cls,
    1509              :                                  &es,
    1510           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1511              :                                  &keys_changed,
    1512              :                                  NULL);
    1513              :   }
    1514              :   {
    1515           14 :     struct GNUNET_DB_EventHeaderP es = {
    1516           14 :       .size = htons (sizeof (es)),
    1517           14 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
    1518              :     };
    1519              : 
    1520              :     eh_rule
    1521           28 :       = db_plugin->event_listen (db_plugin->cls,
    1522              :                                  &es,
    1523           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1524              :                                  &rule_triggered,
    1525              :                                  NULL);
    1526              :   }
    1527           14 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1528              :                                          &accept_exchanges,
    1529              :                                          NULL);
    1530              :   {
    1531           14 :     struct GNUNET_DB_EventHeaderP es = {
    1532           14 :       .size = htons (sizeof (es)),
    1533           14 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
    1534              :     };
    1535              : 
    1536              :     eh_accounts
    1537           28 :       = db_plugin->event_listen (db_plugin->cls,
    1538              :                                  &es,
    1539           14 :                                  GNUNET_TIME_UNIT_FOREVER_REL,
    1540              :                                  &account_changed,
    1541              :                                  NULL);
    1542              :   }
    1543           14 :   GNUNET_assert (NULL == account_task);
    1544              :   account_task
    1545           14 :     = 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           14 : main (int argc,
    1559              :       char *const *argv)
    1560              : {
    1561           14 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    1562           14 :     GNUNET_GETOPT_option_timetravel ('T',
    1563              :                                      "timetravel"),
    1564           14 :     GNUNET_GETOPT_option_flag ('t',
    1565              :                                "test",
    1566              :                                "run in test mode and exit when idle",
    1567              :                                &test_mode),
    1568           14 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
    1569              :     GNUNET_GETOPT_OPTION_END
    1570              :   };
    1571              :   enum GNUNET_GenericReturnValue ret;
    1572              : 
    1573           14 :   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           14 :   if (GNUNET_SYSERR == ret)
    1582            0 :     return EXIT_INVALIDARGUMENT;
    1583           14 :   if (GNUNET_NO == ret)
    1584            0 :     return EXIT_SUCCESS;
    1585           14 :   return global_ret;
    1586              : }
    1587              : 
    1588              : 
    1589              : /* end of taler-merchant-kyccheck.c */
        

Generated by: LCOV version 2.0-1