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

Generated by: LCOV version 2.0-1