merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_dispatcher.c (52414B)


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