LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.4 % 588 408
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 19 19

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

Generated by: LCOV version 2.0-1