merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd.c (56322B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014-2025 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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU 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-httpd.c
     18  * @brief HTTP serving layer intended to perform crypto-work and
     19  * communication with the exchange
     20  * @author Marcello Stanisci
     21  * @author Christian Grothoff
     22  * @author Florian Dold
     23  * @author Priscilla HUANG
     24  */
     25 #include "platform.h"
     26 #include <taler/taler_dbevents.h>
     27 #include <taler/taler_bank_service.h>
     28 #include <taler/taler_mhd_lib.h>
     29 #include <taler/taler_templating_lib.h>
     30 #include <taler/taler_exchange_service.h>
     31 #include <donau/donau_service.h>
     32 #include "taler/taler_merchant_util.h"
     33 #include "taler-merchant-httpd_auth.h"
     34 #include "taler-merchant-httpd_dispatcher.h"
     35 #include "taler-merchant-httpd_exchanges.h"
     36 #include "taler-merchant-httpd_helper.h"
     37 #include "taler-merchant-httpd_mhd.h"
     38 #include "taler-merchant-httpd_mfa.h"
     39 #include "taler-merchant-httpd_post-private-orders.h"
     40 #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
     41 #include "taler-merchant-httpd_post-challenge-ID.h"
     42 #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
     43 #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
     44 #include "taler-merchant-httpd_get-templates-TEMPLATE_ID.h"
     45 #include "taler-merchant-httpd_get-exchanges.h"
     46 #include "taler-merchant-httpd_get-webui.h"
     47 #include "taler-merchant-httpd_get-terms.h"
     48 #include "taler-merchant-httpd_get-private-kyc.h"
     49 #include "taler-merchant-httpd_get-private-statistics-report-transactions.h"
     50 #include "taler-merchant-httpd_post-private-donau.h"
     51 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
     52 #include "taler-merchant-httpd_get-private-orders.h"
     53 #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
     54 #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
     55 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
     56 #include "merchant-database/iterate_instances.h"
     57 #include "merchant-database/set_instance.h"
     58 #include "merchant-database/iterate_accounts_by_instance.h"
     59 #include "merchant-database/event_listen.h"
     60 #include "merchant-database/preflight.h"
     61 #include "merchant-database/event_notify.h"
     62 
     63 /**
     64  * Backlog for listen operation on unix-domain sockets.
     65  */
     66 #define UNIX_BACKLOG 500
     67 
     68 /**
     69  * Default maximum upload size permitted.  Can be overridden
     70  * per handler.
     71  */
     72 #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
     73 
     74 char *TMH_currency;
     75 
     76 char *TMH_base_url;
     77 
     78 char *TMH_spa_dir;
     79 
     80 char *TMH_helper_email;
     81 
     82 char *TMH_helper_sms;
     83 
     84 char *TMH_phone_regex;
     85 
     86 regex_t TMH_phone_rx;
     87 
     88 char *TMH_allowed_payment_targets;
     89 
     90 char *TMH_default_persona;
     91 
     92 char *TMH_payment_target_regex;
     93 
     94 regex_t TMH_payment_target_re;
     95 
     96 int TMH_force_audit;
     97 
     98 struct TALER_MERCHANTDB_PostgresContext *TMH_db;
     99 
    100 struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
    101 
    102 struct GNUNET_TIME_Relative TMH_default_pay_delay;
    103 
    104 struct GNUNET_TIME_Relative TMH_default_refund_delay;
    105 
    106 struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
    107 
    108 enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
    109 
    110 int TMH_strict_v19;
    111 
    112 int TMH_auth_disabled;
    113 
    114 int TMH_have_self_provisioning;
    115 
    116 int TMH_password_change_mfa;
    117 
    118 enum TEH_TanChannelSet TEH_mandatory_tan_channels;
    119 
    120 struct GNUNET_TIME_Relative TMH_legal_expiration;
    121 
    122 unsigned int TMH_num_cspecs;
    123 
    124 json_t *TMH_global_spa_config_data;
    125 
    126 struct TALER_CurrencySpecification *TMH_cspecs;
    127 
    128 struct GNUNET_CURL_Context *TMH_curl_ctx;
    129 
    130 /**
    131  * Event handler for instance settings changes.
    132  */
    133 static struct GNUNET_DB_EventHandler *instance_eh;
    134 
    135 /**
    136  * True if we started any HTTP daemon.
    137  */
    138 static bool have_daemons;
    139 
    140 /**
    141  * Should a "Connection: close" header be added to each HTTP response?
    142  */
    143 static int merchant_connection_close;
    144 
    145 /**
    146  * Should we enable HTTP/2 and HTTP/3 when talking to the exchange
    147  * (and donau)?  Those are not expected to be terribly beneficial for
    148  * a server with stable connections to an exchange, but they could
    149  * cause stability issues with libcurl.  Per default, we *enforce*
    150  * HTTP/1.x-only, as that is the conservative and most tested code
    151  * path.
    152  */
    153 static int enable_h3;
    154 
    155 /**
    156  * Context for integrating #TMH_curl_ctx with the
    157  * GNUnet event loop.
    158  */
    159 static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
    160 
    161 /**
    162  * Global return code
    163  */
    164 static int global_ret;
    165 
    166 /**
    167  * Our configuration.
    168  */
    169 const struct GNUNET_CONFIGURATION_Handle *TMH_cfg;
    170 
    171 
    172 void
    173 TMH_wire_method_free (struct TMH_WireMethod *wm)
    174 {
    175   GNUNET_free (wm->payto_uri.full_payto);
    176   GNUNET_free (wm->wire_method);
    177   GNUNET_free (wm->extra_wire_subject_metadata);
    178   GNUNET_free (wm->credit_facade_url);
    179   json_decref (wm->credit_facade_credentials);
    180   GNUNET_free (wm);
    181 }
    182 
    183 
    184 void
    185 TMH_instance_decref (struct TMH_MerchantInstance *mi)
    186 {
    187   struct TMH_WireMethod *wm;
    188 
    189   mi->rc--;
    190   if (0 != mi->rc)
    191     return;
    192   TMH_force_get_orders_resume (mi);
    193   while (NULL != (wm = (mi->wm_head)))
    194   {
    195     GNUNET_CONTAINER_DLL_remove (mi->wm_head,
    196                                  mi->wm_tail,
    197                                  wm);
    198     TMH_wire_method_free (wm);
    199   }
    200 
    201   GNUNET_free (mi->settings.id);
    202   GNUNET_free (mi->settings.name);
    203   GNUNET_free (mi->settings.email);
    204   GNUNET_free (mi->settings.phone);
    205   GNUNET_free (mi->settings.website);
    206   GNUNET_free (mi->settings.logo);
    207   json_decref (mi->settings.address);
    208   json_decref (mi->settings.jurisdiction);
    209   GNUNET_free (mi);
    210 }
    211 
    212 
    213 enum GNUNET_GenericReturnValue
    214 TMH_instance_free_cb (void *cls,
    215                       const struct GNUNET_HashCode *key,
    216                       void *value)
    217 {
    218   struct TMH_MerchantInstance *mi = value;
    219 
    220   (void) cls;
    221   (void) key;
    222   TMH_force_get_orders_resume (mi);
    223   GNUNET_assert (GNUNET_OK ==
    224                  GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
    225                                                        &mi->h_instance,
    226                                                        mi));
    227   TMH_instance_decref (mi);
    228   return GNUNET_YES;
    229 }
    230 
    231 
    232 /**
    233  * Shutdown task (invoked when the application is being
    234  * terminated for any reason)
    235  *
    236  * @param cls NULL
    237  */
    238 static void
    239 do_shutdown (void *cls)
    240 {
    241   (void) cls;
    242   TALER_MHD_daemons_halt ();
    243   TMH_handler_statistic_report_transactions_cleanup ();
    244   TMH_force_kac_resume ();
    245   TMH_force_orders_resume ();
    246   TMH_force_get_sessions_ID_resume ();
    247   TMH_force_get_templates_ID_resume ();
    248   TMH_force_get_orders_resume_typst ();
    249   TMH_force_ac_resume ();
    250   TMH_force_pc_resume ();
    251   TMH_force_kyc_resume ();
    252   TMH_force_gorc_resume ();
    253   TMH_force_wallet_get_order_resume ();
    254   TMH_force_wallet_refund_order_resume ();
    255   TMH_force_post_donau_resume ();
    256   TMH_challenge_done ();
    257   if (NULL != instance_eh)
    258   {
    259     TALER_MERCHANTDB_event_listen_cancel (instance_eh);
    260     instance_eh = NULL;
    261   }
    262   if (NULL != TMH_by_id_map)
    263   {
    264     GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
    265                                            &TMH_instance_free_cb,
    266                                            NULL);
    267     GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
    268     TMH_by_id_map = NULL;
    269   }
    270   TALER_MHD_daemons_destroy ();
    271   TMH_EXCHANGES_done ();
    272   if (NULL != TMH_db)
    273   {
    274     TALER_MERCHANTDB_disconnect (TMH_db);
    275     TMH_db = NULL;
    276   }
    277   TALER_TEMPLATING_done ();
    278   if (NULL != TMH_curl_ctx)
    279   {
    280     GNUNET_CURL_fini (TMH_curl_ctx);
    281     TMH_curl_ctx = NULL;
    282   }
    283   if (NULL != merchant_curl_rc)
    284   {
    285     GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
    286     merchant_curl_rc = NULL;
    287   }
    288   if (NULL != TMH_payment_target_regex)
    289   {
    290     regfree (&TMH_payment_target_re);
    291     GNUNET_free (TMH_payment_target_regex);
    292   }
    293 }
    294 
    295 
    296 /**
    297  * Function called whenever MHD is done with a request.  If the
    298  * request was a POST, we may have stored a `struct Buffer *` in the
    299  * @a con_cls that might still need to be cleaned up.  Call the
    300  * respective function to free the memory.
    301  *
    302  * @param cls client-defined closure
    303  * @param connection connection handle
    304  * @param con_cls value as set by the last call to
    305  *        the #MHD_AccessHandlerCallback
    306  * @param toe reason for request termination
    307  * @see #MHD_OPTION_NOTIFY_COMPLETED
    308  * @ingroup request
    309  */
    310 static void
    311 handle_mhd_completion_callback (void *cls,
    312                                 struct MHD_Connection *connection,
    313                                 void **con_cls,
    314                                 enum MHD_RequestTerminationCode toe)
    315 {
    316   struct TMH_HandlerContext *hc = *con_cls;
    317 
    318   (void) cls;
    319   if (NULL == hc)
    320     return;
    321   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    322   {
    323 #if MHD_VERSION >= 0x00097304
    324     const union MHD_ConnectionInfo *ci;
    325     unsigned int http_status = 0;
    326 
    327     ci = MHD_get_connection_info (connection,
    328                                   MHD_CONNECTION_INFO_HTTP_STATUS);
    329     if (NULL != ci)
    330       http_status = ci->http_status;
    331     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    332                 "Request for `%s' completed with HTTP status %u (%d)\n",
    333                 hc->url,
    334                 http_status,
    335                 toe);
    336 #else
    337     (void) connection;
    338     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    339                 "Finished handling request for `%s' with MHD termination code %d\n",
    340                 hc->url,
    341                 (int) toe);
    342 #endif
    343   }
    344   if (NULL != hc->cc)
    345     hc->cc (hc->ctx);
    346   TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
    347   GNUNET_free (hc->infix);
    348   GNUNET_free (hc->rewritten_url);
    349   if (NULL != hc->request_body)
    350     json_decref (hc->request_body);
    351   if (NULL != hc->instance)
    352     TMH_instance_decref (hc->instance);
    353   TALER_MERCHANTDB_preflight (TMH_db);
    354   GNUNET_free (hc->full_url);
    355   GNUNET_free (hc);
    356   *con_cls = NULL;
    357 }
    358 
    359 
    360 struct TMH_MerchantInstance *
    361 TMH_lookup_instance (const char *instance_id)
    362 {
    363   struct GNUNET_HashCode h_instance;
    364   char *id;
    365 
    366   if (NULL == instance_id)
    367     id = GNUNET_strdup ("admin");
    368   else
    369     id = GNUNET_STRINGS_utf8_tolower (instance_id);
    370   GNUNET_CRYPTO_hash (id,
    371                       strlen (id),
    372                       &h_instance);
    373   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    374               "Looking for by-id key %s of '%s' in hashmap\n",
    375               GNUNET_h2s (&h_instance),
    376               id);
    377   GNUNET_free (id);
    378   /* We're fine if that returns NULL, the calling routine knows how
    379      to handle that */
    380   return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
    381                                             &h_instance);
    382 }
    383 
    384 
    385 /**
    386  * Add instance definition to our active set of instances.
    387  *
    388  * @param[in,out] mi merchant instance details to define
    389  * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
    390  */
    391 enum GNUNET_GenericReturnValue
    392 TMH_add_instance (struct TMH_MerchantInstance *mi)
    393 {
    394   const char *id;
    395   enum GNUNET_GenericReturnValue ret;
    396 
    397   id = mi->settings.id;
    398   if (NULL == id)
    399     id = "admin";
    400   GNUNET_CRYPTO_hash (id,
    401                       strlen (id),
    402                       &mi->h_instance);
    403   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    404               "Looking for by-id key %s of `%s' in hashmap\n",
    405               GNUNET_h2s (&mi->h_instance),
    406               id);
    407   ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
    408                                            &mi->h_instance,
    409                                            mi,
    410                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
    411   if (GNUNET_OK == ret)
    412   {
    413     GNUNET_assert (mi->rc < UINT_MAX);
    414     mi->rc++;
    415   }
    416   return ret;
    417 }
    418 
    419 
    420 /**
    421  * Function called first by MHD with the full URL.
    422  *
    423  * @param cls NULL
    424  * @param full_url the full URL
    425  * @param con MHD connection object
    426  * @return our handler context
    427  */
    428 static void *
    429 full_url_track_callback (void *cls,
    430                          const char *full_url,
    431                          struct MHD_Connection *con)
    432 {
    433   struct TMH_HandlerContext *hc;
    434 
    435   hc = GNUNET_new (struct TMH_HandlerContext);
    436   hc->connection = con;
    437   GNUNET_async_scope_fresh (&hc->async_scope_id);
    438   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    439   hc->full_url = GNUNET_strdup (full_url);
    440   return hc;
    441 }
    442 
    443 
    444 /**
    445  * The callback was called again by MHD, continue processing
    446  * the request with the already identified handler.
    447  *
    448  * @param hc the handler context
    449  * @param upload_data the data being uploaded (excluding HEADERS,
    450  *        for a POST that fits into memory and that is encoded
    451  *        with a supported encoding, the POST data will NOT be
    452  *        given in upload_data and is instead available as
    453  *        part of #MHD_get_connection_values; very large POST
    454  *        data *will* be made available incrementally in
    455  *        @a upload_data)
    456  * @param upload_data_size set initially to the size of the
    457  *        @a upload_data provided; the method must update this
    458  *        value to the number of bytes NOT processed;
    459  * @return #MHD_YES if the connection was handled successfully,
    460  *         #MHD_NO if the socket must be closed due to a serious
    461  *         error while handling the request
    462  */
    463 static enum MHD_Result
    464 process_upload_with_handler (struct TMH_HandlerContext *hc,
    465                              const char *upload_data,
    466                              size_t *upload_data_size)
    467 {
    468   GNUNET_assert (NULL != hc->rh);
    469   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    470   if ( (hc->has_body) &&
    471        (NULL == hc->request_body) )
    472   {
    473     size_t mul = hc->rh->max_upload;
    474     enum GNUNET_GenericReturnValue res;
    475 
    476     if (0 == mul)
    477       mul = DEFAULT_MAX_UPLOAD_SIZE;
    478     if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
    479          (hc->total_upload + *upload_data_size > mul) )
    480     {
    481       /* Client exceeds upload limit. Should _usually_ be checked earlier
    482          when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
    483          chunked encoding an uploader MAY have omitted this, and thus
    484          not permitted us to check on time. In this case, we just close
    485          the connection once it exceeds our limit (instead of waiting
    486          for the upload to complete and then fail). This could theoretically
    487          cause some clients to retry, alas broken or malicious clients
    488          are likely to retry anyway, so little we can do about it, and
    489          failing earlier seems the best option here.  */
    490       GNUNET_break_op (0);
    491       return MHD_NO;
    492     }
    493     hc->total_upload += *upload_data_size;
    494     res = TALER_MHD_parse_post_json (hc->connection,
    495                                      &hc->json_parse_context,
    496                                      upload_data,
    497                                      upload_data_size,
    498                                      &hc->request_body);
    499     if (GNUNET_SYSERR == res)
    500       return MHD_NO;
    501     /* A error response was already generated */
    502     if ( (GNUNET_NO == res) ||
    503          /* or, need more data to accomplish parsing */
    504          (NULL == hc->request_body) )
    505       return MHD_YES;   /* let MHD call us *again* */
    506   }
    507   /* Upload complete (if any), call handler to generate reply */
    508   return hc->rh->handler (hc->rh,
    509                           hc->connection,
    510                           hc);
    511 }
    512 
    513 
    514 /**
    515  * Log information about the request being handled.
    516  *
    517  * @param hc handler context
    518  * @param method HTTP method of the request
    519  */
    520 static void
    521 log_request (const struct TMH_HandlerContext *hc,
    522              const char *method)
    523 {
    524   const char *correlation_id;
    525 
    526   correlation_id = MHD_lookup_connection_value (hc->connection,
    527                                                 MHD_HEADER_KIND,
    528                                                 "Taler-Correlation-Id");
    529   if ( (NULL != correlation_id) &&
    530        (GNUNET_YES !=
    531         GNUNET_CURL_is_valid_scope_id (correlation_id)) )
    532   {
    533     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    534                 "Illegal incoming correlation ID\n");
    535     correlation_id = NULL;
    536   }
    537   if (NULL != correlation_id)
    538     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    539                 "Handling request for (%s) URL '%s', correlation_id=%s\n",
    540                 method,
    541                 hc->url,
    542                 correlation_id);
    543   else
    544     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    545                 "Handling request (%s) for URL '%s'\n",
    546                 method,
    547                 hc->url);
    548 }
    549 
    550 
    551 /**
    552  * Identify the instance of the request from the URL.
    553  *
    554  * @param[in,out] hc handler context
    555  * @param[in,out] urlp URL path of the request, updated to point to the rest
    556  * @param[out] use_admin set to true if we are using the admin instance
    557  * @return #GNUNET_OK on success,
    558  *         #GNUNET_NO if an error was queued (return #MHD_YES)
    559  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
    560  */
    561 static enum GNUNET_GenericReturnValue
    562 identify_instance (struct TMH_HandlerContext *hc,
    563                    const char **urlp,
    564                    bool *use_admin)
    565 {
    566   const char *url = *urlp;
    567   const char *instance_prefix = "/instances/";
    568 
    569   if (0 == strncmp (url,
    570                     instance_prefix,
    571                     strlen (instance_prefix)))
    572   {
    573     /* url starts with "/instances/" */
    574     const char *istart = url + strlen (instance_prefix);
    575     const char *slash = strchr (istart, '/');
    576     char *raw_id;
    577     char *instance_id;
    578 
    579     if (NULL == slash)
    580       raw_id = GNUNET_strdup (istart);
    581     else
    582       raw_id = GNUNET_strndup (istart,
    583                                slash - istart);
    584     /* Instance IDs are case-insensitive, so fold the segment before comparing
    585        it against "admin" below.  Without this, '/instances/Admin/' misses the
    586        redirect to the modern path, leaving use_admin false, and every
    587        'default_only' handler that '/instances/admin/' reaches (all of
    588        /management/) then replies 404. */
    589     instance_id = GNUNET_STRINGS_utf8_tolower (raw_id);
    590     GNUNET_free (raw_id);
    591     if (0 == strcmp (instance_id,
    592                      "admin"))
    593     {
    594       enum MHD_Result ret;
    595       struct MHD_Response *response;
    596       const char *rstart = hc->full_url + strlen (instance_prefix);
    597       const char *rslash = strchr (rstart, '/');
    598 
    599       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    600                   "Client used deprecated '/instances/admin/' path. Redirecting to modern path\n");
    601 
    602       response
    603         = MHD_create_response_from_buffer (0,
    604                                            NULL,
    605                                            MHD_RESPMEM_PERSISTENT);
    606       TALER_MHD_add_global_headers (response,
    607                                     true);
    608       if (MHD_NO ==
    609           MHD_add_response_header (response,
    610                                    MHD_HTTP_HEADER_LOCATION,
    611                                    NULL == rslash
    612                                      ? "/"
    613                                      : rslash))
    614       {
    615         GNUNET_break (0);
    616         MHD_destroy_response (response);
    617         GNUNET_free (instance_id);
    618         return GNUNET_SYSERR;
    619       }
    620       ret = MHD_queue_response (hc->connection,
    621                                 MHD_HTTP_PERMANENT_REDIRECT,
    622                                 response);
    623       MHD_destroy_response (response);
    624       GNUNET_free (instance_id);
    625       return (MHD_YES == ret) ? GNUNET_NO : GNUNET_SYSERR;
    626     }
    627     hc->instance = TMH_lookup_instance (instance_id);
    628     if ( (NULL == hc->instance) &&
    629          (0 == strcmp ("admin",
    630                        instance_id)) )
    631       hc->instance = TMH_lookup_instance (NULL);
    632     GNUNET_free (instance_id);
    633     if (NULL == slash)
    634       *urlp = "";
    635     else
    636       *urlp = slash;
    637   }
    638   else
    639   {
    640     /* use 'default' */
    641     *use_admin = true;
    642     hc->instance = TMH_lookup_instance (NULL);
    643   }
    644   if (NULL != hc->instance)
    645   {
    646     GNUNET_assert (hc->instance->rc < UINT_MAX);
    647     hc->instance->rc++;
    648   }
    649   return GNUNET_OK;
    650 }
    651 
    652 
    653 /**
    654  * A client has requested the given url using the given method
    655  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
    656  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
    657  * must call MHD callbacks to provide content to give back to the
    658  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
    659  * #MHD_HTTP_NOT_FOUND, etc.).
    660  *
    661  * @param cls argument given together with the function
    662  *        pointer when the handler was registered with MHD
    663  * @param connection the MHD connection to handle
    664  * @param url the requested url
    665  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
    666  *        #MHD_HTTP_METHOD_PUT, etc.)
    667  * @param version the HTTP version string (i.e.
    668  *        #MHD_HTTP_VERSION_1_1)
    669  * @param upload_data the data being uploaded (excluding HEADERS,
    670  *        for a POST that fits into memory and that is encoded
    671  *        with a supported encoding, the POST data will NOT be
    672  *        given in upload_data and is instead available as
    673  *        part of #MHD_get_connection_values; very large POST
    674  *        data *will* be made available incrementally in
    675  *        @a upload_data)
    676  * @param upload_data_size set initially to the size of the
    677  *        @a upload_data provided; the method must update this
    678  *        value to the number of bytes NOT processed;
    679  * @param con_cls pointer that the callback can set to some
    680  *        address and that will be preserved by MHD for future
    681  *        calls for this request; since the access handler may
    682  *        be called many times (i.e., for a PUT/POST operation
    683  *        with plenty of upload data) this allows the application
    684  *        to easily associate some request-specific state.
    685  *        If necessary, this state can be cleaned up in the
    686  *        global #MHD_RequestCompletedCallback (which
    687  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
    688  *        Initially, `*con_cls` will be set up by the
    689  *        full_url_track_callback().
    690  * @return #MHD_YES if the connection was handled successfully,
    691  *         #MHD_NO if the socket must be closed due to a serious
    692  *         error while handling the request
    693  */
    694 static enum MHD_Result
    695 url_handler (void *cls,
    696              struct MHD_Connection *connection,
    697              const char *url,
    698              const char *method,
    699              const char *version,
    700              const char *upload_data,
    701              size_t *upload_data_size,
    702              void **con_cls)
    703 {
    704   struct TMH_HandlerContext *hc = *con_cls;
    705   bool use_admin = false;
    706   bool is_public = false;
    707 
    708   (void) cls;
    709   (void) version;
    710   if (NULL == hc->url)
    711   {
    712     /* First time.
    713      * Find out the merchant backend instance for the request.
    714      * If there is an instance, remove the instance specification
    715      * from the beginning of the request URL. */
    716     enum GNUNET_GenericReturnValue ret;
    717 
    718     hc->url = url;
    719     log_request (hc,
    720                  method);
    721     ret = identify_instance (hc,
    722                              &url,
    723                              &use_admin);
    724     if (GNUNET_OK != ret)
    725       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    726   }
    727 
    728   if (NULL != hc->instance)
    729   {
    730     /* Narrow DB interaction to selected instance */
    731     enum GNUNET_DB_QueryStatus qs;
    732 
    733     qs = TALER_MERCHANTDB_set_instance (TMH_db,
    734                                         hc->instance->settings.id);
    735     switch (qs)
    736     {
    737     case GNUNET_DB_STATUS_HARD_ERROR:
    738       GNUNET_break (0);
    739       return TALER_MHD_reply_with_error (
    740         connection,
    741         MHD_HTTP_INTERNAL_SERVER_ERROR,
    742         TALER_EC_GENERIC_DB_SETUP_FAILED,
    743         "set_instance");
    744     case GNUNET_DB_STATUS_SOFT_ERROR:
    745       GNUNET_break (0);
    746       return TALER_MHD_reply_with_error (
    747         connection,
    748         MHD_HTTP_INTERNAL_SERVER_ERROR,
    749         TALER_EC_GENERIC_DB_SETUP_FAILED,
    750         "set_instance");
    751     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    752       return TALER_MHD_reply_with_error (
    753         connection,
    754         MHD_HTTP_NOT_FOUND,
    755         TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    756         hc->url);
    757     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    758       break;
    759     }
    760   }
    761 
    762   if (NULL != hc->rh)
    763   {
    764     enum MHD_Result res;
    765 
    766     /* MHD calls us again for a request, we already identified
    767        the handler, just continue processing with the handler */
    768     res = process_upload_with_handler (hc,
    769                                        upload_data,
    770                                        upload_data_size);
    771     if (NULL != hc->instance)
    772     {
    773       GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    774                     TALER_MERCHANTDB_set_instance (TMH_db,
    775                                                    NULL));
    776     }
    777     return res;
    778   }
    779 
    780   /* First time, let's figure out the handler */
    781   {
    782     enum GNUNET_GenericReturnValue ret;
    783 
    784     ret = TMH_dispatch_request (hc,
    785                                 url,
    786                                 method,
    787                                 use_admin,
    788                                 &is_public);
    789     if (GNUNET_OK != ret)
    790     {
    791       if (NULL != hc->instance)
    792       {
    793         GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    794                       TALER_MERCHANTDB_set_instance (TMH_db,
    795                                                      NULL));
    796       }
    797       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    798     }
    799   }
    800 
    801   /* At this point, we must have found a handler */
    802   GNUNET_assert (NULL != hc->rh);
    803 
    804   /* If an instance must be there, check one exists */
    805   if ( (NULL == hc->instance) &&
    806        (! hc->rh->skip_instance) )
    807   {
    808     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    809                 "Instance for `%s' not known\n",
    810                 hc->url);
    811     return TALER_MHD_reply_with_error (connection,
    812                                        MHD_HTTP_NOT_FOUND,
    813                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    814                                        hc->url);
    815   }
    816 
    817   /* Perform access control for non-public handlers */
    818   if (! is_public)
    819   {
    820     enum GNUNET_GenericReturnValue ret;
    821 
    822     ret = TMH_perform_access_control (hc);
    823     if (GNUNET_OK != ret)
    824     {
    825       if (NULL != hc->instance)
    826       {
    827         GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    828                       TALER_MERCHANTDB_set_instance (TMH_db,
    829                                                      NULL));
    830       }
    831       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    832     }
    833   }
    834   if (NULL != hc->instance)
    835   {
    836     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    837                   TALER_MERCHANTDB_set_instance (TMH_db,
    838                                                  NULL));
    839   }
    840 
    841   if ( (NULL != hc->instance) && /* make static analysis happy */
    842        (! hc->rh->skip_instance) &&
    843        (hc->instance->deleted) &&
    844        (! hc->rh->allow_deleted_instance) )
    845   {
    846     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    847                 "Instance `%s' was deleted\n",
    848                 hc->instance->settings.id);
    849     return TALER_MHD_reply_with_error (connection,
    850                                        MHD_HTTP_NOT_FOUND,
    851                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
    852                                        hc->instance->settings.id);
    853   }
    854 
    855   /* Check upload constraints */
    856   hc->has_body = ( (0 == strcasecmp (method,
    857                                      MHD_HTTP_METHOD_POST)) ||
    858                    /* PUT is not yet used */
    859                    (0 == strcasecmp (method,
    860                                      MHD_HTTP_METHOD_PATCH)) );
    861   if (hc->has_body)
    862   {
    863     /* This is a macro: it will queue an error response and return
    864        from this function if the upload would be too large. */
    865     TALER_MHD_check_content_length (connection,
    866                                     0 == hc->rh->max_upload
    867                                     ? DEFAULT_MAX_UPLOAD_SIZE
    868                                     : hc->rh->max_upload);
    869     GNUNET_break (NULL == hc->request_body); /* can't have it already */
    870   }
    871   /* wait for MHD to call us again, this time hc->url will be non-NULL
    872      and we should jump straight into process_upload_with_handler(). */
    873   return MHD_YES;
    874 }
    875 
    876 
    877 /**
    878  * Callback invoked with information about a bank account.
    879  *
    880  * @param cls closure with a `struct TMH_MerchantInstance *`
    881  * @param merchant_priv private key of the merchant instance
    882  * @param acc details about the account
    883  */
    884 static void
    885 add_account_cb (void *cls,
    886                 const struct TALER_MerchantPrivateKeyP *merchant_priv,
    887                 const struct TALER_MERCHANTDB_AccountDetails *acc)
    888 {
    889   struct TMH_MerchantInstance *mi = cls;
    890   struct TMH_WireMethod *wm;
    891 
    892   (void) merchant_priv;
    893   wm = GNUNET_new (struct TMH_WireMethod);
    894   wm->h_wire = acc->h_wire;
    895   wm->payto_uri.full_payto
    896     = GNUNET_strdup (acc->payto_uri.full_payto);
    897   if (NULL != acc->extra_wire_subject_metadata)
    898     wm->extra_wire_subject_metadata
    899       = GNUNET_strdup (acc->extra_wire_subject_metadata);
    900   wm->wire_salt = acc->salt;
    901   wm->wire_method
    902     = TALER_payto_get_method (acc->payto_uri.full_payto);
    903   wm->active = acc->active;
    904   GNUNET_CONTAINER_DLL_insert (mi->wm_head,
    905                                mi->wm_tail,
    906                                wm);
    907 }
    908 
    909 
    910 /**
    911  * Function called during startup to add all known instances to our
    912  * hash map in memory for faster lookups when we receive requests.
    913  *
    914  * @param cls closure, NULL, unused
    915  * @param merchant_pub public key of the instance
    916  * @param merchant_priv private key of the instance, NULL if not available
    917  * @param is detailed configuration settings for the instance
    918  * @param ias authentication settings for the instance
    919  */
    920 static void
    921 add_instance_cb (void *cls,
    922                  const struct TALER_MerchantPublicKeyP *merchant_pub,
    923                  const struct TALER_MerchantPrivateKeyP *merchant_priv,
    924                  const struct TALER_MERCHANTDB_InstanceSettings *is,
    925                  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
    926 {
    927   struct TMH_MerchantInstance *mi;
    928   enum GNUNET_DB_QueryStatus qs;
    929 
    930   (void) cls;
    931   mi = TMH_lookup_instance (is->id);
    932   if (NULL != mi)
    933   {
    934     /* (outdated) entry exists, remove old entry */
    935     (void) TMH_instance_free_cb (NULL,
    936                                  &mi->h_instance,
    937                                  mi);
    938   }
    939   mi = GNUNET_new (struct TMH_MerchantInstance);
    940   mi->settings = *is;
    941   mi->auth = *ias;
    942   mi->settings.id = GNUNET_STRINGS_utf8_tolower (mi->settings.id);
    943   mi->settings.name = GNUNET_strdup (mi->settings.name);
    944   if (NULL != mi->settings.email)
    945     mi->settings.email = GNUNET_strdup (mi->settings.email);
    946   if (NULL != mi->settings.phone)
    947     mi->settings.phone = GNUNET_strdup (mi->settings.phone);
    948   if (NULL != mi->settings.website)
    949     mi->settings.website = GNUNET_strdup (mi->settings.website);
    950   if (NULL != mi->settings.logo)
    951     mi->settings.logo = GNUNET_strdup (mi->settings.logo);
    952   mi->settings.address = json_incref (mi->settings.address);
    953   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
    954   if (NULL != merchant_priv)
    955     mi->merchant_priv = *merchant_priv;
    956   else
    957     mi->deleted = true;
    958   mi->merchant_pub = *merchant_pub;
    959   qs = TALER_MERCHANTDB_set_instance (TMH_db,
    960                                       mi->settings.id);
    961   if (0 > qs)
    962   {
    963     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    964                 "Error setting instance `%s'\n",
    965                 mi->settings.id);
    966     GNUNET_SCHEDULER_shutdown ();
    967     return;
    968   }
    969   qs = TALER_MERCHANTDB_iterate_accounts_by_instance (
    970     TMH_db,
    971     mi->settings.id,
    972     &add_account_cb,
    973     mi);
    974   if (0 > qs)
    975   {
    976     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    977                 "Error loading accounts of `%s' from database\n",
    978                 mi->settings.id);
    979     GNUNET_SCHEDULER_shutdown ();
    980     return;
    981   }
    982   GNUNET_assert (GNUNET_OK ==
    983                  TMH_add_instance (mi));
    984   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    985                 TALER_MERCHANTDB_set_instance (TMH_db,
    986                                                NULL));
    987 }
    988 
    989 
    990 /**
    991  * Trigger (re)loading of instance settings from DB.
    992  *
    993  * @param cls NULL
    994  * @param extra ID of the instance that changed, NULL
    995  *              to load all instances (will not handle purges!)
    996  * @param extra_len number of bytes in @a extra
    997  */
    998 static void
    999 load_instances (void *cls,
   1000                 const void *extra,
   1001                 size_t extra_len)
   1002 {
   1003   enum GNUNET_DB_QueryStatus qs;
   1004   const char *id = extra;
   1005 
   1006   (void) cls;
   1007   if ( (NULL != extra) &&
   1008        ( (0 == extra_len) ||
   1009          ('\0' != id[extra_len - 1]) ) )
   1010   {
   1011     GNUNET_break (0 == extra_len);
   1012     extra = NULL;
   1013   }
   1014   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1015               "Received instance settings notification: reload `%s'\n",
   1016               id);
   1017   if (NULL == extra)
   1018   {
   1019     qs = TALER_MERCHANTDB_iterate_instances (TMH_db,
   1020                                              false,
   1021                                              &add_instance_cb,
   1022                                              NULL);
   1023   }
   1024   else
   1025   {
   1026     struct TMH_MerchantInstance *mi;
   1027 
   1028     /* This must be done here to handle instance
   1029        purging, as for purged instances, the DB
   1030        lookup below will otherwise do nothing */
   1031     mi = TMH_lookup_instance (id);
   1032     if (NULL != mi)
   1033     {
   1034       (void) TMH_instance_free_cb (NULL,
   1035                                    &mi->h_instance,
   1036                                    mi);
   1037     }
   1038     qs = TALER_MERCHANTDB_iterate_instances_by_id (TMH_db,
   1039                                                    id,
   1040                                                    false,
   1041                                                    &add_instance_cb,
   1042                                                    NULL);
   1043   }
   1044   if (0 > qs)
   1045   {
   1046     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1047                 "Failed initialization. Check database setup.\n");
   1048     global_ret = EXIT_FAILURE;
   1049     GNUNET_SCHEDULER_shutdown ();
   1050     return;
   1051   }
   1052 }
   1053 
   1054 
   1055 /**
   1056  * A transaction modified an instance setting (or created/deleted/purged
   1057  * one). Notify all backends about the change.
   1058  *
   1059  * @param id ID of the instance that changed
   1060  */
   1061 void
   1062 TMH_reload_instances (const char *id)
   1063 {
   1064   struct GNUNET_DB_EventHeaderP es = {
   1065     .size = htons (sizeof (es)),
   1066     .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   1067   };
   1068 
   1069   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1070               "Generating instance settings notification: reload `%s'\n",
   1071               id);
   1072   TALER_MERCHANTDB_event_notify (TMH_db,
   1073                                  &es,
   1074                                  id,
   1075                                  (NULL == id)
   1076                         ? 0
   1077                         : strlen (id) + 1);
   1078 }
   1079 
   1080 
   1081 /**
   1082  * Callback invoked on every listen socket to start the
   1083  * respective MHD HTTP daemon.
   1084  *
   1085  * @param cls unused
   1086  * @param lsock the listen socket
   1087  */
   1088 static void
   1089 start_daemon (void *cls,
   1090               int lsock)
   1091 {
   1092   struct MHD_Daemon *mhd;
   1093 
   1094   (void) cls;
   1095   GNUNET_assert (-1 != lsock);
   1096   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
   1097                           | MHD_USE_AUTO,
   1098                           0 /* port */,
   1099                           NULL, NULL,
   1100                           &url_handler, NULL,
   1101                           MHD_OPTION_LISTEN_SOCKET, lsock,
   1102                           MHD_OPTION_URI_LOG_CALLBACK,
   1103                           &full_url_track_callback, NULL,
   1104                           MHD_OPTION_NOTIFY_COMPLETED,
   1105                           &handle_mhd_completion_callback, NULL,
   1106                           MHD_OPTION_CONNECTION_TIMEOUT,
   1107                           (unsigned int) 10 /* 10s */,
   1108                           MHD_OPTION_END);
   1109   if (NULL == mhd)
   1110   {
   1111     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1112                 "Failed to launch HTTP service.\n");
   1113     GNUNET_SCHEDULER_shutdown ();
   1114     return;
   1115   }
   1116   have_daemons = true;
   1117   TALER_MHD_daemon_start (mhd);
   1118 }
   1119 
   1120 
   1121 /**
   1122  * Main function that will be run by the scheduler.
   1123  *
   1124  * @param cls closure
   1125  * @param args remaining command-line arguments
   1126  * @param cfgfile name of the configuration file used (for saving, can be
   1127  *        NULL!)
   1128  * @param config configuration
   1129  */
   1130 static void
   1131 run (void *cls,
   1132      char *const *args,
   1133      const char *cfgfile,
   1134      const struct GNUNET_CONFIGURATION_Handle *config)
   1135 {
   1136   enum TALER_MHD_GlobalOptions go;
   1137   int elen;
   1138 
   1139   (void) cls;
   1140   (void) args;
   1141   (void) cfgfile;
   1142   TMH_cfg = config;
   1143   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1144               "Starting taler-merchant-httpd\n");
   1145   go = TALER_MHD_GO_NONE;
   1146   if (merchant_connection_close)
   1147     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   1148   TALER_MHD_setup (go);
   1149   TALER_EXCHANGE_setup (enable_h3
   1150                         ? TALER_EXCHANGE_GO_ENABLE_HTTP3
   1151                         : TALER_EXCHANGE_GO_FORCE_HTTP1_1);
   1152   DONAU_setup (enable_h3
   1153                ? DONAU_GO_ENABLE_HTTP3
   1154                : DONAU_GO_FORCE_HTTP1_1);
   1155 
   1156   global_ret = EXIT_SUCCESS;
   1157   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1158                                  NULL);
   1159 
   1160   TMH_curl_ctx
   1161     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1162                         &merchant_curl_rc);
   1163   if (NULL == TMH_curl_ctx)
   1164   {
   1165     GNUNET_break (0);
   1166     global_ret = EXIT_FAILURE;
   1167     GNUNET_SCHEDULER_shutdown ();
   1168     return;
   1169   }
   1170   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
   1171   /* Disable 100 continue processing */
   1172   GNUNET_break (GNUNET_OK ==
   1173                 GNUNET_CURL_append_header (TMH_curl_ctx,
   1174                                            MHD_HTTP_HEADER_EXPECT ":"));
   1175   GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
   1176                                          "Taler-Correlation-Id");
   1177 
   1178   if (GNUNET_SYSERR ==
   1179       TALER_config_get_currency (TMH_cfg,
   1180                                  "merchant",
   1181                                  &TMH_currency))
   1182   {
   1183     global_ret = EXIT_NOTCONFIGURED;
   1184     GNUNET_SCHEDULER_shutdown ();
   1185     return;
   1186   }
   1187   if (GNUNET_OK !=
   1188       TALER_CONFIG_parse_currencies (TMH_cfg,
   1189                                      TMH_currency,
   1190                                      &TMH_num_cspecs,
   1191                                      &TMH_cspecs))
   1192   {
   1193     global_ret = EXIT_NOTCONFIGURED;
   1194     GNUNET_SCHEDULER_shutdown ();
   1195     return;
   1196   }
   1197 
   1198   {
   1199     char *spa_data;
   1200 
   1201     if (GNUNET_OK ==
   1202         GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1203                                                "merchant",
   1204                                                "GLOBAL_SPA_CONFIG_DATA",
   1205                                                &spa_data))
   1206     {
   1207       json_error_t err;
   1208 
   1209       TMH_global_spa_config_data = json_loads (spa_data,
   1210                                                JSON_REJECT_DUPLICATES,
   1211                                                &err);
   1212       GNUNET_free (spa_data);
   1213       if (NULL == TMH_global_spa_config_data)
   1214       {
   1215         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1216                                    "merchant",
   1217                                    "GLOBAL_SPA_CONFIG_DATA",
   1218                                    err.text);
   1219         global_ret = EXIT_NOTCONFIGURED;
   1220         GNUNET_SCHEDULER_shutdown ();
   1221         return;
   1222       }
   1223     }
   1224   }
   1225 
   1226 
   1227   if (GNUNET_SYSERR ==
   1228       (TMH_strict_v19
   1229          = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1230                                                  "merchant",
   1231                                                  "STRICT_PROTOCOL_V19")))
   1232   {
   1233     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1234                                "merchant",
   1235                                "STRICT_PROTOCOL_V19");
   1236     TMH_strict_v19 = GNUNET_NO;
   1237   }
   1238   if (GNUNET_SYSERR ==
   1239       (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1240                                                                  "merchant",
   1241                                                                  "DISABLE_AUTHENTICATION")))
   1242   {
   1243     TMH_auth_disabled = GNUNET_NO;
   1244   }
   1245   if (GNUNET_YES == TMH_auth_disabled)
   1246   {
   1247     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1248                 "DANGEROUS: Endpoint Authentication disabled!");
   1249   }
   1250 
   1251   if (GNUNET_SYSERR ==
   1252       (TMH_have_self_provisioning
   1253          = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1254                                                  "merchant",
   1255                                                  "ENABLE_SELF_PROVISIONING")))
   1256   {
   1257     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1258                                "merchant",
   1259                                "ENABLE_SELF_PROVISIONING");
   1260     TMH_have_self_provisioning = GNUNET_NO;
   1261   }
   1262 
   1263   if (GNUNET_SYSERR ==
   1264       (TMH_password_change_mfa
   1265          = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1266                                                  "merchant",
   1267                                                  "PASSWORD_CHANGE_MFA")))
   1268   {
   1269     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1270                                "merchant",
   1271                                "PASSWORD_CHANGE_MFA");
   1272     TMH_password_change_mfa = GNUNET_NO;
   1273   }
   1274 
   1275   if (GNUNET_OK !=
   1276       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1277                                            "merchant",
   1278                                            "LEGAL_PRESERVATION",
   1279                                            &TMH_legal_expiration))
   1280   {
   1281     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1282                                "merchant",
   1283                                "LEGAL_PRESERVATION");
   1284     global_ret = EXIT_NOTCONFIGURED;
   1285     GNUNET_SCHEDULER_shutdown ();
   1286     return;
   1287   }
   1288 
   1289   if (GNUNET_OK !=
   1290       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1291                                            "merchant",
   1292                                            "DEFAULT_PAY_DELAY",
   1293                                            &TMH_default_pay_delay))
   1294   {
   1295     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1296                                "merchant",
   1297                                "DEFAULT_PAY_DELAY");
   1298     TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
   1299   }
   1300   if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
   1301   {
   1302     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1303                                "merchant",
   1304                                "DEFAULT_PAY_DELAY",
   1305                                "forever is not allowed");
   1306     global_ret = EXIT_NOTCONFIGURED;
   1307     GNUNET_SCHEDULER_shutdown ();
   1308     return;
   1309   }
   1310   if (GNUNET_OK !=
   1311       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1312                                            "merchant",
   1313                                            "DEFAULT_REFUND_DELAY",
   1314                                            &TMH_default_refund_delay))
   1315   {
   1316     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1317                                "merchant",
   1318                                "DEFAULT_REFUND_DELAY");
   1319     TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
   1320       GNUNET_TIME_UNIT_DAYS,
   1321       15);
   1322   }
   1323   if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
   1324   {
   1325     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1326                                "merchant",
   1327                                "DEFAULT_REFUND_DELAY",
   1328                                "forever is not allowed");
   1329     global_ret = EXIT_NOTCONFIGURED;
   1330     GNUNET_SCHEDULER_shutdown ();
   1331     return;
   1332   }
   1333 
   1334   if (GNUNET_OK !=
   1335       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1336                                            "merchant",
   1337                                            "DEFAULT_WIRE_TRANSFER_DELAY",
   1338                                            &TMH_default_wire_transfer_delay))
   1339   {
   1340     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1341                                "merchant",
   1342                                "DEFAULT_WIRE_TRANSFER_DELAY");
   1343     TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
   1344   }
   1345   if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
   1346   {
   1347     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1348                                "merchant",
   1349                                "DEFAULT_WIRE_TRANSFER_DELAY",
   1350                                "forever is not allowed");
   1351     global_ret = EXIT_NOTCONFIGURED;
   1352     GNUNET_SCHEDULER_shutdown ();
   1353     return;
   1354   }
   1355 
   1356   {
   1357     char *dwtri;
   1358 
   1359     if (GNUNET_OK !=
   1360         GNUNET_CONFIGURATION_get_value_string (
   1361           TMH_cfg,
   1362           "merchant",
   1363           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   1364           &dwtri))
   1365     {
   1366       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1367                                  "merchant",
   1368                                  "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
   1369       TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
   1370     }
   1371     else
   1372     {
   1373       if (GNUNET_OK !=
   1374           GNUNET_TIME_string_to_round_interval (
   1375             dwtri,
   1376             &TMH_default_wire_transfer_rounding_interval))
   1377       {
   1378         GNUNET_log_config_invalid (
   1379           GNUNET_ERROR_TYPE_ERROR,
   1380           "merchant",
   1381           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   1382           "invalid time rounding interval");
   1383         global_ret = EXIT_NOTCONFIGURED;
   1384         GNUNET_free (dwtri);
   1385         GNUNET_SCHEDULER_shutdown ();
   1386         return;
   1387       }
   1388       GNUNET_free (dwtri);
   1389     }
   1390   }
   1391 
   1392   TMH_load_terms (TMH_cfg);
   1393 
   1394   if (GNUNET_OK !=
   1395       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1396                                              "merchant",
   1397                                              "PAYMENT_TARGET_TYPES",
   1398                                              &TMH_allowed_payment_targets))
   1399   {
   1400     TMH_allowed_payment_targets = GNUNET_strdup ("*");
   1401   }
   1402 
   1403   if (GNUNET_OK !=
   1404       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1405                                              "merchant",
   1406                                              "DEFAULT_PERSONA",
   1407                                              &TMH_default_persona))
   1408   {
   1409     TMH_default_persona = GNUNET_strdup ("expert");
   1410   }
   1411 
   1412   if (GNUNET_OK !=
   1413       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1414                                              "merchant",
   1415                                              "PAYMENT_TARGET_REGEX",
   1416                                              &TMH_payment_target_regex))
   1417   {
   1418     TMH_payment_target_regex = NULL;
   1419   }
   1420   else
   1421   {
   1422     if (0 == strlen (TMH_payment_target_regex))
   1423     {
   1424       GNUNET_free (TMH_payment_target_regex);
   1425     }
   1426     else
   1427     {
   1428       if (0 != regcomp (&TMH_payment_target_re,
   1429                         TMH_payment_target_regex,
   1430                         REG_NOSUB | REG_EXTENDED))
   1431       {
   1432         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1433                                    "merchant",
   1434                                    "PAYMENT_TARGET_REGEX",
   1435                                    "malformed regular expression");
   1436         global_ret = EXIT_NOTCONFIGURED;
   1437         GNUNET_free (TMH_payment_target_regex);
   1438         GNUNET_SCHEDULER_shutdown ();
   1439         return;
   1440       }
   1441     }
   1442   }
   1443   if (GNUNET_OK !=
   1444       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1445                                              "merchant",
   1446                                              "PHONE_REGEX",
   1447                                              &TMH_phone_regex))
   1448   {
   1449     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1450                                "merchant",
   1451                                "PHONE_REGEX",
   1452                                "no restrictions on phone number specified");
   1453   }
   1454   else
   1455   {
   1456     if (0 != regcomp (&TMH_phone_rx,
   1457                       TMH_phone_regex,
   1458                       REG_EXTENDED))
   1459     {
   1460       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1461                                  "merchant",
   1462                                  "PHONE_REGEX",
   1463                                  "Invalid regex specified");
   1464       global_ret = EXIT_NOTCONFIGURED;
   1465       GNUNET_SCHEDULER_shutdown ();
   1466       return;
   1467     }
   1468   }
   1469 
   1470   if (GNUNET_OK !=
   1471       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1472                                              "merchant",
   1473                                              "HELPER_SMS",
   1474                                              &TMH_helper_sms))
   1475   {
   1476     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1477                                "merchant",
   1478                                "HELPER_SMS",
   1479                                "no helper specified");
   1480   }
   1481 
   1482   if (GNUNET_OK !=
   1483       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1484                                              "merchant",
   1485                                              "HELPER_EMAIL",
   1486                                              &TMH_helper_email))
   1487   {
   1488     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1489                                "merchant",
   1490                                "HELPER_EMAIL",
   1491                                "no helper specified");
   1492   }
   1493 
   1494   {
   1495     char *tan_channels;
   1496 
   1497     if (GNUNET_OK ==
   1498         GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1499                                                "merchant",
   1500                                                "MANDATORY_TAN_CHANNELS",
   1501                                                &tan_channels))
   1502     {
   1503       for (char *tok = strtok (tan_channels,
   1504                                " ");
   1505            NULL != tok;
   1506            tok = strtok (NULL,
   1507                          " "))
   1508       {
   1509         if (0 == strcasecmp (tok,
   1510                              "sms"))
   1511         {
   1512           if (NULL == TMH_helper_sms)
   1513           {
   1514             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1515                                        "merchant",
   1516                                        "MANDATORY_TAN_CHANNELS",
   1517                                        "SMS mandatory, but no HELPER_SMS configured");
   1518             global_ret = EXIT_NOTCONFIGURED;
   1519             GNUNET_SCHEDULER_shutdown ();
   1520             GNUNET_free (tan_channels);
   1521             return;
   1522           }
   1523           TEH_mandatory_tan_channels |= TMH_TCS_SMS;
   1524         }
   1525         else if (0 == strcasecmp (tok,
   1526                                   "email"))
   1527         {
   1528           if (NULL == TMH_helper_email)
   1529           {
   1530             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1531                                        "merchant",
   1532                                        "MANDATORY_TAN_CHANNELS",
   1533                                        "EMAIL mandatory, but no HELPER_EMAIL configured");
   1534             global_ret = EXIT_NOTCONFIGURED;
   1535             GNUNET_SCHEDULER_shutdown ();
   1536             GNUNET_free (tan_channels);
   1537             return;
   1538           }
   1539           TEH_mandatory_tan_channels |= TMH_TCS_EMAIL;
   1540         }
   1541         else
   1542         {
   1543           GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1544                                      "merchant",
   1545                                      "MANDATORY_TAN_CHANNELS",
   1546                                      tok);
   1547           global_ret = EXIT_NOTCONFIGURED;
   1548           GNUNET_SCHEDULER_shutdown ();
   1549           GNUNET_free (tan_channels);
   1550           return;
   1551         }
   1552       }
   1553       GNUNET_free (tan_channels);
   1554     }
   1555   }
   1556 
   1557   if ( (GNUNET_YES == TMH_password_change_mfa) &&
   1558        (TMH_TCS_NONE == TEH_mandatory_tan_channels) )
   1559   {
   1560     GNUNET_log_config_invalid (
   1561       GNUNET_ERROR_TYPE_ERROR,
   1562       "merchant",
   1563       "PASSWORD_CHANGE_MFA",
   1564       "requires at least one MANDATORY_TAN_CHANNELS entry");
   1565     global_ret = EXIT_NOTCONFIGURED;
   1566     GNUNET_SCHEDULER_shutdown ();
   1567     return;
   1568   }
   1569 
   1570   if (GNUNET_OK ==
   1571       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1572                                              "merchant",
   1573                                              "BASE_URL",
   1574                                              &TMH_base_url))
   1575   {
   1576     if (! TALER_is_web_url (TMH_base_url))
   1577     {
   1578       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1579                                  "merchant",
   1580                                  "BASE_URL",
   1581                                  "Needs to start with 'http://' or 'https://'");
   1582       global_ret = EXIT_NOTCONFIGURED;
   1583       GNUNET_SCHEDULER_shutdown ();
   1584       return;
   1585     }
   1586   }
   1587   if (GNUNET_OK ==
   1588       GNUNET_CONFIGURATION_get_value_filename (TMH_cfg,
   1589                                                "merchant",
   1590                                                "BACKOFFICE_SPA_DIR",
   1591                                                &TMH_spa_dir))
   1592   {
   1593     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1594                 "Loading merchant SPA from %s\n",
   1595                 TMH_spa_dir);
   1596   }
   1597   else
   1598   {
   1599     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1600                 "Loading merchant SPA from default location\n");
   1601   }
   1602 
   1603   if (GNUNET_YES ==
   1604       GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1605                                             "merchant",
   1606                                             "FORCE_AUDIT"))
   1607     TMH_force_audit = GNUNET_YES;
   1608   if (GNUNET_OK !=
   1609       TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
   1610   {
   1611     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1612                 "Failed to setup templates\n");
   1613     global_ret = EXIT_NO_RESTART;
   1614     GNUNET_SCHEDULER_shutdown ();
   1615     return;
   1616   }
   1617   if (GNUNET_OK !=
   1618       TMH_spa_init (TMH_spa_dir))
   1619   {
   1620     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1621                 "Failed to load single page app\n");
   1622     global_ret = EXIT_NO_RESTART;
   1623     GNUNET_SCHEDULER_shutdown ();
   1624     return;
   1625   }
   1626   /* /static/ is currently not used */
   1627   /* (void) TMH_statics_init (); */
   1628   if (NULL ==
   1629       (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
   1630                                                              GNUNET_YES)))
   1631   {
   1632     global_ret = EXIT_FAILURE;
   1633     GNUNET_SCHEDULER_shutdown ();
   1634     return;
   1635   }
   1636   if (NULL ==
   1637       (TMH_db = TALER_MERCHANTDB_connect (TMH_cfg)))
   1638   {
   1639     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1640                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
   1641     global_ret = EXIT_FAILURE;
   1642     GNUNET_SCHEDULER_shutdown ();
   1643     return;
   1644   }
   1645   elen = TMH_EXCHANGES_init (config);
   1646   if (GNUNET_SYSERR == elen)
   1647   {
   1648     global_ret = EXIT_NOTCONFIGURED;
   1649     GNUNET_SCHEDULER_shutdown ();
   1650     return;
   1651   }
   1652   if (0 == elen)
   1653   {
   1654     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1655                 "Fatal: no trusted exchanges configured. Exiting.\n");
   1656     global_ret = EXIT_NOTCONFIGURED;
   1657     GNUNET_SCHEDULER_shutdown ();
   1658     return;
   1659   }
   1660 
   1661   {
   1662     struct GNUNET_DB_EventHeaderP es = {
   1663       .size = htons (sizeof (es)),
   1664       .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   1665     };
   1666 
   1667     instance_eh = TALER_MERCHANTDB_event_listen (TMH_db,
   1668                                                  &es,
   1669                                                  GNUNET_TIME_UNIT_FOREVER_REL,
   1670                                                  &load_instances,
   1671                                                  NULL);
   1672   }
   1673   load_instances (NULL,
   1674                   NULL,
   1675                   0);
   1676   {
   1677     enum GNUNET_GenericReturnValue ret;
   1678 
   1679     ret = TALER_MHD_listen_bind (TMH_cfg,
   1680                                  "merchant",
   1681                                  &start_daemon,
   1682                                  NULL);
   1683     switch (ret)
   1684     {
   1685     case GNUNET_SYSERR:
   1686       global_ret = EXIT_NOTCONFIGURED;
   1687       GNUNET_SCHEDULER_shutdown ();
   1688       return;
   1689     case GNUNET_NO:
   1690       if (! have_daemons)
   1691       {
   1692         global_ret = EXIT_FAILURE;
   1693         GNUNET_SCHEDULER_shutdown ();
   1694         return;
   1695       }
   1696       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1697                   "Could not open all configured listen sockets\n");
   1698       break;
   1699     case GNUNET_OK:
   1700       break;
   1701     }
   1702   }
   1703   global_ret = EXIT_SUCCESS;
   1704 }
   1705 
   1706 
   1707 /**
   1708  * The main function of the serve tool
   1709  *
   1710  * @param argc number of arguments from the command line
   1711  * @param argv command line arguments
   1712  * @return 0 ok, non-zero on error
   1713  */
   1714 int
   1715 main (int argc,
   1716       char *const *argv)
   1717 {
   1718   enum GNUNET_GenericReturnValue res;
   1719   struct GNUNET_GETOPT_CommandLineOption options[] = {
   1720     GNUNET_GETOPT_option_flag ('C',
   1721                                "connection-close",
   1722                                "force HTTP connections to be closed after each request",
   1723                                &merchant_connection_close),
   1724     GNUNET_GETOPT_option_flag ('3',
   1725                                "http3",
   1726                                "enable support for HTTP/2 and HTTP/3",
   1727                                &enable_h3),
   1728     GNUNET_GETOPT_option_timetravel ('T',
   1729                                      "timetravel"),
   1730     GNUNET_GETOPT_option_version (PACKAGE_VERSION),
   1731     GNUNET_GETOPT_OPTION_END
   1732   };
   1733 
   1734   res = GNUNET_PROGRAM_run (
   1735     TALER_MERCHANT_project_data (),
   1736     argc, argv,
   1737     "taler-merchant-httpd",
   1738     "Taler merchant's HTTP backend interface",
   1739     options,
   1740     &run, NULL);
   1741   if (GNUNET_SYSERR == res)
   1742     return EXIT_NOTCONFIGURED;
   1743   if (GNUNET_NO == res)
   1744     return EXIT_SUCCESS;
   1745   return global_ret;
   1746 }