LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 68.4 % 516 353
Test Date: 2026-01-01 16:44:56 Functions: 100.0 % 19 19

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

Generated by: LCOV version 2.0-1