merchant

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

taler-merchant-httpd.c (53985B)


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