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