merchant

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

taler-merchant-httpd_dispatcher.c (56462B)


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