LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_dispatcher.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.1 % 144 101
Test Date: 2026-09-11 17:45:24 Functions: 83.3 % 6 5

            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 src/backend/taler-merchant-httpd_dispatcher.c
      18              :  * @brief map requested URL and method to the respective request handler
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include "taler-merchant-httpd_get-config.h"
      23              : #include "taler-merchant-httpd_get-exchanges.h"
      24              : #include "taler-merchant-httpd_dispatcher.h"
      25              : #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
      26              : #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
      27              : #include "taler-merchant-httpd_get-products-IMAGE_HASH-image.h"
      28              : #include "taler-merchant-httpd_get-templates-TEMPLATE_ID.h"
      29              : #include "taler-merchant-httpd_mhd.h"
      30              : #include "taler-merchant-httpd_delete-private-accounts-H_WIRE.h"
      31              : #include "taler-merchant-httpd_delete-private-categories-CATEGORY_ID.h"
      32              : #include "taler-merchant-httpd_delete-private-units-UNIT.h"
      33              : #include "taler-merchant-httpd_delete-management-instances-INSTANCE.h"
      34              : #include "taler-merchant-httpd_delete-private-token.h"
      35              : #include "taler-merchant-httpd_delete-private-tokens-SERIAL.h"
      36              : #include "taler-merchant-httpd_delete-private-products-PRODUCT_ID.h"
      37              : #include "taler-merchant-httpd_delete-private-orders-ORDER_ID.h"
      38              : #include "taler-merchant-httpd_delete-private-otp-devices-DEVICE_ID.h"
      39              : #include "taler-merchant-httpd_delete-private-templates-TEMPLATE_ID.h"
      40              : #include "taler-merchant-httpd_delete-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
      41              : #include "taler-merchant-httpd_delete-private-transfers-TID.h"
      42              : #include "taler-merchant-httpd_delete-private-webhooks-WEBHOOK_ID.h"
      43              : #include "taler-merchant-httpd_get-private-accounts.h"
      44              : #include "taler-merchant-httpd_get-private-accounts-H_WIRE.h"
      45              : #include "taler-merchant-httpd_get-private-categories.h"
      46              : #include "taler-merchant-httpd_get-private-categories-CATEGORY_ID.h"
      47              : #include "taler-merchant-httpd_get-private-units.h"
      48              : #include "taler-merchant-httpd_get-private-units-UNIT.h"
      49              : #include "taler-merchant-httpd_get-private-incoming.h"
      50              : #include "taler-merchant-httpd_get-private-incoming-ID.h"
      51              : #include "taler-merchant-httpd_get-management-instances.h"
      52              : #include "taler-merchant-httpd_get-management-instances-INSTANCE.h"
      53              : #include "taler-merchant-httpd_get-private-kyc.h"
      54              : #include "taler-merchant-httpd_get-private-tokens.h"
      55              : #include "taler-merchant-httpd_get-private-pos.h"
      56              : #include "taler-merchant-httpd_get-private-products.h"
      57              : #include "taler-merchant-httpd_get-private-products-PRODUCT_ID.h"
      58              : #include "taler-merchant-httpd_get-private-orders.h"
      59              : #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
      60              : #include "taler-merchant-httpd_get-private-otp-devices.h"
      61              : #include "taler-merchant-httpd_get-private-otp-devices-DEVICE_ID.h"
      62              : #include "taler-merchant-httpd_get-private-statistics-amount-SLUG.h"
      63              : #include "taler-merchant-httpd_get-private-statistics-counter-SLUG.h"
      64              : #include "taler-merchant-httpd_get-private-statistics-report-transactions.h"
      65              : #include "taler-merchant-httpd_get-private-templates.h"
      66              : #include "taler-merchant-httpd_get-private-templates-TEMPLATE_ID.h"
      67              : #include "taler-merchant-httpd_get-private-tokenfamilies.h"
      68              : #include "taler-merchant-httpd_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
      69              : #include "taler-merchant-httpd_get-private-transfers.h"
      70              : #include "taler-merchant-httpd_get-private-webhooks.h"
      71              : #include "taler-merchant-httpd_get-private-webhooks-WEBHOOK_ID.h"
      72              : #include "taler-merchant-httpd_patch-private-accounts-H_WIRE.h"
      73              : #include "taler-merchant-httpd_patch-private-categories-CATEGORY_ID.h"
      74              : #include "taler-merchant-httpd_patch-private-units-UNIT.h"
      75              : #include "taler-merchant-httpd_patch-management-instances-INSTANCE.h"
      76              : #include "taler-merchant-httpd_patch-private-orders-ORDER_ID-forget.h"
      77              : #include "taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.h"
      78              : #include "taler-merchant-httpd_patch-private-products-PRODUCT_ID.h"
      79              : #include "taler-merchant-httpd_patch-private-templates-TEMPLATE_ID.h"
      80              : #include "taler-merchant-httpd_patch-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
      81              : #include "taler-merchant-httpd_patch-private-webhooks-WEBHOOK_ID.h"
      82              : #include "taler-merchant-httpd_post-private-accounts.h"
      83              : #include "taler-merchant-httpd_post-private-categories.h"
      84              : #include "taler-merchant-httpd_post-private-units.h"
      85              : #include "taler-merchant-httpd_post-management-instances.h"
      86              : #include "taler-merchant-httpd_post-management-instances-INSTANCE-auth.h"
      87              : #include "taler-merchant-httpd_post-private-token.h"
      88              : #include "taler-merchant-httpd_post-private-otp-devices.h"
      89              : #include "taler-merchant-httpd_post-private-orders.h"
      90              : #include "taler-merchant-httpd_post-private-orders-ORDER_ID-collect.h"
      91              : #include "taler-merchant-httpd_post-private-orders-ORDER_ID-refund.h"
      92              : #include "taler-merchant-httpd_post-private-orders-ORDER_ID-refund-external.h"
      93              : #include "taler-merchant-httpd_post-private-products.h"
      94              : #include "taler-merchant-httpd_post-private-products-PRODUCT_ID-lock.h"
      95              : #include "taler-merchant-httpd_post-private-templates.h"
      96              : #include "taler-merchant-httpd_post-private-tokenfamilies.h"
      97              : #include "taler-merchant-httpd_post-private-transfers.h"
      98              : #include "taler-merchant-httpd_post-private-webhooks.h"
      99              : #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
     100              : #include "taler-merchant-httpd_post-private-accept-tos-early.h"
     101              : #include "taler-merchant-httpd_post-challenge-ID.h"
     102              : #include "taler-merchant-httpd_post-challenge-ID-confirm.h"
     103              : #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
     104              : #include "taler-merchant-httpd_post-orders-ORDER_ID-claim.h"
     105              : #include "taler-merchant-httpd_post-orders-ORDER_ID-paid.h"
     106              : #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
     107              : #include "taler-merchant-httpd_post-orders-ORDER_ID-unclaim.h"
     108              : #include "taler-merchant-httpd_post-templates-TEMPLATE_ID.h"
     109              : #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
     110              : #include "taler-merchant-httpd_get-webui.h"
     111              : #include "taler-merchant-httpd_statics.h"
     112              : #include "taler-merchant-httpd_get-terms.h"
     113              : #include "taler-merchant-httpd_post-reports-REPORT_ID.h"
     114              : #include "taler-merchant-httpd_delete-private-reports-REPORT_ID.h"
     115              : #include "taler-merchant-httpd_get-private-reports-REPORT_ID.h"
     116              : #include "taler-merchant-httpd_get-private-reports.h"
     117              : #include "taler-merchant-httpd_patch-private-reports-REPORT_ID.h"
     118              : #include "taler-merchant-httpd_post-private-reports.h"
     119              : #include "taler-merchant-httpd_delete-private-pots-POT_ID.h"
     120              : #include "taler-merchant-httpd_get-private-pots-POT_ID.h"
     121              : #include "taler-merchant-httpd_get-private-pots.h"
     122              : #include "taler-merchant-httpd_patch-private-pots-POT_ID.h"
     123              : #include "taler-merchant-httpd_post-private-pots.h"
     124              : #include "taler-merchant-httpd_get-private-groups.h"
     125              : #include "taler-merchant-httpd_post-private-groups.h"
     126              : #include "taler-merchant-httpd_patch-private-groups-GROUP_ID.h"
     127              : #include "taler-merchant-httpd_delete-private-groups-GROUP_ID.h"
     128              : #include "taler-merchant-httpd_get-private-donau.h"
     129              : #include "taler-merchant-httpd_post-private-donau.h"
     130              : #include "taler-merchant-httpd_delete-private-donau-DONAU_SERIAL.h"
     131              : 
     132              : 
     133              : /**
     134              :  * Handle a OPTIONS "*" request.
     135              :  *
     136              :  * @param rh context of the handler
     137              :  * @param connection the MHD connection to handle
     138              :  * @param[in,out] hc context with further information about the request
     139              :  * @return MHD result code
     140              :  */
     141              : static enum MHD_Result
     142            0 : handle_server_options (const struct TMH_RequestHandler *rh,
     143              :                        struct MHD_Connection *connection,
     144              :                        struct TMH_HandlerContext *hc)
     145              : {
     146              :   (void) rh;
     147              :   (void) hc;
     148            0 :   return TALER_MHD_reply_cors_preflight (connection);
     149              : }
     150              : 
     151              : 
     152              : /**
     153              :  * Generates the response for "/", redirecting the
     154              :  * client to the "/webui/" from where we serve the SPA.
     155              :  *
     156              :  * @param rh request handler
     157              :  * @param connection MHD connection
     158              :  * @param hc handler context
     159              :  * @return MHD result code
     160              :  */
     161              : static enum MHD_Result
     162            1 : spa_redirect (const struct TMH_RequestHandler *rh,
     163              :               struct MHD_Connection *connection,
     164              :               struct TMH_HandlerContext *hc)
     165              : {
     166            1 :   const char *text = "Redirecting to /webui/";
     167              :   struct MHD_Response *response;
     168              :   char *dst;
     169              : 
     170            1 :   response = MHD_create_response_from_buffer (strlen (text),
     171              :                                               (void *) text,
     172              :                                               MHD_RESPMEM_PERSISTENT);
     173            1 :   if (NULL == response)
     174              :   {
     175            0 :     GNUNET_break (0);
     176            0 :     return MHD_NO;
     177              :   }
     178            1 :   TALER_MHD_add_global_headers (response,
     179              :                                 true);
     180            1 :   GNUNET_break (MHD_YES ==
     181              :                 MHD_add_response_header (response,
     182              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     183              :                                          "text/plain"));
     184            1 :   if ( (NULL == hc->instance) ||
     185            1 :        (0 == strcmp ("admin",
     186            1 :                      hc->instance->settings.id)) )
     187            1 :     dst = GNUNET_strdup ("/webui/");
     188              :   else
     189            0 :     GNUNET_asprintf (&dst,
     190              :                      "/instances/%s/webui/",
     191            0 :                      hc->instance->settings.id);
     192            1 :   if (MHD_NO ==
     193            1 :       MHD_add_response_header (response,
     194              :                                MHD_HTTP_HEADER_LOCATION,
     195              :                                dst))
     196              :   {
     197            0 :     GNUNET_break (0);
     198            0 :     MHD_destroy_response (response);
     199            0 :     GNUNET_free (dst);
     200            0 :     return MHD_NO;
     201              :   }
     202            1 :   GNUNET_free (dst);
     203              : 
     204              :   {
     205              :     enum MHD_Result ret;
     206              : 
     207            1 :     ret = MHD_queue_response (connection,
     208              :                               MHD_HTTP_FOUND,
     209              :                               response);
     210            1 :     MHD_destroy_response (response);
     211            1 :     return ret;
     212              :   }
     213              : }
     214              : 
     215              : 
     216              : /**
     217              :  * Determine the group of request handlers to call for the
     218              :  * given URL. Removes a possible prefix from @a purl by advancing
     219              :  * the pointer.
     220              :  *
     221              :  * @param[in,out] urlp pointer to the URL to analyze and update
     222              :  * @param[out] is_public set to true if these are public handlers
     223              :  * @return handler group to consider for the given URL
     224              :  */
     225              : static const struct TMH_RequestHandler *
     226         1144 : determine_handler_group (const char **urlp,
     227              :                          bool *is_public)
     228              : {
     229              :   static struct TMH_RequestHandler management_handlers[] = {
     230              :     /* GET /instances */
     231              :     {
     232              :       .url_prefix = "/instances",
     233              :       .method = MHD_HTTP_METHOD_GET,
     234              :       .permission = "instances-write",
     235              :       .skip_instance = true,
     236              :       .default_only = true,
     237              :       .handler = &TMH_private_get_instances
     238              :     },
     239              :     /* POST /instances */
     240              :     {
     241              :       .url_prefix = "/instances",
     242              :       .method = MHD_HTTP_METHOD_POST,
     243              :       .permission = "instances-write",
     244              :       .skip_instance = true,
     245              :       .default_only = true,
     246              :       .handler = &TMH_private_post_instances,
     247              :       /* allow instance data of up to 8 MB, that should be plenty;
     248              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     249              :          would require further changes to the allocation logic
     250              :          in the code... */
     251              :       .max_upload = 1024 * 1024 * 8
     252              :     },
     253              :     /* GET /instances/$ID/ */
     254              :     {
     255              :       .url_prefix = "/instances/",
     256              :       .method = MHD_HTTP_METHOD_GET,
     257              :       .permission = "instances-write",
     258              :       .skip_instance = true,
     259              :       .default_only = true,
     260              :       .have_id_segment = true,
     261              :       .handler = &TMH_private_get_instances_default_ID
     262              :     },
     263              :     /* DELETE /instances/$ID */
     264              :     {
     265              :       .url_prefix = "/instances/",
     266              :       .method = MHD_HTTP_METHOD_DELETE,
     267              :       .permission = "instances-write",
     268              :       .skip_instance = true,
     269              :       .default_only = true,
     270              :       .have_id_segment = true,
     271              :       .handler = &TMH_private_delete_instances_default_ID
     272              :     },
     273              :     /* PATCH /instances/$ID */
     274              :     {
     275              :       .url_prefix = "/instances/",
     276              :       .method = MHD_HTTP_METHOD_PATCH,
     277              :       .permission = "instances-write",
     278              :       .skip_instance = true,
     279              :       .default_only = true,
     280              :       .have_id_segment = true,
     281              :       .handler = &TMH_private_patch_instances_default_ID,
     282              :       /* allow instance data of up to 8 MB, that should be plenty;
     283              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     284              :          would require further changes to the allocation logic
     285              :          in the code... */
     286              :       .max_upload = 1024 * 1024 * 8
     287              :     },
     288              :     /* POST /auth: */
     289              :     {
     290              :       .url_prefix = "/instances/",
     291              :       .url_suffix = "auth",
     292              :       .method = MHD_HTTP_METHOD_POST,
     293              :       .permission = "instances-auth-write",
     294              :       .skip_instance = true,
     295              :       .default_only = true,
     296              :       .have_id_segment = true,
     297              :       .handler = &TMH_private_post_instances_default_ID_auth,
     298              :       /* Body should be pretty small. */
     299              :       .max_upload = 1024 * 1024
     300              :     },
     301              :     /* GET /kyc: */
     302              :     {
     303              :       .url_prefix = "/instances/",
     304              :       .url_suffix = "kyc",
     305              :       .method = MHD_HTTP_METHOD_GET,
     306              :       .permission = "instances-kyc-read",
     307              :       .skip_instance = true,
     308              :       .default_only = true,
     309              :       .have_id_segment = true,
     310              :       .handler = &TMH_private_get_instances_default_ID_kyc,
     311              :     },
     312              :     {
     313              :       .url_prefix = NULL
     314              :     }
     315              :   };
     316              : 
     317              :   static struct TMH_RequestHandler private_handlers[] = {
     318              :     /* GET /instances/$ID/: */
     319              :     {
     320              :       .url_prefix = "/",
     321              :       .method = MHD_HTTP_METHOD_GET,
     322              :       .permission = "instances-read",
     323              :       .handler = &TMH_private_get_instances_ID
     324              :     },
     325              :     /* DELETE /instances/$ID/: */
     326              :     {
     327              :       .url_prefix = "/",
     328              :       .method = MHD_HTTP_METHOD_DELETE,
     329              :       .permission = "instances-write",
     330              :       .allow_deleted_instance = true,
     331              :       .handler = &TMH_private_delete_instances_ID
     332              :     },
     333              :     /* PATCH /instances/$ID/: */
     334              :     {
     335              :       .url_prefix = "/",
     336              :       .method = MHD_HTTP_METHOD_PATCH,
     337              :       .handler = &TMH_private_patch_instances_ID,
     338              :       .permission = "instances-write",
     339              :       .allow_deleted_instance = true,
     340              :       /* allow instance data of up to 8 MB, that should be plenty;
     341              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     342              :          would require further changes to the allocation logic
     343              :          in the code... */
     344              :       .max_upload = 1024 * 1024 * 8
     345              :     },
     346              :     /* POST /auth: */
     347              :     {
     348              :       .url_prefix = "/auth",
     349              :       .method = MHD_HTTP_METHOD_POST,
     350              :       .handler = &TMH_private_post_instances_ID_auth,
     351              :       .permission = "auth-write",
     352              :       /* Body should be pretty small. */
     353              :       .max_upload = 1024 * 1024,
     354              :     },
     355              :     /* GET /kyc: */
     356              :     {
     357              :       .url_prefix = "/kyc",
     358              :       .method = MHD_HTTP_METHOD_GET,
     359              :       .permission = "kyc-read",
     360              :       .handler = &TMH_private_get_instances_ID_kyc,
     361              :     },
     362              :     /* GET /pos: */
     363              :     {
     364              :       .url_prefix = "/pos",
     365              :       .method = MHD_HTTP_METHOD_GET,
     366              :       .permission = "pos-read",
     367              :       .handler = &TMH_private_get_pos
     368              :     },
     369              :     /* GET /categories: */
     370              :     {
     371              :       .url_prefix = "/categories",
     372              :       .method = MHD_HTTP_METHOD_GET,
     373              :       .permission = "categories-read",
     374              :       .handler = &TMH_private_get_categories
     375              :     },
     376              :     /* POST /categories: */
     377              :     {
     378              :       .url_prefix = "/categories",
     379              :       .method = MHD_HTTP_METHOD_POST,
     380              :       .permission = "categories-write",
     381              :       .handler = &TMH_private_post_categories,
     382              :       /* allow category data of up to 8 kb, that should be plenty */
     383              :       .max_upload = 1024 * 8
     384              :     },
     385              :     /* GET /categories/$ID: */
     386              :     {
     387              :       .url_prefix = "/categories/",
     388              :       .method = MHD_HTTP_METHOD_GET,
     389              :       .permission = "categories-read",
     390              :       .have_id_segment = true,
     391              :       .allow_deleted_instance = true,
     392              :       .handler = &TMH_private_get_categories_ID
     393              :     },
     394              :     /* DELETE /categories/$ID: */
     395              :     {
     396              :       .url_prefix = "/categories/",
     397              :       .method = MHD_HTTP_METHOD_DELETE,
     398              :       .permission = "categories-write",
     399              :       .have_id_segment = true,
     400              :       .allow_deleted_instance = true,
     401              :       .handler = &TMH_private_delete_categories_ID
     402              :     },
     403              :     /* PATCH /categories/$ID/: */
     404              :     {
     405              :       .url_prefix = "/categories/",
     406              :       .method = MHD_HTTP_METHOD_PATCH,
     407              :       .permission = "categories-write",
     408              :       .have_id_segment = true,
     409              :       .allow_deleted_instance = true,
     410              :       .handler = &TMH_private_patch_categories_ID,
     411              :       /* allow category data of up to 8 kb, that should be plenty */
     412              :       .max_upload = 1024 * 8
     413              :     },
     414              :     /* GET /units: */
     415              :     {
     416              :       .url_prefix = "/units",
     417              :       .method = MHD_HTTP_METHOD_GET,
     418              :       .permission = "units-read",
     419              :       .handler = &TMH_private_get_units
     420              :     },
     421              :     /* POST /units: */
     422              :     {
     423              :       .url_prefix = "/units",
     424              :       .method = MHD_HTTP_METHOD_POST,
     425              :       .permission = "units-write",
     426              :       .handler = &TMH_private_post_units,
     427              :       .max_upload = 1024 * 8
     428              :     },
     429              :     /* GET /units/$UNIT: */
     430              :     {
     431              :       .url_prefix = "/units/",
     432              :       .method = MHD_HTTP_METHOD_GET,
     433              :       .have_id_segment = true,
     434              :       .allow_deleted_instance = true,
     435              :       .permission = "units-read",
     436              :       .handler = &TMH_private_get_units_ID
     437              :     },
     438              :     /* DELETE /units/$UNIT: */
     439              :     {
     440              :       .url_prefix = "/units/",
     441              :       .method = MHD_HTTP_METHOD_DELETE,
     442              :       .permission = "units-write",
     443              :       .have_id_segment = true,
     444              :       .allow_deleted_instance = true,
     445              :       .handler = &TMH_private_delete_units_ID
     446              :     },
     447              :     /* PATCH /units/$UNIT: */
     448              :     {
     449              :       .url_prefix = "/units/",
     450              :       .method = MHD_HTTP_METHOD_PATCH,
     451              :       .permission = "units-write",
     452              :       .have_id_segment = true,
     453              :       .allow_deleted_instance = true,
     454              :       .handler = &TMH_private_patch_units_ID,
     455              :       .max_upload = 1024 * 8
     456              :     },
     457              :     /* GET /products: */
     458              :     {
     459              :       .url_prefix = "/products",
     460              :       .permission = "products-read",
     461              :       .method = MHD_HTTP_METHOD_GET,
     462              :       .handler = &TMH_private_get_products
     463              :     },
     464              :     /* POST /products: */
     465              :     {
     466              :       .url_prefix = "/products",
     467              :       .method = MHD_HTTP_METHOD_POST,
     468              :       .permission = "products-write",
     469              :       .handler = &TMH_private_post_products,
     470              :       /* allow product data of up to 8 MB, that should be plenty;
     471              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     472              :          would require further changes to the allocation logic
     473              :          in the code... */
     474              :       .max_upload = 1024 * 1024 * 8
     475              :     },
     476              :     /* GET /products/$ID: */
     477              :     {
     478              :       .url_prefix = "/products/",
     479              :       .method = MHD_HTTP_METHOD_GET,
     480              :       .have_id_segment = true,
     481              :       .permission = "products-read",
     482              :       .allow_deleted_instance = true,
     483              :       .handler = &TMH_private_get_products_ID
     484              :     },
     485              :     /* DELETE /products/$ID/: */
     486              :     {
     487              :       .url_prefix = "/products/",
     488              :       .method = MHD_HTTP_METHOD_DELETE,
     489              :       .have_id_segment = true,
     490              :       .permission = "products-write",
     491              :       .allow_deleted_instance = true,
     492              :       .handler = &TMH_private_delete_products_ID
     493              :     },
     494              :     /* PATCH /products/$ID/: */
     495              :     {
     496              :       .url_prefix = "/products/",
     497              :       .method = MHD_HTTP_METHOD_PATCH,
     498              :       .have_id_segment = true,
     499              :       .allow_deleted_instance = true,
     500              :       .permission = "products-write",
     501              :       .handler = &TMH_private_patch_products_ID,
     502              :       /* allow product data of up to 8 MB, that should be plenty;
     503              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     504              :          would require further changes to the allocation logic
     505              :          in the code... */
     506              :       .max_upload = 1024 * 1024 * 8
     507              :     },
     508              :     /* POST /products/$ID/lock: */
     509              :     {
     510              :       .url_prefix = "/products/",
     511              :       .url_suffix = "lock",
     512              :       .method = MHD_HTTP_METHOD_POST,
     513              :       .have_id_segment = true,
     514              :       .permission = "products-lock",
     515              :       .handler = &TMH_private_post_products_ID_lock,
     516              :       /* the body should be pretty small, allow 1 MB of upload
     517              :          to set a conservative bound for sane wallets */
     518              :       .max_upload = 1024 * 1024
     519              :     },
     520              :     /* POST /orders: */
     521              :     {
     522              :       .url_prefix = "/orders",
     523              :       .method = MHD_HTTP_METHOD_POST,
     524              :       .permission = "orders-write",
     525              :       .handler = &TMH_private_post_orders,
     526              :       /* allow contracts of up to 8 MB, that should be plenty;
     527              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     528              :          would require further changes to the allocation logic
     529              :          in the code... */
     530              :       .max_upload = 1024 * 1024 * 8
     531              :     },
     532              :     /* GET /orders/$ID: */
     533              :     {
     534              :       .url_prefix = "/orders/",
     535              :       .method = MHD_HTTP_METHOD_GET,
     536              :       .permission = "orders-read",
     537              :       .have_id_segment = true,
     538              :       .allow_deleted_instance = true,
     539              :       .handler = &TMH_private_get_orders_ID
     540              :     },
     541              :     /* GET /orders: */
     542              :     {
     543              :       .url_prefix = "/orders",
     544              :       .method = MHD_HTTP_METHOD_GET,
     545              :       .permission = "orders-read",
     546              :       .allow_deleted_instance = true,
     547              :       .handler = &TMH_private_get_orders
     548              :     },
     549              :     /* POST /orders/$ID/refund: */
     550              :     {
     551              :       .url_prefix = "/orders/",
     552              :       .url_suffix = "refund",
     553              :       .method = MHD_HTTP_METHOD_POST,
     554              :       .have_id_segment = true,
     555              :       .permission = "orders-refund",
     556              :       .handler = &TMH_private_post_orders_ID_refund,
     557              :       /* the body should be pretty small, allow 1 MB of upload
     558              :          to set a conservative bound for sane wallets */
     559              :       .max_upload = 1024 * 1024
     560              :     },
     561              :     /* POST /orders/$ID/refund-external: */
     562              :     {
     563              :       .url_prefix = "/orders/",
     564              :       .url_suffix = "refund-external",
     565              :       .method = MHD_HTTP_METHOD_POST,
     566              :       .have_id_segment = true,
     567              :       .permission = "orders-refund",
     568              :       .handler = &TMH_private_post_orders_ID_refund_external,
     569              :       /* the body should be pretty small, allow 1 MB of upload
     570              :          to set a conservative bound for sane wallets */
     571              :       .max_upload = 1024 * 1024
     572              :     },
     573              :     /* POST /orders/$ID/collect: */
     574              :     {
     575              :       .url_prefix = "/orders/",
     576              :       .url_suffix = "collect",
     577              :       .method = MHD_HTTP_METHOD_POST,
     578              :       .have_id_segment = true,
     579              :       .permission = "orders-write",
     580              :       .handler = &TMH_private_post_orders_ID_collect,
     581              :       /* the body should be pretty small, allow 1 MB of upload
     582              :          to set a conservative bound for sane wallets */
     583              :       .max_upload = 1024 * 1024
     584              :     },
     585              :     /* PATCH /orders/$ID/forget: */
     586              :     {
     587              :       .url_prefix = "/orders/",
     588              :       .url_suffix = "forget",
     589              :       .method = MHD_HTTP_METHOD_PATCH,
     590              :       .permission = "orders-write",
     591              :       .have_id_segment = true,
     592              :       .allow_deleted_instance = true,
     593              :       .handler = &TMH_private_patch_orders_ID_forget,
     594              :       /* the body should be pretty small, allow 1 MB of upload
     595              :          to set a conservative bound for sane wallets */
     596              :       .max_upload = 1024 * 1024
     597              :     },
     598              :     /* DELETE /orders/$ID: */
     599              :     {
     600              :       .url_prefix = "/orders/",
     601              :       .method = MHD_HTTP_METHOD_DELETE,
     602              :       .permission = "orders-write",
     603              :       .have_id_segment = true,
     604              :       .allow_deleted_instance = true,
     605              :       .handler = &TMH_private_delete_orders_ID
     606              :     },
     607              :     /* POST /transfers: */
     608              :     {
     609              :       .url_prefix = "/transfers",
     610              :       .method = MHD_HTTP_METHOD_POST,
     611              :       .allow_deleted_instance = true,
     612              :       .handler = &TMH_private_post_transfers,
     613              :       .permission = "transfers-write",
     614              :       /* the body should be pretty small, allow 1 MB of upload
     615              :          to set a conservative bound for sane wallets */
     616              :       .max_upload = 1024 * 1024
     617              :     },
     618              :     /* DELETE /transfers/$ID: */
     619              :     {
     620              :       .url_prefix = "/transfers/",
     621              :       .method = MHD_HTTP_METHOD_DELETE,
     622              :       .permission = "transfers-write",
     623              :       .allow_deleted_instance = true,
     624              :       .handler = &TMH_private_delete_transfers_ID,
     625              :       .have_id_segment = true,
     626              :       /* the body should be pretty small, allow 1 MB of upload
     627              :          to set a conservative bound for sane wallets */
     628              :       .max_upload = 1024 * 1024
     629              :     },
     630              :     /* GET /transfers: */
     631              :     {
     632              :       .url_prefix = "/transfers",
     633              :       .permission = "transfers-read",
     634              :       .method = MHD_HTTP_METHOD_GET,
     635              :       .allow_deleted_instance = true,
     636              :       .handler = &TMH_private_get_transfers
     637              :     },
     638              :     /* GET /incoming: */
     639              :     {
     640              :       .url_prefix = "/incoming",
     641              :       .permission = "transfers-read",
     642              :       .method = MHD_HTTP_METHOD_GET,
     643              :       .allow_deleted_instance = true,
     644              :       .handler = &TMH_private_get_incoming
     645              :     },
     646              :     /* GET /incoming/$ID: */
     647              :     {
     648              :       .url_prefix = "/incoming/",
     649              :       .permission = "transfers-read",
     650              :       .method = MHD_HTTP_METHOD_GET,
     651              :       .allow_deleted_instance = true,
     652              :       .have_id_segment = true,
     653              :       .handler = &TMH_private_get_incoming_ID
     654              :     },
     655              :     /* POST /otp-devices: */
     656              :     {
     657              :       .url_prefix = "/otp-devices",
     658              :       .permission = "otp-devices-write",
     659              :       .method = MHD_HTTP_METHOD_POST,
     660              :       .handler = &TMH_private_post_otp_devices
     661              :     },
     662              :     /* GET /otp-devices: */
     663              :     {
     664              :       .url_prefix = "/otp-devices",
     665              :       .permission = "otp-devices-read",
     666              :       .method = MHD_HTTP_METHOD_GET,
     667              :       .handler = &TMH_private_get_otp_devices
     668              :     },
     669              :     /* GET /otp-devices/$ID: */
     670              :     {
     671              :       .url_prefix = "/otp-devices/",
     672              :       .method = MHD_HTTP_METHOD_GET,
     673              :       .permission = "otp-devices-read",
     674              :       .have_id_segment = true,
     675              :       .handler = &TMH_private_get_otp_devices_ID
     676              :     },
     677              :     /* DELETE /otp-devices/$ID: */
     678              :     {
     679              :       .url_prefix = "/otp-devices/",
     680              :       .method = MHD_HTTP_METHOD_DELETE,
     681              :       .permission = "otp-devices-write",
     682              :       .have_id_segment = true,
     683              :       .handler = &TMH_private_delete_otp_devices_ID
     684              :     },
     685              :     /* PATCH /otp-devices/$ID: */
     686              :     {
     687              :       .url_prefix = "/otp-devices/",
     688              :       .method = MHD_HTTP_METHOD_PATCH,
     689              :       .permission = "otp-devices-write",
     690              :       .have_id_segment = true,
     691              :       .handler = &TMH_private_patch_otp_devices_ID
     692              :     },
     693              :     /* POST /templates: */
     694              :     {
     695              :       .url_prefix = "/templates",
     696              :       .method = MHD_HTTP_METHOD_POST,
     697              :       .permission = "templates-write",
     698              :       .handler = &TMH_private_post_templates,
     699              :       /* allow template data of up to 8 MB, that should be plenty;
     700              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     701              :          would require further changes to the allocation logic
     702              :          in the code... */
     703              :       .max_upload = 1024 * 1024 * 8
     704              :     },
     705              :     /* GET /templates: */
     706              :     {
     707              :       .url_prefix = "/templates",
     708              :       .permission = "templates-read",
     709              :       .method = MHD_HTTP_METHOD_GET,
     710              :       .handler = &TMH_private_get_templates
     711              :     },
     712              :     /* GET /templates/$ID/: */
     713              :     {
     714              :       .url_prefix = "/templates/",
     715              :       .method = MHD_HTTP_METHOD_GET,
     716              :       .permission = "templates-read",
     717              :       .have_id_segment = true,
     718              :       .allow_deleted_instance = true,
     719              :       .handler = &TMH_private_get_templates_ID
     720              :     },
     721              :     /* DELETE /templates/$ID/: */
     722              :     {
     723              :       .url_prefix = "/templates/",
     724              :       .method = MHD_HTTP_METHOD_DELETE,
     725              :       .permission = "templates-write",
     726              :       .have_id_segment = true,
     727              :       .allow_deleted_instance = true,
     728              :       .handler = &TMH_private_delete_templates_ID
     729              :     },
     730              :     /* PATCH /templates/$ID/: */
     731              :     {
     732              :       .url_prefix = "/templates/",
     733              :       .method = MHD_HTTP_METHOD_PATCH,
     734              :       .permission = "templates-write",
     735              :       .have_id_segment = true,
     736              :       .allow_deleted_instance = true,
     737              :       .handler = &TMH_private_patch_templates_ID,
     738              :       /* allow template data of up to 8 MB, that should be plenty;
     739              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     740              :          would require further changes to the allocation logic
     741              :          in the code... */
     742              :       .max_upload = 1024 * 1024 * 8
     743              :     },
     744              : 
     745              :     /* POST /pots: */
     746              :     {
     747              :       .url_prefix = "/pots",
     748              :       .method = MHD_HTTP_METHOD_POST,
     749              :       .permission = "pots-write",
     750              :       .handler = &TMH_private_post_pots,
     751              :     },
     752              :     /* GET /pots: */
     753              :     {
     754              :       .url_prefix = "/pots",
     755              :       .permission = "pots-read",
     756              :       .method = MHD_HTTP_METHOD_GET,
     757              :       .handler = &TMH_private_get_pots
     758              :     },
     759              :     /* DELETE /pots/$ID: */
     760              :     {
     761              :       .url_prefix = "/pots/",
     762              :       .method = MHD_HTTP_METHOD_DELETE,
     763              :       .permission = "pots-write",
     764              :       .have_id_segment = true,
     765              :       .handler = &TMH_private_delete_pot
     766              :     },
     767              :     /* PATCH /pots/$ID: */
     768              :     {
     769              :       .url_prefix = "/pots/",
     770              :       .method = MHD_HTTP_METHOD_PATCH,
     771              :       .permission = "pots-write",
     772              :       .have_id_segment = true,
     773              :       .handler = &TMH_private_patch_pot,
     774              :     },
     775              : 
     776              :     /* GET /webhooks: */
     777              :     {
     778              :       .url_prefix = "/webhooks",
     779              :       .permission = "webhooks-read",
     780              :       .method = MHD_HTTP_METHOD_GET,
     781              :       .handler = &TMH_private_get_webhooks
     782              :     },
     783              :     /* POST /webhooks: */
     784              :     {
     785              :       .url_prefix = "/webhooks",
     786              :       .method = MHD_HTTP_METHOD_POST,
     787              :       .permission = "webhooks-write",
     788              :       .handler = &TMH_private_post_webhooks,
     789              :       /* allow webhook data of up to 8 MB, that should be plenty;
     790              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     791              :          would require further changes to the allocation logic
     792              :          in the code... */
     793              :       .max_upload = 1024 * 1024 * 8
     794              :     },
     795              :     /* GET /webhooks/$ID/: */
     796              :     {
     797              :       .url_prefix = "/webhooks/",
     798              :       .method = MHD_HTTP_METHOD_GET,
     799              :       .permission = "webhooks-read",
     800              :       .have_id_segment = true,
     801              :       .allow_deleted_instance = true,
     802              :       .handler = &TMH_private_get_webhooks_ID
     803              :     },
     804              :     /* DELETE /webhooks/$ID/: */
     805              :     {
     806              :       .url_prefix = "/webhooks/",
     807              :       .permission = "webhooks-write",
     808              :       .method = MHD_HTTP_METHOD_DELETE,
     809              :       .have_id_segment = true,
     810              :       .allow_deleted_instance = true,
     811              :       .handler = &TMH_private_delete_webhooks_ID
     812              :     },
     813              :     /* PATCH /webhooks/$ID/: */
     814              :     {
     815              :       .url_prefix = "/webhooks/",
     816              :       .method = MHD_HTTP_METHOD_PATCH,
     817              :       .permission = "webhooks-write",
     818              :       .have_id_segment = true,
     819              :       .allow_deleted_instance = true,
     820              :       .handler = &TMH_private_patch_webhooks_ID,
     821              :       /* allow webhook data of up to 8 MB, that should be plenty;
     822              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
     823              :          would require further changes to the allocation logic
     824              :          in the code... */
     825              :       .max_upload = 1024 * 1024 * 8
     826              :     },
     827              :     /* POST /accounts: */
     828              :     {
     829              :       .url_prefix = "/accounts",
     830              :       .method = MHD_HTTP_METHOD_POST,
     831              :       .permission = "accounts-write",
     832              :       .handler = &TMH_private_post_account,
     833              :       /* allow account details of up to 8 kb, that should be plenty */
     834              :       .max_upload = 1024 * 8
     835              :     },
     836              :     /* POST /accounts/H_WIRE/kycauth: */
     837              :     {
     838              :       .url_prefix = "/accounts/",
     839              :       .url_suffix = "kycauth",
     840              :       .method = MHD_HTTP_METHOD_POST,
     841              :       .have_id_segment = true,
     842              :       .permission = "accounts-read",
     843              :       .handler = &TMH_private_post_accounts_H_WIRE_kycauth,
     844              :       /* allow exchange URL up to 4 kb, that should be plenty */
     845              :       .max_upload = 1024 * 4
     846              :     },
     847              :     /* POST /accept-tos-early: */
     848              :     {
     849              :       .url_prefix = "/accept-tos-early",
     850              :       .method = MHD_HTTP_METHOD_POST,
     851              :       .permission = "accounts-write",
     852              :       .handler = &TMH_private_post_accept_tos_early,
     853              :       /* allow exchange URL plus terms version up to 4 kb */
     854              :       .max_upload = 1024 * 4
     855              :     },
     856              :     /* PATCH /accounts/$H_WIRE: */
     857              :     {
     858              :       .url_prefix = "/accounts/",
     859              :       .method = MHD_HTTP_METHOD_PATCH,
     860              :       .permission = "accounts-write",
     861              :       .handler = &TMH_private_patch_accounts_ID,
     862              :       .have_id_segment = true,
     863              :       /* allow account details of up to 8 kb, that should be plenty */
     864              :       .max_upload = 1024 * 8
     865              :     },
     866              :     /* GET /accounts: */
     867              :     {
     868              :       .url_prefix = "/accounts",
     869              :       .permission = "accounts-read",
     870              :       .method = MHD_HTTP_METHOD_GET,
     871              :       .handler = &TMH_private_get_accounts
     872              :     },
     873              :     /* GET /accounts/$H_WIRE: */
     874              :     {
     875              :       .url_prefix = "/accounts/",
     876              :       .permission = "accounts-read",
     877              :       .method = MHD_HTTP_METHOD_GET,
     878              :       .have_id_segment = true,
     879              :       .handler = &TMH_private_get_accounts_ID
     880              :     },
     881              :     /* DELETE /accounts/$H_WIRE: */
     882              :     {
     883              :       .url_prefix = "/accounts/",
     884              :       .permission = "accounts-write",
     885              :       .method = MHD_HTTP_METHOD_DELETE,
     886              :       .handler = &TMH_private_delete_account_ID,
     887              :       .have_id_segment = true
     888              :     },
     889              :     /* GET /tokens: */
     890              :     {
     891              :       .url_prefix = "/tokens",
     892              :       .permission = "tokens-read",
     893              :       .method = MHD_HTTP_METHOD_GET,
     894              :       .handler = &TMH_private_get_instances_ID_tokens,
     895              :     },
     896              :     /* POST /token: */
     897              :     {
     898              :       .url_prefix = "/token",
     899              :       .permission = "token-refresh",
     900              :       .method = MHD_HTTP_METHOD_POST,
     901              :       .handler = &TMH_private_post_instances_ID_token,
     902              :       /* Body should be tiny. */
     903              :       .max_upload = 1024
     904              :     },
     905              :     /* DELETE /tokens/$SERIAL: */
     906              :     {
     907              :       .url_prefix = "/tokens/",
     908              :       .permission = "tokens-write",
     909              :       .method = MHD_HTTP_METHOD_DELETE,
     910              :       .handler = &TMH_private_delete_instances_ID_token_SERIAL,
     911              :       .have_id_segment = true
     912              :     },
     913              :     /* DELETE /token: */
     914              :     {
     915              :       .url_prefix = "/token",
     916              :       .method = MHD_HTTP_METHOD_DELETE,
     917              :       .handler = &TMH_private_delete_instances_ID_token,
     918              :       .permission = NULL /* No ACL: anyone can delete any token they have */
     919              :     },
     920              :     /* GET /tokenfamilies: */
     921              :     {
     922              :       .url_prefix = "/tokenfamilies",
     923              :       .permission = "tokenfamilies-read",
     924              :       .method = MHD_HTTP_METHOD_GET,
     925              :       .handler = &TMH_private_get_tokenfamilies
     926              :     },
     927              :     /* POST /tokenfamilies: */
     928              :     {
     929              :       .url_prefix = "/tokenfamilies",
     930              :       .permission = "tokenfamilies-write",
     931              :       .method = MHD_HTTP_METHOD_POST,
     932              :       .handler = &TMH_private_post_token_families
     933              :     },
     934              :     /* GET /tokenfamilies/$SLUG/: */
     935              :     {
     936              :       .url_prefix = "/tokenfamilies/",
     937              :       .method = MHD_HTTP_METHOD_GET,
     938              :       .permission = "tokenfamilies-read",
     939              :       .have_id_segment = true,
     940              :       .handler = &TMH_private_get_tokenfamilies_SLUG
     941              :     },
     942              :     /* DELETE /tokenfamilies/$SLUG/: */
     943              :     {
     944              :       .url_prefix = "/tokenfamilies/",
     945              :       .method = MHD_HTTP_METHOD_DELETE,
     946              :       .permission = "tokenfamilies-write",
     947              :       .have_id_segment = true,
     948              :       .handler = &TMH_private_delete_token_families_SLUG
     949              :     },
     950              :     /* PATCH /tokenfamilies/$SLUG/: */
     951              :     {
     952              :       .url_prefix = "/tokenfamilies/",
     953              :       .method = MHD_HTTP_METHOD_PATCH,
     954              :       .permission = "tokenfamilies-write",
     955              :       .have_id_segment = true,
     956              :       .handler = &TMH_private_patch_token_family_SLUG,
     957              :     },
     958              : 
     959              :     /* Reports endpoints */
     960              :     {
     961              :       .url_prefix = "/reports",
     962              :       .method = MHD_HTTP_METHOD_GET,
     963              :       .permission = "reports-read",
     964              :       .handler = &TMH_private_get_reports,
     965              :     },
     966              :     {
     967              :       .url_prefix = "/reports",
     968              :       .method = MHD_HTTP_METHOD_POST,
     969              :       .permission = "reports-write",
     970              :       .handler = &TMH_private_post_reports,
     971              :     },
     972              :     {
     973              :       .url_prefix = "/reports/",
     974              :       .method = MHD_HTTP_METHOD_GET,
     975              :       .handler = &TMH_private_get_report,
     976              :       .permission = "reports-read",
     977              :       .have_id_segment = true,
     978              :     },
     979              :     {
     980              :       .url_prefix = "/reports/",
     981              :       .method = MHD_HTTP_METHOD_PATCH,
     982              :       .handler = &TMH_private_patch_report,
     983              :       .permission = "reports-write",
     984              :       .have_id_segment = true,
     985              :     },
     986              :     {
     987              :       .url_prefix = "/reports/",
     988              :       .method = MHD_HTTP_METHOD_DELETE,
     989              :       .handler = &TMH_private_delete_report,
     990              :       .permission = "reports-write",
     991              :       .have_id_segment = true,
     992              :     },
     993              : 
     994              :     /* Groups endpoints */
     995              :     {
     996              :       .url_prefix = "/groups",
     997              :       .method = MHD_HTTP_METHOD_GET,
     998              :       .permission = "groups-read",
     999              :       .handler = &TMH_private_get_groups,
    1000              :     },
    1001              :     {
    1002              :       .url_prefix = "/groups",
    1003              :       .method = MHD_HTTP_METHOD_POST,
    1004              :       .permission = "groups-write",
    1005              :       .handler = &TMH_private_post_groups,
    1006              :     },
    1007              :     {
    1008              :       .url_prefix = "/groups/",
    1009              :       .method = MHD_HTTP_METHOD_PATCH,
    1010              :       .handler = &TMH_private_patch_group,
    1011              :       .permission = "groups-write",
    1012              :       .have_id_segment = true,
    1013              :     },
    1014              :     {
    1015              :       .url_prefix = "/groups/",
    1016              :       .method = MHD_HTTP_METHOD_DELETE,
    1017              :       .handler = &TMH_private_delete_group,
    1018              :       .permission = "groups-write",
    1019              :       .have_id_segment = true,
    1020              :     },
    1021              : 
    1022              :     /* Money pots endpoints */
    1023              :     {
    1024              :       .url_prefix = "/pots",
    1025              :       .method = MHD_HTTP_METHOD_GET,
    1026              :       .handler = &TMH_private_get_pots,
    1027              :       .permission = "pots-read",
    1028              :     },
    1029              :     {
    1030              :       .url_prefix = "/pots",
    1031              :       .method = MHD_HTTP_METHOD_POST,
    1032              :       .handler = &TMH_private_post_pots,
    1033              :       .permission = "pots-write"
    1034              :     },
    1035              :     {
    1036              :       .url_prefix = "/pots/",
    1037              :       .method = MHD_HTTP_METHOD_GET,
    1038              :       .handler = &TMH_private_get_pot,
    1039              :       .have_id_segment = true,
    1040              :       .permission =  "pots-read",
    1041              :     },
    1042              :     {
    1043              :       .url_prefix = "/pots/",
    1044              :       .method = MHD_HTTP_METHOD_PATCH,
    1045              :       .handler = &TMH_private_patch_pot,
    1046              :       .have_id_segment = true,
    1047              :       .permission = "pots-write"
    1048              :     },
    1049              :     {
    1050              :       .url_prefix = "/pots/",
    1051              :       .method = MHD_HTTP_METHOD_DELETE,
    1052              :       .handler = &TMH_private_delete_pot,
    1053              :       .have_id_segment = true,
    1054              :       .permission = "pots-write"
    1055              :     },
    1056              : 
    1057              :     /* GET /donau */
    1058              :     {
    1059              :       .url_prefix = "/donau",
    1060              :       .method = MHD_HTTP_METHOD_GET,
    1061              :       .permission = "donau-read",
    1062              :       .handler = &TMH_private_get_donau_instances
    1063              :     },
    1064              :     /* POST /donau */
    1065              :     {
    1066              :       .url_prefix = "/donau",
    1067              :       .method = MHD_HTTP_METHOD_POST,
    1068              :       .permission = "donau-write",
    1069              :       .handler = &TMH_private_post_donau_instance
    1070              :     },
    1071              :     /* DELETE /donau/$charity-id */
    1072              :     {
    1073              :       .url_prefix = "/donau/",
    1074              :       .method = MHD_HTTP_METHOD_DELETE,
    1075              :       .have_id_segment = true,
    1076              :       .permission = "donau-write",
    1077              :       .handler = &TMH_private_delete_donau_instance_ID
    1078              :     },
    1079              :     /* GET /statistics-counter/$SLUG: */
    1080              :     {
    1081              :       .url_prefix = "/statistics-counter/",
    1082              :       .method = MHD_HTTP_METHOD_GET,
    1083              :       .permission = "statistics-read",
    1084              :       .have_id_segment = true,
    1085              :       .handler = &TMH_private_get_statistics_counter_SLUG,
    1086              :     },
    1087              :     /* GET /statistics-amount/$SLUG: */
    1088              :     {
    1089              :       .url_prefix = "/statistics-amount/",
    1090              :       .method = MHD_HTTP_METHOD_GET,
    1091              :       .permission = "statistics-read",
    1092              :       .have_id_segment = true,
    1093              :       .handler = &TMH_private_get_statistics_amount_SLUG,
    1094              :     },
    1095              :     /* GET /statistics-report/transactions: */
    1096              :     {
    1097              :       .url_prefix = "/statistics-report/",
    1098              :       .url_suffix = "transactions",
    1099              :       .method = MHD_HTTP_METHOD_GET,
    1100              :       .permission = "statistics-read",
    1101              :       .handler = &TMH_private_get_statistics_report_transactions,
    1102              :     },
    1103              :     {
    1104              :       .url_prefix = NULL
    1105              :     }
    1106              :   };
    1107              :   static struct TMH_RequestHandler public_handlers[] = {
    1108              :     {
    1109              :       /* for "admin" instance, it does not even
    1110              :          have to exist before we give the WebUI */
    1111              :       .url_prefix = "/",
    1112              :       .method = MHD_HTTP_METHOD_GET,
    1113              :       .mime_type = "text/html",
    1114              :       .skip_instance = true,
    1115              :       .default_only = true,
    1116              :       .handler = &spa_redirect,
    1117              :       .response_code = MHD_HTTP_FOUND
    1118              :     },
    1119              :     {
    1120              :       .url_prefix = "/config",
    1121              :       .method = MHD_HTTP_METHOD_GET,
    1122              :       .skip_instance = true,
    1123              :       .default_only = true,
    1124              :       .handler = &MH_handler_config
    1125              :     },
    1126              :     {
    1127              :       .url_prefix = "/exchanges",
    1128              :       .method = MHD_HTTP_METHOD_GET,
    1129              :       .skip_instance = true,
    1130              :       .default_only = true,
    1131              :       .handler = &MH_handler_exchanges
    1132              :     },
    1133              :     {
    1134              :       /* for "normal" instance,s they must exist
    1135              :          before we give the WebUI */
    1136              :       .url_prefix = "/",
    1137              :       .method = MHD_HTTP_METHOD_GET,
    1138              :       .mime_type = "text/html",
    1139              :       .handler = &spa_redirect,
    1140              :       .response_code = MHD_HTTP_FOUND
    1141              :     },
    1142              :     {
    1143              :       .url_prefix = "/webui/",
    1144              :       .method = MHD_HTTP_METHOD_GET,
    1145              :       .mime_type = "text/html",
    1146              :       .skip_instance = true,
    1147              :       .have_id_segment = true,
    1148              :       .handler = &TMH_return_spa,
    1149              :       .response_code = MHD_HTTP_OK
    1150              :     },
    1151              :     {
    1152              :       .url_prefix = "/agpl",
    1153              :       .method = MHD_HTTP_METHOD_GET,
    1154              :       .skip_instance = true,
    1155              :       .handler = &TMH_MHD_handler_agpl_redirect
    1156              :     },
    1157              :     {
    1158              :       .url_prefix = "/agpl",
    1159              :       .method = MHD_HTTP_METHOD_GET,
    1160              :       .skip_instance = true,
    1161              :       .handler = &TMH_MHD_handler_agpl_redirect
    1162              :     },
    1163              :     {
    1164              :       .url_prefix = "/terms",
    1165              :       .method = MHD_HTTP_METHOD_GET,
    1166              :       .skip_instance = true,
    1167              :       .handler = &TMH_handler_terms
    1168              :     },
    1169              :     {
    1170              :       .url_prefix = "/privacy",
    1171              :       .method = MHD_HTTP_METHOD_GET,
    1172              :       .skip_instance = true,
    1173              :       .handler = &TMH_handler_privacy
    1174              :     },
    1175              :     /* Also serve the same /config per instance */
    1176              :     {
    1177              :       .url_prefix = "/config",
    1178              :       .method = MHD_HTTP_METHOD_GET,
    1179              :       .handler = &MH_handler_config
    1180              :     },
    1181              :     /* POST /orders/$ID/abort: */
    1182              :     {
    1183              :       .url_prefix = "/orders/",
    1184              :       .have_id_segment = true,
    1185              :       .url_suffix = "abort",
    1186              :       .method = MHD_HTTP_METHOD_POST,
    1187              :       .handler = &TMH_post_orders_ID_abort,
    1188              :       /* wallet may give us many coins to sign, allow 1 MB of upload
    1189              :          to set a conservative bound for sane wallets */
    1190              :       .max_upload = 1024 * 1024
    1191              :     },
    1192              :     /* POST /orders/$ID/claim: */
    1193              :     {
    1194              :       .url_prefix = "/orders/",
    1195              :       .have_id_segment = true,
    1196              :       .url_suffix = "claim",
    1197              :       .method = MHD_HTTP_METHOD_POST,
    1198              :       .handler = &TMH_post_orders_ID_claim,
    1199              :       /* the body should be pretty small, allow 1 MB of upload
    1200              :          to set a conservative bound for sane wallets */
    1201              :       .max_upload = 1024 * 1024
    1202              :     },
    1203              :     /* POST /orders/$ID/unclaim: */
    1204              :     {
    1205              :       .url_prefix = "/orders/",
    1206              :       .have_id_segment = true,
    1207              :       .url_suffix = "unclaim",
    1208              :       .method = MHD_HTTP_METHOD_POST,
    1209              :       .handler = &TMH_post_orders_ID_unclaim,
    1210              :       /* the body should be very small */
    1211              :       .max_upload = 1024
    1212              :     },
    1213              :     /* POST /orders/$ID/pay: */
    1214              :     {
    1215              :       .url_prefix = "/orders/",
    1216              :       .have_id_segment = true,
    1217              :       .url_suffix = "pay",
    1218              :       .method = MHD_HTTP_METHOD_POST,
    1219              :       .handler = &TMH_post_orders_ID_pay,
    1220              :       /* wallet may give us many coins to sign, allow 1 MB of upload
    1221              :          to set a conservative bound for sane wallets */
    1222              :       .max_upload = 1024 * 1024
    1223              :     },
    1224              :     /* POST /orders/$ID/paid: */
    1225              :     {
    1226              :       .url_prefix = "/orders/",
    1227              :       .have_id_segment = true,
    1228              :       .allow_deleted_instance = true,
    1229              :       .url_suffix = "paid",
    1230              :       .method = MHD_HTTP_METHOD_POST,
    1231              :       .handler = &TMH_post_orders_ID_paid,
    1232              :       /* the body should be pretty small, allow 1 MB of upload
    1233              :          to set a conservative bound for sane wallets */
    1234              :       .max_upload = 1024 * 1024
    1235              :     },
    1236              :     /* POST /orders/$ID/refund: */
    1237              :     {
    1238              :       .url_prefix = "/orders/",
    1239              :       .have_id_segment = true,
    1240              :       .allow_deleted_instance = true,
    1241              :       .url_suffix = "refund",
    1242              :       .method = MHD_HTTP_METHOD_POST,
    1243              :       .handler = &TMH_post_orders_ID_refund,
    1244              :       /* the body should be pretty small, allow 1 MB of upload
    1245              :          to set a conservative bound for sane wallets */
    1246              :       .max_upload = 1024 * 1024
    1247              :     },
    1248              :     /* GET /orders/$ID: */
    1249              :     {
    1250              :       .url_prefix = "/orders/",
    1251              :       .method = MHD_HTTP_METHOD_GET,
    1252              :       .allow_deleted_instance = true,
    1253              :       .have_id_segment = true,
    1254              :       .handler = &TMH_get_orders_ID
    1255              :     },
    1256              :     /* GET /sessions/$ID: */
    1257              :     {
    1258              :       .url_prefix = "/sessions/",
    1259              :       .method = MHD_HTTP_METHOD_GET,
    1260              :       .allow_deleted_instance = true,
    1261              :       .have_id_segment = true,
    1262              :       .handler = &TMH_get_sessions_ID
    1263              :     },
    1264              :     /* GET /static/ *: */
    1265              :     {
    1266              :       .url_prefix = "/static/",
    1267              :       .method = MHD_HTTP_METHOD_GET,
    1268              :       .have_id_segment = true,
    1269              :       .handler = &TMH_return_static
    1270              :     },
    1271              :     /* POST /reports/$ID/ */
    1272              :     {
    1273              :       .url_prefix = "/reports/",
    1274              :       .method = MHD_HTTP_METHOD_POST,
    1275              :       .have_id_segment = true,
    1276              :       .handler = &TMH_post_reports_ID,
    1277              :     },
    1278              :     /* GET /templates/$ID/: */
    1279              :     {
    1280              :       .url_prefix = "/templates/",
    1281              :       .method = MHD_HTTP_METHOD_GET,
    1282              :       .have_id_segment = true,
    1283              :       .handler = &TMH_get_templates_ID
    1284              :     },
    1285              :     /* GET /products/$HASH/image: */
    1286              :     {
    1287              :       .url_prefix = "/products/",
    1288              :       .method = MHD_HTTP_METHOD_GET,
    1289              :       .have_id_segment = true,
    1290              :       .allow_deleted_instance = true,
    1291              :       .url_suffix = "image",
    1292              :       .handler = &TMH_get_products_image
    1293              :     },
    1294              :     /* POST /templates/$ID: */
    1295              :     {
    1296              :       .url_prefix = "/templates/",
    1297              :       .method = MHD_HTTP_METHOD_POST,
    1298              :       .have_id_segment = true,
    1299              :       .handler = &TMH_post_using_templates_ID,
    1300              :       .max_upload = 1024 * 1024
    1301              :     },
    1302              :     /* POST /challenge/$ID: */
    1303              :     {
    1304              :       .url_prefix = "/challenge/",
    1305              :       .method = MHD_HTTP_METHOD_POST,
    1306              :       .have_id_segment = true,
    1307              :       .handler = &TMH_post_challenge_ID,
    1308              :       .max_upload = 1024
    1309              :     },
    1310              :     /* POST /challenge/$ID/confirm: */
    1311              :     {
    1312              :       .url_prefix = "/challenge/",
    1313              :       .method = MHD_HTTP_METHOD_POST,
    1314              :       .have_id_segment = true,
    1315              :       .url_suffix = "confirm",
    1316              :       .handler = &TMH_post_challenge_ID_confirm,
    1317              :       .max_upload = 1024
    1318              :     },
    1319              :     /* POST /instances */
    1320              :     {
    1321              :       .url_prefix = "/instances",
    1322              :       .method = MHD_HTTP_METHOD_POST,
    1323              :       .skip_instance = true,
    1324              :       .default_only = true,
    1325              :       .handler = &TMH_public_post_instances,
    1326              :       /* allow instance data of up to 8 MB, that should be plenty;
    1327              :          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    1328              :          would require further changes to the allocation logic
    1329              :          in the code... */
    1330              :       .max_upload = 1024 * 1024 * 8
    1331              :     },
    1332              :     /* POST /forgot-password: */
    1333              :     {
    1334              :       .url_prefix = "/forgot-password",
    1335              :       .method = MHD_HTTP_METHOD_POST,
    1336              :       .handler = &TMH_public_post_instances_ID_auth,
    1337              :       /* Body should be pretty small. */
    1338              :       .max_upload = 1024 * 1024
    1339              :     },
    1340              : 
    1341              :     {
    1342              :       .url_prefix = "*",
    1343              :       .method = MHD_HTTP_METHOD_OPTIONS,
    1344              :       .handler = &handle_server_options
    1345              :     },
    1346              :     {
    1347              :       .url_prefix = NULL
    1348              :     }
    1349              :   };
    1350         1144 :   const char *management_prefix = "/management/";
    1351         1144 :   const char *private_prefix = "/private/";
    1352         1144 :   const char *url = *urlp;
    1353              :   struct TMH_RequestHandler *handlers;
    1354              : 
    1355         1144 :   *is_public = false; /* ensure safe default */
    1356         1144 :   if ( (0 == strncmp (url,
    1357              :                       management_prefix,
    1358              :                       strlen (management_prefix))) )
    1359              :   {
    1360           68 :     handlers = management_handlers;
    1361           68 :     *urlp = url + strlen (management_prefix) - 1;
    1362              :   }
    1363         1076 :   else if ( (0 == strncmp (url,
    1364              :                            private_prefix,
    1365          335 :                            strlen (private_prefix))) ||
    1366          335 :             (0 == strcmp (url,
    1367              :                           "/private")) )
    1368              :   {
    1369          756 :     handlers = private_handlers;
    1370         1512 :     if (0 == strcmp (url,
    1371              :                      "/private"))
    1372           15 :       *urlp = "/";
    1373              :     else
    1374          741 :       *urlp = url + strlen (private_prefix) - 1;
    1375              :   }
    1376              :   else
    1377              :   {
    1378          320 :     handlers = public_handlers;
    1379          320 :     *is_public = true;
    1380              :   }
    1381         1144 :   return handlers;
    1382              : }
    1383              : 
    1384              : 
    1385              : /**
    1386              :  * Checks if the @a rh matches the given (parsed) URL.
    1387              :  *
    1388              :  * @param rh handler to compare against
    1389              :  * @param url the main URL (without "/private/" prefix, if any)
    1390              :  * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
    1391              :  * @param infix_url infix text, i.e. "$ORDER_ID".
    1392              :  * @param infix_strlen length of the string in @a infix_url
    1393              :  * @param suffix_url suffix, i.e. "/refund", including the "/"
    1394              :  * @param suffix_strlen number of characters in @a suffix_url
    1395              :  * @return true if @a rh matches this request
    1396              :  */
    1397              : static bool
    1398        27974 : prefix_match (const struct TMH_RequestHandler *rh,
    1399              :               const char *url,
    1400              :               size_t prefix_strlen,
    1401              :               const char *infix_url,
    1402              :               size_t infix_strlen,
    1403              :               const char *suffix_url,
    1404              :               size_t suffix_strlen)
    1405              : {
    1406        27974 :   if ( (prefix_strlen != strlen (rh->url_prefix)) ||
    1407         4136 :        (0 != memcmp (url,
    1408         4136 :                      rh->url_prefix,
    1409              :                      prefix_strlen)) )
    1410        25591 :     return false;
    1411         2383 :   if (! rh->have_id_segment)
    1412              :   {
    1413              :     /* Require /$PREFIX/$SUFFIX or /$PREFIX */
    1414          673 :     if (NULL != suffix_url)
    1415            0 :       return false;       /* too many segments to match */
    1416          673 :     if ( (NULL == infix_url)   /* either or */
    1417          673 :          ^ (NULL == rh->url_suffix) )
    1418            0 :       return false;       /* suffix existence mismatch */
    1419              :     /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
    1420          673 :     if ( (NULL != infix_url) &&
    1421            1 :          ( (infix_strlen != strlen (rh->url_suffix)) ||
    1422            1 :            (0 != memcmp (infix_url,
    1423            1 :                          rh->url_suffix,
    1424              :                          infix_strlen)) ) )
    1425            0 :       return false;       /* cannot use infix as suffix: content mismatch */
    1426              :   }
    1427              :   else
    1428              :   {
    1429              :     /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
    1430         1710 :     if (NULL == infix_url)
    1431            0 :       return false;       /* infix existence mismatch */
    1432         1710 :     if ( ( (NULL == suffix_url)
    1433         1710 :            ^ (NULL == rh->url_suffix) ) )
    1434          446 :       return false;       /* suffix existence mismatch */
    1435         1264 :     if ( (NULL != suffix_url) &&
    1436          680 :          ( (suffix_strlen != strlen (rh->url_suffix)) ||
    1437          389 :            (0 != memcmp (suffix_url,
    1438          389 :                          rh->url_suffix,
    1439              :                          suffix_strlen)) ) )
    1440          398 :       return false;       /* suffix content mismatch */
    1441              :   }
    1442         1539 :   return true;
    1443              : }
    1444              : 
    1445              : 
    1446              : /**
    1447              :  * Identify the handler of the request from the @a url and @a method
    1448              :  *
    1449              :  * @param[in,out] hc handler context to update with applicable handler
    1450              :  * @param handlers array of handlers to consider
    1451              :  * @param url URL to match against the handlers
    1452              :  * @param method HTTP access method to consider
    1453              :  * @param use_admin set to true if we are using the admin instance
    1454              :  * @return #GNUNET_OK on success,
    1455              :  *         #GNUNET_NO if an error was queued (return #MHD_YES)
    1456              :  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
    1457              :  */
    1458              : static enum GNUNET_GenericReturnValue
    1459         1144 : identify_handler (struct TMH_HandlerContext *hc,
    1460              :                   const struct TMH_RequestHandler *handlers,
    1461              :                   const char *url,
    1462              :                   const char *method,
    1463              :                   bool use_admin)
    1464              : {
    1465              :   size_t prefix_strlen;   /* i.e. 8 for "/orders/", or 7 for "/config" */
    1466         1144 :   const char *infix_url = NULL;   /* i.e. "$ORDER_ID", no '/'-es */
    1467         1144 :   size_t infix_strlen = 0;   /* number of characters in infix_url */
    1468         1144 :   const char *suffix_url = NULL;   /* i.e. "refund", excludes '/' at the beginning */
    1469         1144 :   size_t suffix_strlen = 0;   /* number of characters in suffix_url */
    1470              : 
    1471         1144 :   if (0 == strcasecmp (method,
    1472              :                        MHD_HTTP_METHOD_HEAD))
    1473            0 :     method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
    1474         1144 :   if (0 == strcmp (url,
    1475              :                    ""))
    1476            0 :     url = "/"; /* code below does not like empty string */
    1477              : 
    1478              :   /* parse the URL into the three different components */
    1479              :   {
    1480              :     const char *slash;
    1481              : 
    1482         1144 :     slash = strchr (&url[1], '/');
    1483         1144 :     if (NULL == slash)
    1484              :     {
    1485              :       /* the prefix was everything */
    1486          492 :       prefix_strlen = strlen (url);
    1487              :     }
    1488              :     else
    1489              :     {
    1490          652 :       prefix_strlen = slash - url + 1;   /* includes both '/'-es if present! */
    1491          652 :       infix_url = slash + 1;
    1492          652 :       slash = strchr (infix_url, '/');
    1493          652 :       if (NULL == slash)
    1494              :       {
    1495              :         /* the infix was the rest */
    1496          370 :         infix_strlen = strlen (infix_url);
    1497              :       }
    1498              :       else
    1499              :       {
    1500          282 :         infix_strlen = slash - infix_url;   /* excludes both '/'-es */
    1501          282 :         suffix_url = slash + 1;   /* skip the '/' */
    1502          282 :         suffix_strlen = strlen (suffix_url);
    1503              :       }
    1504              :       /* free any previously set infix in case the request is being
    1505              :          re-dispatched (e.g. via POST /reports/$REPORT_ID), otherwise
    1506              :          the earlier infix would be leaked */
    1507          652 :       GNUNET_free (hc->infix);
    1508          652 :       hc->infix = GNUNET_strndup (infix_url,
    1509              :                                   infix_strlen);
    1510              :     }
    1511              :   }
    1512              : 
    1513              :   /* find matching handler */
    1514              :   {
    1515         1144 :     bool url_found = false;
    1516              : 
    1517        28016 :     for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
    1518              :     {
    1519        28016 :       const struct TMH_RequestHandler *rh = &handlers[i];
    1520              : 
    1521        28016 :       if (rh->default_only && (! use_admin))
    1522           42 :         continue;
    1523        27974 :       if (! prefix_match (rh,
    1524              :                           url,
    1525              :                           prefix_strlen,
    1526              :                           infix_url,
    1527              :                           infix_strlen,
    1528              :                           suffix_url,
    1529              :                           suffix_strlen))
    1530        26435 :         continue;
    1531         1539 :       url_found = true;
    1532         1539 :       if (0 == strcasecmp (method,
    1533              :                            MHD_HTTP_METHOD_OPTIONS))
    1534              :       {
    1535              :         return (MHD_YES ==
    1536            1 :                 TALER_MHD_reply_cors_preflight (hc->connection))
    1537              :             ? GNUNET_NO
    1538            1 :             : GNUNET_SYSERR;
    1539              :       }
    1540         1538 :       if ( (rh->method != NULL) &&
    1541         1538 :            (0 != strcasecmp (method,
    1542         1538 :                              rh->method)) )
    1543          395 :         continue;
    1544         1143 :       hc->rh = rh;
    1545         1143 :       break;
    1546              :     }
    1547              :     /* Handle HTTP 405: METHOD NOT ALLOWED case */
    1548         1143 :     if ( (NULL == hc->rh) &&
    1549              :          (url_found) )
    1550              :     {
    1551              :       struct MHD_Response *reply;
    1552              :       enum MHD_Result ret;
    1553            0 :       char *allowed = NULL;
    1554              : 
    1555            0 :       GNUNET_break_op (0);
    1556              :       /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
    1557            0 :       for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
    1558              :       {
    1559            0 :         const struct TMH_RequestHandler *rh = &handlers[i];
    1560              : 
    1561            0 :         if (rh->default_only && (! use_admin))
    1562            0 :           continue;
    1563            0 :         if (! prefix_match (rh,
    1564              :                             url,
    1565              :                             prefix_strlen,
    1566              :                             infix_url,
    1567              :                             infix_strlen,
    1568              :                             suffix_url,
    1569              :                             suffix_strlen))
    1570            0 :           continue;
    1571            0 :         if (NULL == allowed)
    1572              :         {
    1573            0 :           allowed = GNUNET_strdup (rh->method);
    1574              :         }
    1575              :         else
    1576              :         {
    1577              :           char *tmp;
    1578              : 
    1579            0 :           GNUNET_asprintf (&tmp,
    1580              :                            "%s, %s",
    1581              :                            allowed,
    1582            0 :                            rh->method);
    1583            0 :           GNUNET_free (allowed);
    1584            0 :           allowed = tmp;
    1585              :         }
    1586            0 :         if (0 == strcasecmp (rh->method,
    1587              :                              MHD_HTTP_METHOD_GET))
    1588              :         {
    1589              :           char *tmp;
    1590              : 
    1591            0 :           GNUNET_asprintf (&tmp,
    1592              :                            "%s, %s",
    1593              :                            allowed,
    1594              :                            MHD_HTTP_METHOD_HEAD);
    1595            0 :           GNUNET_free (allowed);
    1596            0 :           allowed = tmp;
    1597              :         }
    1598              :       }
    1599            0 :       reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
    1600              :                                     method);
    1601            0 :       GNUNET_break (MHD_YES ==
    1602              :                     MHD_add_response_header (reply,
    1603              :                                              MHD_HTTP_HEADER_ALLOW,
    1604              :                                              allowed));
    1605            0 :       GNUNET_free (allowed);
    1606            0 :       ret = MHD_queue_response (hc->connection,
    1607              :                                 MHD_HTTP_METHOD_NOT_ALLOWED,
    1608              :                                 reply);
    1609            0 :       MHD_destroy_response (reply);
    1610              :       return (MHD_YES == ret)
    1611              :           ? GNUNET_NO
    1612            0 :           : GNUNET_SYSERR;
    1613              :     }
    1614         1143 :     if (NULL == hc->rh)
    1615              :     {
    1616            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1617              :                   "Endpoint `%s' not known\n",
    1618              :                   hc->url);
    1619              :       return (MHD_YES ==
    1620            0 :               TALER_MHD_reply_with_error (hc->connection,
    1621              :                                           MHD_HTTP_NOT_FOUND,
    1622              :                                           TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    1623              :                                           hc->url))
    1624              :           ? GNUNET_NO
    1625            0 :           : GNUNET_SYSERR;
    1626              :     }
    1627              :   }
    1628         1143 :   return GNUNET_OK;
    1629              : }
    1630              : 
    1631              : 
    1632              : enum GNUNET_GenericReturnValue
    1633         1144 : TMH_dispatch_request (struct TMH_HandlerContext *hc,
    1634              :                       const char *url,
    1635              :                       const char *method,
    1636              :                       bool use_admin,
    1637              :                       bool *is_public)
    1638              : {
    1639              :   const struct TMH_RequestHandler *handlers;
    1640              : 
    1641         1144 :   *is_public = false;
    1642         1144 :   handlers = determine_handler_group (&url,
    1643              :                                       is_public);
    1644         1144 :   return identify_handler (hc,
    1645              :                            handlers,
    1646              :                            url,
    1647              :                            method,
    1648              :                            use_admin);
    1649              : }
        

Generated by: LCOV version 2.0-1