merchant

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

taler-merchant-httpd.c (54075B)


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