LCOV - code coverage report
Current view: top level - backend - taler-merchant-kyccheck.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 56.2 % 821 461
Test Date: 2026-09-11 17:45:24 Functions: 75.0 % 36 27

            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 src/backend/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              : struct Inquiry;
      23              : #define TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE struct Inquiry
      24              : #define TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE struct Inquiry
      25              : #define TALER_EXCHANGE_POST_KYC_UPLOAD_RESULT_CLOSURE struct Inquiry
      26              : #include "microhttpd.h"
      27              : #include <gnunet/gnunet_util_lib.h>
      28              : #include <jansson.h>
      29              : #include <pthread.h>
      30              : #include <regex.h>
      31              : #include <taler/taler_dbevents.h>
      32              : #include <taler/taler_json_lib.h>
      33              : #include <taler/taler_exchange_service.h>
      34              : #include <taler/exchange/post-kyc-upload-ID.h>
      35              : #include "taler/taler_merchant_util.h"
      36              : #include "taler/taler_merchant_bank_lib.h"
      37              : #include "merchantdb_lib.h"
      38              : #include "merchantdb_lib.h"
      39              : #include "merchant-database/iterate_outdated_kyc_statuses.h"
      40              : #include "merchant-database/insert_kyc_status.h"
      41              : #include "merchant-database/delete_tos_accepted_early.h"
      42              : #include "merchant-database/get_kyc_status.h"
      43              : #include "merchant-database/get_tos_accepted_early.h"
      44              : #include "merchant-database/set_instance.h"
      45              : #include "merchant-database/iterate_accounts.h"
      46              : #include "merchant-database/get_exchange_keys.h"
      47              : #include "merchant-database/event_listen.h"
      48              : #include "merchant-database/preflight.h"
      49              : #include "merchant-database/start.h"
      50              : 
      51              : 
      52              : /**
      53              :  * Timeout for the exchange interaction.  Rather long as we should do
      54              :  * long-polling and do not want to wake up too often.
      55              :  */
      56              : #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
      57              :           GNUNET_TIME_UNIT_MINUTES, \
      58              :           30)
      59              : 
      60              : /**
      61              :  * How long do we wait between requests if all we wait
      62              :  * for is a change in the AML investigation status?
      63              :  * Default value.
      64              :  */
      65              : #define AML_FREQ GNUNET_TIME_relative_multiply ( \
      66              :           GNUNET_TIME_UNIT_HOURS, \
      67              :           6)
      68              : 
      69              : /**
      70              :  * How long do we wait between requests if all we wait
      71              :  * for is a change in the AML investigation status?
      72              :  */
      73              : static struct GNUNET_TIME_Relative aml_freq;
      74              : 
      75              : /**
      76              :  * How frequently do we check for updates to our KYC status
      77              :  * if there is no actual reason to check? Set to a very low
      78              :  * frequency, just to ensure we eventually notice.
      79              :  * Default value.
      80              :  */
      81              : #define AML_LOW_FREQ GNUNET_TIME_relative_multiply ( \
      82              :           GNUNET_TIME_UNIT_DAYS, \
      83              :           7)
      84              : 
      85              : /**
      86              :  * How frequently do we check for updates to our KYC status
      87              :  * if there is no actual reason to check? Set to a very low
      88              :  * frequency, just to ensure we eventually notice.
      89              :  */
      90              : static struct GNUNET_TIME_Relative aml_low_freq;
      91              : 
      92              : 
      93              : /**
      94              :  * How many inquiries do we process concurrently at most.
      95              :  */
      96              : #define OPEN_INQUIRY_LIMIT 1024
      97              : 
      98              : /**
      99              :  * Name of the KYC form (``FORM_ID``) the exchange uses to affirm
     100              :  * acceptance of the terms of service.  Must match the value submitted
     101              :  * by #TALER_EXCHANGE_post_kyc_upload_accept_tos_create().
     102              :  */
     103              : #define ACCEPT_TOS_FORM "accept-tos"
     104              : 
     105              : /**
     106              :  * Minimum delay before we retry after the exchange returned an
     107              :  * internal error to our attempt to automatically accept the terms
     108              :  * of service on behalf of the user.
     109              :  */
     110              : #define TOS_ERROR_RETRY_DELAY GNUNET_TIME_UNIT_HOURS
     111              : 
     112              : 
     113              : /**
     114              :  * Information about an exchange.
     115              :  */
     116              : struct Exchange
     117              : {
     118              :   /**
     119              :    * Kept in a DLL.
     120              :    */
     121              :   struct Exchange *next;
     122              : 
     123              :   /**
     124              :    * Kept in a DLL.
     125              :    */
     126              :   struct Exchange *prev;
     127              : 
     128              :   /**
     129              :    * The keys of this exchange
     130              :    */
     131              :   struct TALER_EXCHANGE_Keys *keys;
     132              : 
     133              : };
     134              : 
     135              : 
     136              : /**
     137              :  * Information about an Account.
     138              :  */
     139              : struct Account
     140              : {
     141              :   /**
     142              :    * Kept in a DLL.
     143              :    */
     144              :   struct Account *next;
     145              : 
     146              :   /**
     147              :    * Kept in a DLL.
     148              :    */
     149              :   struct Account *prev;
     150              : 
     151              :   /**
     152              :    * Head of inquiries for this account.
     153              :    */
     154              :   struct Inquiry *i_head;
     155              : 
     156              :   /**
     157              :    * Tail of inquiries for this account.
     158              :    */
     159              :   struct Inquiry *i_tail;
     160              : 
     161              :   /**
     162              :    * Merchant instance this account belongs to.
     163              :    */
     164              :   char *instance_id;
     165              : 
     166              :   /**
     167              :    * The payto-URI of this account.
     168              :    */
     169              :   struct TALER_FullPayto merchant_account_uri;
     170              : 
     171              :   /**
     172              :    * Wire hash of the merchant bank account (with the
     173              :    * respective salt).
     174              :    */
     175              :   struct TALER_MerchantWireHashP h_wire;
     176              : 
     177              :   /**
     178              :    * Private key of the instance.
     179              :    */
     180              :   union TALER_AccountPrivateKeyP ap;
     181              : 
     182              :   /**
     183              :    * Hash of the @e merchant_account_uri.
     184              :    */
     185              :   struct TALER_NormalizedPaytoHashP h_payto;
     186              : 
     187              :   /**
     188              :    * Database generation when this account
     189              :    * was last active.
     190              :    */
     191              :   uint64_t account_gen;
     192              : 
     193              : };
     194              : 
     195              : 
     196              : /**
     197              :  * Information about an inquiry job.
     198              :  */
     199              : struct Inquiry
     200              : {
     201              :   /**
     202              :    * Key in the index of inquiries by instance, account and exchange.
     203              :    */
     204              :   struct GNUNET_HashCode key;
     205              : 
     206              :   /**
     207              :    * Kept in a DLL.
     208              :    */
     209              :   struct Inquiry *next;
     210              : 
     211              :   /**
     212              :    * Kept in a DLL.
     213              :    */
     214              :   struct Inquiry *prev;
     215              : 
     216              :   /**
     217              :    * Main task for this inquiry.
     218              :    */
     219              :   struct GNUNET_SCHEDULER_Task *task;
     220              : 
     221              :   /**
     222              :    * Which exchange is this inquiry about.
     223              :    */
     224              :   struct Exchange *e;
     225              : 
     226              :   /**
     227              :    * Which account is this inquiry about.
     228              :    */
     229              :   struct Account *a;
     230              : 
     231              :   /**
     232              :    * AccountLimits that apply to the account, NULL
     233              :    * if unknown.
     234              :    */
     235              :   json_t *jlimits;
     236              : 
     237              :   /**
     238              :    * Handle for the actual HTTP request to the exchange.
     239              :    */
     240              :   struct TALER_EXCHANGE_GetKycCheckHandle *kyc;
     241              : 
     242              :   /**
     243              :    * Handle for fetching /kyc-info to discover the upload ID used to
     244              :    * automatically accept the terms of service, NULL if not active.
     245              :    */
     246              :   struct TALER_EXCHANGE_GetKycInfoHandle *kyc_info;
     247              : 
     248              :   /**
     249              :    * Handle for the /kyc-upload request submitting the automatic
     250              :    * terms-of-service acceptance, NULL if not active.
     251              :    */
     252              :   struct TALER_EXCHANGE_PostKycUploadHandle *tos_upload;
     253              : 
     254              :   /**
     255              :    * If non-NULL, the ``Taler-Terms-Version`` of the terms of service
     256              :    * that the user accepted early (via ``POST /private/accept-tos-early``)
     257              :    * and that we are trying to submit to the exchange on their behalf.
     258              :    * Owned by the inquiry.
     259              :    */
     260              :   char *tos_etag;
     261              : 
     262              :   /**
     263              :    * Access token for the /kyc-info API.
     264              :    */
     265              :   struct TALER_AccountAccessTokenP access_token;
     266              : 
     267              :   /**
     268              :    * Last time we called the /kyc-check endpoint.
     269              :    */
     270              :   struct GNUNET_TIME_Timestamp last_kyc_check;
     271              : 
     272              :   /**
     273              :    * When is the next KYC check due?
     274              :    */
     275              :   struct GNUNET_TIME_Absolute due;
     276              : 
     277              :   /**
     278              :    * When should the current KYC time out?
     279              :    */
     280              :   struct GNUNET_TIME_Absolute timeout;
     281              : 
     282              :   /**
     283              :    * Current exponential backoff.
     284              :    */
     285              :   struct GNUNET_TIME_Relative backoff;
     286              : 
     287              :   /**
     288              :    * Rule generation known to the client, 0 for none.
     289              :    * Corresponds to the decision row in the exchange.
     290              :    */
     291              :   uint64_t rule_gen;
     292              : 
     293              :   /**
     294              :    * Last HTTP status returned by the exchange from
     295              :    * the /kyc-check endpoint.
     296              :    */
     297              :   unsigned int last_http_status;
     298              : 
     299              :   /**
     300              :    * Last Taler error code returned by the exchange from
     301              :    * the /kyc-check endpoint.
     302              :    */
     303              :   enum TALER_ErrorCode last_ec;
     304              : 
     305              :   /**
     306              :    * True if this is not our first time we make this request.
     307              :    */
     308              :   bool not_first_time;
     309              : 
     310              :   /**
     311              :    * Do soft limits on transactions apply to this merchant for operations
     312              :    * merchants care about? If so, we should increase our request frequency
     313              :    * and ask more often to see if they were lifted.
     314              :    */
     315              :   bool zero_limited;
     316              : 
     317              :   /**
     318              :    * Did we not run this inquiry due to limits?
     319              :    */
     320              :   bool limited;
     321              : 
     322              :   /**
     323              :    * Do we believe this account's KYC is in good shape?
     324              :    */
     325              :   bool kyc_ok;
     326              : 
     327              :   /**
     328              :    * True if merchant did perform this account's KYC AUTH transfer and @e access_token is set.
     329              :    */
     330              :   bool auth_ok;
     331              : 
     332              :   /**
     333              :    * True if the account is known to be currently under
     334              :    * investigation by AML staff.
     335              :    */
     336              :   bool aml_review;
     337              : 
     338              : };
     339              : 
     340              : 
     341              : /**
     342              :  * Head of known exchanges.
     343              :  */
     344              : static struct Exchange *e_head;
     345              : 
     346              : /**
     347              :  * Tail of known exchanges.
     348              :  */
     349              : static struct Exchange *e_tail;
     350              : 
     351              : /**
     352              :  * Head of accounts.
     353              :  */
     354              : static struct Account *a_head;
     355              : 
     356              : /**
     357              :  * Tail of accounts.
     358              :  */
     359              : static struct Account *a_tail;
     360              : 
     361              : /**
     362              :  * The merchant's configuration.
     363              :  */
     364              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     365              : 
     366              : /**
     367              :  * Our database connection.
     368              :  */
     369              : static struct TALER_MERCHANTDB_PostgresContext *pg;
     370              : 
     371              : /**
     372              :  * Handle to the context for interacting with the bank.
     373              :  */
     374              : static struct GNUNET_CURL_Context *ctx;
     375              : 
     376              : /**
     377              :  * Scheduler context for running the @e ctx.
     378              :  */
     379              : static struct GNUNET_CURL_RescheduleContext *rc;
     380              : 
     381              : /**
     382              :  * Event handler to learn that there may be new bank
     383              :  * accounts to check.
     384              :  */
     385              : static struct GNUNET_DB_EventHandler *eh_accounts;
     386              : 
     387              : /**
     388              :  * Event handler to learn that there may be new exchange
     389              :  * keys to check.
     390              :  */
     391              : static struct GNUNET_DB_EventHandler *eh_keys;
     392              : 
     393              : /**
     394              :  * Event handler to learn that there was a KYC
     395              :  * rule triggered and we need to check the KYC
     396              :  * status for an account.
     397              :  */
     398              : static struct GNUNET_DB_EventHandler *eh_rule;
     399              : 
     400              : /**
     401              :  * Event handler to learn that higher-frequency KYC
     402              :  * checks were forced by an application actively inspecting
     403              :  * some KYC status values.
     404              :  */
     405              : static struct GNUNET_DB_EventHandler *eh_update_forced;
     406              : 
     407              : /**
     408              :  * Event handler to learn that we got new /keys
     409              :  * from an exchange and should reconsider eligibility.
     410              :  */
     411              : static struct GNUNET_DB_EventHandler *keys_rule;
     412              : 
     413              : /**
     414              :  * Main task to discover (new) accounts.
     415              :  */
     416              : static struct GNUNET_SCHEDULER_Task *account_task;
     417              : 
     418              : /**
     419              :  * Pending refreshes, coalesced by instance serial.
     420              :  */
     421              : struct Refresh
     422              : {
     423              :   struct Refresh *next;
     424              :   struct Refresh *prev;
     425              :   struct GNUNET_HashCode key;
     426              :   uint64_t merchant_serial;
     427              : };
     428              : 
     429              : static struct Refresh *refresh_head;
     430              : static struct Refresh *refresh_tail;
     431              : static struct GNUNET_CONTAINER_MultiHashMap *refresh_map;
     432              : static struct GNUNET_CONTAINER_MultiHashMap *inquiry_map;
     433              : static struct GNUNET_SCHEDULER_Task *refresh_task;
     434              : 
     435              : /**
     436              :  * Counter determining how often we have called
     437              :  * "iterate_accounts" on the database.
     438              :  */
     439              : static uint64_t database_gen;
     440              : 
     441              : /**
     442              :  * How many active inquiries do we have right now.
     443              :  */
     444              : static unsigned int active_inquiries;
     445              : 
     446              : /**
     447              :  * Value to return from main(). 0 on success, non-zero on errors.
     448              :  */
     449              : static int global_ret;
     450              : 
     451              : /**
     452              :  * Should we enable HTTP/2 and HTTP/3 when talking to the exchange?
     453              :  * Those are not expected to be terribly beneficial for a client with
     454              :  * stable connections to a few servers, but they could cause stability
     455              :  * issues with libcurl.  Hence we *default* to HTTP/1.1-only, as that
     456              :  * is the conservative and most tested code path.
     457              :  */
     458              : static int enable_h3;
     459              : 
     460              : /**
     461              :  * #GNUNET_YES if we are in test mode and should exit when idle.
     462              :  */
     463              : static int test_mode;
     464              : 
     465              : /**
     466              :  * True if the last DB query was limited by the
     467              :  * #OPEN_INQUIRY_LIMIT and we thus should check again
     468              :  * as soon as we are substantially below that limit,
     469              :  * and not only when we get a DB notification.
     470              :  */
     471              : static bool at_limit;
     472              : 
     473              : 
     474              : /**
     475              :  * Check about performing a /kyc-check request with the
     476              :  * exchange for the given inquiry.
     477              :  *
     478              :  * @param cls a `struct Inquiry` to process
     479              :  */
     480              : static void
     481              : inquiry_work (void *cls);
     482              : 
     483              : 
     484              : /**
     485              :  * Hash a fully qualified inquiry identity. Include string terminators to
     486              :  * keep adjacent components unambiguous.
     487              :  */
     488              : static void
     489           38 : inquiry_key (const char *instance_id,
     490              :              const struct TALER_MerchantWireHashP *h_wire,
     491              :              const char *exchange_url,
     492              :              struct GNUNET_HashCode *key)
     493              : {
     494           38 :   struct GNUNET_HashContext *hc = GNUNET_CRYPTO_hash_context_start ();
     495              : 
     496           38 :   GNUNET_CRYPTO_hash_context_read (hc,
     497              :                                    instance_id,
     498           38 :                                    strlen (instance_id) + 1);
     499           38 :   GNUNET_CRYPTO_hash_context_read (hc,
     500              :                                    h_wire,
     501              :                                    sizeof (*h_wire));
     502           38 :   GNUNET_CRYPTO_hash_context_read (hc,
     503              :                                    exchange_url,
     504           38 :                                    strlen (exchange_url) + 1);
     505           38 :   GNUNET_CRYPTO_hash_context_finish (hc,
     506              :                                      key);
     507           38 : }
     508              : 
     509              : 
     510              : /**
     511              :  * An inquiry keeps its active slot through automatic ToS acceptance.
     512              :  */
     513              : static bool
     514           17 : inquiry_busy (const struct Inquiry *i)
     515              : {
     516           28 :   return (NULL != i->kyc) ||
     517           27 :          (NULL != i->kyc_info) ||
     518           10 :          (NULL != i->tos_upload);
     519              : }
     520              : 
     521              : 
     522              : /**
     523              :  * Request an immediate check, sharing any active or queued work.
     524              :  */
     525              : static void
     526           17 : request_inquiry (struct Inquiry *i)
     527              : {
     528           17 :   if (inquiry_busy (i))
     529            9 :     return;
     530            9 :   i->due = GNUNET_TIME_UNIT_ZERO_ABS;
     531            9 :   if (i->limited)
     532            1 :     return;
     533            8 :   if (NULL != i->task)
     534            7 :     GNUNET_SCHEDULER_cancel (i->task);
     535            8 :   i->task = GNUNET_SCHEDULER_add_now (&inquiry_work,
     536              :                                       i);
     537              : }
     538              : 
     539              : 
     540              : /**
     541              :  * An inquiry finished, check if we should resume others.
     542              :  */
     543              : static void
     544           36 : end_inquiry (void)
     545              : {
     546           36 :   GNUNET_assert (active_inquiries > 0);
     547           36 :   active_inquiries--;
     548           36 :   if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
     549              :        (at_limit) )
     550              :   {
     551            1 :     at_limit = false;
     552            1 :     for (struct Account *a = a_head;
     553            2 :          NULL != a;
     554            1 :          a = a->next)
     555              :     {
     556            1 :       for (struct Inquiry *i = a->i_head;
     557            2 :            NULL != i;
     558            1 :            i = i->next)
     559              :       {
     560            1 :         if (! i->limited)
     561            0 :           continue;
     562            1 :         i->limited = false;
     563            1 :         GNUNET_assert (NULL == i->task);
     564              :         /* done synchronously so that the active_inquiries
     565              :            is updated immediately */
     566            1 :         inquiry_work (i);
     567            1 :         if (at_limit)
     568            0 :           break;
     569              :       }
     570            1 :       if (at_limit)
     571            0 :         break;
     572              :     }
     573              :   }
     574           36 :   if ( (! at_limit) &&
     575           36 :        (0 == active_inquiries) &&
     576              :        (test_mode) )
     577              :   {
     578            1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     579              :                 "No more open inquiries and in test mode. Existing.\n");
     580            1 :     GNUNET_SCHEDULER_shutdown ();
     581            1 :     return;
     582              :   }
     583              : }
     584              : 
     585              : 
     586              : /**
     587              :  * Pack the given @a limit into the JSON @a limits array.
     588              :  *
     589              :  * @param limit account limit to pack
     590              :  * @param[in,out] limits JSON array to extend
     591              :  */
     592              : static void
     593            5 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
     594              :             json_t *limits)
     595              : {
     596              :   json_t *jl;
     597              : 
     598            5 :   jl = GNUNET_JSON_PACK (
     599              :     TALER_JSON_pack_kycte ("operation_type",
     600              :                            limit->operation_type),
     601              :     GNUNET_JSON_pack_time_rel ("timeframe",
     602              :                                limit->timeframe),
     603              :     TALER_JSON_pack_amount ("threshold",
     604              :                             &limit->threshold),
     605              :     GNUNET_JSON_pack_bool ("soft_limit",
     606              :                            limit->soft_limit)
     607              :     );
     608            5 :   GNUNET_assert (0 ==
     609              :                  json_array_append_new (limits,
     610              :                                         jl));
     611            5 : }
     612              : 
     613              : 
     614              : /**
     615              :  * Update KYC status for @a i based on
     616              :  * @a account_kyc_status
     617              :  *
     618              :  * @param[in,out] i inquiry context, jlimits is updated
     619              :  * @param account_kyc_status account KYC status details
     620              :  */
     621              : static void
     622            7 : store_kyc_status (
     623              :   struct Inquiry *i,
     624              :   const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
     625              : {
     626              :   json_t *jlimits;
     627              : 
     628            7 :   json_decref (i->jlimits);
     629            7 :   jlimits = json_array ();
     630            7 :   GNUNET_assert (NULL != jlimits);
     631            7 :   i->zero_limited = false;
     632           12 :   for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
     633              :   {
     634            5 :     const struct TALER_EXCHANGE_AccountLimit *limit
     635            5 :       = &account_kyc_status->limits[j];
     636              : 
     637            5 :     pack_limit (limit,
     638              :                 jlimits);
     639            5 :     if (TALER_amount_is_zero (&limit->threshold) &&
     640            1 :         limit->soft_limit &&
     641            1 :         ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) ||
     642            1 :           (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) ||
     643            0 :           (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) )
     644              :     {
     645            1 :       i->zero_limited = true;
     646              :     }
     647              :   }
     648            7 :   i->jlimits = jlimits;
     649            7 :   GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token));
     650            7 :   i->access_token = account_kyc_status->access_token;
     651            7 :   i->aml_review = account_kyc_status->aml_review;
     652            7 :   i->kyc_ok = (MHD_HTTP_OK == i->last_http_status);
     653            7 : }
     654              : 
     655              : 
     656              : /**
     657              :  * The current interaction with the exchange for inquiry @a i is
     658              :  * complete (or was aborted).  Schedule the next periodic KYC check
     659              :  * at @a i->due and release the active-inquiry slot.
     660              :  *
     661              :  * @param[in,out] i the inquiry to reschedule
     662              :  */
     663              : static void
     664           35 : finish_inquiry (struct Inquiry *i)
     665              : {
     666           35 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     667              :               "Will repeat inquiry in %s\n",
     668              :               GNUNET_TIME_relative2s (
     669              :                 GNUNET_TIME_absolute_get_remaining (i->due),
     670              :                 true));
     671           35 :   if (! GNUNET_TIME_absolute_is_never (i->due))
     672           35 :     i->task = GNUNET_SCHEDULER_add_at (i->due,
     673              :                                        &inquiry_work,
     674              :                                        i);
     675           35 :   end_inquiry ();
     676           35 : }
     677              : 
     678              : 
     679              : /**
     680              :  * Clear the tos-accepted data from the user, we do not
     681              :  * need the flag anymore, either because we passed it on
     682              :  * to the exchange or because they are too old.
     683              :  *
     684              :  * @param i inquiry this is about
     685              :  */
     686              : static void
     687            0 : clear_tos (const struct Inquiry *i)
     688              : {
     689              :   enum GNUNET_DB_QueryStatus qs;
     690              : 
     691            0 :   qs = TALER_MERCHANTDB_set_instance (pg,
     692            0 :                                       i->a->instance_id);
     693            0 :   if (qs < 0)
     694              :   {
     695            0 :     GNUNET_break (0);
     696            0 :     global_ret = EXIT_FAILURE;
     697            0 :     GNUNET_SCHEDULER_shutdown ();
     698            0 :     return;
     699              :   }
     700            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     701              :   {
     702            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     703              :                 "Instance `%s' vanished, nothing to clear\n",
     704              :                 i->a->instance_id);
     705            0 :     return;
     706              :   }
     707            0 :   qs = TALER_MERCHANTDB_delete_tos_accepted_early (
     708              :     pg,
     709            0 :     i->a->instance_id,
     710            0 :     i->e->keys->exchange_url);
     711            0 :   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     712              :                 TALER_MERCHANTDB_set_instance (pg,
     713              :                                                NULL));
     714            0 :   if (qs < 0)
     715              :   {
     716            0 :     GNUNET_break (0);
     717            0 :     global_ret = EXIT_FAILURE;
     718            0 :     GNUNET_SCHEDULER_shutdown ();
     719            0 :     return;
     720              :   }
     721              : }
     722              : 
     723              : 
     724              : /**
     725              :  * Function called with the result of submitting an automatic
     726              :  * terms-of-service acceptance to the exchange via /kyc-upload.
     727              :  *
     728              :  * @param i the inquiry the acceptance was for
     729              :  * @param pr the exchange's response
     730              :  */
     731              : static void
     732            0 : tos_upload_cb (struct Inquiry *i,
     733              :                const struct TALER_EXCHANGE_PostKycUploadResponse *pr)
     734              : {
     735            0 :   unsigned int http_status = pr->hr.http_status;
     736              : 
     737            0 :   i->tos_upload = NULL;
     738            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     739              :               "Automatic ToS acceptance for `%s' at `%s' returned HTTP %u\n",
     740              :               i->a->merchant_account_uri.full_payto,
     741              :               i->e->keys->exchange_url,
     742              :               http_status);
     743            0 :   switch (http_status)
     744              :   {
     745            0 :   case MHD_HTTP_OK:
     746              :   case MHD_HTTP_NO_CONTENT:
     747              :     /* Exchange accepted the terms of service: re-check KYC now. */
     748            0 :     i->due = GNUNET_TIME_UNIT_ZERO_ABS;
     749            0 :     clear_tos (i);
     750            0 :     break;
     751            0 :   case 0: /* no answer, like network failure */
     752              :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     753              :   case MHD_HTTP_BAD_GATEWAY:
     754              :   case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: /* Wild error */
     755              :     /* Internal/transient error at the exchange: do NOT clear the early
     756              :        acceptance, but back off for at least an hour before retrying
     757              :        with a regular periodic KYC check. */
     758            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     759              :                 "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n",
     760              :                 i->e->keys->exchange_url,
     761              :                 http_status);
     762            0 :     i->due = GNUNET_TIME_relative_to_absolute (
     763              :       GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY));
     764            0 :     break;
     765            0 :   case MHD_HTTP_NOT_FOUND:
     766              :     /* Something must have changed exchange-side, try again
     767              :        immediately, but do not clear ToS acceptance */
     768            0 :     i->due = GNUNET_TIME_UNIT_ZERO_ABS;
     769            0 :     clear_tos (i);
     770            0 :     break;
     771            0 :   case MHD_HTTP_BAD_REQUEST:
     772              :     /* This should not happen, go back to manual KYC */
     773            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     774              :                 "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n",
     775              :                 i->e->keys->exchange_url,
     776              :                 http_status);
     777            0 :     i->due = GNUNET_TIME_relative_to_absolute (
     778              :       GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY));
     779            0 :     break;
     780            0 :   case MHD_HTTP_CONFLICT:
     781              :     /* Exchange rejected the accepted ToS version (ETag not acceptable
     782              :        or ToS acceptance disappeared):
     783              :        clear the early acceptance so we do not loop, then re-check KYC
     784              :        (the user will have to accept the ToS through the regular flow
     785              :        if it still applies). */
     786            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     787              :                 "Exchange `%s' rejected early ToS acceptance (version `%s', HTTP %u); clearing early acceptance\n",
     788              :                 i->e->keys->exchange_url,
     789              :                 i->tos_etag,
     790              :                 http_status);
     791            0 :     clear_tos (i);
     792            0 :     i->due = GNUNET_TIME_UNIT_ZERO_ABS;
     793            0 :     break;
     794              :   }
     795            0 :   GNUNET_free (i->tos_etag);
     796            0 :   finish_inquiry (i);
     797            0 : }
     798              : 
     799              : 
     800              : /**
     801              :  * Submit an automatic terms-of-service acceptance for inquiry @a i to
     802              :  * the exchange, using the @a id of the corresponding KYC requirement
     803              :  * (obtained from /kyc-info) and the early-accepted version in
     804              :  * @a i->tos_etag.
     805              :  *
     806              :  * @param[in,out] i inquiry to submit the ToS acceptance for
     807              :  * @param id KYC requirement / upload ID for the terms-of-service form
     808              :  */
     809              : static void
     810            0 : start_tos_upload (struct Inquiry *i,
     811              :                   const char *id)
     812              : {
     813            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     814              :               "Submitting automatic ToS acceptance (version `%s', id `%s') to `%s'\n",
     815              :               i->tos_etag,
     816              :               id,
     817              :               i->e->keys->exchange_url);
     818            0 :   i->tos_upload = TALER_EXCHANGE_post_kyc_upload_accept_tos_create (
     819              :     ctx,
     820            0 :     i->e->keys->exchange_url,
     821              :     id,
     822            0 :     i->tos_etag);
     823            0 :   if ( (NULL == i->tos_upload) ||
     824              :        (TALER_EC_NONE !=
     825            0 :         TALER_EXCHANGE_post_kyc_upload_start (i->tos_upload,
     826              :                                               &tos_upload_cb,
     827              :                                               i)) )
     828              :   {
     829            0 :     GNUNET_break (0);
     830            0 :     if (NULL != i->tos_upload)
     831              :     {
     832            0 :       TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload);
     833            0 :       i->tos_upload = NULL;
     834              :     }
     835              :     /* Could not even start the upload: treat as transient, keep the
     836              :        early acceptance and retry with a regular periodic check. */
     837            0 :     GNUNET_free (i->tos_etag);
     838            0 :     finish_inquiry (i);
     839              :   }
     840            0 : }
     841              : 
     842              : 
     843              : /**
     844              :  * Function called with the result of fetching /kyc-info while trying
     845              :  * to automatically accept the terms of service.  Finds the ID of the
     846              :  * terms-of-service requirement and submits the acceptance.
     847              :  *
     848              :  * @param i the inquiry the lookup was for
     849              :  * @param ir the exchange's response
     850              :  */
     851              : static void
     852            0 : tos_info_cb (struct Inquiry *i,
     853              :              const struct TALER_EXCHANGE_GetKycInfoResponse *ir)
     854              : {
     855            0 :   i->kyc_info = NULL;
     856            0 :   if (MHD_HTTP_OK == ir->hr.http_status)
     857              :   {
     858            0 :     const char *id = NULL;
     859              : 
     860            0 :     for (size_t j = 0; j < ir->details.ok.requirements_length; j++)
     861              :     {
     862            0 :       const struct TALER_EXCHANGE_RequirementInformation *req
     863            0 :         = &ir->details.ok.requirements[j];
     864              : 
     865            0 :       if ( (NULL != req->form) &&
     866            0 :            (NULL != req->id) &&
     867            0 :            (0 == strcmp (req->form,
     868              :                          ACCEPT_TOS_FORM)) )
     869              :       {
     870            0 :         id = req->id;
     871            0 :         break;
     872              :       }
     873              :     }
     874            0 :     if (NULL != id)
     875              :     {
     876            0 :       start_tos_upload (i,
     877              :                         id);
     878            0 :       return;
     879              :     }
     880            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     881              :                 "No `%s' requirement at `%s'; cannot auto-accept ToS, falling back to periodic check\n",
     882              :                 ACCEPT_TOS_FORM,
     883              :                 i->e->keys->exchange_url);
     884              :   }
     885              :   else
     886              :   {
     887            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     888              :                 "GET /kyc-info at `%s' returned HTTP %u; cannot auto-accept ToS now\n",
     889              :                 i->e->keys->exchange_url,
     890              :                 ir->hr.http_status);
     891              :   }
     892              :   /* Could not determine the upload ID: keep the early acceptance and
     893              :      retry on the next regular periodic KYC check. */
     894            0 :   GNUNET_free (i->tos_etag);
     895            0 :   finish_inquiry (i);
     896              : }
     897              : 
     898              : 
     899              : /**
     900              :  * Start the automatic terms-of-service acceptance for inquiry @a i by
     901              :  * fetching /kyc-info to discover the ID of the terms-of-service
     902              :  * requirement.  The early-accepted version is in @a i->tos_etag.
     903              :  *
     904              :  * @param[in,out] i inquiry to auto-accept the terms of service for
     905              :  */
     906              : static void
     907            0 : start_tos_info (struct Inquiry *i)
     908              : {
     909            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     910              :               "Fetching /kyc-info from `%s' to auto-accept ToS for `%s'\n",
     911              :               i->e->keys->exchange_url,
     912              :               i->a->merchant_account_uri.full_payto);
     913            0 :   i->kyc_info = TALER_EXCHANGE_get_kyc_info_create (ctx,
     914            0 :                                                     i->e->keys->exchange_url,
     915            0 :                                                     &i->access_token);
     916            0 :   if ( (NULL == i->kyc_info) ||
     917              :        (TALER_EC_NONE !=
     918            0 :         TALER_EXCHANGE_get_kyc_info_start (i->kyc_info,
     919              :                                            &tos_info_cb,
     920              :                                            i)) )
     921              :   {
     922            0 :     GNUNET_break (0);
     923            0 :     if (NULL != i->kyc_info)
     924              :     {
     925            0 :       TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info);
     926            0 :       i->kyc_info = NULL;
     927              :     }
     928              :     /* Could not start the lookup: keep the early acceptance and retry
     929              :        with a regular periodic check. */
     930            0 :     GNUNET_free (i->tos_etag);
     931            0 :     finish_inquiry (i);
     932              :   }
     933            0 : }
     934              : 
     935              : 
     936              : /**
     937              :  * The exchange asked us (via @a tos_required) to accept its terms of
     938              :  * service.  Check whether the user already accepted the terms of
     939              :  * service early (via ``POST /private/accept-tos-early``).  If so,
     940              :  * remember the accepted version in @a i->tos_etag so that we will try
     941              :  * to submit it to the exchange automatically.
     942              :  *
     943              :  * @param[in,out] i inquiry for which the exchange requires ToS acceptance
     944              :  * @param req required ETag for the accepted ToS
     945              :  */
     946              : static void
     947            0 : check_early_tos_acceptance (struct Inquiry *i,
     948              :                             const char *req)
     949              : {
     950              :   enum GNUNET_DB_QueryStatus qs;
     951            0 :   char *tos_version = NULL;
     952              : 
     953            0 :   qs = TALER_MERCHANTDB_set_instance (pg,
     954            0 :                                       i->a->instance_id);
     955            0 :   if (qs < 0)
     956              :   {
     957            0 :     GNUNET_break (0);
     958            0 :     global_ret = EXIT_FAILURE;
     959            0 :     GNUNET_SCHEDULER_shutdown ();
     960            0 :     return;
     961              :   }
     962            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     963              :   {
     964            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     965              :                 "Instance `%s' vanished, skipping ToS check\n",
     966              :                 i->a->instance_id);
     967            0 :     return;
     968              :   }
     969            0 :   qs = TALER_MERCHANTDB_get_tos_accepted_early (pg,
     970            0 :                                                 i->a->instance_id,
     971            0 :                                                 i->e->keys->exchange_url,
     972              :                                                 &tos_version);
     973            0 :   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     974              :                 TALER_MERCHANTDB_set_instance (pg,
     975              :                                                NULL));
     976            0 :   if (qs < 0)
     977              :   {
     978            0 :     GNUNET_break (0);
     979            0 :     global_ret = EXIT_FAILURE;
     980            0 :     GNUNET_SCHEDULER_shutdown ();
     981            0 :     return;
     982              :   }
     983            0 :   if (NULL == tos_version)
     984              :   {
     985            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     986              :                 "Exchange `%s' supports early ToS acceptance, but user did not accept ToS early\n",
     987              :                 i->e->keys->exchange_url);
     988            0 :     return;
     989              :   }
     990            0 :   if (0 != strcmp (tos_version,
     991              :                    req))
     992              :   {
     993            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     994              :                 "User accepted outdated ToS version `%s' early, but exchange wants `%s'. User will need to accept the ToS again!\n",
     995              :                 tos_version,
     996              :                 req);
     997            0 :     GNUNET_free (tos_version);
     998            0 :     return;
     999              :   }
    1000            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1001              :               "User accepted ToS version `%s' early; will submit to `%s'\n",
    1002              :               tos_version,
    1003              :               i->e->keys->exchange_url);
    1004            0 :   GNUNET_free (i->tos_etag);
    1005            0 :   i->tos_etag = tos_version;
    1006              : }
    1007              : 
    1008              : 
    1009              : /**
    1010              :  * Function called with the result of a KYC check.
    1011              :  *
    1012              :  * @param cls a `struct Inquiry *`
    1013              :  * @param ks the account's KYC status details
    1014              :  */
    1015              : static void
    1016           35 : exchange_check_cb (
    1017              :   struct Inquiry *i,
    1018              :   const struct TALER_EXCHANGE_GetKycCheckResponse *ks)
    1019              : {
    1020           35 :   bool progress = false;
    1021              : 
    1022           35 :   i->kyc = NULL;
    1023           35 :   if (! i->not_first_time)
    1024           23 :     progress = true;
    1025           35 :   if ( (i->last_http_status != ks->hr.http_status) &&
    1026           26 :        (0 != ks->hr.http_status) )
    1027           26 :     progress = true;
    1028           35 :   if (0 != ks->hr.http_status)
    1029              :   {
    1030           35 :     i->last_http_status = ks->hr.http_status;
    1031           35 :     i->last_ec = ks->hr.ec;
    1032              :   }
    1033           35 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1034              :               "KYC status of `%s' / %s at `%s' is %u\n",
    1035              :               i->a->merchant_account_uri.full_payto,
    1036              :               i->a->instance_id,
    1037              :               i->e->keys->exchange_url,
    1038              :               ks->hr.http_status);
    1039           35 :   switch (ks->hr.http_status)
    1040              :   {
    1041            0 :   case 0:
    1042            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1043              :                 "Exchange did not responded to /kyc-check request!\n");
    1044              :     i->backoff
    1045            0 :       = GNUNET_TIME_randomized_backoff (i->backoff,
    1046              :                                         EXCHANGE_TIMEOUT);
    1047            0 :     i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
    1048            0 :     break;
    1049            6 :   case MHD_HTTP_OK:
    1050            6 :     if (i->rule_gen != ks->details.ok.rule_gen)
    1051            3 :       progress = true;
    1052            6 :     i->rule_gen = ks->details.ok.rule_gen;
    1053            6 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1054              :     /* exchange says KYC is OK, gives status information */
    1055            6 :     i->auth_ok = true;
    1056            6 :     store_kyc_status (i,
    1057              :                       &ks->details.ok);
    1058            6 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1059            6 :     if (i->aml_review || i->zero_limited)
    1060              :     {
    1061            5 :       if (! progress)
    1062            2 :         i->due = GNUNET_TIME_relative_to_absolute (
    1063              :           GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_freq,
    1064              :                                                            i->backoff)));
    1065              :     }
    1066              :     else
    1067              :     {
    1068              :       /* KYC is OK, only check again if triggered */
    1069            1 :       if (! progress)
    1070            0 :         i->due = GNUNET_TIME_relative_to_absolute (
    1071              :           GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
    1072              :                                                            i->backoff)));
    1073              :     }
    1074            6 :     break;
    1075            1 :   case MHD_HTTP_ACCEPTED:
    1076            1 :     if (i->rule_gen != ks->details.accepted.rule_gen)
    1077            0 :       progress = true;
    1078            1 :     i->rule_gen = ks->details.accepted.rule_gen;
    1079            1 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1080              :     /* exchange says KYC is required */
    1081            1 :     i->auth_ok = true;
    1082            1 :     store_kyc_status (i,
    1083              :                       &ks->details.accepted);
    1084            1 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1085              :     /* Start immediately with long-polling */
    1086            1 :     if (! progress)
    1087            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
    1088              :                                          i->timeout);
    1089            1 :     if (NULL != ks->details.accepted.tos_required)
    1090              :     {
    1091              :       /* Exchange wants the user to accept its terms of service.
    1092              :          If the user already accepted them early, try to submit that
    1093              :          acceptance to the exchange automatically. */
    1094            0 :       check_early_tos_acceptance (i,
    1095            0 :                                   ks->details.accepted.tos_required);
    1096              :     }
    1097            1 :     break;
    1098           27 :   case MHD_HTTP_NO_CONTENT:
    1099           27 :     i->rule_gen = 0;
    1100           27 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1101           27 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1102              :     /* exchange claims KYC is off! */
    1103           27 :     i->kyc_ok = true;
    1104           27 :     i->aml_review = false;
    1105              :     /* Clear limits, in case exchange had KYC on previously */
    1106           27 :     json_decref (i->jlimits);
    1107           27 :     i->jlimits = NULL;
    1108              :     /* KYC is OK, only check again if triggered */
    1109           27 :     i->due = GNUNET_TIME_relative_to_absolute (
    1110              :       GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
    1111              :                                                        i->backoff)));
    1112           27 :     break;
    1113            0 :   case MHD_HTTP_FORBIDDEN: /* bad signature */
    1114            0 :     i->rule_gen = 0;
    1115            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1116              :     /* Forbidden => KYC auth must be wrong */
    1117            0 :     i->auth_ok = false;
    1118              :     /* Start with long-polling */
    1119            0 :     if (! progress)
    1120            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
    1121              :                                          i->timeout);
    1122            0 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1123            0 :     break;
    1124            1 :   case MHD_HTTP_NOT_FOUND: /* account unknown */
    1125            1 :     i->rule_gen = 0;
    1126            1 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1127              :     /* Account unknown => no KYC auth yet */
    1128            1 :     i->auth_ok = false;
    1129              :     /* unknown account => wire transfer required! */
    1130            1 :     i->kyc_ok = false;
    1131              :     /* There should not be any limits yet, but clear them
    1132              :        just in case the exchange has amnesia */
    1133            1 :     json_decref (i->jlimits);
    1134            1 :     i->jlimits = NULL;
    1135              :     /* Start immediately with Long-polling */
    1136            1 :     if (! progress)
    1137            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
    1138              :                                          i->timeout);
    1139            1 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1140            1 :     break;
    1141            0 :   case MHD_HTTP_CONFLICT: /* no account_pub known */
    1142            0 :     i->rule_gen = 0;
    1143            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1144              :     /* Conflict => KYC auth wire transfer missing! */
    1145            0 :     i->auth_ok = false;
    1146              :     /* Start immediately with Long-polling */
    1147            0 :     if (! progress)
    1148            0 :       i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
    1149              :                                          i->timeout);
    1150            0 :     i->backoff = GNUNET_TIME_UNIT_MINUTES;
    1151            0 :     break;
    1152            0 :   default:
    1153            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1154              :                 "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
    1155              :                 ks->hr.http_status,
    1156              :                 ks->hr.ec);
    1157              :     i->backoff
    1158            0 :       = GNUNET_TIME_randomized_backoff (i->backoff,
    1159              :                                         EXCHANGE_TIMEOUT);
    1160            0 :     i->last_kyc_check = GNUNET_TIME_timestamp_get ();
    1161            0 :     i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
    1162            0 :     i->auth_ok = false;
    1163            0 :     break;
    1164              :   }
    1165              : 
    1166              :   {
    1167              :     enum GNUNET_DB_QueryStatus qs;
    1168              : 
    1169           35 :     qs = TALER_MERCHANTDB_set_instance (pg,
    1170           35 :                                         i->a->instance_id);
    1171           35 :     if (qs < 0)
    1172              :     {
    1173            0 :       GNUNET_break (0);
    1174            0 :       global_ret = EXIT_FAILURE;
    1175            0 :       GNUNET_SCHEDULER_shutdown ();
    1176            0 :       return;
    1177              :     }
    1178           35 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1179              :     {
    1180            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1181              :                   "Instance `%s' vanished, discarding KYC status\n",
    1182              :                   i->a->instance_id);
    1183            0 :       finish_inquiry (i);
    1184            0 :       return;
    1185              :     }
    1186           35 :     qs = TALER_MERCHANTDB_insert_kyc_status (
    1187              :       pg,
    1188           35 :       i->a->instance_id,
    1189           35 :       &i->a->h_wire,
    1190           35 :       i->e->keys->exchange_url,
    1191              :       i->last_kyc_check,
    1192              :       i->due,
    1193              :       i->backoff,
    1194              :       i->last_http_status,
    1195              :       i->last_ec,
    1196              :       i->rule_gen,
    1197           35 :       (i->auth_ok)
    1198              :       ? &i->access_token
    1199              :       : NULL,
    1200           35 :       i->jlimits,
    1201           35 :       i->aml_review,
    1202           35 :       i->kyc_ok);
    1203           35 :     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    1204              :                   TALER_MERCHANTDB_set_instance (
    1205              :                     pg,
    1206              :                     NULL));
    1207           35 :     if (qs < 0)
    1208              :     {
    1209            0 :       GNUNET_break (0);
    1210            0 :       global_ret = EXIT_FAILURE;
    1211            0 :       GNUNET_SCHEDULER_shutdown ();
    1212            0 :       return;
    1213              :     }
    1214           35 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1215              :                 "insert_kyc_status (%s, %s, %u, %s, %s) returned %d\n",
    1216              :                 i->a->instance_id,
    1217              :                 i->e->keys->exchange_url,
    1218              :                 i->last_http_status,
    1219              :                 i->auth_ok ? "auth OK" : "auth needed",
    1220              :                 NULL == i->jlimits ? "default limits" : "custom limits",
    1221              :                 (int) qs);
    1222           35 :     i->not_first_time = true;
    1223              :   }
    1224           35 :   if (NULL != i->tos_etag)
    1225              :   {
    1226              :     /* The user accepted the terms of service early and the exchange now
    1227              :        requires acceptance: try to submit it automatically (this keeps
    1228              :        the active-inquiry slot) instead of waiting for the next check. */
    1229            0 :     start_tos_info (i);
    1230            0 :     return;
    1231              :   }
    1232           35 :   finish_inquiry (i);
    1233              : }
    1234              : 
    1235              : 
    1236              : static void
    1237           37 : inquiry_work (void *cls)
    1238              : {
    1239           37 :   struct Inquiry *i = cls;
    1240              :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
    1241              : 
    1242           37 :   i->task = NULL;
    1243           37 :   if (! GNUNET_TIME_absolute_is_past (i->due))
    1244              :   {
    1245            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1246              :                 "Will start inquiry on %s for %s in %s\n",
    1247              :                 i->a->merchant_account_uri.full_payto,
    1248              :                 i->e->keys->exchange_url,
    1249              :                 GNUNET_TIME_relative2s (
    1250              :                   GNUNET_TIME_absolute_get_remaining (i->due),
    1251              :                   true));
    1252              :     i->task
    1253            0 :       = GNUNET_SCHEDULER_add_at (i->due,
    1254              :                                  &inquiry_work,
    1255              :                                  i);
    1256            0 :     goto finish;
    1257              :   }
    1258              : 
    1259           37 :   GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
    1260           37 :   if (OPEN_INQUIRY_LIMIT <= active_inquiries)
    1261              :   {
    1262            1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1263              :                 "Not looking for work: at limit\n");
    1264            1 :     i->limited = true;
    1265            1 :     at_limit = true;
    1266            1 :     return;
    1267              :   }
    1268           36 :   at_limit = false;
    1269              :   i->timeout
    1270           36 :     = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
    1271           36 :   lpt = TALER_EXCHANGE_KLPT_NONE;
    1272           36 :   if (! i->auth_ok)
    1273           30 :     lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER;
    1274            6 :   else if (! i->kyc_ok)
    1275            1 :     lpt = TALER_EXCHANGE_KLPT_KYC_OK;
    1276            5 :   else if (i->aml_review)
    1277            3 :     lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
    1278           36 :   if (! i->not_first_time)
    1279           24 :     lpt = TALER_EXCHANGE_KLPT_NONE; /* no long polling on 1st call */
    1280           36 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1281              :               "Starting KYC status of `%s' for %s at `%s' (%d, %d, %d) using LPT %d\n",
    1282              :               i->a->merchant_account_uri.full_payto,
    1283              :               i->a->instance_id,
    1284              :               i->e->keys->exchange_url,
    1285              :               i->not_first_time,
    1286              :               i->auth_ok,
    1287              :               i->kyc_ok,
    1288              :               lpt);
    1289           72 :   i->kyc = TALER_EXCHANGE_get_kyc_check_create (
    1290              :     ctx,
    1291           36 :     i->e->keys->exchange_url,
    1292           36 :     &i->a->h_payto,
    1293           36 :     &i->a->ap);
    1294           36 :   if (NULL == i->kyc)
    1295              :   {
    1296            0 :     GNUNET_break (0);
    1297            0 :     i->due = i->timeout;
    1298              :     i->task
    1299            0 :       = GNUNET_SCHEDULER_add_at (i->due,
    1300              :                                  &inquiry_work,
    1301              :                                  i);
    1302            0 :     goto finish;
    1303              :   }
    1304           36 :   GNUNET_assert (GNUNET_OK ==
    1305              :                  TALER_EXCHANGE_get_kyc_check_set_options (
    1306              :                    i->kyc,
    1307              :                    TALER_EXCHANGE_get_kyc_check_option_known_rule_gen (
    1308              :                      i->rule_gen),
    1309              :                    TALER_EXCHANGE_get_kyc_check_option_lpt (lpt),
    1310              :                    TALER_EXCHANGE_get_kyc_check_option_timeout (
    1311              :                      i->not_first_time && (! test_mode)
    1312              :                      ? EXCHANGE_TIMEOUT
    1313              :                      : GNUNET_TIME_UNIT_ZERO)));
    1314           36 :   GNUNET_assert (TALER_EC_NONE ==
    1315              :                  TALER_EXCHANGE_get_kyc_check_start (i->kyc,
    1316              :                                                      &exchange_check_cb,
    1317              :                                                      i));
    1318           36 :   active_inquiries++;
    1319           36 : finish:
    1320           36 :   if ( (0 == active_inquiries) &&
    1321              :        (test_mode) )
    1322              :   {
    1323            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1324              :                 "No more open inquiries and in test mode. Existing.\n");
    1325            0 :     GNUNET_SCHEDULER_shutdown ();
    1326            0 :     return;
    1327              :   }
    1328              : }
    1329              : 
    1330              : 
    1331              : /**
    1332              :  * Check if the account @a could work with exchange that
    1333              :  * has keys @a keys.
    1334              :  *
    1335              :  * @param keys the keys of an exchange
    1336              :  * @param a an account
    1337              :  */
    1338              : static bool
    1339           24 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
    1340              :              const struct Account *a)
    1341              : {
    1342              :   struct TALER_NormalizedPayto np;
    1343              :   bool ret;
    1344              : 
    1345           24 :   np = TALER_payto_normalize (a->merchant_account_uri);
    1346           24 :   ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
    1347              :                                                   true,
    1348              :                                                   np);
    1349           24 :   GNUNET_free (np.normalized_payto);
    1350           24 :   return ret;
    1351              : }
    1352              : 
    1353              : 
    1354              : /**
    1355              :  * Start the KYC checking for account @a at exchange @a e.
    1356              :  *
    1357              :  * @param e an exchange
    1358              :  * @param a an account
    1359              :  */
    1360              : static void
    1361           23 : start_inquiry (struct Exchange *e,
    1362              :                struct Account *a)
    1363              : {
    1364              :   struct Inquiry *i;
    1365              :   enum GNUNET_DB_QueryStatus qs;
    1366              : 
    1367           23 :   i = GNUNET_new (struct Inquiry);
    1368           23 :   i->e = e;
    1369           23 :   i->a = a;
    1370           23 :   inquiry_key (a->instance_id,
    1371           23 :                 &a->h_wire,
    1372           23 :                 e->keys->exchange_url,
    1373              :                 &i->key);
    1374           23 :   GNUNET_assert (GNUNET_OK ==
    1375              :                  GNUNET_CONTAINER_multihashmap_put (
    1376              :                    inquiry_map,
    1377              :                    &i->key,
    1378              :                    i,
    1379              :                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    1380           23 :   GNUNET_CONTAINER_DLL_insert (a->i_head,
    1381              :                                a->i_tail,
    1382              :                                i);
    1383           23 :   qs = TALER_MERCHANTDB_set_instance (pg,
    1384           23 :                                       a->instance_id);
    1385           23 :   if (qs < 0)
    1386              :   {
    1387            0 :     GNUNET_break (0);
    1388            0 :     global_ret = EXIT_FAILURE;
    1389            0 :     GNUNET_SCHEDULER_shutdown ();
    1390            0 :     return;
    1391              :   }
    1392           23 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1393              :   {
    1394            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1395              :                 "Instance `%s' vanished, not starting inquiry\n",
    1396              :                 a->instance_id);
    1397            0 :     return;
    1398              :   }
    1399           23 :   qs = TALER_MERCHANTDB_get_kyc_status (pg,
    1400              :                                         a->merchant_account_uri,
    1401           23 :                                         a->instance_id,
    1402           23 :                                         e->keys->exchange_url,
    1403              :                                         &i->auth_ok,
    1404              :                                         &i->access_token,
    1405              :                                         &i->kyc_ok,
    1406              :                                         &i->last_http_status,
    1407              :                                         &i->last_ec,
    1408              :                                         &i->rule_gen,
    1409              :                                         &i->last_kyc_check,
    1410              :                                         &i->due,
    1411              :                                         &i->backoff,
    1412              :                                         &i->aml_review,
    1413              :                                         &i->jlimits);
    1414           23 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1415              :               "iterate_kyc_statuses (%s, %s, %s) returned %d (%u, #%llu)\n",
    1416              :               i->a->instance_id,
    1417              :               e->keys->exchange_url,
    1418              :               a->merchant_account_uri.full_payto,
    1419              :               (int) qs,
    1420              :               i->last_http_status,
    1421              :               (unsigned long long) i->rule_gen);
    1422           23 :   if (qs < 0)
    1423              :   {
    1424            0 :     GNUNET_break (0);
    1425            0 :     global_ret = EXIT_FAILURE;
    1426            0 :     GNUNET_SCHEDULER_shutdown ();
    1427            0 :     return;
    1428              :   }
    1429           23 :   if (qs > 0)
    1430            0 :     i->not_first_time = true;
    1431           23 :   if (TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE == i->last_ec)
    1432              :   {
    1433              :     /* Eligibility was restored. Do not retain the ineligible status's
    1434              :        infinite delay or backoff. */
    1435            0 :     i->due = GNUNET_TIME_UNIT_ZERO_ABS;
    1436            0 :     i->backoff = GNUNET_TIME_UNIT_ZERO;
    1437            0 :     i->not_first_time = false;
    1438              :   }
    1439           23 :   if (GNUNET_YES == test_mode)
    1440            1 :     i->due = GNUNET_TIME_UNIT_ZERO_ABS; /* immediately */
    1441           23 :   inquiry_work (i);
    1442              : }
    1443              : 
    1444              : 
    1445              : /**
    1446              :  * Stop KYC inquiry @a i.
    1447              :  *
    1448              :  * @param[in] i the inquiry to stop
    1449              :  */
    1450              : static void
    1451           24 : stop_inquiry (struct Inquiry *i)
    1452              : {
    1453           24 :   struct Account *a = i->a;
    1454              : 
    1455           24 :   GNUNET_assert (GNUNET_YES ==
    1456              :                  GNUNET_CONTAINER_multihashmap_remove (inquiry_map,
    1457              :                                                        &i->key,
    1458              :                                                        i));
    1459           24 :   GNUNET_CONTAINER_DLL_remove (a->i_head,
    1460              :                                a->i_tail,
    1461              :                                i);
    1462           24 :   if (NULL != i->task)
    1463              :   {
    1464           23 :     GNUNET_SCHEDULER_cancel (i->task);
    1465           23 :     i->task = NULL;
    1466              :   }
    1467           24 :   if (NULL != i->kyc)
    1468              :   {
    1469            1 :     TALER_EXCHANGE_get_kyc_check_cancel (i->kyc);
    1470            1 :     i->kyc = NULL;
    1471              :   }
    1472           24 :   if (NULL != i->kyc_info)
    1473              :   {
    1474            0 :     TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info);
    1475            0 :     i->kyc_info = NULL;
    1476              :   }
    1477           24 :   if (NULL != i->tos_upload)
    1478              :   {
    1479            0 :     TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload);
    1480            0 :     i->tos_upload = NULL;
    1481              :   }
    1482           24 :   GNUNET_free (i->tos_etag);
    1483           24 :   if (NULL != i->jlimits)
    1484              :   {
    1485            1 :     json_decref (i->jlimits);
    1486            1 :     i->jlimits = NULL;
    1487              :   }
    1488           24 :   GNUNET_free (i);
    1489           24 : }
    1490              : 
    1491              : 
    1492              : /**
    1493              :  * Stop KYC inquiry for account @a at exchange @a e.
    1494              :  *
    1495              :  * @param e an exchange
    1496              :  * @param a an account
    1497              :  */
    1498              : static void
    1499            0 : stop_inquiry_at (struct Exchange *e,
    1500              :                  struct Account *a)
    1501              : {
    1502            0 :   for (struct Inquiry *i = a->i_head;
    1503            0 :        NULL != i;
    1504            0 :        i = i->next)
    1505              :   {
    1506            0 :     if (e == i->e)
    1507              :     {
    1508            0 :       stop_inquiry (i);
    1509            0 :       return;
    1510              :     }
    1511              :   }
    1512              :   /* strange, there should have been a match! */
    1513            0 :   GNUNET_break (0);
    1514              : }
    1515              : 
    1516              : 
    1517              : /**
    1518              :  * Set the account @a h_wire of @a instance_id to be ineligible
    1519              :  * for the exchange at @a exchange_url and thus no need to do KYC checks.
    1520              :  *
    1521              :  * @param instance_id instance that has the account
    1522              :  * @param exchange_url base URL of the exchange
    1523              :  * @param h_wire hash of the merchant bank account that is ineligible
    1524              :  */
    1525              : static void
    1526            1 : flag_ineligible (const char *instance_id,
    1527              :                  const char *exchange_url,
    1528              :                  const struct TALER_MerchantWireHashP *h_wire)
    1529              : {
    1530              :   enum GNUNET_DB_QueryStatus qs;
    1531              : 
    1532            1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1533              :               "Account %s not eligible at exchange %s\n",
    1534              :               TALER_B2S (h_wire),
    1535              :               exchange_url);
    1536            1 :   qs = TALER_MERCHANTDB_set_instance (pg,
    1537              :                                       instance_id);
    1538            1 :   if (qs < 0)
    1539              :   {
    1540            0 :     GNUNET_break (0);
    1541            0 :     global_ret = EXIT_FAILURE;
    1542            0 :     GNUNET_SCHEDULER_shutdown ();
    1543            0 :     return;
    1544              :   }
    1545            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1546              :   {
    1547            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1548              :                 "Instance `%s' vanished, not flagging account\n",
    1549              :                 instance_id);
    1550            0 :     return;
    1551              :   }
    1552            1 :   qs = TALER_MERCHANTDB_insert_kyc_status (
    1553              :     pg,
    1554              :     instance_id,
    1555              :     h_wire,
    1556              :     exchange_url,
    1557              :     GNUNET_TIME_timestamp_get (),
    1558            1 :     GNUNET_TIME_UNIT_FOREVER_ABS,
    1559            1 :     GNUNET_TIME_UNIT_FOREVER_REL,
    1560              :     0,
    1561              :     TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE,
    1562              :     0,
    1563              :     NULL,
    1564              :     NULL,
    1565              :     false,
    1566              :     false);
    1567            1 :   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    1568              :                 TALER_MERCHANTDB_set_instance (
    1569              :                   pg,
    1570              :                   NULL));
    1571            1 :   if (qs < 0)
    1572              :   {
    1573            0 :     GNUNET_break (0);
    1574            0 :     global_ret = EXIT_FAILURE;
    1575            0 :     GNUNET_SCHEDULER_shutdown ();
    1576            0 :     return;
    1577              :   }
    1578            1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1579              :               "insert_kyc_status (%s) returned %d\n",
    1580              :               exchange_url,
    1581              :               (int) qs);
    1582              : }
    1583              : 
    1584              : 
    1585              : /**
    1586              :  * Start inquries for all exchanges on account @a a.
    1587              :  *
    1588              :  * @param a an account
    1589              :  */
    1590              : static void
    1591           28 : start_inquiries (struct Account *a)
    1592              : {
    1593           28 :   for (struct Exchange *e = e_head;
    1594           52 :        NULL != e;
    1595           24 :        e = e->next)
    1596              :   {
    1597           24 :     if (is_eligible (e->keys,
    1598              :                      a))
    1599              :     {
    1600           23 :       start_inquiry (e,
    1601              :                      a);
    1602              :     }
    1603              :     else
    1604              :     {
    1605            1 :       flag_ineligible (a->instance_id,
    1606            1 :                        e->keys->exchange_url,
    1607            1 :                        &a->h_wire);
    1608              :     }
    1609              :   }
    1610           28 : }
    1611              : 
    1612              : 
    1613              : /**
    1614              :  * Stop all inquries involving account @a a.
    1615              :  *
    1616              :  * @param a an account
    1617              :  */
    1618              : static void
    1619           59 : stop_inquiries (struct Account *a)
    1620              : {
    1621              :   struct Inquiry *i;
    1622              : 
    1623           82 :   while (NULL != (i = a->i_head))
    1624           23 :     stop_inquiry (i);
    1625           59 : }
    1626              : 
    1627              : 
    1628              : /**
    1629              :  * Callback invoked with information about a bank account.
    1630              :  *
    1631              :  * @param cls closure
    1632              :  * @param merchant_priv private key of the merchant instance
    1633              :  * @param ad details about the account
    1634              :  */
    1635              : static void
    1636           50 : account_cb (
    1637              :   void *cls,
    1638              :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
    1639              :   const struct TALER_MERCHANTDB_AccountDetails *ad)
    1640              : {
    1641           50 :   struct TALER_FullPayto payto_uri = ad->payto_uri;
    1642              : 
    1643           50 :   if (! ad->active)
    1644           22 :     return;
    1645           49 :   if (NULL == merchant_priv)
    1646            0 :     return; /* instance was deleted */
    1647           49 :   for (struct Account *a = a_head;
    1648           74 :        NULL != a;
    1649           25 :        a = a->next)
    1650              :   {
    1651           46 :     if ( (0 ==
    1652           46 :           TALER_full_payto_cmp (payto_uri,
    1653           35 :                                 a->merchant_account_uri)) &&
    1654              :          (0 ==
    1655           35 :           GNUNET_memcmp (&a->h_wire,
    1656           21 :                          &ad->h_wire)) &&
    1657              :          (0 ==
    1658           21 :           strcmp (ad->instance_id,
    1659           21 :                   a->instance_id)) )
    1660              :     {
    1661           21 :       a->account_gen = database_gen;
    1662           21 :       return;
    1663              :     }
    1664              :   }
    1665              :   {
    1666           28 :     struct Account *a = GNUNET_new (struct Account);
    1667              : 
    1668           28 :     a->account_gen = database_gen;
    1669              :     a->merchant_account_uri.full_payto
    1670           28 :       = GNUNET_strdup (ad->payto_uri.full_payto);
    1671              :     a->instance_id
    1672           28 :       = GNUNET_strdup (ad->instance_id);
    1673              :     a->h_wire
    1674           28 :       = ad->h_wire;
    1675              :     a->ap.merchant_priv
    1676           28 :       = *merchant_priv;
    1677           28 :     TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
    1678              :                                          &a->h_payto);
    1679           28 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1680              :                 "Found account %s of instance %s with H_PAYTO %s\n",
    1681              :                 ad->payto_uri.full_payto,
    1682              :                 ad->instance_id,
    1683              :                 GNUNET_sh2s (&a->h_payto.hash));
    1684           28 :     GNUNET_CONTAINER_DLL_insert (a_head,
    1685              :                                  a_tail,
    1686              :                                  a);
    1687           28 :     start_inquiries (a);
    1688              :   }
    1689              : }
    1690              : 
    1691              : 
    1692              : /**
    1693              :  * The set of bank accounts has changed, update our
    1694              :  * list of active inquiries.
    1695              :  *
    1696              :  * @param cls unused
    1697              :  */
    1698              : static void
    1699           64 : find_accounts (void *cls)
    1700              : {
    1701              :   enum GNUNET_DB_QueryStatus qs;
    1702              : 
    1703              :   (void) cls;
    1704           64 :   account_task = NULL;
    1705           64 :   database_gen++;
    1706           64 :   qs = TALER_MERCHANTDB_iterate_accounts (pg,
    1707              :                                           &account_cb,
    1708              :                                           NULL);
    1709           64 :   if (qs < 0)
    1710              :   {
    1711            0 :     GNUNET_break (0);
    1712            0 :     global_ret = EXIT_FAILURE;
    1713            0 :     GNUNET_SCHEDULER_shutdown ();
    1714            0 :     return;
    1715              :   }
    1716           64 :   for (struct Account *a = a_head;
    1717          144 :        NULL != a;
    1718           80 :        a = a->next)
    1719              :   {
    1720           80 :     if (a->account_gen < database_gen)
    1721           31 :       stop_inquiries (a);
    1722              :   }
    1723           64 :   if ( (! at_limit) &&
    1724           64 :        (0 == active_inquiries) &&
    1725              :        (test_mode) )
    1726              :   {
    1727            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1728              :                 "No more open inquiries and in test mode. Existing.\n");
    1729            0 :     GNUNET_SCHEDULER_shutdown ();
    1730            0 :     return;
    1731              :   }
    1732              : }
    1733              : 
    1734              : 
    1735              : /**
    1736              :  * Function called when transfers are added to the merchant database.  We look
    1737              :  * for more work.
    1738              :  *
    1739              :  * @param cls closure (NULL)
    1740              :  * @param extra additional event data provided
    1741              :  * @param extra_size number of bytes in @a extra
    1742              :  */
    1743              : static void
    1744           46 : account_changed (void *cls,
    1745              :                  const void *extra,
    1746              :                  size_t extra_size)
    1747              : {
    1748              :   (void) cls;
    1749              :   (void) extra;
    1750              :   (void) extra_size;
    1751           46 :   if (NULL != account_task)
    1752            1 :     return;
    1753           45 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1754              :               "Received account change notification: reloading accounts\n");
    1755              :   account_task
    1756           45 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    1757              :                                 NULL);
    1758              : }
    1759              : 
    1760              : 
    1761              : /**
    1762              :  * Interact with the database to get the current set
    1763              :  * of exchange keys known to us.
    1764              :  *
    1765              :  * @param exchange_url the exchange URL to check
    1766              :  */
    1767              : static void
    1768           32 : find_keys (const char *exchange_url)
    1769              : {
    1770              :   enum GNUNET_DB_QueryStatus qs;
    1771              :   struct TALER_EXCHANGE_Keys *keys;
    1772              :   struct Exchange *e;
    1773              :   struct GNUNET_TIME_Absolute first_retry;
    1774              : 
    1775           32 :   qs = TALER_MERCHANTDB_get_exchange_keys (pg,
    1776              :                                            exchange_url,
    1777              :                                            &first_retry,
    1778              :                                            &keys);
    1779           32 :   if (qs < 0)
    1780              :   {
    1781            0 :     GNUNET_break (0);
    1782            0 :     global_ret = EXIT_FAILURE;
    1783            0 :     GNUNET_SCHEDULER_shutdown ();
    1784           18 :     return;
    1785              :   }
    1786           32 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
    1787           17 :        (NULL == keys) )
    1788              :   {
    1789           18 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1790              :                 "No %s/keys yet!\n",
    1791              :                 exchange_url);
    1792           18 :     return;
    1793              :   }
    1794           14 :   for (e = e_head; NULL != e; e = e->next)
    1795              :   {
    1796            0 :     if (0 == strcmp (e->keys->exchange_url,
    1797            0 :                      keys->exchange_url))
    1798              :     {
    1799            0 :       struct TALER_EXCHANGE_Keys *old_keys = e->keys;
    1800              : 
    1801            0 :       e->keys = keys;
    1802            0 :       for (struct Account *a = a_head;
    1803            0 :            NULL != a;
    1804            0 :            a = a->next)
    1805              :       {
    1806              :         bool was_eligible;
    1807              :         bool now_eligible;
    1808              : 
    1809            0 :         if (a->account_gen != database_gen)
    1810            0 :           continue;
    1811            0 :         was_eligible = is_eligible (old_keys,
    1812              :                                     a);
    1813            0 :         now_eligible = is_eligible (keys,
    1814              :                                     a);
    1815              : 
    1816            0 :         if (was_eligible == now_eligible)
    1817            0 :           continue; /* no change, do nothing */
    1818            0 :         if (was_eligible)
    1819              :         {
    1820            0 :           flag_ineligible (a->instance_id,
    1821            0 :                            keys->exchange_url,
    1822            0 :                            &a->h_wire);
    1823            0 :           stop_inquiry_at (e,
    1824              :                            a);
    1825              :         }
    1826              :         else /* is_eligible */
    1827            0 :           start_inquiry (e,
    1828              :                          a);
    1829              :       }
    1830            0 :       TALER_EXCHANGE_keys_decref (old_keys);
    1831            0 :       return;
    1832              :     }
    1833              :   }
    1834           14 :   e = GNUNET_new (struct Exchange);
    1835           14 :   e->keys = keys;
    1836           14 :   GNUNET_CONTAINER_DLL_insert (e_head,
    1837              :                                e_tail,
    1838              :                                e);
    1839           14 :   for (struct Account *a = a_head;
    1840           14 :        NULL != a;
    1841            0 :        a = a->next)
    1842              :   {
    1843            0 :     if (a->account_gen != database_gen)
    1844            0 :       continue;
    1845            0 :     if (is_eligible (e->keys,
    1846              :                      a))
    1847              :     {
    1848            0 :       start_inquiry (e,
    1849              :                      a);
    1850              :     }
    1851              :     else
    1852              :     {
    1853            0 :       flag_ineligible (a->instance_id,
    1854            0 :                        e->keys->exchange_url,
    1855            0 :                        &a->h_wire);
    1856              :     }
    1857              :   }
    1858              : }
    1859              : 
    1860              : 
    1861              : /**
    1862              :  * Function called when keys were changed in the
    1863              :  * merchant database. Updates ours.
    1864              :  *
    1865              :  * @param cls closure (NULL)
    1866              :  * @param extra additional event data provided
    1867              :  * @param extra_size number of bytes in @a extra
    1868              :  */
    1869              : static void
    1870           13 : keys_changed (void *cls,
    1871              :               const void *extra,
    1872              :               size_t extra_size)
    1873              : {
    1874           13 :   const char *url = extra;
    1875              : 
    1876              :   (void) cls;
    1877           13 :   if ( (NULL == extra) ||
    1878              :        (0 == extra_size) )
    1879              :   {
    1880            0 :     GNUNET_break (0);
    1881            0 :     global_ret = EXIT_FAILURE;
    1882            0 :     GNUNET_SCHEDULER_shutdown ();
    1883            0 :     return;
    1884              :   }
    1885           13 :   if ('\0' != url[extra_size - 1])
    1886              :   {
    1887            0 :     GNUNET_break (0);
    1888            0 :     global_ret = EXIT_FAILURE;
    1889            0 :     GNUNET_SCHEDULER_shutdown ();
    1890            0 :     return;
    1891              :   }
    1892           13 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1893              :               "Received keys change notification: reload `%s'\n",
    1894              :               url);
    1895           13 :   find_keys (url);
    1896              : }
    1897              : 
    1898              : 
    1899              : /**
    1900              :  * Function called when a KYC rule was triggered by
    1901              :  * a transaction and we need to get the latest KYC
    1902              :  * status immediately.
    1903              :  *
    1904              :  * @param cls closure (NULL)
    1905              :  * @param extra additional event data provided
    1906              :  * @param extra_size number of bytes in @a extra
    1907              :  */
    1908              : static void
    1909            0 : rule_triggered (void *cls,
    1910              :                 const void *extra,
    1911              :                 size_t extra_size)
    1912              : {
    1913            0 :   const char *text = extra;
    1914              :   const char *space;
    1915              :   struct TALER_MerchantWireHashP h_wire;
    1916              :   const char *exchange_url;
    1917              : 
    1918              :   (void) cls;
    1919            0 :   if ( (NULL == extra) ||
    1920              :        (0 == extra_size) )
    1921              :   {
    1922            0 :     GNUNET_break (0);
    1923            0 :     global_ret = EXIT_FAILURE;
    1924            0 :     GNUNET_SCHEDULER_shutdown ();
    1925            0 :     return;
    1926              :   }
    1927            0 :   if ('\0' != text[extra_size - 1])
    1928              :   {
    1929            0 :     GNUNET_break (0);
    1930            0 :     global_ret = EXIT_FAILURE;
    1931            0 :     GNUNET_SCHEDULER_shutdown ();
    1932            0 :     return;
    1933              :   }
    1934            0 :   space = memchr (extra,
    1935              :                   ' ',
    1936              :                   extra_size);
    1937            0 :   if (NULL == space)
    1938              :   {
    1939            0 :     GNUNET_break (0);
    1940            0 :     global_ret = EXIT_FAILURE;
    1941            0 :     GNUNET_SCHEDULER_shutdown ();
    1942            0 :     return;
    1943              :   }
    1944            0 :   if (GNUNET_OK !=
    1945            0 :       GNUNET_STRINGS_string_to_data (extra,
    1946            0 :                                      space - text,
    1947              :                                      &h_wire,
    1948              :                                      sizeof (h_wire)))
    1949              :   {
    1950            0 :     GNUNET_break (0);
    1951            0 :     global_ret = EXIT_FAILURE;
    1952            0 :     GNUNET_SCHEDULER_shutdown ();
    1953            0 :     return;
    1954              :   }
    1955            0 :   exchange_url = &space[1];
    1956            0 :   if (! TALER_is_web_url (exchange_url))
    1957              :   {
    1958            0 :     GNUNET_break (0);
    1959            0 :     global_ret = EXIT_FAILURE;
    1960            0 :     GNUNET_SCHEDULER_shutdown ();
    1961            0 :     return;
    1962              :   }
    1963              : 
    1964            0 :   for (struct Account *a = a_head;
    1965            0 :        NULL != a;
    1966            0 :        a = a->next)
    1967              :   {
    1968            0 :     if (0 !=
    1969            0 :         GNUNET_memcmp (&h_wire,
    1970              :                        &a->h_wire))
    1971            0 :       continue;
    1972            0 :     for (struct Inquiry *i = a->i_head;
    1973            0 :          NULL != i;
    1974            0 :          i = i->next)
    1975              :     {
    1976            0 :       if (0 != strcmp (exchange_url,
    1977            0 :                        i->e->keys->exchange_url))
    1978            0 :         continue;
    1979            0 :       i->kyc_ok = false;
    1980            0 :       if (inquiry_busy (i))
    1981              :       {
    1982            0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1983              :                     "/kyc-check already running for %s\n",
    1984              :                     text);
    1985            0 :         return;
    1986              :       }
    1987            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1988              :                   "Starting %skyc-check for `%s' due to KYC rule trigger\n",
    1989              :                   exchange_url,
    1990              :                   i->a->merchant_account_uri.full_payto);
    1991            0 :       request_inquiry (i);
    1992            0 :       return;
    1993              :     }
    1994              :   }
    1995            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1996              :               "KYC rule trigger notification `%s' matches none of our accounts\n",
    1997              :               text);
    1998              : }
    1999              : 
    2000              : 
    2001              : /**
    2002              :  * Function called on each configuration section. Finds sections
    2003              :  * about exchanges, parses the entries.
    2004              :  *
    2005              :  * @param cls NULL
    2006              :  * @param section name of the section
    2007              :  */
    2008              : static void
    2009          779 : accept_exchanges (void *cls,
    2010              :                   const char *section)
    2011              : {
    2012              :   char *url;
    2013              : 
    2014              :   (void) cls;
    2015          779 :   if (0 !=
    2016          779 :       strncasecmp (section,
    2017              :                    "merchant-exchange-",
    2018              :                    strlen ("merchant-exchange-")))
    2019          760 :     return;
    2020           57 :   if (GNUNET_YES ==
    2021           57 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    2022              :                                             section,
    2023              :                                             "DISABLED"))
    2024           38 :     return;
    2025           19 :   if (GNUNET_OK !=
    2026           19 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    2027              :                                              section,
    2028              :                                              "EXCHANGE_BASE_URL",
    2029              :                                              &url))
    2030              :   {
    2031            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2032              :                                section,
    2033              :                                "EXCHANGE_BASE_URL");
    2034            0 :     global_ret = EXIT_NOTCONFIGURED;
    2035            0 :     GNUNET_SCHEDULER_shutdown ();
    2036            0 :     return;
    2037              :   }
    2038           19 :   find_keys (url);
    2039           19 :   GNUNET_free (url);
    2040              : }
    2041              : 
    2042              : 
    2043              : /**
    2044              :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
    2045              :  *
    2046              :  * @param cls closure (NULL)
    2047              :  */
    2048              : static void
    2049           19 : shutdown_task (void *cls)
    2050              : {
    2051              :   (void) cls;
    2052           19 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2053              :               "Running shutdown\n");
    2054           19 :   if (NULL != refresh_task)
    2055              :   {
    2056            0 :     GNUNET_SCHEDULER_cancel (refresh_task);
    2057            0 :     refresh_task = NULL;
    2058              :   }
    2059           19 :   while (NULL != refresh_head)
    2060              :   {
    2061            0 :     struct Refresh *r = refresh_head;
    2062              : 
    2063            0 :     GNUNET_CONTAINER_DLL_remove (refresh_head, refresh_tail, r);
    2064            0 :     GNUNET_free (r);
    2065              :   }
    2066           19 :   GNUNET_CONTAINER_multihashmap_destroy (refresh_map);
    2067           33 :   while (NULL != e_head)
    2068              :   {
    2069           14 :     struct Exchange *e = e_head;
    2070              : 
    2071           14 :     if (NULL != e->keys)
    2072              :     {
    2073           14 :       TALER_EXCHANGE_keys_decref (e->keys);
    2074           14 :       e->keys = NULL;
    2075              :     }
    2076           14 :     GNUNET_CONTAINER_DLL_remove (e_head,
    2077              :                                  e_tail,
    2078              :                                  e);
    2079           14 :     GNUNET_free (e);
    2080              :   }
    2081           47 :   while (NULL != a_head)
    2082              :   {
    2083           28 :     struct Account *a = a_head;
    2084              : 
    2085           28 :     stop_inquiries (a);
    2086           28 :     GNUNET_CONTAINER_DLL_remove (a_head,
    2087              :                                  a_tail,
    2088              :                                  a);
    2089           28 :     GNUNET_free (a->merchant_account_uri.full_payto);
    2090           28 :     GNUNET_free (a->instance_id);
    2091           28 :     GNUNET_free (a);
    2092              :   }
    2093           19 :   GNUNET_CONTAINER_multihashmap_destroy (inquiry_map);
    2094           19 :   if (NULL != eh_accounts)
    2095              :   {
    2096           19 :     TALER_MERCHANTDB_event_listen_cancel (eh_accounts);
    2097           19 :     eh_accounts = NULL;
    2098              :   }
    2099           19 :   if (NULL != account_task)
    2100              :   {
    2101            0 :     GNUNET_SCHEDULER_cancel (account_task);
    2102            0 :     account_task = NULL;
    2103              :   }
    2104           19 :   if (NULL != eh_keys)
    2105              :   {
    2106           19 :     TALER_MERCHANTDB_event_listen_cancel (eh_keys);
    2107           19 :     eh_keys = NULL;
    2108              :   }
    2109           19 :   if (NULL != eh_rule)
    2110              :   {
    2111           19 :     TALER_MERCHANTDB_event_listen_cancel (eh_rule);
    2112           19 :     eh_rule = NULL;
    2113              :   }
    2114           19 :   if (NULL != eh_update_forced)
    2115              :   {
    2116           19 :     TALER_MERCHANTDB_event_listen_cancel (eh_update_forced);
    2117           19 :     eh_update_forced = NULL;
    2118              :   }
    2119           19 :   if (NULL != keys_rule)
    2120              :   {
    2121            0 :     TALER_MERCHANTDB_event_listen_cancel (keys_rule);
    2122            0 :     keys_rule = NULL;
    2123              :   }
    2124           19 :   if (NULL != pg)
    2125              :   {
    2126           19 :     TALER_MERCHANTDB_disconnect (pg);
    2127           19 :     pg = NULL;
    2128              :   }
    2129           19 :   cfg = NULL;
    2130           19 :   if (NULL != ctx)
    2131              :   {
    2132           19 :     GNUNET_CURL_fini (ctx);
    2133           19 :     ctx = NULL;
    2134              :   }
    2135           19 :   if (NULL != rc)
    2136              :   {
    2137           19 :     GNUNET_CURL_gnunet_rc_destroy (rc);
    2138           19 :     rc = NULL;
    2139              :   }
    2140           19 : }
    2141              : 
    2142              : 
    2143              : /**
    2144              :  * Function called when we urgently need to re-check the KYC status
    2145              :  * of some account. Finds the respective inquiry and re-launches
    2146              :  * the check, unless we are already doing it.
    2147              :  *
    2148              :  * @param cls NULL
    2149              :  * @param instance_id instance for which to force the check
    2150              :  * @param exchange_url base URL of the exchange to check
    2151              :  * @param h_wire hash of the wire account to check KYC status for
    2152              :  */
    2153              : static void
    2154           13 : force_check_now (void *cls,
    2155              :                  const char *instance_id,
    2156              :                  const char *exchange_url,
    2157              :                  const struct TALER_MerchantWireHashP *h_wire)
    2158              : {
    2159              :   struct GNUNET_HashCode key;
    2160              :   struct Inquiry *i;
    2161              : 
    2162              :   (void) cls;
    2163           13 :   inquiry_key (instance_id, h_wire, exchange_url, &key);
    2164           13 :   i = GNUNET_CONTAINER_multihashmap_get (inquiry_map, &key);
    2165           13 :   if (NULL == i)
    2166              :   {
    2167              :     /* Account discovery and exchange-key loading recover the persisted due
    2168              :        time. Absence from the index does not prove the account ineligible. */
    2169            1 :     return;
    2170              :   }
    2171           12 :   GNUNET_assert (0 == strcmp (instance_id, i->a->instance_id));
    2172           12 :   GNUNET_assert (0 == GNUNET_memcmp (h_wire, &i->a->h_wire));
    2173           12 :   GNUNET_assert (0 == strcmp (exchange_url, i->e->keys->exchange_url));
    2174           12 :   if (i->a->account_gen != database_gen)
    2175            0 :     return;
    2176           12 :   request_inquiry (i);
    2177              : }
    2178              : 
    2179              : 
    2180              : /**
    2181              :  * Process one instance per scheduler turn, after pending account discovery.
    2182              :  */
    2183              : static void
    2184            8 : process_refresh (void *cls)
    2185              : {
    2186            8 :   struct Refresh *r = refresh_head;
    2187              :   enum GNUNET_DB_QueryStatus qs;
    2188              : 
    2189              :   (void) cls;
    2190            8 :   refresh_task = NULL;
    2191            8 :   if (NULL != account_task)
    2192              :   {
    2193            0 :     refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
    2194            0 :     return;
    2195              :   }
    2196            8 :   GNUNET_assert (NULL != r);
    2197            8 :   GNUNET_CONTAINER_DLL_remove (refresh_head, refresh_tail, r);
    2198            8 :   GNUNET_assert (GNUNET_YES ==
    2199              :                  GNUNET_CONTAINER_multihashmap_remove (refresh_map,
    2200              :                                                        &r->key,
    2201              :                                                        r));
    2202            8 :   qs = TALER_MERCHANTDB_iterate_outdated_kyc_statuses (
    2203              :     pg,
    2204              :     r->merchant_serial,
    2205              :     &force_check_now,
    2206              :     NULL);
    2207            8 :   GNUNET_free (r);
    2208            8 :   if (qs < 0)
    2209              :   {
    2210            0 :     GNUNET_break (0);
    2211            0 :     global_ret = EXIT_FAILURE;
    2212            0 :     GNUNET_SCHEDULER_shutdown ();
    2213            0 :     return;
    2214              :   }
    2215            8 :   if (NULL != refresh_head)
    2216            0 :     refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
    2217              : }
    2218              : 
    2219              : 
    2220              : /**
    2221              :  * Queue an instance refresh. The notification payload is exactly one
    2222              :  * unsigned 64-bit merchant serial in network byte order.
    2223              :  */
    2224              : static void
    2225           14 : update_forced (void *cls,
    2226              :                const void *extra,
    2227              :                size_t extra_size)
    2228              : {
    2229              :   uint64_t serial;
    2230              :   struct GNUNET_HashCode key;
    2231              :   struct Refresh *r;
    2232              : 
    2233              :   (void) cls;
    2234           14 :   if ( (NULL == extra) || (sizeof (serial) != extra_size) )
    2235              :   {
    2236            2 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2237              :                 "Ignoring KYC refresh notification with invalid payload size\n");
    2238            4 :     return;
    2239              :   }
    2240           12 :   memcpy (&serial, extra, sizeof (serial));
    2241           12 :   GNUNET_CRYPTO_hash (&serial, sizeof (serial), &key);
    2242           12 :   serial = GNUNET_ntohll (serial);
    2243           12 :   if ( (0 == serial) || (serial > INT64_MAX) )
    2244              :   {
    2245            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2246              :                 "Ignoring KYC refresh notification with invalid instance serial\n");
    2247            1 :     return;
    2248              :   }
    2249           11 :   if (GNUNET_CONTAINER_multihashmap_contains (refresh_map, &key))
    2250            1 :     return;
    2251           10 :   r = GNUNET_new (struct Refresh);
    2252           10 :   r->key = key;
    2253           10 :   r->merchant_serial = serial;
    2254           10 :   GNUNET_CONTAINER_DLL_insert_tail (refresh_head, refresh_tail, r);
    2255           10 :   GNUNET_assert (GNUNET_OK ==
    2256              :                  GNUNET_CONTAINER_multihashmap_put (
    2257              :                    refresh_map, &r->key, r,
    2258              :                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    2259           10 :   if (NULL == refresh_task)
    2260            9 :     refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
    2261              : }
    2262              : 
    2263              : /**
    2264              :  * First task.
    2265              :  *
    2266              :  * @param cls closure, NULL
    2267              :  * @param args remaining command-line arguments
    2268              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    2269              :  * @param c configuration
    2270              :  */
    2271              : static void
    2272           19 : run (void *cls,
    2273              :      char *const *args,
    2274              :      const char *cfgfile,
    2275              :      const struct GNUNET_CONFIGURATION_Handle *c)
    2276              : {
    2277              :   (void) args;
    2278              :   (void) cfgfile;
    2279              : 
    2280           19 :   cfg = c;
    2281           19 :   inquiry_map = GNUNET_CONTAINER_multihashmap_create (256, GNUNET_YES);
    2282           19 :   refresh_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
    2283           19 :   TALER_EXCHANGE_setup (enable_h3
    2284           19 :                         ? TALER_EXCHANGE_GO_ENABLE_HTTP3
    2285              :                         : TALER_EXCHANGE_GO_FORCE_HTTP1_1);
    2286           19 :   if (GNUNET_OK !=
    2287           19 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    2288              :                                            "merchant-kyccheck",
    2289              :                                            "AML_FREQ",
    2290              :                                            &aml_freq))
    2291              :   {
    2292            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    2293              :                                "merchant-kyccheck",
    2294              :                                "AML_FREQ");
    2295              :     /* use default */
    2296            0 :     aml_freq = AML_FREQ;
    2297              :   }
    2298           19 :   if (GNUNET_OK !=
    2299           19 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    2300              :                                            "merchant-kyccheck",
    2301              :                                            "AML_LOW_FREQ",
    2302              :                                            &aml_low_freq))
    2303              :   {
    2304            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    2305              :                                "merchant-kyccheck",
    2306              :                                "AML_LOW_FREQ");
    2307              :     /* use default */
    2308            0 :     aml_low_freq = AML_LOW_FREQ;
    2309              :   }
    2310           19 :   if (GNUNET_TIME_relative_cmp (aml_low_freq,
    2311              :                                 <,
    2312              :                                 aml_freq))
    2313              :   {
    2314            0 :     aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq,
    2315              :                                                   10);
    2316            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2317              :                 "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n",
    2318              :                 GNUNET_TIME_relative2s (aml_low_freq,
    2319              :                                         true));
    2320              :   }
    2321           19 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    2322              :                                  NULL);
    2323           19 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    2324              :                           &rc);
    2325           19 :   if (NULL == ctx)
    2326              :   {
    2327            0 :     GNUNET_break (0);
    2328            0 :     GNUNET_SCHEDULER_shutdown ();
    2329            0 :     global_ret = EXIT_FAILURE;
    2330            0 :     return;
    2331              :   }
    2332           19 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    2333           19 :   if (NULL ==
    2334           19 :       (pg = TALER_MERCHANTDB_connect (cfg)))
    2335              :   {
    2336            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2337              :                 "Failed to initialize DB subsystem. Consider running taler-merchant-dbconfig.\n");
    2338            0 :     GNUNET_SCHEDULER_shutdown ();
    2339            0 :     global_ret = EXIT_FAILURE;
    2340            0 :     return;
    2341              :   }
    2342              :   {
    2343           19 :     struct GNUNET_DB_EventHeaderP es = {
    2344           19 :       .size = htons (sizeof (es)),
    2345           19 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    2346              :     };
    2347              : 
    2348              :     eh_keys
    2349           38 :       = TALER_MERCHANTDB_event_listen (pg,
    2350              :                                        &es,
    2351           19 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2352              :                                        &keys_changed,
    2353              :                                        NULL);
    2354              :   }
    2355              :   {
    2356           19 :     struct GNUNET_DB_EventHeaderP es = {
    2357           19 :       .size = htons (sizeof (es)),
    2358           19 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_UPDATE_FORCED)
    2359              :     };
    2360              : 
    2361              :     eh_update_forced
    2362           38 :       = TALER_MERCHANTDB_event_listen (pg,
    2363              :                                        &es,
    2364           19 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2365              :                                        &update_forced,
    2366              :                                        NULL);
    2367              :   }
    2368              :   {
    2369           19 :     struct GNUNET_DB_EventHeaderP es = {
    2370           19 :       .size = htons (sizeof (es)),
    2371           19 :       .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
    2372              :     };
    2373              : 
    2374              :     eh_rule
    2375           38 :       = TALER_MERCHANTDB_event_listen (pg,
    2376              :                                        &es,
    2377           19 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2378              :                                        &rule_triggered,
    2379              :                                        NULL);
    2380              :   }
    2381           19 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    2382              :                                          &accept_exchanges,
    2383              :                                          NULL);
    2384              :   {
    2385           19 :     struct GNUNET_DB_EventHeaderP es = {
    2386           19 :       .size = htons (sizeof (es)),
    2387           19 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
    2388              :     };
    2389              : 
    2390              :     eh_accounts
    2391           38 :       = TALER_MERCHANTDB_event_listen (pg,
    2392              :                                        &es,
    2393           19 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2394              :                                        &account_changed,
    2395              :                                        NULL);
    2396              :   }
    2397           19 :   GNUNET_assert (NULL == account_task);
    2398              :   account_task
    2399           19 :     = GNUNET_SCHEDULER_add_now (&find_accounts,
    2400              :                                 NULL);
    2401              : }
    2402              : 
    2403              : 
    2404              : /**
    2405              :  * The main function of taler-merchant-kyccheck
    2406              :  *
    2407              :  * @param argc number of arguments from the command line
    2408              :  * @param argv command line arguments
    2409              :  * @return 0 ok, 1 on error
    2410              :  */
    2411              : int
    2412           19 : main (int argc,
    2413              :       char *const *argv)
    2414              : {
    2415           19 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    2416           19 :     GNUNET_GETOPT_option_flag ('3',
    2417              :                                "http3",
    2418              :                                "enable support for HTTP/2 and HTTP/3",
    2419              :                                &enable_h3),
    2420           19 :     GNUNET_GETOPT_option_timetravel ('T',
    2421              :                                      "timetravel"),
    2422           19 :     GNUNET_GETOPT_option_flag ('t',
    2423              :                                "test",
    2424              :                                "run in test mode and exit when idle",
    2425              :                                &test_mode),
    2426           19 :     GNUNET_GETOPT_option_version (VERSION),
    2427              :     GNUNET_GETOPT_OPTION_END
    2428              :   };
    2429              :   enum GNUNET_GenericReturnValue ret;
    2430              : 
    2431           19 :   ret = GNUNET_PROGRAM_run (
    2432              :     TALER_MERCHANT_project_data (),
    2433              :     argc, argv,
    2434              :     "taler-merchant-kyccheck",
    2435              :     gettext_noop (
    2436              :       "background process that checks the KYC state of our bank accounts at various exchanges"),
    2437              :     options,
    2438              :     &run, NULL);
    2439           19 :   if (GNUNET_SYSERR == ret)
    2440            0 :     return EXIT_NOTCONFIGURED;
    2441           19 :   if (GNUNET_NO == ret)
    2442            0 :     return EXIT_SUCCESS;
    2443           19 :   return global_ret;
    2444              : }
    2445              : 
    2446              : 
    2447              : /* end of taler-merchant-kyccheck.c */
        

Generated by: LCOV version 2.0-1