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 }