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

Generated by: LCOV version 2.0-1