LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.4 % 860 597
Test Date: 2025-11-28 21:09:21 Functions: 96.7 % 30 29

            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_config.h"
      33              : #include "taler-merchant-httpd_exchanges.h"
      34              : #include "taler-merchant-httpd_get-orders-ID.h"
      35              : #include "taler-merchant-httpd_get-products-image.h"
      36              : #include "taler-merchant-httpd_get-templates-ID.h"
      37              : #include "taler-merchant-httpd_helper.h"
      38              : #include "taler-merchant-httpd_mhd.h"
      39              : #include "taler-merchant-httpd_mfa.h"
      40              : #include "taler-merchant-httpd_private-delete-account-ID.h"
      41              : #include "taler-merchant-httpd_private-delete-categories-ID.h"
      42              : #include "taler-merchant-httpd_private-delete-units-ID.h"
      43              : #include "taler-merchant-httpd_private-delete-instances-ID.h"
      44              : #include "taler-merchant-httpd_private-delete-instances-ID-token.h"
      45              : #include "taler-merchant-httpd_private-delete-products-ID.h"
      46              : #include "taler-merchant-httpd_private-delete-orders-ID.h"
      47              : #include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
      48              : #include "taler-merchant-httpd_private-delete-templates-ID.h"
      49              : #include "taler-merchant-httpd_private-delete-token-families-SLUG.h"
      50              : #include "taler-merchant-httpd_private-delete-transfers-ID.h"
      51              : #include "taler-merchant-httpd_private-delete-webhooks-ID.h"
      52              : #include "taler-merchant-httpd_private-get-accounts.h"
      53              : #include "taler-merchant-httpd_private-get-accounts-ID.h"
      54              : #include "taler-merchant-httpd_private-get-categories.h"
      55              : #include "taler-merchant-httpd_private-get-categories-ID.h"
      56              : #include "taler-merchant-httpd_private-get-units.h"
      57              : #include "taler-merchant-httpd_private-get-units-ID.h"
      58              : #include "taler-merchant-httpd_private-get-incoming.h"
      59              : #include "taler-merchant-httpd_private-get-instances.h"
      60              : #include "taler-merchant-httpd_private-get-instances-ID.h"
      61              : #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
      62              : #include "taler-merchant-httpd_private-get-instances-ID-tokens.h"
      63              : #include "taler-merchant-httpd_private-get-pos.h"
      64              : #include "taler-merchant-httpd_private-get-products.h"
      65              : #include "taler-merchant-httpd_private-get-products-ID.h"
      66              : #include "taler-merchant-httpd_private-get-orders.h"
      67              : #include "taler-merchant-httpd_private-get-orders-ID.h"
      68              : #include "taler-merchant-httpd_private-get-otp-devices.h"
      69              : #include "taler-merchant-httpd_private-get-otp-devices-ID.h"
      70              : #include "taler-merchant-httpd_private-get-statistics-amount-SLUG.h"
      71              : #include "taler-merchant-httpd_private-get-statistics-counter-SLUG.h"
      72              : #include "taler-merchant-httpd_private-get-templates.h"
      73              : #include "taler-merchant-httpd_private-get-templates-ID.h"
      74              : #include "taler-merchant-httpd_private-get-token-families.h"
      75              : #include "taler-merchant-httpd_private-get-token-families-SLUG.h"
      76              : #include "taler-merchant-httpd_private-get-transfers.h"
      77              : #include "taler-merchant-httpd_private-get-webhooks.h"
      78              : #include "taler-merchant-httpd_private-get-webhooks-ID.h"
      79              : #include "taler-merchant-httpd_private-patch-accounts-ID.h"
      80              : #include "taler-merchant-httpd_private-patch-categories-ID.h"
      81              : #include "taler-merchant-httpd_private-patch-units-ID.h"
      82              : #include "taler-merchant-httpd_private-patch-instances-ID.h"
      83              : #include "taler-merchant-httpd_private-patch-orders-ID-forget.h"
      84              : #include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
      85              : #include "taler-merchant-httpd_private-patch-products-ID.h"
      86              : #include "taler-merchant-httpd_private-patch-templates-ID.h"
      87              : #include "taler-merchant-httpd_private-patch-token-families-SLUG.h"
      88              : #include "taler-merchant-httpd_private-patch-webhooks-ID.h"
      89              : #include "taler-merchant-httpd_private-post-account.h"
      90              : #include "taler-merchant-httpd_private-post-categories.h"
      91              : #include "taler-merchant-httpd_private-post-units.h"
      92              : #include "taler-merchant-httpd_private-post-instances.h"
      93              : #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
      94              : #include "taler-merchant-httpd_private-post-instances-ID-token.h"
      95              : #include "taler-merchant-httpd_private-post-otp-devices.h"
      96              : #include "taler-merchant-httpd_private-post-orders.h"
      97              : #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
      98              : #include "taler-merchant-httpd_private-post-products.h"
      99              : #include "taler-merchant-httpd_private-post-products-ID-lock.h"
     100              : #include "taler-merchant-httpd_private-post-templates.h"
     101              : #include "taler-merchant-httpd_private-post-token-families.h"
     102              : #include "taler-merchant-httpd_private-post-transfers.h"
     103              : #include "taler-merchant-httpd_private-post-webhooks.h"
     104              : #include "taler-merchant-httpd_post-challenge-ID.h"
     105              : #include "taler-merchant-httpd_post-challenge-ID-confirm.h"
     106              : #include "taler-merchant-httpd_post-orders-ID-abort.h"
     107              : #include "taler-merchant-httpd_post-orders-ID-claim.h"
     108              : #include "taler-merchant-httpd_post-orders-ID-paid.h"
     109              : #include "taler-merchant-httpd_post-orders-ID-pay.h"
     110              : #include "taler-merchant-httpd_post-using-templates.h"
     111              : #include "taler-merchant-httpd_post-orders-ID-refund.h"
     112              : #include "taler-merchant-httpd_spa.h"
     113              : #include "taler-merchant-httpd_statics.h"
     114              : #include "taler-merchant-httpd_terms.h"
     115              : 
     116              : #ifdef HAVE_DONAU_DONAU_SERVICE_H
     117              : #include "taler-merchant-httpd_private-get-donau-instances.h"
     118              : #include "taler-merchant-httpd_private-post-donau-instance.h"
     119              : #include "taler-merchant-httpd_private-delete-donau-instance-ID.h"
     120              : #endif
     121              : 
     122              : /**
     123              :  * Backlog for listen operation on unix-domain sockets.
     124              :  */
     125              : #define UNIX_BACKLOG 500
     126              : 
     127              : /**
     128              :  * Default maximum upload size permitted.  Can be overridden
     129              :  * per handler.
     130              :  */
     131              : #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
     132              : 
     133              : char *TMH_currency;
     134              : 
     135              : char *TMH_base_url;
     136              : 
     137              : char *TMH_spa_dir;
     138              : 
     139              : char *TMH_helper_email;
     140              : 
     141              : char *TMH_helper_sms;
     142              : 
     143              : char *TMH_allowed_payment_targets;
     144              : 
     145              : char *TMH_default_persona;
     146              : 
     147              : char *TMH_payment_target_regex;
     148              : 
     149              : regex_t TMH_payment_target_re;
     150              : 
     151              : int TMH_force_audit;
     152              : 
     153              : struct TALER_MERCHANTDB_Plugin *TMH_db;
     154              : 
     155              : struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
     156              : 
     157              : struct GNUNET_TIME_Relative TMH_default_pay_delay;
     158              : 
     159              : struct GNUNET_TIME_Relative TMH_default_refund_delay;
     160              : 
     161              : struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
     162              : 
     163              : enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
     164              : 
     165              : int TMH_strict_v19;
     166              : 
     167              : int TMH_auth_disabled;
     168              : 
     169              : int TMH_have_self_provisioning;
     170              : 
     171              : enum TEH_TanChannelSet TEH_mandatory_tan_channels;
     172              : 
     173              : struct GNUNET_TIME_Relative TMH_legal_expiration;
     174              : 
     175              : unsigned int TMH_num_cspecs;
     176              : 
     177              : struct TALER_CurrencySpecification *TMH_cspecs;
     178              : 
     179              : struct GNUNET_CURL_Context *TMH_curl_ctx;
     180              : 
     181              : /**
     182              :  * Event handler for instance settings changes.
     183              :  */
     184              : static struct GNUNET_DB_EventHandler *instance_eh;
     185              : 
     186              : /**
     187              :  * True if we started any HTTP daemon.
     188              :  */
     189              : static bool have_daemons;
     190              : 
     191              : /**
     192              :  * Should a "Connection: close" header be added to each HTTP response?
     193              :  */
     194              : static int merchant_connection_close;
     195              : 
     196              : /**
     197              :  * Context for integrating #TMH_curl_ctx with the
     198              :  * GNUnet event loop.
     199              :  */
     200              : static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
     201              : 
     202              : /**
     203              :  * Global return code
     204              :  */
     205              : static int global_ret;
     206              : 
     207              : /**
     208              :  * Our configuration.
     209              :  */
     210              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     211              : 
     212              : /**
     213              :  * Maximum length of a permissions string of a scope
     214              :  */
     215              : #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
     216              : 
     217              : /**
     218              :  * Maximum length of a name of a scope
     219              :  */
     220              : #define TMH_MAX_NAME_LEN 255
     221              : 
     222              : /**
     223              :  * Represents a hard-coded set of default scopes with their
     224              :  * permissions and names
     225              :  */
     226              : struct ScopePermissionMap
     227              : {
     228              :   /**
     229              :    * The scope enum value
     230              :    */
     231              :   enum TMH_AuthScope as;
     232              : 
     233              :   /**
     234              :    * The scope name
     235              :    */
     236              :   char name[TMH_MAX_NAME_LEN];
     237              : 
     238              :   /**
     239              :    * The scope permissions string.
     240              :    * Comma-separated.
     241              :    */
     242              :   char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
     243              : };
     244              : 
     245              : /**
     246              :  * The default scopes array for merchant
     247              :  */
     248              : struct ScopePermissionMap scope_permissions[] = {
     249              :   /* Deprecated since v19 */
     250              :   {
     251              :     .as = TMH_AS_ALL,
     252              :     .name = "write",
     253              :     .permissions = "*"
     254              :   },
     255              :   /* Full access for SPA */
     256              :   {
     257              :     .as = TMH_AS_ALL,
     258              :     .name = "all",
     259              :     .permissions = "*"
     260              :   },
     261              :   /* Full access for SPA */
     262              :   {
     263              :     .as = TMH_AS_SPA,
     264              :     .name = "spa",
     265              :     .permissions = "*"
     266              :   },
     267              :   /* Read-only access */
     268              :   {
     269              :     .as = TMH_AS_READ_ONLY,
     270              :     .name = "readonly",
     271              :     .permissions = "*-read"
     272              :   },
     273              :   /* Simple order management */
     274              :   {
     275              :     .as = TMH_AS_ORDER_SIMPLE,
     276              :     .name = "order-simple",
     277              :     .permissions = "orders-read,orders-write"
     278              :   },
     279              :   /* Simple order management for PoS, also allows inventory locking */
     280              :   {
     281              :     .as = TMH_AS_ORDER_POS,
     282              :     .name = "order-pos",
     283              :     .permissions = "orders-read,orders-write,inventory-lock"
     284              :   },
     285              :   /* Simple order management, also allows refunding */
     286              :   {
     287              :     .as = TMH_AS_ORDER_MGMT,
     288              :     .name = "order-mgmt",
     289              :     .permissions = "orders-read,orders-write,orders-refund"
     290              :   },
     291              :   /* Full order management, allows inventory locking and refunds */
     292              :   {
     293              :     .as = TMH_AS_ORDER_FULL,
     294              :     .name = "order-full",
     295              :     .permissions = "orders-read,orders-write,inventory-lock,orders-refund"
     296              :   },
     297              :   /* No permissions, dummy scope */
     298              :   {
     299              :     .as = TMH_AS_NONE,
     300              :   }
     301              : };
     302              : 
     303              : 
     304              : /**
     305              :  * Get permissions string for scope.
     306              :  * Also extracts the leftmost bit into the @a refreshable
     307              :  * output parameter.
     308              :  *
     309              :  * @param as the scope to get the permissions string from
     310              :  * @param[out] refreshable true if the token associated with this scope is refreshable.
     311              :  * @return the permissions string, or NULL if no such scope found
     312              :  */
     313              : static const char*
     314          566 : get_scope_permissions (enum TMH_AuthScope as,
     315              :                        bool *refreshable)
     316              : {
     317          566 :   *refreshable = as & TMH_AS_REFRESHABLE;
     318          767 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     319              :   {
     320              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     321          749 :     if ( (as & ~TMH_AS_REFRESHABLE)  ==
     322          749 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     323          548 :       return scope_permissions[i].permissions;
     324              :   }
     325           18 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     326              :               "Failed to find required permissions for scope %d\n",
     327              :               as);
     328           18 :   return NULL;
     329              : }
     330              : 
     331              : 
     332              : /**
     333              :  * Checks if @a permission_required is in permissions of
     334              :  * @a scope.
     335              :  *
     336              :  * @param permission_required the permission to check.
     337              :  * @param scope the scope to check.
     338              :  * @return true if @a permission_required is in the permissions set of @a scope.
     339              :  */
     340              : static bool
     341          536 : permission_in_scope (const char *permission_required,
     342              :                      enum TMH_AuthScope scope)
     343              : {
     344              :   char *permissions;
     345              :   const char *perms_tmp;
     346          536 :   bool is_read_perm = false;
     347          536 :   bool is_write_perm = false;
     348              :   bool refreshable;
     349              :   const char *last_dash;
     350              : 
     351          536 :   perms_tmp = get_scope_permissions (scope,
     352              :                                      &refreshable);
     353          536 :   if (NULL == perms_tmp)
     354              :   {
     355           18 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     356              :                 "Permission check failed: scope %d not understood\n",
     357              :                 (int) scope);
     358           18 :     return false;
     359              :   }
     360          518 :   last_dash = strrchr (permission_required,
     361              :                        '-');
     362          518 :   if (NULL != last_dash)
     363              :   {
     364          517 :     is_write_perm = (0 == strcmp (last_dash,
     365              :                                   "-write"));
     366          517 :     is_read_perm = (0 == strcmp (last_dash,
     367              :                                  "-read"));
     368              :   }
     369              : 
     370          518 :   if (0 == strcmp ("token-refresh",
     371              :                    permission_required))
     372              :   {
     373           16 :     if (! refreshable)
     374              :     {
     375            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     376              :                   "Permission check failed: token not refreshable\n");
     377              :     }
     378           16 :     return refreshable;
     379              :   }
     380          502 :   permissions = GNUNET_strdup (perms_tmp);
     381              :   {
     382          502 :     const char *perm = strtok (permissions,
     383              :                                ",");
     384              : 
     385          502 :     if (NULL == perm)
     386              :     {
     387            0 :       GNUNET_free (permissions);
     388            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     389              :                   "Permission check failed: empty permission set\n");
     390            0 :       return false;
     391              :     }
     392          503 :     while (NULL != perm)
     393              :     {
     394          502 :       if (0 == strcmp ("*",
     395              :                        perm))
     396              :       {
     397          500 :         GNUNET_free (permissions);
     398          500 :         return true;
     399              :       }
     400            2 :       if ( (0 == strcmp ("*-write",
     401            0 :                          perm)) &&
     402              :            (is_write_perm) )
     403              :       {
     404            0 :         GNUNET_free (permissions);
     405            0 :         return true;
     406              :       }
     407            2 :       if ( (0 == strcmp ("*-read",
     408            2 :                          perm)) &&
     409              :            (is_read_perm) )
     410              :       {
     411            1 :         GNUNET_free (permissions);
     412            1 :         return true;
     413              :       }
     414            1 :       if (0 == strcmp (permission_required,
     415              :                        perm))
     416              :       {
     417            0 :         GNUNET_free (permissions);
     418            0 :         return true;
     419              :       }
     420            1 :       perm = strtok (NULL,
     421              :                      ",");
     422              :     }
     423              :   }
     424            1 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     425              :               "Permission check failed: %s not found in %s\n",
     426              :               permission_required,
     427              :               permissions);
     428            1 :   GNUNET_free (permissions);
     429            1 :   return false;
     430              : }
     431              : 
     432              : 
     433              : bool
     434           15 : TMH_scope_is_subset (enum TMH_AuthScope as,
     435              :                      enum TMH_AuthScope candidate)
     436              : {
     437              :   const char *as_perms;
     438              :   const char *candidate_perms;
     439              :   char *permissions;
     440              :   bool as_refreshable;
     441              :   bool cand_refreshable;
     442              : 
     443           15 :   as_perms = get_scope_permissions (as,
     444              :                                     &as_refreshable);
     445           15 :   candidate_perms = get_scope_permissions (candidate,
     446              :                                            &cand_refreshable);
     447           15 :   if (! as_refreshable && cand_refreshable)
     448            0 :     return false;
     449           15 :   if ( (NULL == as_perms) &&
     450              :        (NULL != candidate_perms) )
     451            0 :     return false;
     452           15 :   if ( (NULL == candidate_perms) ||
     453           15 :        (0 == strcmp ("*",
     454              :                      as_perms)))
     455           14 :     return true;
     456            1 :   permissions = GNUNET_strdup (candidate_perms);
     457              :   {
     458              :     const char *perm;
     459              : 
     460            1 :     perm = strtok (permissions,
     461              :                    ",");
     462            1 :     if (NULL == perm)
     463              :     {
     464            0 :       GNUNET_free (permissions);
     465            0 :       return true;
     466              :     }
     467            1 :     while (NULL != perm)
     468              :     {
     469            1 :       if (! permission_in_scope (perm,
     470              :                                  as))
     471              :       {
     472            1 :         GNUNET_free (permissions);
     473            1 :         return false;
     474              :       }
     475            0 :       perm = strtok (NULL,
     476              :                      ",");
     477              :     }
     478              :   }
     479            0 :   GNUNET_free (permissions);
     480            0 :   return true;
     481              : }
     482              : 
     483              : 
     484              : enum TMH_AuthScope
     485           15 : TMH_get_scope_by_name (const char *name)
     486              : {
     487           36 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     488              :   {
     489           36 :     if (0 == strcasecmp (scope_permissions[i].name,
     490              :                          name))
     491           15 :       return scope_permissions[i].as;
     492              :   }
     493            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     494              :               "Name `%s' does not match any scope we understand\n",
     495              :               name);
     496            0 :   return TMH_AS_NONE;
     497              : }
     498              : 
     499              : 
     500              : const char*
     501            2 : TMH_get_name_by_scope (enum TMH_AuthScope scope,
     502              :                        bool *refreshable)
     503              : {
     504            2 :   *refreshable = scope & TMH_AS_REFRESHABLE;
     505            8 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     506              :   {
     507              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     508            8 :     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
     509            8 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     510            2 :       return scope_permissions[i].name;
     511              :   }
     512            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     513              :               "Scope #%d does not match any scope we understand\n",
     514              :               (int) scope);
     515            0 :   return NULL;
     516              : }
     517              : 
     518              : 
     519              : enum GNUNET_GenericReturnValue
     520           23 : TMH_check_auth (const char *password,
     521              :                 struct TALER_MerchantAuthenticationSaltP *salt,
     522              :                 struct TALER_MerchantAuthenticationHashP *hash)
     523              : {
     524              :   struct TALER_MerchantAuthenticationHashP val;
     525              : 
     526           23 :   if (GNUNET_is_zero (hash))
     527            0 :     return GNUNET_OK;
     528           23 :   if (NULL == password)
     529              :   {
     530            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     531              :                 "Denying access: empty password provided\n");
     532            0 :     return GNUNET_SYSERR;
     533              :   }
     534           23 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     535              :               "Checking against token with salt %s\n",
     536              :               TALER_B2S (salt));
     537           23 :   TALER_merchant_instance_auth_hash_with_salt (&val,
     538              :                                                salt,
     539              :                                                password);
     540           23 :   if (0 !=
     541           23 :       GNUNET_memcmp (&val,
     542              :                      hash))
     543              :   {
     544           10 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     545              :                 "Access denied: password does not match\n");
     546           10 :     return GNUNET_SYSERR;
     547              :   }
     548           13 :   return GNUNET_OK;
     549              : }
     550              : 
     551              : 
     552              : /**
     553              :  * Check if @a userpass grants access to @a instance.
     554              :  *
     555              :  * @param userpass base64 encoded "$USERNAME:$PASSWORD" value
     556              :  *        from HTTP Basic "Authentication" header
     557              :  * @param instance the access controlled instance
     558              :  */
     559              : static enum GNUNET_GenericReturnValue
     560           14 : check_auth_instance (const char *userpass,
     561              :                      struct TMH_MerchantInstance *instance)
     562              : {
     563              :   char *tmp;
     564              :   char *colon;
     565              :   const char *instance_name;
     566              :   const char *password;
     567           14 :   const char *target_instance = "admin";
     568              :   enum GNUNET_GenericReturnValue ret;
     569              : 
     570              :   /* implicitly a zeroed out hash means no authentication */
     571           14 :   if (GNUNET_is_zero (&instance->auth.auth_hash))
     572            0 :     return GNUNET_OK;
     573           14 :   if (NULL == userpass)
     574              :   {
     575            0 :     GNUNET_break_op (0);
     576            0 :     return GNUNET_SYSERR;
     577              :   }
     578           14 :   if (0 ==
     579           14 :       GNUNET_STRINGS_base64_decode (userpass,
     580              :                                     strlen (userpass),
     581              :                                     (void**) &tmp))
     582              :   {
     583            0 :     GNUNET_break_op (0);
     584            0 :     return GNUNET_SYSERR;
     585              :   }
     586           14 :   colon = strchr (tmp,
     587              :                   ':');
     588           14 :   if (NULL == colon)
     589              :   {
     590            0 :     GNUNET_break_op (0);
     591            0 :     GNUNET_free (tmp);
     592            0 :     return GNUNET_SYSERR;
     593              :   }
     594           14 :   *colon = '\0';
     595           14 :   instance_name = tmp;
     596           14 :   password = colon + 1;
     597              :   /* instance->settings.id can be NULL if there is no instance yet */
     598           14 :   if (NULL != instance->settings.id)
     599           14 :     target_instance = instance->settings.id;
     600           14 :   if (0 != strcmp (instance_name,
     601              :                    target_instance))
     602              :   {
     603            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     604              :                 "Somebody tried to login to instance %s with username %s (login failed).\n",
     605              :                 target_instance,
     606              :                 instance_name);
     607            0 :     GNUNET_free (tmp);
     608            0 :     return GNUNET_SYSERR;
     609              :   }
     610           14 :   ret = TMH_check_auth (password,
     611              :                         &instance->auth.auth_salt,
     612              :                         &instance->auth.auth_hash);
     613           14 :   GNUNET_free (tmp);
     614           14 :   if (GNUNET_OK != ret)
     615              :   {
     616            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     617              :                 "Password provided does not match credentials for %s\n",
     618              :                 target_instance);
     619              :   }
     620           14 :   return ret;
     621              : }
     622              : 
     623              : 
     624              : void
     625           13 : TMH_compute_auth (const char *token,
     626              :                   struct TALER_MerchantAuthenticationSaltP *salt,
     627              :                   struct TALER_MerchantAuthenticationHashP *hash)
     628              : {
     629           13 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     630              :                               salt,
     631              :                               sizeof (*salt));
     632           13 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     633              :               "Computing initial auth using token with salt %s\n",
     634              :               TALER_B2S (salt));
     635           13 :   TALER_merchant_instance_auth_hash_with_salt (hash,
     636              :                                                salt,
     637              :                                                token);
     638           13 : }
     639              : 
     640              : 
     641              : void
     642           54 : TMH_wire_method_free (struct TMH_WireMethod *wm)
     643              : {
     644           54 :   GNUNET_free (wm->payto_uri.full_payto);
     645           54 :   GNUNET_free (wm->wire_method);
     646           54 :   GNUNET_free (wm->credit_facade_url);
     647           54 :   json_decref (wm->credit_facade_credentials);
     648           54 :   GNUNET_free (wm);
     649           54 : }
     650              : 
     651              : 
     652              : void
     653          807 : TMH_instance_decref (struct TMH_MerchantInstance *mi)
     654              : {
     655              :   struct TMH_WireMethod *wm;
     656              : 
     657          807 :   mi->rc--;
     658          807 :   if (0 != mi->rc)
     659          705 :     return;
     660          102 :   TMH_force_get_orders_resume (mi);
     661          156 :   while (NULL != (wm = (mi->wm_head)))
     662              :   {
     663           54 :     GNUNET_CONTAINER_DLL_remove (mi->wm_head,
     664              :                                  mi->wm_tail,
     665              :                                  wm);
     666           54 :     TMH_wire_method_free (wm);
     667              :   }
     668              : 
     669          102 :   GNUNET_free (mi->settings.id);
     670          102 :   GNUNET_free (mi->settings.name);
     671          102 :   GNUNET_free (mi->settings.email);
     672          102 :   GNUNET_free (mi->settings.phone);
     673          102 :   GNUNET_free (mi->settings.website);
     674          102 :   GNUNET_free (mi->settings.logo);
     675          102 :   json_decref (mi->settings.address);
     676          102 :   json_decref (mi->settings.jurisdiction);
     677          102 :   GNUNET_free (mi);
     678              : }
     679              : 
     680              : 
     681              : enum GNUNET_GenericReturnValue
     682          102 : TMH_instance_free_cb (void *cls,
     683              :                       const struct GNUNET_HashCode *key,
     684              :                       void *value)
     685              : {
     686          102 :   struct TMH_MerchantInstance *mi = value;
     687              : 
     688              :   (void) cls;
     689              :   (void) key;
     690          102 :   GNUNET_assert (GNUNET_OK ==
     691              :                  GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
     692              :                                                        &mi->h_instance,
     693              :                                                        mi));
     694          102 :   TMH_instance_decref (mi);
     695          102 :   return GNUNET_YES;
     696              : }
     697              : 
     698              : 
     699              : /**
     700              :  * Shutdown task (invoked when the application is being
     701              :  * terminated for any reason)
     702              :  *
     703              :  * @param cls NULL
     704              :  */
     705              : static void
     706           15 : do_shutdown (void *cls)
     707              : {
     708              :   (void) cls;
     709           15 :   TALER_MHD_daemons_halt ();
     710           15 :   TMH_force_orders_resume ();
     711           15 :   TMH_force_ac_resume ();
     712           15 :   TMH_force_pc_resume ();
     713           15 :   TMH_force_kyc_resume ();
     714           15 :   TMH_force_gorc_resume ();
     715           15 :   TMH_force_wallet_get_order_resume ();
     716           15 :   TMH_force_wallet_refund_order_resume ();
     717           15 :   TMH_challenge_done ();
     718           15 :   TALER_MHD_daemons_destroy ();
     719           15 :   if (NULL != instance_eh)
     720              :   {
     721           15 :     TMH_db->event_listen_cancel (instance_eh);
     722           15 :     instance_eh = NULL;
     723              :   }
     724           15 :   TMH_EXCHANGES_done ();
     725           15 :   if (NULL != TMH_db)
     726              :   {
     727           15 :     TALER_MERCHANTDB_plugin_unload (TMH_db);
     728           15 :     TMH_db = NULL;
     729              :   }
     730           15 :   if (NULL != TMH_by_id_map)
     731              :   {
     732           15 :     GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
     733              :                                            &TMH_instance_free_cb,
     734              :                                            NULL);
     735           15 :     GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
     736           15 :     TMH_by_id_map = NULL;
     737              :   }
     738           15 :   TALER_TEMPLATING_done ();
     739           15 :   if (NULL != TMH_curl_ctx)
     740              :   {
     741           15 :     GNUNET_CURL_fini (TMH_curl_ctx);
     742           15 :     TMH_curl_ctx = NULL;
     743              :   }
     744           15 :   if (NULL != merchant_curl_rc)
     745              :   {
     746           15 :     GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
     747           15 :     merchant_curl_rc = NULL;
     748              :   }
     749           15 :   if (NULL != TMH_payment_target_regex)
     750              :   {
     751            0 :     regfree (&TMH_payment_target_re);
     752            0 :     GNUNET_free (TMH_payment_target_regex);
     753              :   }
     754           15 : }
     755              : 
     756              : 
     757              : /**
     758              :  * Function called whenever MHD is done with a request.  If the
     759              :  * request was a POST, we may have stored a `struct Buffer *` in the
     760              :  * @a con_cls that might still need to be cleaned up.  Call the
     761              :  * respective function to free the memory.
     762              :  *
     763              :  * @param cls client-defined closure
     764              :  * @param connection connection handle
     765              :  * @param con_cls value as set by the last call to
     766              :  *        the #MHD_AccessHandlerCallback
     767              :  * @param toe reason for request termination
     768              :  * @see #MHD_OPTION_NOTIFY_COMPLETED
     769              :  * @ingroup request
     770              :  */
     771              : static void
     772          749 : handle_mhd_completion_callback (void *cls,
     773              :                                 struct MHD_Connection *connection,
     774              :                                 void **con_cls,
     775              :                                 enum MHD_RequestTerminationCode toe)
     776              : {
     777          749 :   struct TMH_HandlerContext *hc = *con_cls;
     778              : 
     779              :   (void) cls;
     780          749 :   if (NULL == hc)
     781            0 :     return;
     782          749 :   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
     783              :   {
     784              : #if MHD_VERSION >= 0x00097304
     785              :     const union MHD_ConnectionInfo *ci;
     786          749 :     unsigned int http_status = 0;
     787              : 
     788          749 :     ci = MHD_get_connection_info (connection,
     789              :                                   MHD_CONNECTION_INFO_HTTP_STATUS);
     790          749 :     if (NULL != ci)
     791          749 :       http_status = ci->http_status;
     792          749 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     793              :                 "Request for `%s' completed with HTTP status %u (%d)\n",
     794              :                 hc->url,
     795              :                 http_status,
     796              :                 toe);
     797              : #else
     798              :     (void) connection;
     799              :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     800              :                 "Finished handling request for `%s' with MHD termination code %d\n",
     801              :                 hc->url,
     802              :                 (int) toe);
     803              : #endif
     804              :   }
     805          749 :   if (NULL != hc->cc)
     806          256 :     hc->cc (hc->ctx);
     807          749 :   TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
     808          749 :   GNUNET_free (hc->infix);
     809          749 :   if (NULL != hc->request_body)
     810          424 :     json_decref (hc->request_body);
     811          749 :   if (NULL != hc->instance)
     812          705 :     TMH_instance_decref (hc->instance);
     813          749 :   TMH_db->preflight (TMH_db->cls);
     814          749 :   GNUNET_free (hc->full_url);
     815          749 :   GNUNET_free (hc);
     816          749 :   *con_cls = NULL;
     817              : }
     818              : 
     819              : 
     820              : struct TMH_MerchantInstance *
     821         1014 : TMH_lookup_instance (const char *instance_id)
     822              : {
     823              :   struct GNUNET_HashCode h_instance;
     824              : 
     825         1014 :   if (NULL == instance_id)
     826          666 :     instance_id = "admin";
     827         1014 :   GNUNET_CRYPTO_hash (instance_id,
     828              :                       strlen (instance_id),
     829              :                       &h_instance);
     830         1014 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     831              :               "Looking for by-id key %s of '%s' in hashmap\n",
     832              :               GNUNET_h2s (&h_instance),
     833              :               instance_id);
     834              :   /* We're fine if that returns NULL, the calling routine knows how
     835              :      to handle that */
     836         1014 :   return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
     837              :                                             &h_instance);
     838              : }
     839              : 
     840              : 
     841              : /**
     842              :  * Add instance definition to our active set of instances.
     843              :  *
     844              :  * @param[in,out] mi merchant instance details to define
     845              :  * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
     846              :  */
     847              : enum GNUNET_GenericReturnValue
     848          102 : TMH_add_instance (struct TMH_MerchantInstance *mi)
     849              : {
     850              :   const char *id;
     851              :   int ret;
     852              : 
     853          102 :   id = mi->settings.id;
     854          102 :   if (NULL == id)
     855            0 :     id = "admin";
     856          102 :   GNUNET_CRYPTO_hash (id,
     857              :                       strlen (id),
     858              :                       &mi->h_instance);
     859          102 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     860              :               "Looking for by-id key %s of `%s' in hashmap\n",
     861              :               GNUNET_h2s (&mi->h_instance),
     862              :               id);
     863          102 :   ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
     864          102 :                                            &mi->h_instance,
     865              :                                            mi,
     866              :                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
     867          102 :   if (GNUNET_OK == ret)
     868              :   {
     869          102 :     GNUNET_assert (mi->rc < UINT_MAX);
     870          102 :     mi->rc++;
     871              :   }
     872          102 :   return ret;
     873              : }
     874              : 
     875              : 
     876              : /**
     877              :  * Handle a OPTIONS "*" request.
     878              :  *
     879              :  * @param rh context of the handler
     880              :  * @param connection the MHD connection to handle
     881              :  * @param[in,out] hc context with further information about the request
     882              :  * @return MHD result code
     883              :  */
     884              : static MHD_RESULT
     885            0 : handle_server_options (const struct TMH_RequestHandler *rh,
     886              :                        struct MHD_Connection *connection,
     887              :                        struct TMH_HandlerContext *hc)
     888              : {
     889              :   (void) rh;
     890              :   (void) hc;
     891            0 :   return TALER_MHD_reply_cors_preflight (connection);
     892              : }
     893              : 
     894              : 
     895              : /**
     896              :  * Generates the response for "/", redirecting the
     897              :  * client to the "/webui/" from where we serve the SPA.
     898              :  *
     899              :  * @param rh request handler
     900              :  * @param connection MHD connection
     901              :  * @param hc handler context
     902              :  * @return MHD result code
     903              :  */
     904              : static MHD_RESULT
     905            1 : spa_redirect (const struct TMH_RequestHandler *rh,
     906              :               struct MHD_Connection *connection,
     907              :               struct TMH_HandlerContext *hc)
     908              : {
     909            1 :   const char *text = "Redirecting to /webui/";
     910              :   struct MHD_Response *response;
     911              :   char *dst;
     912              : 
     913            1 :   response = MHD_create_response_from_buffer (strlen (text),
     914              :                                               (void *) text,
     915              :                                               MHD_RESPMEM_PERSISTENT);
     916            1 :   if (NULL == response)
     917              :   {
     918            0 :     GNUNET_break (0);
     919            0 :     return MHD_NO;
     920              :   }
     921            1 :   TALER_MHD_add_global_headers (response,
     922              :                                 true);
     923            1 :   GNUNET_break (MHD_YES ==
     924              :                 MHD_add_response_header (response,
     925              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     926              :                                          "text/plain"));
     927            1 :   if ( (NULL == hc->instance) ||
     928            1 :        (0 == strcmp ("admin",
     929            1 :                      hc->instance->settings.id)) )
     930            1 :     dst = GNUNET_strdup ("/webui/");
     931              :   else
     932            0 :     GNUNET_asprintf (&dst,
     933              :                      "/instances/%s/webui/",
     934            0 :                      hc->instance->settings.id);
     935            1 :   if (MHD_NO ==
     936            1 :       MHD_add_response_header (response,
     937              :                                MHD_HTTP_HEADER_LOCATION,
     938              :                                dst))
     939              :   {
     940            0 :     GNUNET_break (0);
     941            0 :     MHD_destroy_response (response);
     942            0 :     GNUNET_free (dst);
     943            0 :     return MHD_NO;
     944              :   }
     945            1 :   GNUNET_free (dst);
     946              : 
     947              :   {
     948              :     MHD_RESULT ret;
     949              : 
     950            1 :     ret = MHD_queue_response (connection,
     951              :                               MHD_HTTP_FOUND,
     952              :                               response);
     953            1 :     MHD_destroy_response (response);
     954            1 :     return ret;
     955              :   }
     956              : }
     957              : 
     958              : 
     959              : /**
     960              :  * Extract the token from authorization header value @a auth.
     961              :  * The @a auth value can be a bearer token or a Basic
     962              :  * authentication header. In both cases, this function
     963              :  * updates @a auth to point to the actual credential,
     964              :  * skipping spaces.
     965              :  *
     966              :  * NOTE: We probably want to replace this function with MHD2
     967              :  * API calls in the future that are more robust.
     968              :  *
     969              :  * @param[in,out] auth pointer to authorization header value,
     970              :  *        will be updated to point to the start of the token
     971              :  *        or set to NULL if header value is invalid
     972              :  * @param[out] is_basic_auth will be set to true if the
     973              :  *        authorization header uses basic authentication,
     974              :  *        otherwise to false
     975              :  */
     976              : static void
     977           71 : extract_auth (const char **auth,
     978              :               bool *is_basic_auth)
     979              : {
     980           71 :   const char *bearer = "Bearer ";
     981           71 :   const char *basic = "Basic ";
     982           71 :   const char *tok = *auth;
     983           71 :   size_t offset = 0;
     984           71 :   bool is_bearer = false;
     985              : 
     986           71 :   *is_basic_auth = false;
     987           71 :   if (0 == strncmp (tok,
     988              :                     bearer,
     989              :                     strlen (bearer)))
     990              :   {
     991           57 :     offset = strlen (bearer);
     992           57 :     is_bearer = true;
     993              :   }
     994           14 :   else if (0 == strncmp (tok,
     995              :                          basic,
     996              :                          strlen (basic)))
     997              :   {
     998           14 :     offset = strlen (basic);
     999           14 :     *is_basic_auth = true;
    1000              :   }
    1001              :   else
    1002              :   {
    1003            0 :     *auth = NULL;
    1004            0 :     return;
    1005              :   }
    1006           71 :   tok += offset;
    1007           71 :   while (' ' == *tok)
    1008            0 :     tok++;
    1009           71 :   if ( (is_bearer) &&
    1010           57 :        (0 != strncasecmp (tok,
    1011              :                           RFC_8959_PREFIX,
    1012              :                           strlen (RFC_8959_PREFIX))) )
    1013              :   {
    1014            0 :     *auth = NULL;
    1015            0 :     return;
    1016              :   }
    1017           71 :   *auth = tok;
    1018              : }
    1019              : 
    1020              : 
    1021              : /**
    1022              :  * Checks if the @a rh matches the given (parsed) URL.
    1023              :  *
    1024              :  * @param rh handler to compare against
    1025              :  * @param url the main URL (without "/private/" prefix, if any)
    1026              :  * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
    1027              :  * @param infix_url infix text, i.e. "$ORDER_ID".
    1028              :  * @param infix_strlen length of the string in @a infix_url
    1029              :  * @param suffix_url suffix, i.e. "/refund", including the "/"
    1030              :  * @param suffix_strlen number of characters in @a suffix_url
    1031              :  * @return true if @a rh matches this request
    1032              :  */
    1033              : static bool
    1034        15790 : prefix_match (const struct TMH_RequestHandler *rh,
    1035              :               const char *url,
    1036              :               size_t prefix_strlen,
    1037              :               const char *infix_url,
    1038              :               size_t infix_strlen,
    1039              :               const char *suffix_url,
    1040              :               size_t suffix_strlen)
    1041              : {
    1042        15790 :   if ( (prefix_strlen != strlen (rh->url_prefix)) ||
    1043         2447 :        (0 != memcmp (url,
    1044         2447 :                      rh->url_prefix,
    1045              :                      prefix_strlen)) )
    1046        14353 :     return false;
    1047         1437 :   if (! rh->have_id_segment)
    1048              :   {
    1049              :     /* Require /$PREFIX/$SUFFIX or /$PREFIX */
    1050          456 :     if (NULL != suffix_url)
    1051            0 :       return false;       /* too many segments to match */
    1052          456 :     if ( (NULL == infix_url)   /* either or */
    1053          456 :          ^ (NULL == rh->url_suffix) )
    1054            0 :       return false;       /* suffix existence mismatch */
    1055              :     /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
    1056          456 :     if ( (NULL != infix_url) &&
    1057            0 :          ( (infix_strlen != strlen (rh->url_suffix)) ||
    1058            0 :            (0 != memcmp (infix_url,
    1059            0 :                          rh->url_suffix,
    1060              :                          infix_strlen)) ) )
    1061            0 :       return false;       /* cannot use infix as suffix: content mismatch */
    1062              :   }
    1063              :   else
    1064              :   {
    1065              :     /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
    1066          981 :     if (NULL == infix_url)
    1067            0 :       return false;       /* infix existence mismatch */
    1068          981 :     if ( ( (NULL == suffix_url)
    1069          981 :            ^ (NULL == rh->url_suffix) ) )
    1070          253 :       return false;       /* suffix existence mismatch */
    1071          728 :     if ( (NULL != suffix_url) &&
    1072          363 :          ( (suffix_strlen != strlen (rh->url_suffix)) ||
    1073          253 :            (0 != memcmp (suffix_url,
    1074          253 :                          rh->url_suffix,
    1075              :                          suffix_strlen)) ) )
    1076          192 :       return false;       /* suffix content mismatch */
    1077              :   }
    1078          992 :   return true;
    1079              : }
    1080              : 
    1081              : 
    1082              : /**
    1083              :  * Function called first by MHD with the full URL.
    1084              :  *
    1085              :  * @param cls NULL
    1086              :  * @param full_url the full URL
    1087              :  * @param con MHD connection object
    1088              :  * @return our handler context
    1089              :  */
    1090              : static void *
    1091          749 : full_url_track_callback (void *cls,
    1092              :                          const char *full_url,
    1093              :                          struct MHD_Connection *con)
    1094              : {
    1095              :   struct TMH_HandlerContext *hc;
    1096              : 
    1097          749 :   hc = GNUNET_new (struct TMH_HandlerContext);
    1098          749 :   hc->connection = con;
    1099          749 :   GNUNET_async_scope_fresh (&hc->async_scope_id);
    1100          749 :   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    1101          749 :   hc->full_url = GNUNET_strdup (full_url);
    1102          749 :   return hc;
    1103              : }
    1104              : 
    1105              : 
    1106              : /**
    1107              :  * Function used to process Basic authorization header value.
    1108              :  * Sets correct scope in the auth_scope parameter of the
    1109              :  * #TMH_HandlerContext.
    1110              :  *
    1111              :  * @param hc the handler context
    1112              :  * @param authn_s the value of the authorization header
    1113              :  */
    1114              : static void
    1115           14 : process_basic_auth (struct TMH_HandlerContext *hc,
    1116              :                     const char *authn_s)
    1117              : {
    1118              :   /* Handle token endpoint slightly differently: Only allow
    1119              :    * instance password (Basic auth) to retrieve access token.
    1120              :    * We need to handle authorization with Basic auth here first
    1121              :    * The only time we need to handle authentication like this is
    1122              :    * for the token endpoint!
    1123              :    */
    1124           14 :   if ( (0 != strncmp (hc->rh->url_prefix,
    1125              :                       "/token",
    1126           14 :                       strlen ("/token"))) ||
    1127           14 :        (0 != strncmp (MHD_HTTP_METHOD_POST,
    1128           14 :                       hc->rh->method,
    1129           14 :                       strlen (MHD_HTTP_METHOD_POST))) ||
    1130           14 :        (NULL == hc->instance))
    1131              :   {
    1132            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1133              :                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
    1134              :                 hc->rh->url_prefix);
    1135            0 :     hc->auth_scope = TMH_AS_NONE;
    1136            0 :     return;
    1137              :   }
    1138           14 :   if (GNUNET_OK ==
    1139           14 :       check_auth_instance (authn_s,
    1140              :                            hc->instance))
    1141              :   {
    1142           13 :     hc->auth_scope = TMH_AS_ALL;
    1143              :   }
    1144              :   else
    1145              :   {
    1146            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1147              :                 "Basic authentication failed!\n");
    1148            1 :     hc->auth_scope = TMH_AS_NONE;
    1149              :   }
    1150              : }
    1151              : 
    1152              : 
    1153              : /**
    1154              :  * Function used to process Bearer authorization header value.
    1155              :  * Sets correct scope in the auth_scope parameter of the
    1156              :  * #TMH_HandlerContext..
    1157              :  *
    1158              :  * @param hc the handler context
    1159              :  * @param authn_s the value of the authorization header
    1160              :  * @return TALER_EC_NONE on success.
    1161              :  */
    1162              : static enum TALER_ErrorCode
    1163          513 : process_bearer_auth (struct TMH_HandlerContext *hc,
    1164              :                      const char *authn_s)
    1165              : {
    1166          513 :   if (NULL == hc->instance)
    1167              :   {
    1168            2 :     hc->auth_scope = TMH_AS_NONE;
    1169            2 :     return TALER_EC_NONE;
    1170              :   }
    1171          511 :   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
    1172              :   {
    1173              :     /* hash zero means no authentication for instance */
    1174          468 :     hc->auth_scope = TMH_AS_ALL;
    1175          468 :     return TALER_EC_NONE;
    1176              :   }
    1177              :   {
    1178              :     enum TALER_ErrorCode ec;
    1179              : 
    1180           43 :     ec = TMH_check_token (authn_s,
    1181           43 :                           hc->instance->settings.id,
    1182              :                           &hc->auth_scope);
    1183           43 :     if (TALER_EC_NONE != ec)
    1184              :     {
    1185              :       char *dec;
    1186              :       size_t dec_len;
    1187              :       const char *token;
    1188              : 
    1189              :       /* NOTE: Deprecated, remove sometime after v1.1 */
    1190            9 :       if (0 != strncasecmp (authn_s,
    1191              :                             RFC_8959_PREFIX,
    1192              :                             strlen (RFC_8959_PREFIX)))
    1193              :       {
    1194            0 :         GNUNET_break_op (0);
    1195            0 :         hc->auth_scope = TMH_AS_NONE;
    1196            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1197              :                     "Authentication token invalid: %d\n",
    1198              :                     (int) ec);
    1199            9 :         return ec;
    1200              :       }
    1201            9 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1202              :                   "Trying deprecated secret-token:password API authN\n");
    1203            9 :       token = authn_s + strlen (RFC_8959_PREFIX);
    1204            9 :       dec_len = GNUNET_STRINGS_urldecode (token,
    1205              :                                           strlen (token),
    1206              :                                           &dec);
    1207           18 :       if ( (0 == dec_len) ||
    1208              :            (GNUNET_OK !=
    1209            9 :             TMH_check_auth (dec,
    1210            9 :                             &hc->instance->auth.auth_salt,
    1211            9 :                             &hc->instance->auth.auth_hash)) )
    1212              :       {
    1213            9 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1214              :                     "Login failed\n");
    1215            9 :         hc->auth_scope = TMH_AS_NONE;
    1216            9 :         GNUNET_free (dec);
    1217            9 :         return TALER_EC_NONE;
    1218              :       }
    1219            0 :       hc->auth_scope = TMH_AS_ALL;
    1220            0 :       GNUNET_free (dec);
    1221              :     }
    1222              :   }
    1223           34 :   return TALER_EC_NONE;
    1224              : }
    1225              : 
    1226              : 
    1227              : /**
    1228              :  * A client has requested the given url using the given method
    1229              :  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
    1230              :  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
    1231              :  * must call MHD callbacks to provide content to give back to the
    1232              :  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
    1233              :  * #MHD_HTTP_NOT_FOUND, etc.).
    1234              :  *
    1235              :  * @param cls argument given together with the function
    1236              :  *        pointer when the handler was registered with MHD
    1237              :  * @param connection the MHD connection to handle
    1238              :  * @param url the requested url
    1239              :  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
    1240              :  *        #MHD_HTTP_METHOD_PUT, etc.)
    1241              :  * @param version the HTTP version string (i.e.
    1242              :  *        #MHD_HTTP_VERSION_1_1)
    1243              :  * @param upload_data the data being uploaded (excluding HEADERS,
    1244              :  *        for a POST that fits into memory and that is encoded
    1245              :  *        with a supported encoding, the POST data will NOT be
    1246              :  *        given in upload_data and is instead available as
    1247              :  *        part of #MHD_get_connection_values; very large POST
    1248              :  *        data *will* be made available incrementally in
    1249              :  *        @a upload_data)
    1250              :  * @param upload_data_size set initially to the size of the
    1251              :  *        @a upload_data provided; the method must update this
    1252              :  *        value to the number of bytes NOT processed;
    1253              :  * @param con_cls pointer that the callback can set to some
    1254              :  *        address and that will be preserved by MHD for future
    1255              :  *        calls for this request; since the access handler may
    1256              :  *        be called many times (i.e., for a PUT/POST operation
    1257              :  *        with plenty of upload data) this allows the application
    1258              :  *        to easily associate some request-specific state.
    1259              :  *        If necessary, this state can be cleaned up in the
    1260              :  *        global #MHD_RequestCompletedCallback (which
    1261              :  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
    1262              :  *        Initially, `*con_cls` will be set up by the
    1263              :  *        full_url_track_callback().
    1264              :  * @return #MHD_YES if the connection was handled successfully,
    1265              :  *         #MHD_NO if the socket must be closed due to a serious
    1266              :  *         error while handling the request
    1267              :  */
    1268              : static MHD_RESULT
    1269         2018 : url_handler (void *cls,
    1270              :              struct MHD_Connection *connection,
    1271              :              const char *url,
    1272              :              const char *method,
    1273              :              const char *version,
    1274              :              const char *upload_data,
    1275              :              size_t *upload_data_size,
    1276              :              void **con_cls)
    1277              : {
    1278              :   static struct TMH_RequestHandler management_handlers[] = {
    1279              :     /* GET /instances */
    1280              :     {
    1281              :       .url_prefix = "/instances",
    1282              :       .method = MHD_HTTP_METHOD_GET,
    1283              :       .permission = "instances-write",
    1284              :       .skip_instance = true,
    1285              :       .default_only = true,
    1286              :       .handler = &TMH_private_get_instances
    1287              :     },
    1288              :     /* POST /instances */
    1289              :     {
    1290              :       .url_prefix = "/instances",
    1291              :       .method = MHD_HTTP_METHOD_POST,
    1292              :       .permission = "instances-write",
    1293              :       .skip_instance = true,
    1294              :       .default_only = true,
    1295              :       .handler = &TMH_private_post_instances,
    1296              :       /* allow instance data of up to 8 MB, that should be plenty;
    1297              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1298              :          would require further changes to the allocation logic
    1299              :          in the code... */
    1300              :       .max_upload = 1024 * 1024 * 8
    1301              :     },
    1302              :     /* GET /instances/$ID/ */
    1303              :     {
    1304              :       .url_prefix = "/instances/",
    1305              :       .method = MHD_HTTP_METHOD_GET,
    1306              :       .permission = "instances-write",
    1307              :       .skip_instance = true,
    1308              :       .default_only = true,
    1309              :       .have_id_segment = true,
    1310              :       .handler = &TMH_private_get_instances_default_ID
    1311              :     },
    1312              :     /* DELETE /instances/$ID */
    1313              :     {
    1314              :       .url_prefix = "/instances/",
    1315              :       .method = MHD_HTTP_METHOD_DELETE,
    1316              :       .permission = "instances-write",
    1317              :       .skip_instance = true,
    1318              :       .default_only = true,
    1319              :       .have_id_segment = true,
    1320              :       .handler = &TMH_private_delete_instances_default_ID
    1321              :     },
    1322              :     /* PATCH /instances/$ID */
    1323              :     {
    1324              :       .url_prefix = "/instances/",
    1325              :       .method = MHD_HTTP_METHOD_PATCH,
    1326              :       .permission = "instances-write",
    1327              :       .skip_instance = true,
    1328              :       .default_only = true,
    1329              :       .have_id_segment = true,
    1330              :       .handler = &TMH_private_patch_instances_default_ID,
    1331              :       /* allow instance data of up to 8 MB, that should be plenty;
    1332              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1333              :          would require further changes to the allocation logic
    1334              :          in the code... */
    1335              :       .max_upload = 1024 * 1024 * 8
    1336              :     },
    1337              :     /* POST /auth: */
    1338              :     {
    1339              :       .url_prefix = "/instances/",
    1340              :       .url_suffix = "auth",
    1341              :       .method = MHD_HTTP_METHOD_POST,
    1342              :       .permission = "instances-auth-write",
    1343              :       .skip_instance = true,
    1344              :       .default_only = true,
    1345              :       .have_id_segment = true,
    1346              :       .handler = &TMH_private_post_instances_default_ID_auth,
    1347              :       /* Body should be pretty small. */
    1348              :       .max_upload = 1024 * 1024
    1349              :     },
    1350              :     /* GET /kyc: */
    1351              :     {
    1352              :       .url_prefix = "/instances/",
    1353              :       .url_suffix = "kyc",
    1354              :       .method = MHD_HTTP_METHOD_GET,
    1355              :       .permission = "instances-kyc-read",
    1356              :       .skip_instance = true,
    1357              :       .default_only = true,
    1358              :       .have_id_segment = true,
    1359              :       .handler = &TMH_private_get_instances_default_ID_kyc,
    1360              :     },
    1361              :     {
    1362              :       .url_prefix = NULL
    1363              :     }
    1364              :   };
    1365              : 
    1366              :   static struct TMH_RequestHandler private_handlers[] = {
    1367              :     /* GET /instances/$ID/: */
    1368              :     {
    1369              :       .url_prefix = "/",
    1370              :       .method = MHD_HTTP_METHOD_GET,
    1371              :       .permission = "instances-read",
    1372              :       .handler = &TMH_private_get_instances_ID
    1373              :     },
    1374              :     /* DELETE /instances/$ID/: */
    1375              :     {
    1376              :       .url_prefix = "/",
    1377              :       .method = MHD_HTTP_METHOD_DELETE,
    1378              :       .permission = "instances-write",
    1379              :       .allow_deleted_instance = true,
    1380              :       .handler = &TMH_private_delete_instances_ID
    1381              :     },
    1382              :     /* PATCH /instances/$ID/: */
    1383              :     {
    1384              :       .url_prefix = "/",
    1385              :       .method = MHD_HTTP_METHOD_PATCH,
    1386              :       .handler = &TMH_private_patch_instances_ID,
    1387              :       .permission = "instances-write",
    1388              :       .allow_deleted_instance = true,
    1389              :       /* allow instance data of up to 8 MB, that should be plenty;
    1390              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1391              :          would require further changes to the allocation logic
    1392              :          in the code... */
    1393              :       .max_upload = 1024 * 1024 * 8
    1394              :     },
    1395              :     /* POST /auth: */
    1396              :     {
    1397              :       .url_prefix = "/auth",
    1398              :       .method = MHD_HTTP_METHOD_POST,
    1399              :       .handler = &TMH_private_post_instances_ID_auth,
    1400              :       .permission = "auth-write",
    1401              :       /* Body should be pretty small. */
    1402              :       .max_upload = 1024 * 1024,
    1403              :     },
    1404              :     /* GET /kyc: */
    1405              :     {
    1406              :       .url_prefix = "/kyc",
    1407              :       .method = MHD_HTTP_METHOD_GET,
    1408              :       .permission = "kyc-read",
    1409              :       .handler = &TMH_private_get_instances_ID_kyc,
    1410              :     },
    1411              :     /* GET /pos: */
    1412              :     {
    1413              :       .url_prefix = "/pos",
    1414              :       .method = MHD_HTTP_METHOD_GET,
    1415              :       .permission = "pos-read",
    1416              :       .handler = &TMH_private_get_pos
    1417              :     },
    1418              :     /* GET /categories: */
    1419              :     {
    1420              :       .url_prefix = "/categories",
    1421              :       .method = MHD_HTTP_METHOD_GET,
    1422              :       .permission = "categories-read",
    1423              :       .handler = &TMH_private_get_categories
    1424              :     },
    1425              :     /* POST /categories: */
    1426              :     {
    1427              :       .url_prefix = "/categories",
    1428              :       .method = MHD_HTTP_METHOD_POST,
    1429              :       .permission = "categories-write",
    1430              :       .handler = &TMH_private_post_categories,
    1431              :       /* allow category data of up to 8 kb, that should be plenty */
    1432              :       .max_upload = 1024 * 8
    1433              :     },
    1434              :     /* GET /categories/$ID: */
    1435              :     {
    1436              :       .url_prefix = "/categories/",
    1437              :       .method = MHD_HTTP_METHOD_GET,
    1438              :       .permission = "categories-read",
    1439              :       .have_id_segment = true,
    1440              :       .allow_deleted_instance = true,
    1441              :       .handler = &TMH_private_get_categories_ID
    1442              :     },
    1443              :     /* DELETE /categories/$ID: */
    1444              :     {
    1445              :       .url_prefix = "/categories/",
    1446              :       .method = MHD_HTTP_METHOD_DELETE,
    1447              :       .permission = "categories-write",
    1448              :       .have_id_segment = true,
    1449              :       .allow_deleted_instance = true,
    1450              :       .handler = &TMH_private_delete_categories_ID
    1451              :     },
    1452              :     /* PATCH /categories/$ID/: */
    1453              :     {
    1454              :       .url_prefix = "/categories/",
    1455              :       .method = MHD_HTTP_METHOD_PATCH,
    1456              :       .permission = "categories-write",
    1457              :       .have_id_segment = true,
    1458              :       .allow_deleted_instance = true,
    1459              :       .handler = &TMH_private_patch_categories_ID,
    1460              :       /* allow category data of up to 8 kb, that should be plenty */
    1461              :       .max_upload = 1024 * 8
    1462              :     },
    1463              :     /* GET /units: */
    1464              :     {
    1465              :       .url_prefix = "/units",
    1466              :       .method = MHD_HTTP_METHOD_GET,
    1467              :       .handler = &TMH_private_get_units
    1468              :     },
    1469              :     /* POST /units: */
    1470              :     {
    1471              :       .url_prefix = "/units",
    1472              :       .method = MHD_HTTP_METHOD_POST,
    1473              :       .permission = "units-write",
    1474              :       .handler = &TMH_private_post_units,
    1475              :       .max_upload = 1024 * 8
    1476              :     },
    1477              :     /* GET /units/$UNIT: */
    1478              :     {
    1479              :       .url_prefix = "/units/",
    1480              :       .method = MHD_HTTP_METHOD_GET,
    1481              :       .have_id_segment = true,
    1482              :       .allow_deleted_instance = true,
    1483              :       .handler = &TMH_private_get_units_ID
    1484              :     },
    1485              :     /* DELETE /units/$UNIT: */
    1486              :     {
    1487              :       .url_prefix = "/units/",
    1488              :       .method = MHD_HTTP_METHOD_DELETE,
    1489              :       .permission = "units-write",
    1490              :       .have_id_segment = true,
    1491              :       .allow_deleted_instance = true,
    1492              :       .handler = &TMH_private_delete_units_ID
    1493              :     },
    1494              :     /* PATCH /units/$UNIT: */
    1495              :     {
    1496              :       .url_prefix = "/units/",
    1497              :       .method = MHD_HTTP_METHOD_PATCH,
    1498              :       .permission = "units-write",
    1499              :       .have_id_segment = true,
    1500              :       .allow_deleted_instance = true,
    1501              :       .handler = &TMH_private_patch_units_ID,
    1502              :       .max_upload = 1024 * 8
    1503              :     },
    1504              :     /* GET /products: */
    1505              :     {
    1506              :       .url_prefix = "/products",
    1507              :       .permission = "products-read",
    1508              :       .method = MHD_HTTP_METHOD_GET,
    1509              :       .handler = &TMH_private_get_products
    1510              :     },
    1511              :     /* POST /products: */
    1512              :     {
    1513              :       .url_prefix = "/products",
    1514              :       .method = MHD_HTTP_METHOD_POST,
    1515              :       .permission = "products-write",
    1516              :       .handler = &TMH_private_post_products,
    1517              :       /* allow product data of up to 8 MB, that should be plenty;
    1518              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1519              :          would require further changes to the allocation logic
    1520              :          in the code... */
    1521              :       .max_upload = 1024 * 1024 * 8
    1522              :     },
    1523              :     /* GET /products/$ID: */
    1524              :     {
    1525              :       .url_prefix = "/products/",
    1526              :       .method = MHD_HTTP_METHOD_GET,
    1527              :       .have_id_segment = true,
    1528              :       .permission = "products-read",
    1529              :       .allow_deleted_instance = true,
    1530              :       .handler = &TMH_private_get_products_ID
    1531              :     },
    1532              :     /* DELETE /products/$ID/: */
    1533              :     {
    1534              :       .url_prefix = "/products/",
    1535              :       .method = MHD_HTTP_METHOD_DELETE,
    1536              :       .have_id_segment = true,
    1537              :       .permission = "products-write",
    1538              :       .allow_deleted_instance = true,
    1539              :       .handler = &TMH_private_delete_products_ID
    1540              :     },
    1541              :     /* PATCH /products/$ID/: */
    1542              :     {
    1543              :       .url_prefix = "/products/",
    1544              :       .method = MHD_HTTP_METHOD_PATCH,
    1545              :       .have_id_segment = true,
    1546              :       .allow_deleted_instance = true,
    1547              :       .permission = "products-write",
    1548              :       .handler = &TMH_private_patch_products_ID,
    1549              :       /* allow product data of up to 8 MB, that should be plenty;
    1550              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1551              :          would require further changes to the allocation logic
    1552              :          in the code... */
    1553              :       .max_upload = 1024 * 1024 * 8
    1554              :     },
    1555              :     /* POST /products/$ID/lock: */
    1556              :     {
    1557              :       .url_prefix = "/products/",
    1558              :       .url_suffix = "lock",
    1559              :       .method = MHD_HTTP_METHOD_POST,
    1560              :       .have_id_segment = true,
    1561              :       .permission = "products-lock",
    1562              :       .handler = &TMH_private_post_products_ID_lock,
    1563              :       /* the body should be pretty small, allow 1 MB of upload
    1564              :          to set a conservative bound for sane wallets */
    1565              :       .max_upload = 1024 * 1024
    1566              :     },
    1567              :     /* POST /orders: */
    1568              :     {
    1569              :       .url_prefix = "/orders",
    1570              :       .method = MHD_HTTP_METHOD_POST,
    1571              :       .permission = "orders-write",
    1572              :       .handler = &TMH_private_post_orders,
    1573              :       /* allow contracts of up to 8 MB, that should be plenty;
    1574              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1575              :          would require further changes to the allocation logic
    1576              :          in the code... */
    1577              :       .max_upload = 1024 * 1024 * 8
    1578              :     },
    1579              :     /* GET /orders/$ID: */
    1580              :     {
    1581              :       .url_prefix = "/orders/",
    1582              :       .method = MHD_HTTP_METHOD_GET,
    1583              :       .permission = "orders-read",
    1584              :       .have_id_segment = true,
    1585              :       .allow_deleted_instance = true,
    1586              :       .handler = &TMH_private_get_orders_ID
    1587              :     },
    1588              :     /* GET /orders: */
    1589              :     {
    1590              :       .url_prefix = "/orders",
    1591              :       .method = MHD_HTTP_METHOD_GET,
    1592              :       .permission = "orders-read",
    1593              :       .allow_deleted_instance = true,
    1594              :       .handler = &TMH_private_get_orders
    1595              :     },
    1596              :     /* POST /orders/$ID/refund: */
    1597              :     {
    1598              :       .url_prefix = "/orders/",
    1599              :       .url_suffix = "refund",
    1600              :       .method = MHD_HTTP_METHOD_POST,
    1601              :       .have_id_segment = true,
    1602              :       .permission = "orders-refund",
    1603              :       .handler = &TMH_private_post_orders_ID_refund,
    1604              :       /* the body should be pretty small, allow 1 MB of upload
    1605              :          to set a conservative bound for sane wallets */
    1606              :       .max_upload = 1024 * 1024
    1607              :     },
    1608              :     /* PATCH /orders/$ID/forget: */
    1609              :     {
    1610              :       .url_prefix = "/orders/",
    1611              :       .url_suffix = "forget",
    1612              :       .method = MHD_HTTP_METHOD_PATCH,
    1613              :       .permission = "orders-write",
    1614              :       .have_id_segment = true,
    1615              :       .allow_deleted_instance = true,
    1616              :       .handler = &TMH_private_patch_orders_ID_forget,
    1617              :       /* the body should be pretty small, allow 1 MB of upload
    1618              :          to set a conservative bound for sane wallets */
    1619              :       .max_upload = 1024 * 1024
    1620              :     },
    1621              :     /* DELETE /orders/$ID: */
    1622              :     {
    1623              :       .url_prefix = "/orders/",
    1624              :       .method = MHD_HTTP_METHOD_DELETE,
    1625              :       .permission = "orders-write",
    1626              :       .have_id_segment = true,
    1627              :       .allow_deleted_instance = true,
    1628              :       .handler = &TMH_private_delete_orders_ID
    1629              :     },
    1630              :     /* POST /transfers: */
    1631              :     {
    1632              :       .url_prefix = "/transfers",
    1633              :       .method = MHD_HTTP_METHOD_POST,
    1634              :       .allow_deleted_instance = true,
    1635              :       .handler = &TMH_private_post_transfers,
    1636              :       .permission = "transfers-write",
    1637              :       /* the body should be pretty small, allow 1 MB of upload
    1638              :          to set a conservative bound for sane wallets */
    1639              :       .max_upload = 1024 * 1024
    1640              :     },
    1641              :     /* DELETE /transfers/$ID: */
    1642              :     {
    1643              :       .url_prefix = "/transfers/",
    1644              :       .method = MHD_HTTP_METHOD_DELETE,
    1645              :       .permission = "transfers-write",
    1646              :       .allow_deleted_instance = true,
    1647              :       .handler = &TMH_private_delete_transfers_ID,
    1648              :       .have_id_segment = true,
    1649              :       /* the body should be pretty small, allow 1 MB of upload
    1650              :          to set a conservative bound for sane wallets */
    1651              :       .max_upload = 1024 * 1024
    1652              :     },
    1653              :     /* GET /transfers: */
    1654              :     {
    1655              :       .url_prefix = "/transfers",
    1656              :       .permission = "transfers-read",
    1657              :       .method = MHD_HTTP_METHOD_GET,
    1658              :       .allow_deleted_instance = true,
    1659              :       .handler = &TMH_private_get_transfers
    1660              :     },
    1661              :     /* GET /incoming: */
    1662              :     {
    1663              :       .url_prefix = "/incoming",
    1664              :       .permission = "transfers-read",
    1665              :       .method = MHD_HTTP_METHOD_GET,
    1666              :       .allow_deleted_instance = true,
    1667              :       .handler = &TMH_private_get_incoming
    1668              :     },
    1669              :     /* POST /otp-devices: */
    1670              :     {
    1671              :       .url_prefix = "/otp-devices",
    1672              :       .permission = "otp-devices-write",
    1673              :       .method = MHD_HTTP_METHOD_POST,
    1674              :       .handler = &TMH_private_post_otp_devices
    1675              :     },
    1676              :     /* GET /otp-devices: */
    1677              :     {
    1678              :       .url_prefix = "/otp-devices",
    1679              :       .permission = "opt-devices-read",
    1680              :       .method = MHD_HTTP_METHOD_GET,
    1681              :       .handler = &TMH_private_get_otp_devices
    1682              :     },
    1683              :     /* GET /otp-devices/$ID/: */
    1684              :     {
    1685              :       .url_prefix = "/otp-devices/",
    1686              :       .method = MHD_HTTP_METHOD_GET,
    1687              :       .permission = "otp-devices-read",
    1688              :       .have_id_segment = true,
    1689              :       .handler = &TMH_private_get_otp_devices_ID
    1690              :     },
    1691              :     /* DELETE /otp-devices/$ID/: */
    1692              :     {
    1693              :       .url_prefix = "/otp-devices/",
    1694              :       .method = MHD_HTTP_METHOD_DELETE,
    1695              :       .permission = "otp-devices-write",
    1696              :       .have_id_segment = true,
    1697              :       .handler = &TMH_private_delete_otp_devices_ID
    1698              :     },
    1699              :     /* PATCH /otp-devices/$ID/: */
    1700              :     {
    1701              :       .url_prefix = "/otp-devices/",
    1702              :       .method = MHD_HTTP_METHOD_PATCH,
    1703              :       .permission = "otp-devices-write",
    1704              :       .have_id_segment = true,
    1705              :       .handler = &TMH_private_patch_otp_devices_ID
    1706              :     },
    1707              :     /* POST /templates: */
    1708              :     {
    1709              :       .url_prefix = "/templates",
    1710              :       .method = MHD_HTTP_METHOD_POST,
    1711              :       .permission = "templates-write",
    1712              :       .handler = &TMH_private_post_templates,
    1713              :       /* allow template data of up to 8 MB, that should be plenty;
    1714              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1715              :          would require further changes to the allocation logic
    1716              :          in the code... */
    1717              :       .max_upload = 1024 * 1024 * 8
    1718              :     },
    1719              :     /* GET /templates: */
    1720              :     {
    1721              :       .url_prefix = "/templates",
    1722              :       .permission = "templates-read",
    1723              :       .method = MHD_HTTP_METHOD_GET,
    1724              :       .handler = &TMH_private_get_templates
    1725              :     },
    1726              :     /* GET /templates/$ID/: */
    1727              :     {
    1728              :       .url_prefix = "/templates/",
    1729              :       .method = MHD_HTTP_METHOD_GET,
    1730              :       .permission = "templates-read",
    1731              :       .have_id_segment = true,
    1732              :       .allow_deleted_instance = true,
    1733              :       .handler = &TMH_private_get_templates_ID
    1734              :     },
    1735              :     /* DELETE /templates/$ID/: */
    1736              :     {
    1737              :       .url_prefix = "/templates/",
    1738              :       .method = MHD_HTTP_METHOD_DELETE,
    1739              :       .permission = "templates-write",
    1740              :       .have_id_segment = true,
    1741              :       .allow_deleted_instance = true,
    1742              :       .handler = &TMH_private_delete_templates_ID
    1743              :     },
    1744              :     /* PATCH /templates/$ID/: */
    1745              :     {
    1746              :       .url_prefix = "/templates/",
    1747              :       .method = MHD_HTTP_METHOD_PATCH,
    1748              :       .permission = "templates-write",
    1749              :       .have_id_segment = true,
    1750              :       .allow_deleted_instance = true,
    1751              :       .handler = &TMH_private_patch_templates_ID,
    1752              :       /* allow template data of up to 8 MB, that should be plenty;
    1753              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1754              :          would require further changes to the allocation logic
    1755              :          in the code... */
    1756              :       .max_upload = 1024 * 1024 * 8
    1757              :     },
    1758              :     /* GET /webhooks: */
    1759              :     {
    1760              :       .url_prefix = "/webhooks",
    1761              :       .permission = "webhooks-read",
    1762              :       .method = MHD_HTTP_METHOD_GET,
    1763              :       .handler = &TMH_private_get_webhooks
    1764              :     },
    1765              :     /* POST /webhooks: */
    1766              :     {
    1767              :       .url_prefix = "/webhooks",
    1768              :       .method = MHD_HTTP_METHOD_POST,
    1769              :       .permission = "webhooks-write",
    1770              :       .handler = &TMH_private_post_webhooks,
    1771              :       /* allow webhook data of up to 8 MB, that should be plenty;
    1772              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1773              :          would require further changes to the allocation logic
    1774              :          in the code... */
    1775              :       .max_upload = 1024 * 1024 * 8
    1776              :     },
    1777              :     /* GET /webhooks/$ID/: */
    1778              :     {
    1779              :       .url_prefix = "/webhooks/",
    1780              :       .method = MHD_HTTP_METHOD_GET,
    1781              :       .permission = "webhooks-read",
    1782              :       .have_id_segment = true,
    1783              :       .allow_deleted_instance = true,
    1784              :       .handler = &TMH_private_get_webhooks_ID
    1785              :     },
    1786              :     /* DELETE /webhooks/$ID/: */
    1787              :     {
    1788              :       .url_prefix = "/webhooks/",
    1789              :       .permission = "webhooks-write",
    1790              :       .method = MHD_HTTP_METHOD_DELETE,
    1791              :       .have_id_segment = true,
    1792              :       .allow_deleted_instance = true,
    1793              :       .handler = &TMH_private_delete_webhooks_ID
    1794              :     },
    1795              :     /* PATCH /webhooks/$ID/: */
    1796              :     {
    1797              :       .url_prefix = "/webhooks/",
    1798              :       .method = MHD_HTTP_METHOD_PATCH,
    1799              :       .permission = "webhooks-write",
    1800              :       .have_id_segment = true,
    1801              :       .allow_deleted_instance = true,
    1802              :       .handler = &TMH_private_patch_webhooks_ID,
    1803              :       /* allow webhook data of up to 8 MB, that should be plenty;
    1804              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1805              :          would require further changes to the allocation logic
    1806              :          in the code... */
    1807              :       .max_upload = 1024 * 1024 * 8
    1808              :     },
    1809              :     /* POST /accounts: */
    1810              :     {
    1811              :       .url_prefix = "/accounts",
    1812              :       .method = MHD_HTTP_METHOD_POST,
    1813              :       .permission = "accounts-write",
    1814              :       .handler = &TMH_private_post_account,
    1815              :       /* allow account details of up to 8 kb, that should be plenty */
    1816              :       .max_upload = 1024 * 8
    1817              :     },
    1818              :     /* PATCH /accounts/$H_WIRE: */
    1819              :     {
    1820              :       .url_prefix = "/accounts/",
    1821              :       .method = MHD_HTTP_METHOD_PATCH,
    1822              :       .permission = "accounts-write",
    1823              :       .handler = &TMH_private_patch_accounts_ID,
    1824              :       .have_id_segment = true,
    1825              :       /* allow account details of up to 8 kb, that should be plenty */
    1826              :       .max_upload = 1024 * 8
    1827              :     },
    1828              :     /* GET /accounts: */
    1829              :     {
    1830              :       .url_prefix = "/accounts",
    1831              :       .permission = "accounts-read",
    1832              :       .method = MHD_HTTP_METHOD_GET,
    1833              :       .handler = &TMH_private_get_accounts
    1834              :     },
    1835              :     /* GET /accounts/$H_WIRE: */
    1836              :     {
    1837              :       .url_prefix = "/accounts/",
    1838              :       .permission = "accounts-read",
    1839              :       .method = MHD_HTTP_METHOD_GET,
    1840              :       .have_id_segment = true,
    1841              :       .handler = &TMH_private_get_accounts_ID
    1842              :     },
    1843              :     /* DELETE /accounts/$H_WIRE: */
    1844              :     {
    1845              :       .url_prefix = "/accounts/",
    1846              :       .permission = "accounts-write",
    1847              :       .method = MHD_HTTP_METHOD_DELETE,
    1848              :       .handler = &TMH_private_delete_account_ID,
    1849              :       .have_id_segment = true
    1850              :     },
    1851              :     /* GET /tokens: */
    1852              :     {
    1853              :       .url_prefix = "/tokens",
    1854              :       .permission = "tokens-read",
    1855              :       .method = MHD_HTTP_METHOD_GET,
    1856              :       .handler = &TMH_private_get_instances_ID_tokens,
    1857              :     },
    1858              :     /* POST /token: */
    1859              :     {
    1860              :       .url_prefix = "/token",
    1861              :       .permission = "token-refresh",
    1862              :       .method = MHD_HTTP_METHOD_POST,
    1863              :       .handler = &TMH_private_post_instances_ID_token,
    1864              :       /* Body should be tiny. */
    1865              :       .max_upload = 1024
    1866              :     },
    1867              :     /* DELETE /tokens/$SERIAL: */
    1868              :     {
    1869              :       .url_prefix = "/tokens/",
    1870              :       .permission = "tokens-write",
    1871              :       .method = MHD_HTTP_METHOD_DELETE,
    1872              :       .handler = &TMH_private_delete_instances_ID_token_SERIAL,
    1873              :       .have_id_segment = true
    1874              :     },
    1875              :     /* DELETE /token: */
    1876              :     {
    1877              :       .url_prefix = "/token",
    1878              :       .method = MHD_HTTP_METHOD_DELETE,
    1879              :       .handler = &TMH_private_delete_instances_ID_token,
    1880              :     },
    1881              :     /* GET /tokenfamilies: */
    1882              :     {
    1883              :       .url_prefix = "/tokenfamilies",
    1884              :       .permission = "tokenfamilies-read",
    1885              :       .method = MHD_HTTP_METHOD_GET,
    1886              :       .handler = &TMH_private_get_tokenfamilies
    1887              :     },
    1888              :     /* POST /tokenfamilies: */
    1889              :     {
    1890              :       .url_prefix = "/tokenfamilies",
    1891              :       .permission = "tokenfamilies-write",
    1892              :       .method = MHD_HTTP_METHOD_POST,
    1893              :       .handler = &TMH_private_post_token_families
    1894              :     },
    1895              :     /* GET /tokenfamilies/$SLUG/: */
    1896              :     {
    1897              :       .url_prefix = "/tokenfamilies/",
    1898              :       .method = MHD_HTTP_METHOD_GET,
    1899              :       .permission = "tokenfamilies-read",
    1900              :       .have_id_segment = true,
    1901              :       .handler = &TMH_private_get_tokenfamilies_SLUG
    1902              :     },
    1903              :     /* DELETE /tokenfamilies/$SLUG/: */
    1904              :     {
    1905              :       .url_prefix = "/tokenfamilies/",
    1906              :       .method = MHD_HTTP_METHOD_DELETE,
    1907              :       .permission = "tokenfamilies-write",
    1908              :       .have_id_segment = true,
    1909              :       .handler = &TMH_private_delete_token_families_SLUG
    1910              :     },
    1911              :     /* PATCH /tokenfamilies/$SLUG/: */
    1912              :     {
    1913              :       .url_prefix = "/tokenfamilies/",
    1914              :       .method = MHD_HTTP_METHOD_PATCH,
    1915              :       .permission = "tokenfamilies-write",
    1916              :       .have_id_segment = true,
    1917              :       .handler = &TMH_private_patch_token_family_SLUG,
    1918              :     },
    1919              :     #ifdef HAVE_DONAU_DONAU_SERVICE_H
    1920              :     /* GET /donau */
    1921              :     {
    1922              :       .url_prefix = "/donau",
    1923              :       .method = MHD_HTTP_METHOD_GET,
    1924              :       .handler = &TMH_private_get_donau_instances
    1925              :     },
    1926              :     /* POST /donau */
    1927              :     {
    1928              :       .url_prefix = "/donau",
    1929              :       .method = MHD_HTTP_METHOD_POST,
    1930              :       .handler = &TMH_private_post_donau_instance
    1931              :     },
    1932              :     /* DELETE /donau/$charity-id */
    1933              :     {
    1934              :       .url_prefix = "/donau/",
    1935              :       .method = MHD_HTTP_METHOD_DELETE,
    1936              :       .have_id_segment = true,
    1937              :       .handler = &TMH_private_delete_donau_instance_ID
    1938              :     },
    1939              :     #endif
    1940              :     /* GET /statistics-counter/$SLUG: */
    1941              :     {
    1942              :       .url_prefix = "/statistics-counter/",
    1943              :       .method = MHD_HTTP_METHOD_GET,
    1944              :       .permission = "statistics-read",
    1945              :       .have_id_segment = true,
    1946              :       .handler = &TMH_private_get_statistics_counter_SLUG,
    1947              :     },
    1948              :     /* GET /statistics-amount/$SLUG: */
    1949              :     {
    1950              :       .url_prefix = "/statistics-amount/",
    1951              :       .method = MHD_HTTP_METHOD_GET,
    1952              :       .permission = "statistics-read",
    1953              :       .have_id_segment = true,
    1954              :       .handler = &TMH_private_get_statistics_amount_SLUG,
    1955              :     },
    1956              :     {
    1957              :       .url_prefix = NULL
    1958              :     }
    1959              :   };
    1960              :   static struct TMH_RequestHandler public_handlers[] = {
    1961              :     {
    1962              :       /* for "admin" instance, it does not even
    1963              :          have to exist before we give the WebUI */
    1964              :       .url_prefix = "/",
    1965              :       .method = MHD_HTTP_METHOD_GET,
    1966              :       .mime_type = "text/html",
    1967              :       .skip_instance = true,
    1968              :       .default_only = true,
    1969              :       .handler = &spa_redirect,
    1970              :       .response_code = MHD_HTTP_FOUND
    1971              :     },
    1972              :     {
    1973              :       .url_prefix = "/config",
    1974              :       .method = MHD_HTTP_METHOD_GET,
    1975              :       .skip_instance = true,
    1976              :       .default_only = true,
    1977              :       .handler = &MH_handler_config
    1978              :     },
    1979              :     {
    1980              :       /* for "normal" instance,s they must exist
    1981              :          before we give the WebUI */
    1982              :       .url_prefix = "/",
    1983              :       .method = MHD_HTTP_METHOD_GET,
    1984              :       .mime_type = "text/html",
    1985              :       .handler = &spa_redirect,
    1986              :       .response_code = MHD_HTTP_FOUND
    1987              :     },
    1988              :     {
    1989              :       .url_prefix = "/webui/",
    1990              :       .method = MHD_HTTP_METHOD_GET,
    1991              :       .mime_type = "text/html",
    1992              :       .skip_instance = true,
    1993              :       .have_id_segment = true,
    1994              :       .handler = &TMH_return_spa,
    1995              :       .response_code = MHD_HTTP_OK
    1996              :     },
    1997              :     {
    1998              :       .url_prefix = "/agpl",
    1999              :       .method = MHD_HTTP_METHOD_GET,
    2000              :       .skip_instance = true,
    2001              :       .handler = &TMH_MHD_handler_agpl_redirect
    2002              :     },
    2003              :     {
    2004              :       .url_prefix = "/agpl",
    2005              :       .method = MHD_HTTP_METHOD_GET,
    2006              :       .skip_instance = true,
    2007              :       .handler = &TMH_MHD_handler_agpl_redirect
    2008              :     },
    2009              :     {
    2010              :       .url_prefix = "/terms",
    2011              :       .method = MHD_HTTP_METHOD_GET,
    2012              :       .skip_instance = true,
    2013              :       .handler = &TMH_handler_terms
    2014              :     },
    2015              :     {
    2016              :       .url_prefix = "/privacy",
    2017              :       .method = MHD_HTTP_METHOD_GET,
    2018              :       .skip_instance = true,
    2019              :       .handler = &TMH_handler_privacy
    2020              :     },
    2021              :     /* Also serve the same /config per instance */
    2022              :     {
    2023              :       .url_prefix = "/config",
    2024              :       .method = MHD_HTTP_METHOD_GET,
    2025              :       .handler = &MH_handler_config
    2026              :     },
    2027              :     /* POST /orders/$ID/abort: */
    2028              :     {
    2029              :       .url_prefix = "/orders/",
    2030              :       .have_id_segment = true,
    2031              :       .url_suffix = "abort",
    2032              :       .method = MHD_HTTP_METHOD_POST,
    2033              :       .handler = &TMH_post_orders_ID_abort,
    2034              :       /* wallet may give us many coins to sign, allow 1 MB of upload
    2035              :          to set a conservative bound for sane wallets */
    2036              :       .max_upload = 1024 * 1024
    2037              :     },
    2038              :     /* POST /orders/$ID/claim: */
    2039              :     {
    2040              :       .url_prefix = "/orders/",
    2041              :       .have_id_segment = true,
    2042              :       .url_suffix = "claim",
    2043              :       .method = MHD_HTTP_METHOD_POST,
    2044              :       .handler = &TMH_post_orders_ID_claim,
    2045              :       /* the body should be pretty small, allow 1 MB of upload
    2046              :          to set a conservative bound for sane wallets */
    2047              :       .max_upload = 1024 * 1024
    2048              :     },
    2049              :     /* POST /orders/$ID/pay: */
    2050              :     {
    2051              :       .url_prefix = "/orders/",
    2052              :       .have_id_segment = true,
    2053              :       .url_suffix = "pay",
    2054              :       .method = MHD_HTTP_METHOD_POST,
    2055              :       .handler = &TMH_post_orders_ID_pay,
    2056              :       /* wallet may give us many coins to sign, allow 1 MB of upload
    2057              :          to set a conservative bound for sane wallets */
    2058              :       .max_upload = 1024 * 1024
    2059              :     },
    2060              :     /* POST /orders/$ID/paid: */
    2061              :     {
    2062              :       .url_prefix = "/orders/",
    2063              :       .have_id_segment = true,
    2064              :       .allow_deleted_instance = true,
    2065              :       .url_suffix = "paid",
    2066              :       .method = MHD_HTTP_METHOD_POST,
    2067              :       .handler = &TMH_post_orders_ID_paid,
    2068              :       /* the body should be pretty small, allow 1 MB of upload
    2069              :          to set a conservative bound for sane wallets */
    2070              :       .max_upload = 1024 * 1024
    2071              :     },
    2072              :     /* POST /orders/$ID/refund: */
    2073              :     {
    2074              :       .url_prefix = "/orders/",
    2075              :       .have_id_segment = true,
    2076              :       .allow_deleted_instance = true,
    2077              :       .url_suffix = "refund",
    2078              :       .method = MHD_HTTP_METHOD_POST,
    2079              :       .handler = &TMH_post_orders_ID_refund,
    2080              :       /* the body should be pretty small, allow 1 MB of upload
    2081              :          to set a conservative bound for sane wallets */
    2082              :       .max_upload = 1024 * 1024
    2083              :     },
    2084              :     /* GET /orders/$ID: */
    2085              :     {
    2086              :       .url_prefix = "/orders/",
    2087              :       .method = MHD_HTTP_METHOD_GET,
    2088              :       .allow_deleted_instance = true,
    2089              :       .have_id_segment = true,
    2090              :       .handler = &TMH_get_orders_ID
    2091              :     },
    2092              :     /* GET /static/ *: */
    2093              :     {
    2094              :       .url_prefix = "/static/",
    2095              :       .method = MHD_HTTP_METHOD_GET,
    2096              :       .have_id_segment = true,
    2097              :       .handler = &TMH_return_static
    2098              :     },
    2099              :     /* GET /templates/$ID/: */
    2100              :     {
    2101              :       .url_prefix = "/templates/",
    2102              :       .method = MHD_HTTP_METHOD_GET,
    2103              :       .have_id_segment = true,
    2104              :       .handler = &TMH_get_templates_ID
    2105              :     },
    2106              :     /* GET /products/$HASH/image: */
    2107              :     {
    2108              :       .url_prefix = "/products/",
    2109              :       .method = MHD_HTTP_METHOD_GET,
    2110              :       .have_id_segment = true,
    2111              :       .allow_deleted_instance = true,
    2112              :       .url_suffix = "image",
    2113              :       .handler = &TMH_get_products_image
    2114              :     },
    2115              :     /* POST /templates/$ID: */
    2116              :     {
    2117              :       .url_prefix = "/templates/",
    2118              :       .method = MHD_HTTP_METHOD_POST,
    2119              :       .have_id_segment = true,
    2120              :       .handler = &TMH_post_using_templates_ID,
    2121              :       .max_upload = 1024 * 1024
    2122              :     },
    2123              :     /* POST /challenge/$ID: */
    2124              :     {
    2125              :       .url_prefix = "/challenge/",
    2126              :       .method = MHD_HTTP_METHOD_POST,
    2127              :       .have_id_segment = true,
    2128              :       .handler = &TMH_post_challenge_ID,
    2129              :       .max_upload = 1024
    2130              :     },
    2131              :     /* POST /challenge/$ID/confirm: */
    2132              :     {
    2133              :       .url_prefix = "/challenge/",
    2134              :       .method = MHD_HTTP_METHOD_POST,
    2135              :       .have_id_segment = true,
    2136              :       .url_suffix = "confirm",
    2137              :       .handler = &TMH_post_challenge_ID_confirm,
    2138              :       .max_upload = 1024
    2139              :     },
    2140              :     /* POST /instances */
    2141              :     {
    2142              :       .url_prefix = "/instances",
    2143              :       .method = MHD_HTTP_METHOD_POST,
    2144              :       .skip_instance = true,
    2145              :       .default_only = true,
    2146              :       .handler = &TMH_public_post_instances,
    2147              :       /* allow instance data of up to 8 MB, that should be plenty;
    2148              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    2149              :          would require further changes to the allocation logic
    2150              :          in the code... */
    2151              :       .max_upload = 1024 * 1024 * 8
    2152              :     },
    2153              :     /* POST /forgot-password: */
    2154              :     {
    2155              :       .url_prefix = "/forgot-password",
    2156              :       .method = MHD_HTTP_METHOD_POST,
    2157              :       .handler = &TMH_public_post_instances_ID_auth,
    2158              :       /* Body should be pretty small. */
    2159              :       .max_upload = 1024 * 1024
    2160              :     },
    2161              :     {
    2162              :       .url_prefix = "*",
    2163              :       .method = MHD_HTTP_METHOD_OPTIONS,
    2164              :       .handler = &handle_server_options
    2165              :     },
    2166              :     {
    2167              :       .url_prefix = NULL
    2168              :     }
    2169              :   };
    2170         2018 :   struct TMH_HandlerContext *hc = *con_cls;
    2171              :   struct TMH_RequestHandler *handlers;
    2172         2018 :   bool use_default = false;
    2173              : 
    2174              :   (void) cls;
    2175              :   (void) version;
    2176         2018 :   if (NULL != hc->url)
    2177              :   {
    2178              :     /* MHD calls us again for a request, for first call
    2179              :        see 'else' case below */
    2180         1269 :     GNUNET_assert (NULL != hc->rh);
    2181         1269 :     GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    2182         1269 :     if ( (hc->has_body) &&
    2183          954 :          (NULL == hc->request_body) )
    2184              :     {
    2185          848 :       size_t mul = hc->rh->max_upload;
    2186              :       enum GNUNET_GenericReturnValue res;
    2187              : 
    2188          848 :       if (0 == mul)
    2189           20 :         mul = DEFAULT_MAX_UPLOAD_SIZE;
    2190          848 :       if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
    2191          848 :            (hc->total_upload + *upload_data_size > mul) )
    2192              :       {
    2193              :         /* Client exceeds upload limit. Should _usually_ be checked earlier
    2194              :            when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
    2195              :            chunked encoding an uploader MAY have omitted this, and thus
    2196              :            not permitted us to check on time. In this case, we just close
    2197              :            the connection once it exceeds our limit (instead of waiting
    2198              :            for the upload to complete and then fail). This could theoretically
    2199              :            cause some clients to retry, alas broken or malicious clients
    2200              :            are likely to retry anyway, so little we can do about it, and
    2201              :            failing earlier seems the best option here.  */
    2202            0 :         GNUNET_break_op (0);
    2203            0 :         return MHD_NO;
    2204              :       }
    2205          848 :       hc->total_upload += *upload_data_size;
    2206          848 :       res = TALER_MHD_parse_post_json (connection,
    2207              :                                        &hc->json_parse_context,
    2208              :                                        upload_data,
    2209              :                                        upload_data_size,
    2210              :                                        &hc->request_body);
    2211          848 :       if (GNUNET_SYSERR == res)
    2212            0 :         return MHD_NO;
    2213              :       /* A error response was already generated */
    2214          848 :       if ( (GNUNET_NO == res) ||
    2215              :            /* or, need more data to accomplish parsing */
    2216          848 :            (NULL == hc->request_body) )
    2217          424 :         return MHD_YES; /* let MHD call us *again* */
    2218              :     }
    2219              :     /* Upload complete (if any), call handler to generate reply */
    2220          845 :     return hc->rh->handler (hc->rh,
    2221              :                             connection,
    2222              :                             hc);
    2223              :   }
    2224          749 :   hc->url = url;
    2225              :   {
    2226              :     const char *correlation_id;
    2227              : 
    2228          749 :     correlation_id = MHD_lookup_connection_value (connection,
    2229              :                                                   MHD_HEADER_KIND,
    2230              :                                                   "Taler-Correlation-Id");
    2231          749 :     if ( (NULL != correlation_id) &&
    2232              :          (GNUNET_YES !=
    2233            0 :           GNUNET_CURL_is_valid_scope_id (correlation_id)) )
    2234              :     {
    2235            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2236              :                   "Illegal incoming correlation ID\n");
    2237            0 :       correlation_id = NULL;
    2238              :     }
    2239          749 :     if (NULL != correlation_id)
    2240            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2241              :                   "Handling request for (%s) URL '%s', correlation_id=%s\n",
    2242              :                   method,
    2243              :                   url,
    2244              :                   correlation_id);
    2245              :     else
    2246          749 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2247              :                   "Handling request (%s) for URL '%s'\n",
    2248              :                   method,
    2249              :                   url);
    2250              :   }
    2251              : 
    2252          749 :   if (0 == strcasecmp (method,
    2253              :                        MHD_HTTP_METHOD_HEAD))
    2254            0 :     method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
    2255              : 
    2256              : 
    2257              :   /* Find out the merchant backend instance for the request.
    2258              :    * If there is an instance, remove the instance specification
    2259              :    * from the beginning of the request URL. */
    2260              :   {
    2261          749 :     const char *instance_prefix = "/instances/";
    2262              : 
    2263          749 :     if (0 == strncmp (url,
    2264              :                       instance_prefix,
    2265              :                       strlen (instance_prefix)))
    2266              :     {
    2267              :       /* url starts with "/instances/" */
    2268           83 :       const char *istart = url + strlen (instance_prefix);
    2269           83 :       const char *slash = strchr (istart, '/');
    2270              :       char *instance_id;
    2271              : 
    2272           83 :       if (NULL == slash)
    2273            0 :         instance_id = GNUNET_strdup (istart);
    2274              :       else
    2275           83 :         instance_id = GNUNET_strndup (istart,
    2276              :                                       slash - istart);
    2277           83 :       if (0 == strcmp (instance_id,
    2278              :                        "admin"))
    2279              :       {
    2280              :         MHD_RESULT ret;
    2281              :         struct MHD_Response *response;
    2282            0 :         const char *rstart = hc->full_url + strlen (instance_prefix);
    2283            0 :         const char *rslash = strchr (rstart, '/');
    2284              : 
    2285            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2286              :                     "Client used deprecated '/instances/default/' path. Redirecting to modern path\n");
    2287              : 
    2288              :         response
    2289            0 :           = MHD_create_response_from_buffer (0,
    2290              :                                              NULL,
    2291              :                                              MHD_RESPMEM_PERSISTENT);
    2292            0 :         TALER_MHD_add_global_headers (response,
    2293              :                                       true);
    2294            0 :         if (MHD_NO ==
    2295            0 :             MHD_add_response_header (response,
    2296              :                                      MHD_HTTP_HEADER_LOCATION,
    2297              :                                      NULL == rslash
    2298              :                                      ? "/"
    2299              :                                      : rslash))
    2300              :         {
    2301            0 :           GNUNET_break (0);
    2302            0 :           MHD_destroy_response (response);
    2303            0 :           GNUNET_free (instance_id);
    2304            0 :           return MHD_NO;
    2305              :         }
    2306            0 :         ret = MHD_queue_response (connection,
    2307              :                                   MHD_HTTP_PERMANENT_REDIRECT,
    2308              :                                   response);
    2309            0 :         MHD_destroy_response (response);
    2310            0 :         GNUNET_free (instance_id);
    2311            0 :         return ret;
    2312              :       }
    2313           83 :       hc->instance = TMH_lookup_instance (instance_id);
    2314           83 :       if ( (NULL == hc->instance) &&
    2315            2 :            (0 == strcmp ("admin",
    2316              :                          instance_id)) )
    2317            0 :         hc->instance = TMH_lookup_instance (NULL);
    2318           83 :       GNUNET_free (instance_id);
    2319           83 :       if (NULL == slash)
    2320            0 :         url = "";
    2321              :       else
    2322           83 :         url = slash;
    2323              :     }
    2324              :     else
    2325              :     {
    2326              :       /* use 'default' */
    2327          666 :       use_default = true;
    2328          666 :       hc->instance = TMH_lookup_instance (NULL);
    2329              :     }
    2330          749 :     if (NULL != hc->instance)
    2331              :     {
    2332          705 :       GNUNET_assert (hc->instance->rc < UINT_MAX);
    2333          705 :       hc->instance->rc++;
    2334              :     }
    2335              :   }
    2336              : 
    2337              :   {
    2338          749 :     const char *management_prefix = "/management/";
    2339          749 :     const char *private_prefix = "/private/";
    2340              : 
    2341          749 :     if ( (0 == strncmp (url,
    2342              :                         management_prefix,
    2343              :                         strlen (management_prefix))) )
    2344              :     {
    2345           64 :       handlers = management_handlers;
    2346           64 :       url += strlen (management_prefix) - 1;
    2347              :     }
    2348          685 :     else if ( (0 == strncmp (url,
    2349              :                              private_prefix,
    2350          212 :                              strlen (private_prefix))) ||
    2351          212 :               (0 == strcmp (url,
    2352              :                             "/private")) )
    2353              :     {
    2354          488 :       handlers = private_handlers;
    2355          976 :       if (0 == strcmp (url,
    2356              :                        "/private"))
    2357           15 :         url = "/";
    2358              :       else
    2359          473 :         url += strlen (private_prefix) - 1;
    2360              :     }
    2361              :     else
    2362              :     {
    2363          197 :       handlers = public_handlers;
    2364              :     }
    2365              :   }
    2366              : 
    2367          749 :   if (0 == strcmp (url,
    2368              :                    ""))
    2369            0 :     url = "/"; /* code below does not like empty string */
    2370              : 
    2371              :   {
    2372              :     /* Matching URL found, but maybe method doesn't match */
    2373              :     size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
    2374          749 :     const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
    2375          749 :     size_t infix_strlen = 0; /* number of characters in infix_url */
    2376          749 :     const char *suffix_url = NULL; /* i.e. "refund", excludes '/' at the beginning */
    2377          749 :     size_t suffix_strlen = 0; /* number of characters in suffix_url */
    2378              : 
    2379              :     /* parse the URL into the three different components */
    2380              :     {
    2381              :       const char *slash;
    2382              : 
    2383          749 :       slash = strchr (&url[1], '/');
    2384          749 :       if (NULL == slash)
    2385              :       {
    2386              :         /* the prefix was everything */
    2387          335 :         prefix_strlen = strlen (url);
    2388              :       }
    2389              :       else
    2390              :       {
    2391          414 :         prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
    2392          414 :         infix_url = slash + 1;
    2393          414 :         slash = strchr (infix_url, '/');
    2394          414 :         if (NULL == slash)
    2395              :         {
    2396              :           /* the infix was the rest */
    2397          243 :           infix_strlen = strlen (infix_url);
    2398              :         }
    2399              :         else
    2400              :         {
    2401          171 :           infix_strlen = slash - infix_url; /* excludes both '/'-es */
    2402          171 :           suffix_url = slash + 1; /* skip the '/' */
    2403          171 :           suffix_strlen = strlen (suffix_url);
    2404              :         }
    2405          414 :         hc->infix = GNUNET_strndup (infix_url,
    2406              :                                     infix_strlen);
    2407              :       }
    2408              :     }
    2409              : 
    2410              :     /* find matching handler */
    2411              :     {
    2412          749 :       bool url_found = false;
    2413              : 
    2414        15802 :       for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
    2415              :       {
    2416        15802 :         struct TMH_RequestHandler *rh = &handlers[i];
    2417              : 
    2418        15802 :         if (rh->default_only && (! use_default))
    2419           12 :           continue;
    2420        15790 :         if (! prefix_match (rh,
    2421              :                             url,
    2422              :                             prefix_strlen,
    2423              :                             infix_url,
    2424              :                             infix_strlen,
    2425              :                             suffix_url,
    2426              :                             suffix_strlen))
    2427        14798 :           continue;
    2428          992 :         url_found = true;
    2429          992 :         if (0 == strcasecmp (method,
    2430              :                              MHD_HTTP_METHOD_OPTIONS))
    2431              :         {
    2432            1 :           return TALER_MHD_reply_cors_preflight (connection);
    2433              :         }
    2434          991 :         if ( (rh->method != NULL) &&
    2435          991 :              (0 != strcasecmp (method,
    2436              :                                rh->method)) )
    2437          243 :           continue;
    2438          748 :         hc->rh = rh;
    2439          748 :         break;
    2440              :       }
    2441              :       /* Handle HTTP 405: METHOD NOT ALLOWED case */
    2442          748 :       if ( (NULL == hc->rh) &&
    2443              :            (url_found) )
    2444              :       {
    2445              :         struct MHD_Response *reply;
    2446              :         MHD_RESULT ret;
    2447            0 :         char *allowed = NULL;
    2448              : 
    2449            0 :         GNUNET_break_op (0);
    2450              :         /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
    2451            0 :         for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
    2452              :         {
    2453            0 :           struct TMH_RequestHandler *rh = &handlers[i];
    2454              : 
    2455            0 :           if (rh->default_only && (! use_default))
    2456            0 :             continue;
    2457            0 :           if (! prefix_match (rh,
    2458              :                               url,
    2459              :                               prefix_strlen,
    2460              :                               infix_url,
    2461              :                               infix_strlen,
    2462              :                               suffix_url,
    2463              :                               suffix_strlen))
    2464            0 :             continue;
    2465            0 :           if (NULL == allowed)
    2466              :           {
    2467            0 :             allowed = GNUNET_strdup (rh->method);
    2468              :           }
    2469              :           else
    2470              :           {
    2471              :             char *tmp;
    2472              : 
    2473            0 :             GNUNET_asprintf (&tmp,
    2474              :                              "%s, %s",
    2475              :                              allowed,
    2476              :                              rh->method);
    2477            0 :             GNUNET_free (allowed);
    2478            0 :             allowed = tmp;
    2479              :           }
    2480            0 :           if (0 == strcasecmp (rh->method,
    2481              :                                MHD_HTTP_METHOD_GET))
    2482              :           {
    2483              :             char *tmp;
    2484              : 
    2485            0 :             GNUNET_asprintf (&tmp,
    2486              :                              "%s, %s",
    2487              :                              allowed,
    2488              :                              MHD_HTTP_METHOD_HEAD);
    2489            0 :             GNUNET_free (allowed);
    2490            0 :             allowed = tmp;
    2491              :           }
    2492              :         }
    2493            0 :         reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
    2494              :                                       method);
    2495            0 :         GNUNET_break (MHD_YES ==
    2496              :                       MHD_add_response_header (reply,
    2497              :                                                MHD_HTTP_HEADER_ALLOW,
    2498              :                                                allowed));
    2499            0 :         GNUNET_free (allowed);
    2500            0 :         ret = MHD_queue_response (connection,
    2501              :                                   MHD_HTTP_METHOD_NOT_ALLOWED,
    2502              :                                   reply);
    2503            0 :         MHD_destroy_response (reply);
    2504            0 :         return ret;
    2505              :       }
    2506          748 :       if (NULL == hc->rh)
    2507              :       {
    2508            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2509              :                     "Endpoint `%s' not known\n",
    2510              :                     hc->url);
    2511            0 :         return TALER_MHD_reply_with_error (connection,
    2512              :                                            MHD_HTTP_NOT_FOUND,
    2513              :                                            TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    2514              :                                            hc->url);
    2515              :       }
    2516              :     }
    2517              :   }
    2518              :   /* At this point, we must have found a handler */
    2519          748 :   GNUNET_assert (NULL != hc->rh);
    2520              : 
    2521              :   /* If an instance should be there, check one exists */
    2522          748 :   if ( (NULL == hc->instance) &&
    2523           43 :        (! hc->rh->skip_instance) )
    2524              :   {
    2525            3 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2526              :                 "Instance for `%s' not known\n",
    2527              :                 hc->url);
    2528            3 :     return TALER_MHD_reply_with_error (connection,
    2529              :                                        MHD_HTTP_NOT_FOUND,
    2530              :                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    2531            3 :                                        hc->infix);
    2532              :   }
    2533              : 
    2534              :   /* Access control for non-public handlers */
    2535          745 :   if (public_handlers != handlers)
    2536              :   {
    2537              :     const char *auth;
    2538          548 :     bool is_basic_auth = false;
    2539          548 :     bool auth_malformed = false;
    2540              : 
    2541          548 :     auth = MHD_lookup_connection_value (connection,
    2542              :                                         MHD_HEADER_KIND,
    2543              :                                         MHD_HTTP_HEADER_AUTHORIZATION);
    2544              : 
    2545          548 :     if (NULL != auth)
    2546              :     {
    2547           71 :       extract_auth (&auth,
    2548              :                     &is_basic_auth);
    2549           71 :       if (NULL == auth)
    2550            0 :         auth_malformed = true;
    2551           71 :       hc->auth_token = auth;
    2552              :     }
    2553              : 
    2554              :     /* If we have zero configured instances (not even ones that have been
    2555              :        purged) or explicitly disabled authentication, THEN we accept anything
    2556              :        (no access control), as we then also have no data to protect. */
    2557          548 :     if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
    2558          527 :         (GNUNET_YES == TMH_auth_disabled))
    2559              :     {
    2560           21 :       hc->auth_scope = TMH_AS_ALL;
    2561              :     }
    2562          527 :     else if (is_basic_auth)
    2563              :     {
    2564           14 :       process_basic_auth (hc, auth);
    2565              :     }
    2566              :     else /* Check bearer token */
    2567              :     {
    2568              :       enum TALER_ErrorCode ec;
    2569              : 
    2570          513 :       ec = process_bearer_auth (hc,
    2571              :                                 auth);
    2572          513 :       if (TALER_EC_NONE != ec)
    2573              :       {
    2574            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2575              :                     "Bearer authentication failed: %d\n",
    2576              :                     (int) ec);
    2577           19 :         return TALER_MHD_reply_with_ec (connection,
    2578              :                                         ec,
    2579              :                                         NULL);
    2580              :       }
    2581              :     }
    2582              :     /* We grant access if:
    2583              :        - Endpoint does not require permissions
    2584              :        - Authorization scope of bearer token contains permissions
    2585              :          required by endpoint.
    2586              :      */
    2587          548 :     if ( (NULL != hc->rh->permission) &&
    2588          535 :          (! permission_in_scope (hc->rh->permission,
    2589              :                                  hc->auth_scope)))
    2590              :     {
    2591           19 :       if (auth_malformed &&
    2592            0 :           (TMH_AS_NONE == hc->auth_scope) )
    2593              :       {
    2594            0 :         GNUNET_break_op (0);
    2595            0 :         return TALER_MHD_reply_with_error (
    2596              :           connection,
    2597              :           MHD_HTTP_UNAUTHORIZED,
    2598              :           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    2599              :           "'" RFC_8959_PREFIX
    2600              :           "' prefix or 'Bearer' missing in 'Authorization' header");
    2601              :       }
    2602           19 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2603              :                   "Credentials provided are %d which are insufficient for access to `%s'\n",
    2604              :                   (int) hc->auth_scope,
    2605              :                   hc->rh->permission);
    2606           19 :       return TALER_MHD_reply_with_error (connection,
    2607              :                                          MHD_HTTP_UNAUTHORIZED,
    2608              :                                          TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
    2609              :                                          "Check credentials in 'Authorization' header");
    2610              :     }
    2611              :   } /* if (use_private) */
    2612              : 
    2613              : 
    2614          726 :   if ( (NULL == hc->instance) &&
    2615           38 :        (! hc->rh->skip_instance) )
    2616              :   {
    2617            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2618              :                 "Instance for URL `%s' not known\n",
    2619              :                 url);
    2620            0 :     return TALER_MHD_reply_with_error (connection,
    2621              :                                        MHD_HTTP_NOT_FOUND,
    2622              :                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    2623              :                                        url);
    2624              :   }
    2625          726 :   if ( (NULL != hc->instance) && /* make static analysis happy */
    2626          688 :        (! hc->rh->skip_instance) &&
    2627          646 :        (hc->instance->deleted) &&
    2628            3 :        (! hc->rh->allow_deleted_instance) )
    2629              :   {
    2630            3 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2631              :                 "Instance `%s' was deleted\n",
    2632              :                 hc->instance->settings.id);
    2633            3 :     return TALER_MHD_reply_with_error (connection,
    2634              :                                        MHD_HTTP_NOT_FOUND,
    2635              :                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
    2636            3 :                                        hc->instance->settings.id);
    2637              :   }
    2638              :   /* parse request body */
    2639         1446 :   hc->has_body = ( (0 == strcasecmp (method,
    2640         1064 :                                      MHD_HTTP_METHOD_POST)) ||
    2641              :                    /* PUT is not yet used */
    2642          341 :                    (0 == strcasecmp (method,
    2643          723 :                                      MHD_HTTP_METHOD_PATCH)) );
    2644          723 :   if (hc->has_body)
    2645              :   {
    2646          424 :     TALER_MHD_check_content_length (connection,
    2647              :                                     0 == hc->rh->max_upload
    2648              :                                     ? DEFAULT_MAX_UPLOAD_SIZE
    2649              :                                     : hc->rh->max_upload);
    2650          424 :     GNUNET_break (NULL == hc->request_body); /* can't have it already */
    2651              :   }
    2652          723 :   return MHD_YES; /* wait for MHD to call us again */
    2653              : }
    2654              : 
    2655              : 
    2656              : /**
    2657              :  * Callback invoked with information about a bank account.
    2658              :  *
    2659              :  * @param cls closure with a `struct TMH_MerchantInstance *`
    2660              :  * @param merchant_priv private key of the merchant instance
    2661              :  * @param acc details about the account
    2662              :  */
    2663              : static void
    2664           33 : add_account_cb (void *cls,
    2665              :                 const struct TALER_MerchantPrivateKeyP *merchant_priv,
    2666              :                 const struct TALER_MERCHANTDB_AccountDetails *acc)
    2667              : {
    2668           33 :   struct TMH_MerchantInstance *mi = cls;
    2669              :   struct TMH_WireMethod *wm;
    2670              : 
    2671              :   (void) merchant_priv;
    2672           33 :   wm = GNUNET_new (struct TMH_WireMethod);
    2673           33 :   wm->h_wire = acc->h_wire;
    2674              :   wm->payto_uri.full_payto
    2675           33 :     = GNUNET_strdup (acc->payto_uri.full_payto);
    2676           33 :   wm->wire_salt = acc->salt;
    2677              :   wm->wire_method
    2678           33 :     = TALER_payto_get_method (acc->payto_uri.full_payto);
    2679           33 :   wm->active = acc->active;
    2680           33 :   GNUNET_CONTAINER_DLL_insert (mi->wm_head,
    2681              :                                mi->wm_tail,
    2682              :                                wm);
    2683           33 : }
    2684              : 
    2685              : 
    2686              : /**
    2687              :  * Function called during startup to add all known instances to our
    2688              :  * hash map in memory for faster lookups when we receive requests.
    2689              :  *
    2690              :  * @param cls closure, NULL, unused
    2691              :  * @param merchant_pub public key of the instance
    2692              :  * @param merchant_priv private key of the instance, NULL if not available
    2693              :  * @param is detailed configuration settings for the instance
    2694              :  * @param ias authentication settings for the instance
    2695              :  */
    2696              : static void
    2697           71 : add_instance_cb (void *cls,
    2698              :                  const struct TALER_MerchantPublicKeyP *merchant_pub,
    2699              :                  const struct TALER_MerchantPrivateKeyP *merchant_priv,
    2700              :                  const struct TALER_MERCHANTDB_InstanceSettings *is,
    2701              :                  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
    2702              : {
    2703              :   struct TMH_MerchantInstance *mi;
    2704              :   enum GNUNET_DB_QueryStatus qs;
    2705              : 
    2706              :   (void) cls;
    2707           71 :   mi = TMH_lookup_instance (is->id);
    2708           71 :   if (NULL != mi)
    2709              :   {
    2710              :     /* (outdated) entry exists, remove old entry */
    2711            0 :     (void) TMH_instance_free_cb (NULL,
    2712            0 :                                  &mi->h_instance,
    2713              :                                  mi);
    2714              :   }
    2715           71 :   mi = GNUNET_new (struct TMH_MerchantInstance);
    2716           71 :   mi->settings = *is;
    2717           71 :   mi->auth = *ias;
    2718           71 :   mi->settings.id = GNUNET_strdup (mi->settings.id);
    2719           71 :   mi->settings.name = GNUNET_strdup (mi->settings.name);
    2720           71 :   if (NULL != mi->settings.email)
    2721            0 :     mi->settings.email = GNUNET_strdup (mi->settings.email);
    2722           71 :   if (NULL != mi->settings.phone)
    2723            0 :     mi->settings.phone = GNUNET_strdup (mi->settings.phone);
    2724           71 :   if (NULL != mi->settings.website)
    2725            0 :     mi->settings.website = GNUNET_strdup (mi->settings.website);
    2726           71 :   if (NULL != mi->settings.logo)
    2727            0 :     mi->settings.logo = GNUNET_strdup (mi->settings.logo);
    2728           71 :   mi->settings.address = json_incref (mi->settings.address);
    2729           71 :   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
    2730           71 :   if (NULL != merchant_priv)
    2731           67 :     mi->merchant_priv = *merchant_priv;
    2732              :   else
    2733            4 :     mi->deleted = true;
    2734           71 :   mi->merchant_pub = *merchant_pub;
    2735           71 :   qs = TMH_db->select_accounts (TMH_db->cls,
    2736           71 :                                 mi->settings.id,
    2737              :                                 &add_account_cb,
    2738              :                                 mi);
    2739           71 :   if (0 > qs)
    2740              :   {
    2741            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2742              :                 "Error loading accounts of `%s' from database\n",
    2743              :                 mi->settings.id);
    2744              :   }
    2745           71 :   GNUNET_assert (GNUNET_OK ==
    2746              :                  TMH_add_instance (mi));
    2747           71 : }
    2748              : 
    2749              : 
    2750              : /**
    2751              :  * Trigger (re)loading of instance settings from DB.
    2752              :  *
    2753              :  * @param cls NULL
    2754              :  * @param extra ID of the instance that changed, NULL
    2755              :  *              to load all instances (will not handle purges!)
    2756              :  * @param extra_len number of bytes in @a extra
    2757              :  */
    2758              : static void
    2759           98 : load_instances (void *cls,
    2760              :                 const void *extra,
    2761              :                 size_t extra_len)
    2762              : {
    2763              :   enum GNUNET_DB_QueryStatus qs;
    2764           98 :   const char *id = extra;
    2765              : 
    2766              :   (void) cls;
    2767           98 :   if ( (NULL != extra) &&
    2768           83 :        ( (0 == extra_len) ||
    2769           83 :          ('\0' != id[extra_len - 1]) ) )
    2770              :   {
    2771            0 :     GNUNET_break (0 == extra_len);
    2772            0 :     extra = NULL;
    2773              :   }
    2774           98 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2775              :               "Received instance settings notification: reload `%s'\n",
    2776              :               id);
    2777           98 :   if (NULL == extra)
    2778              :   {
    2779           15 :     qs = TMH_db->lookup_instances (TMH_db->cls,
    2780              :                                    false,
    2781              :                                    &add_instance_cb,
    2782              :                                    NULL);
    2783              :   }
    2784              :   else
    2785              :   {
    2786              :     struct TMH_MerchantInstance *mi;
    2787              : 
    2788              :     /* This must be done here to handle instance
    2789              :        purging, as for purged instances, the DB
    2790              :        lookup below will otherwise do nothing */
    2791           83 :     mi = TMH_lookup_instance (id);
    2792           83 :     if (NULL != mi)
    2793              :     {
    2794           83 :       (void) TMH_instance_free_cb (NULL,
    2795           83 :                                    &mi->h_instance,
    2796              :                                    mi);
    2797              :     }
    2798           83 :     qs = TMH_db->lookup_instance (TMH_db->cls,
    2799              :                                   id,
    2800              :                                   false,
    2801              :                                   &add_instance_cb,
    2802              :                                   NULL);
    2803              :   }
    2804           98 :   if (0 > qs)
    2805              :   {
    2806            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2807              :                 "Failed initialization. Check database setup.\n");
    2808            0 :     global_ret = EXIT_NOPERMISSION;
    2809            0 :     GNUNET_SCHEDULER_shutdown ();
    2810            0 :     return;
    2811              :   }
    2812              : }
    2813              : 
    2814              : 
    2815              : /**
    2816              :  * A transaction modified an instance setting (or created/deleted/purged
    2817              :  * one). Notify all backends about the change.
    2818              :  *
    2819              :  * @param id ID of the instance that changed
    2820              :  */
    2821              : void
    2822           83 : TMH_reload_instances (const char *id)
    2823              : {
    2824           83 :   struct GNUNET_DB_EventHeaderP es = {
    2825           83 :     .size = ntohs (sizeof (es)),
    2826           83 :     .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
    2827              :   };
    2828              : 
    2829           83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2830              :               "Generating instance settings notification: reload `%s'\n",
    2831              :               id);
    2832          166 :   TMH_db->event_notify (TMH_db->cls,
    2833              :                         &es,
    2834              :                         id,
    2835              :                         (NULL == id)
    2836              :                         ? 0
    2837           83 :                         : strlen (id) + 1);
    2838           83 : }
    2839              : 
    2840              : 
    2841              : /**
    2842              :  * Callback invoked on every listen socket to start the
    2843              :  * respective MHD HTTP daemon.
    2844              :  *
    2845              :  * @param cls unused
    2846              :  * @param lsock the listen socket
    2847              :  */
    2848              : static void
    2849           30 : start_daemon (void *cls,
    2850              :               int lsock)
    2851              : {
    2852              :   struct MHD_Daemon *mhd;
    2853              : 
    2854              :   (void) cls;
    2855           30 :   GNUNET_assert (-1 != lsock);
    2856           30 :   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
    2857              :                           | MHD_USE_AUTO,
    2858              :                           0 /* port */,
    2859              :                           NULL, NULL,
    2860              :                           &url_handler, NULL,
    2861              :                           MHD_OPTION_LISTEN_SOCKET, lsock,
    2862              :                           MHD_OPTION_URI_LOG_CALLBACK,
    2863              :                           &full_url_track_callback, NULL,
    2864              :                           MHD_OPTION_NOTIFY_COMPLETED,
    2865              :                           &handle_mhd_completion_callback, NULL,
    2866              :                           MHD_OPTION_CONNECTION_TIMEOUT,
    2867              :                           (unsigned int) 10 /* 10s */,
    2868              :                           MHD_OPTION_END);
    2869           30 :   if (NULL == mhd)
    2870              :   {
    2871            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2872              :                 "Failed to launch HTTP service.\n");
    2873            0 :     GNUNET_SCHEDULER_shutdown ();
    2874            0 :     return;
    2875              :   }
    2876           30 :   have_daemons = true;
    2877           30 :   TALER_MHD_daemon_start (mhd);
    2878              : }
    2879              : 
    2880              : 
    2881              : /**
    2882              :  * Main function that will be run by the scheduler.
    2883              :  *
    2884              :  * @param cls closure
    2885              :  * @param args remaining command-line arguments
    2886              :  * @param cfgfile name of the configuration file used (for saving, can be
    2887              :  *        NULL!)
    2888              :  * @param config configuration
    2889              :  */
    2890              : static void
    2891           15 : run (void *cls,
    2892              :      char *const *args,
    2893              :      const char *cfgfile,
    2894              :      const struct GNUNET_CONFIGURATION_Handle *config)
    2895              : {
    2896              :   enum TALER_MHD_GlobalOptions go;
    2897              :   int elen;
    2898              : 
    2899              :   (void) cls;
    2900              :   (void) args;
    2901              :   (void) cfgfile;
    2902           15 :   cfg = config;
    2903           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2904              :               "Starting taler-merchant-httpd\n");
    2905           15 :   go = TALER_MHD_GO_NONE;
    2906           15 :   if (merchant_connection_close)
    2907            0 :     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
    2908           15 :   TALER_MHD_setup (go);
    2909              : 
    2910           15 :   global_ret = EXIT_SUCCESS;
    2911           15 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2912              :                                  NULL);
    2913              : 
    2914              :   TMH_curl_ctx
    2915           15 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    2916              :                         &merchant_curl_rc);
    2917           15 :   if (NULL == TMH_curl_ctx)
    2918              :   {
    2919            0 :     GNUNET_break (0);
    2920            0 :     global_ret = EXIT_NO_RESTART;
    2921            0 :     GNUNET_SCHEDULER_shutdown ();
    2922            0 :     return;
    2923              :   }
    2924           15 :   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
    2925              :   /* Disable 100 continue processing */
    2926           15 :   GNUNET_break (GNUNET_OK ==
    2927              :                 GNUNET_CURL_append_header (TMH_curl_ctx,
    2928              :                                            MHD_HTTP_HEADER_EXPECT ":"));
    2929           15 :   GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
    2930              :                                          "Taler-Correlation-Id");
    2931              : 
    2932           15 :   if (GNUNET_SYSERR ==
    2933           15 :       TALER_config_get_currency (cfg,
    2934              :                                  "merchant",
    2935              :                                  &TMH_currency))
    2936              :   {
    2937            0 :     global_ret = EXIT_NOTCONFIGURED;
    2938            0 :     GNUNET_SCHEDULER_shutdown ();
    2939            0 :     return;
    2940              :   }
    2941           15 :   if (GNUNET_OK !=
    2942           15 :       TALER_CONFIG_parse_currencies (cfg,
    2943              :                                      TMH_currency,
    2944              :                                      &TMH_num_cspecs,
    2945              :                                      &TMH_cspecs))
    2946              :   {
    2947            0 :     global_ret = EXIT_NOTCONFIGURED;
    2948            0 :     GNUNET_SCHEDULER_shutdown ();
    2949            0 :     return;
    2950              :   }
    2951              : 
    2952           15 :   if (GNUNET_SYSERR ==
    2953              :       (TMH_strict_v19
    2954           15 :          = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    2955              :                                                  "merchant",
    2956              :                                                  "STRICT_PROTOCOL_V19")))
    2957              :   {
    2958            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    2959              :                                "merchant",
    2960              :                                "STRICT_PROTOCOL_V19");
    2961            0 :     TMH_strict_v19 = GNUNET_NO;
    2962              :   }
    2963           15 :   if (GNUNET_SYSERR ==
    2964           15 :       (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    2965              :                                                                  "merchant",
    2966              :                                                                  "DISABLE_AUTHENTICATION")))
    2967              :   {
    2968            0 :     TMH_auth_disabled = GNUNET_NO;
    2969              :   }
    2970           15 :   if (GNUNET_YES == TMH_auth_disabled)
    2971              :   {
    2972            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2973              :                 "DANGEROUS: Endpoint Authentication disabled!");
    2974              :   }
    2975              : 
    2976           15 :   if (GNUNET_SYSERR ==
    2977              :       (TMH_have_self_provisioning
    2978           15 :          = GNUNET_CONFIGURATION_get_value_yesno (cfg,
    2979              :                                                  "merchant",
    2980              :                                                  "ENABLE_SELF_PROVISIONING")))
    2981              :   {
    2982            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    2983              :                                "merchant",
    2984              :                                "ENABLE_SELF_PROVISIONING");
    2985            0 :     TMH_have_self_provisioning = GNUNET_NO;
    2986              :   }
    2987              : 
    2988           15 :   if (GNUNET_OK !=
    2989           15 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    2990              :                                            "merchant",
    2991              :                                            "LEGAL_PRESERVATION",
    2992              :                                            &TMH_legal_expiration))
    2993              :   {
    2994            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2995              :                                "merchant",
    2996              :                                "LEGAL_PRESERVATION");
    2997            0 :     global_ret = EXIT_NOTCONFIGURED;
    2998            0 :     GNUNET_SCHEDULER_shutdown ();
    2999            0 :     return;
    3000              :   }
    3001              : 
    3002           15 :   if (GNUNET_OK !=
    3003           15 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    3004              :                                            "merchant",
    3005              :                                            "LEGAL_PRESERVATION",
    3006              :                                            &TMH_legal_expiration))
    3007              :   {
    3008            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    3009              :                                "merchant",
    3010              :                                "LEGAL_PRESERVATION");
    3011            0 :     global_ret = EXIT_NOTCONFIGURED;
    3012            0 :     GNUNET_SCHEDULER_shutdown ();
    3013            0 :     return;
    3014              :   }
    3015              : 
    3016           15 :   if (GNUNET_OK !=
    3017           15 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    3018              :                                            "merchant",
    3019              :                                            "DEFAULT_PAY_DELAY",
    3020              :                                            &TMH_default_pay_delay))
    3021              :   {
    3022           15 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    3023              :                                "merchant",
    3024              :                                "DEFAULT_PAY_DELAY");
    3025           15 :     TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
    3026              :   }
    3027           15 :   if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
    3028              :   {
    3029            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
    3030              :                                "merchant",
    3031              :                                "DEFAULT_PAY_DELAY",
    3032              :                                "forever is not allowed");
    3033            0 :     global_ret = EXIT_NOTCONFIGURED;
    3034            0 :     GNUNET_SCHEDULER_shutdown ();
    3035            0 :     return;
    3036              :   }
    3037           15 :   if (GNUNET_OK !=
    3038           15 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    3039              :                                            "merchant",
    3040              :                                            "DEFAULT_REFUND_DELAY",
    3041              :                                            &TMH_default_refund_delay))
    3042              :   {
    3043           15 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    3044              :                                "merchant",
    3045              :                                "DEFAULT_REFUND_DELAY");
    3046           15 :     TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
    3047              :       GNUNET_TIME_UNIT_DAYS,
    3048              :       15);
    3049              :   }
    3050           15 :   if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
    3051              :   {
    3052            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
    3053              :                                "merchant",
    3054              :                                "DEFAULT_REFUND_DELAY",
    3055              :                                "forever is not allowed");
    3056            0 :     global_ret = EXIT_NOTCONFIGURED;
    3057            0 :     GNUNET_SCHEDULER_shutdown ();
    3058            0 :     return;
    3059              :   }
    3060              : 
    3061           15 :   if (GNUNET_OK !=
    3062           15 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    3063              :                                            "merchant",
    3064              :                                            "DEFAULT_WIRE_TRANSFER_DELAY",
    3065              :                                            &TMH_default_wire_transfer_delay))
    3066              :   {
    3067           15 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    3068              :                                "merchant",
    3069              :                                "DEFAULT_WIRE_TRANSFER_DELAY");
    3070           15 :     TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
    3071              :   }
    3072           15 :   if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
    3073              :   {
    3074            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
    3075              :                                "merchant",
    3076              :                                "DEFAULT_WIRE_TRANSFER_DELAY",
    3077              :                                "forever is not allowed");
    3078            0 :     global_ret = EXIT_NOTCONFIGURED;
    3079            0 :     GNUNET_SCHEDULER_shutdown ();
    3080            0 :     return;
    3081              :   }
    3082              : 
    3083              :   {
    3084              :     char *dwtri;
    3085              : 
    3086           15 :     if (GNUNET_OK !=
    3087           15 :         GNUNET_CONFIGURATION_get_value_string (
    3088              :           cfg,
    3089              :           "merchant",
    3090              :           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
    3091              :           &dwtri))
    3092              :     {
    3093           15 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    3094              :                                  "merchant",
    3095              :                                  "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
    3096           15 :       TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
    3097              :     }
    3098              :     else
    3099              :     {
    3100            0 :       if (GNUNET_OK !=
    3101            0 :           GNUNET_TIME_string_to_round_interval (
    3102              :             dwtri,
    3103              :             &TMH_default_wire_transfer_rounding_interval))
    3104              :       {
    3105            0 :         GNUNET_log_config_invalid (
    3106              :           GNUNET_ERROR_TYPE_ERROR,
    3107              :           "merchant",
    3108              :           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
    3109              :           "invalid time rounding interval");
    3110            0 :         global_ret = EXIT_NOTCONFIGURED;
    3111            0 :         GNUNET_free (dwtri);
    3112            0 :         GNUNET_SCHEDULER_shutdown ();
    3113            0 :         return;
    3114              :       }
    3115            0 :       GNUNET_free (dwtri);
    3116              :     }
    3117              :   }
    3118              : 
    3119           15 :   TMH_load_terms (cfg);
    3120              : 
    3121           15 :   if (GNUNET_OK !=
    3122           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3123              :                                              "merchant",
    3124              :                                              "PAYMENT_TARGET_TYPES",
    3125              :                                              &TMH_allowed_payment_targets))
    3126              :   {
    3127           15 :     TMH_allowed_payment_targets = GNUNET_strdup ("*");
    3128              :   }
    3129              : 
    3130           15 :   if (GNUNET_OK !=
    3131           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3132              :                                              "merchant",
    3133              :                                              "DEFAULT_PERSONA",
    3134              :                                              &TMH_default_persona))
    3135              :   {
    3136           15 :     TMH_default_persona = GNUNET_strdup ("expert");
    3137              :   }
    3138              : 
    3139           15 :   if (GNUNET_OK !=
    3140           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3141              :                                              "merchant",
    3142              :                                              "PAYMENT_TARGET_REGEX",
    3143              :                                              &TMH_payment_target_regex))
    3144              :   {
    3145           15 :     TMH_payment_target_regex = NULL;
    3146              :   }
    3147              :   else
    3148              :   {
    3149            0 :     if (0 == strlen (TMH_payment_target_regex))
    3150              :     {
    3151            0 :       GNUNET_free (TMH_payment_target_regex);
    3152              :     }
    3153              :     else
    3154              :     {
    3155            0 :       if (0 != regcomp (&TMH_payment_target_re,
    3156              :                         TMH_payment_target_regex,
    3157              :                         REG_NOSUB | REG_EXTENDED))
    3158              :       {
    3159            0 :         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    3160              :                                    "merchant",
    3161              :                                    "PAYMENT_TARGET_REGEX",
    3162              :                                    "malformed regular expression");
    3163            0 :         global_ret = EXIT_NOTCONFIGURED;
    3164            0 :         GNUNET_free (TMH_payment_target_regex);
    3165            0 :         GNUNET_SCHEDULER_shutdown ();
    3166            0 :         return;
    3167              :       }
    3168              :     }
    3169              :   }
    3170              : 
    3171           15 :   if (GNUNET_OK !=
    3172           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3173              :                                              "merchant",
    3174              :                                              "HELPER_SMS",
    3175              :                                              &TMH_helper_sms))
    3176              :   {
    3177           15 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
    3178              :                                "merchant",
    3179              :                                "HELPER_SMS",
    3180              :                                "no helper specified");
    3181              :   }
    3182              : 
    3183           15 :   if (GNUNET_OK !=
    3184           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3185              :                                              "merchant",
    3186              :                                              "HELPER_EMAIL",
    3187              :                                              &TMH_helper_email))
    3188              :   {
    3189           15 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
    3190              :                                "merchant",
    3191              :                                "HELPER_EMAIL",
    3192              :                                "no helper specified");
    3193              :   }
    3194              : 
    3195              :   {
    3196              :     char *tan_channels;
    3197              : 
    3198           15 :     if (GNUNET_OK ==
    3199           15 :         GNUNET_CONFIGURATION_get_value_string (cfg,
    3200              :                                                "merchant",
    3201              :                                                "MANDATORY_TAN_CHANNELS",
    3202              :                                                &tan_channels))
    3203              :     {
    3204            0 :       for (char *tok = strtok (tan_channels,
    3205              :                                " ");
    3206            0 :            NULL != tok;
    3207            0 :            tok = strtok (NULL,
    3208              :                          " "))
    3209              :       {
    3210            0 :         if (0 == strcasecmp (tok,
    3211              :                              "sms"))
    3212              :         {
    3213            0 :           if (NULL == TMH_helper_sms)
    3214              :           {
    3215            0 :             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    3216              :                                        "merchant",
    3217              :                                        "MANDATORY_TAN_CHANNELS",
    3218              :                                        "SMS mandatory, but no HELPER_SMS configured");
    3219            0 :             global_ret = EXIT_NOTCONFIGURED;
    3220            0 :             GNUNET_SCHEDULER_shutdown ();
    3221            0 :             GNUNET_free (tan_channels);
    3222            0 :             return;
    3223              :           }
    3224            0 :           TEH_mandatory_tan_channels |= TEH_TCS_SMS;
    3225              :         }
    3226            0 :         else if (0 == strcasecmp (tok,
    3227              :                                   "email"))
    3228              :         {
    3229            0 :           if (NULL == TMH_helper_email)
    3230              :           {
    3231            0 :             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    3232              :                                        "merchant",
    3233              :                                        "MANDATORY_TAN_CHANNELS",
    3234              :                                        "EMAIL mandatory, but no HELPER_EMAIL configured");
    3235            0 :             global_ret = EXIT_NOTCONFIGURED;
    3236            0 :             GNUNET_SCHEDULER_shutdown ();
    3237            0 :             GNUNET_free (tan_channels);
    3238            0 :             return;
    3239              :           }
    3240            0 :           TEH_mandatory_tan_channels |= TEH_TCS_EMAIL;
    3241              :         }
    3242              :         else
    3243              :         {
    3244            0 :           GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    3245              :                                      "merchant",
    3246              :                                      "MANDATORY_TAN_CHANNELS",
    3247              :                                      tok);
    3248            0 :           global_ret = EXIT_NOTCONFIGURED;
    3249            0 :           GNUNET_SCHEDULER_shutdown ();
    3250            0 :           GNUNET_free (tan_channels);
    3251            0 :           return;
    3252              :         }
    3253              :       }
    3254            0 :       GNUNET_free (tan_channels);
    3255              :     }
    3256              :   }
    3257              : 
    3258           15 :   if (GNUNET_OK ==
    3259           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3260              :                                              "merchant",
    3261              :                                              "BASE_URL",
    3262              :                                              &TMH_base_url))
    3263              :   {
    3264            0 :     if (! TALER_is_web_url (TMH_base_url))
    3265              :     {
    3266            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    3267              :                                  "merchant",
    3268              :                                  "BASE_URL",
    3269              :                                  "Needs to start with 'http://' or 'https://'");
    3270            0 :       global_ret = EXIT_NOTCONFIGURED;
    3271            0 :       GNUNET_SCHEDULER_shutdown ();
    3272            0 :       return;
    3273              :     }
    3274              :   }
    3275           15 :   if (GNUNET_OK ==
    3276           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    3277              :                                              "merchant",
    3278              :                                              "BACKOFFICE_SPA_DIR",
    3279              :                                              &TMH_spa_dir))
    3280              :   {
    3281            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3282              :                 "Loading merchant SPA from %s\n",
    3283              :                 TMH_spa_dir);
    3284              :   }
    3285              :   else
    3286              :   {
    3287           15 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3288              :                 "Loading merchant SPA from default location\n");
    3289              :   }
    3290              : 
    3291           15 :   if (GNUNET_YES ==
    3292           15 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    3293              :                                             "merchant",
    3294              :                                             "FORCE_AUDIT"))
    3295           11 :     TMH_force_audit = GNUNET_YES;
    3296           15 :   if (GNUNET_OK !=
    3297           15 :       TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
    3298              :   {
    3299            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3300              :                 "Failed to setup templates\n");
    3301            0 :     global_ret = EXIT_NOTINSTALLED;
    3302            0 :     GNUNET_SCHEDULER_shutdown ();
    3303            0 :     return;
    3304              :   }
    3305           15 :   if (GNUNET_OK !=
    3306           15 :       TMH_spa_init (TMH_spa_dir))
    3307              :   {
    3308            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3309              :                 "Failed to load single page app\n");
    3310            0 :     global_ret = EXIT_NOTINSTALLED;
    3311            0 :     GNUNET_SCHEDULER_shutdown ();
    3312            0 :     return;
    3313              :   }
    3314              :   /* /static/ is currently not used */
    3315              :   /* (void) TMH_statics_init (); */
    3316           15 :   if (NULL ==
    3317           15 :       (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
    3318              :                                                              GNUNET_YES)))
    3319              :   {
    3320            0 :     global_ret = EXIT_FAILURE;
    3321            0 :     GNUNET_SCHEDULER_shutdown ();
    3322            0 :     return;
    3323              :   }
    3324           15 :   if (NULL ==
    3325           15 :       (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
    3326              :   {
    3327            0 :     global_ret = EXIT_NOTINSTALLED;
    3328            0 :     GNUNET_SCHEDULER_shutdown ();
    3329            0 :     return;
    3330              :   }
    3331           15 :   if (GNUNET_OK !=
    3332           15 :       TMH_db->connect (TMH_db->cls))
    3333              :   {
    3334            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3335              :                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
    3336            0 :     global_ret = EXIT_FAILURE;
    3337            0 :     GNUNET_SCHEDULER_shutdown ();
    3338            0 :     return;
    3339              :   }
    3340           15 :   elen = TMH_EXCHANGES_init (config);
    3341           15 :   if (GNUNET_SYSERR == elen)
    3342              :   {
    3343            0 :     global_ret = EXIT_NOTCONFIGURED;
    3344            0 :     GNUNET_SCHEDULER_shutdown ();
    3345            0 :     return;
    3346              :   }
    3347           15 :   if (0 == elen)
    3348              :   {
    3349            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3350              :                 "Fatal: no trusted exchanges configured. Exiting.\n");
    3351            0 :     global_ret = EXIT_NOTCONFIGURED;
    3352            0 :     GNUNET_SCHEDULER_shutdown ();
    3353            0 :     return;
    3354              :   }
    3355              : 
    3356              :   {
    3357           15 :     struct GNUNET_DB_EventHeaderP es = {
    3358           15 :       .size = ntohs (sizeof (es)),
    3359           15 :       .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
    3360              :     };
    3361              : 
    3362           30 :     instance_eh = TMH_db->event_listen (TMH_db->cls,
    3363              :                                         &es,
    3364           15 :                                         GNUNET_TIME_UNIT_FOREVER_REL,
    3365              :                                         &load_instances,
    3366              :                                         NULL);
    3367              :   }
    3368           15 :   load_instances (NULL,
    3369              :                   NULL,
    3370              :                   0);
    3371              :   {
    3372              :     enum GNUNET_GenericReturnValue ret;
    3373              : 
    3374           15 :     ret = TALER_MHD_listen_bind (cfg,
    3375              :                                  "merchant",
    3376              :                                  &start_daemon,
    3377              :                                  NULL);
    3378           15 :     switch (ret)
    3379              :     {
    3380            0 :     case GNUNET_SYSERR:
    3381            0 :       global_ret = EXIT_NOTCONFIGURED;
    3382            0 :       GNUNET_SCHEDULER_shutdown ();
    3383            0 :       return;
    3384            0 :     case GNUNET_NO:
    3385            0 :       if (! have_daemons)
    3386              :       {
    3387            0 :         global_ret = EXIT_NOTCONFIGURED;
    3388            0 :         GNUNET_SCHEDULER_shutdown ();
    3389            0 :         return;
    3390              :       }
    3391            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    3392              :                   "Could not open all configured listen sockets\n");
    3393            0 :       break;
    3394           15 :     case GNUNET_OK:
    3395           15 :       break;
    3396              :     }
    3397              :   }
    3398           15 :   global_ret = EXIT_SUCCESS;
    3399              : }
    3400              : 
    3401              : 
    3402              : /**
    3403              :  * The main function of the serve tool
    3404              :  *
    3405              :  * @param argc number of arguments from the command line
    3406              :  * @param argv command line arguments
    3407              :  * @return 0 ok, non-zero on error
    3408              :  */
    3409              : int
    3410           29 : main (int argc,
    3411              :       char *const *argv)
    3412              : {
    3413              :   enum GNUNET_GenericReturnValue res;
    3414           29 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    3415           29 :     GNUNET_GETOPT_option_flag ('C',
    3416              :                                "connection-close",
    3417              :                                "force HTTP connections to be closed after each request",
    3418              :                                &merchant_connection_close),
    3419           29 :     GNUNET_GETOPT_option_timetravel ('T',
    3420              :                                      "timetravel"),
    3421           29 :     GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
    3422              :     GNUNET_GETOPT_OPTION_END
    3423              :   };
    3424              : 
    3425           29 :   res = GNUNET_PROGRAM_run (
    3426              :     TALER_MERCHANT_project_data (),
    3427              :     argc, argv,
    3428              :     "taler-merchant-httpd",
    3429              :     "Taler merchant's HTTP backend interface",
    3430              :     options,
    3431              :     &run, NULL);
    3432           29 :   if (GNUNET_SYSERR == res)
    3433            0 :     return EXIT_INVALIDARGUMENT;
    3434           29 :   if (GNUNET_NO == res)
    3435           14 :     return EXIT_SUCCESS;
    3436           15 :   return global_ret;
    3437              : }
        

Generated by: LCOV version 2.0-1