taler_mhd_lib.h (41604B)
1 /* 2 This file is part of TALER 3 Copyright (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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler_mhd_lib.h 18 * @brief API for generating MHD replies 19 * @author Florian Dold 20 * @author Benedikt Mueller 21 * @author Christian Grothoff 22 */ 23 #ifndef TALER_MHD_LIB_H 24 #define TALER_MHD_LIB_H 25 26 #include <jansson.h> 27 #include <microhttpd.h> 28 #include <gnunet/gnunet_json_lib.h> 29 #include <gnunet/gnunet_mhd_compat.h> 30 #include <taler/taler_error_codes.h> 31 #include <taler/taler_util.h> 32 33 34 /** 35 * Maximum POST request size. 36 */ 37 #define TALER_MHD_REQUEST_BUFFER_MAX (1024 * 1024 * 16) 38 39 40 /** 41 * Global options for response generation. 42 */ 43 enum TALER_MHD_GlobalOptions 44 { 45 46 /** 47 * Use defaults. 48 */ 49 TALER_MHD_GO_NONE = 0, 50 51 /** 52 * Add "Connection: Close" header. 53 */ 54 TALER_MHD_GO_FORCE_CONNECTION_CLOSE = 1, 55 56 /** 57 * Disable use of compression, even if the client 58 * supports it. 59 */ 60 TALER_MHD_GO_DISABLE_COMPRESSION = 2 61 62 }; 63 64 65 #if MHD_VERSION < 0x00097701 66 #define MHD_create_response_from_buffer_static(s, b) \ 67 MHD_create_response_from_buffer (s, \ 68 (const char *) b, \ 69 MHD_RESPMEM_PERSISTENT) 70 #endif 71 72 73 /** 74 * Find out if an MHD connection is using HTTPS (either 75 * directly or via proxy). 76 * 77 * @param connection MHD connection 78 * @returns #GNUNET_YES if the MHD connection is using https, 79 * #GNUNET_NO if the MHD connection is using http, 80 * #GNUNET_SYSERR if the connection type couldn't be determined 81 */ 82 enum GNUNET_GenericReturnValue 83 TALER_mhd_is_https (struct MHD_Connection *connection); 84 85 86 /** 87 * Convert query argument to @a yna value. 88 * 89 * @param connection connection to take query argument from 90 * @param arg argument to try for 91 * @param default_val value to assign if the argument is not present 92 * @param[out] yna value to set 93 * @return true on success, false if the parameter was malformed 94 */ 95 bool 96 TALER_MHD_arg_to_yna (struct MHD_Connection *connection, 97 const char *arg, 98 enum TALER_EXCHANGE_YesNoAll default_val, 99 enum TALER_EXCHANGE_YesNoAll *yna); 100 101 102 /** 103 * Set global options for response generation within libtalermhd. 104 * 105 * @param go global options to use 106 */ 107 void 108 TALER_MHD_setup (enum TALER_MHD_GlobalOptions go); 109 110 111 /** 112 * Add headers we want to return in every response. Useful for testing, like 113 * if we want to always close connections. 114 * 115 * @param response response to modify 116 * @param allow_store set to true to NOT add a "Cache-Control" 117 * directive that prevents caches and browsers from storing the data; 118 * if false, we set "Cache-Control: no-store" (privacy by default); 119 * set to true if the response contains no personal data 120 */ 121 void 122 TALER_MHD_add_global_headers (struct MHD_Response *response, 123 bool allow_store); 124 125 126 /** 127 * Try to compress a response body. Updates @a buf and @a buf_size. 128 * 129 * @param[in,out] buf pointer to body to compress 130 * @param[in,out] buf_size pointer to initial size of @a buf 131 * @return #MHD_YES if @a buf was compressed 132 */ 133 MHD_RESULT 134 TALER_MHD_body_compress (void **buf, 135 size_t *buf_size); 136 137 138 /** 139 * List of compression types we check for. Larger numeric values 140 * indicate a preferred algorithm. 141 */ 142 enum TALER_MHD_CompressionType 143 { 144 /** 145 * Compression is not supported. 146 */ 147 TALER_MHD_CT_NONE = 0, 148 149 /** 150 * Deflate compression supported. 151 */ 152 TALER_MHD_CT_DEFLATE, 153 154 /** 155 * gzip compression supported. 156 */ 157 TALER_MHD_CT_GZIP, 158 159 /** 160 * zstd compression supported. 161 */ 162 TALER_MHD_CT_ZSTD, 163 164 /** 165 * End of list marker. 166 */ 167 TALER_MHD_CT_MAX 168 }; 169 170 171 /** 172 * What type of HTTP compression is supported by the client? 173 * 174 * @param connection connection to check 175 * @param max maximum compression level to check for 176 * @return #MHD_YES if 'deflate' compression is allowed 177 */ 178 enum TALER_MHD_CompressionType 179 TALER_MHD_can_compress (struct MHD_Connection *connection, 180 enum TALER_MHD_CompressionType max); 181 182 183 /** 184 * Send JSON object as response. 185 * 186 * @param connection the MHD connection 187 * @param json the json object 188 * @param response_code the http response code 189 * @return MHD result code 190 */ 191 MHD_RESULT 192 TALER_MHD_reply_json (struct MHD_Connection *connection, 193 const json_t *json, 194 unsigned int response_code); 195 196 197 /** 198 * Send JSON object as response, and free the @a json 199 * object. 200 * 201 * @param connection the MHD connection 202 * @param json the json object (freed!) 203 * @param response_code the http response code 204 * @return MHD result code 205 */ 206 MHD_RESULT 207 TALER_MHD_reply_json_steal (struct MHD_Connection *connection, 208 json_t *json, 209 unsigned int response_code); 210 211 212 /** 213 * Function to call to handle the request by building a JSON 214 * reply from varargs. 215 * 216 * @param connection the MHD connection to handle 217 * @param response_code HTTP response code to use 218 * @param ... varargs of JSON pack specification 219 * @return MHD result code 220 */ 221 #define TALER_MHD_REPLY_JSON_PACK(connection,response_code,...) \ 222 TALER_MHD_reply_json_steal (connection, GNUNET_JSON_PACK (__VA_ARGS__), \ 223 response_code) 224 225 226 /** 227 * Send a response indicating an error. 228 * 229 * @param connection the MHD connection to use 230 * @param ec error code uniquely identifying the error 231 * @param http_status HTTP status code to use 232 * @param detail additional optional detail about the error 233 * @return a MHD result code 234 */ 235 MHD_RESULT 236 TALER_MHD_reply_with_error (struct MHD_Connection *connection, 237 unsigned int http_status, 238 enum TALER_ErrorCode ec, 239 const char *detail); 240 241 242 /** 243 * Send a response indicating an error. The HTTP status code is 244 * to be derived from the @a ec. 245 * 246 * @param connection the MHD connection to use 247 * @param ec error code uniquely identifying the error 248 * @param detail additional optional detail about the error 249 * @return a MHD result code 250 */ 251 MHD_RESULT 252 TALER_MHD_reply_with_ec (struct MHD_Connection *connection, 253 enum TALER_ErrorCode ec, 254 const char *detail); 255 256 257 /** 258 * Produce HTTP "Date:" header. 259 * 260 * @param at time to write to @a date 261 * @param[out] date where to write the header, with 262 * at least 128 bytes available space. 263 */ 264 void 265 TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at, 266 char date[128]); 267 268 269 /** 270 * Make JSON response object. 271 * 272 * @param json the json object 273 * @return MHD response object 274 */ 275 struct MHD_Response * 276 TALER_MHD_make_json (const json_t *json); 277 278 279 /** 280 * Make JSON response object and free @a json. 281 * 282 * @param json the json object, freed. 283 * @return MHD response object 284 */ 285 struct MHD_Response * 286 TALER_MHD_make_json_steal (json_t *json); 287 288 289 /** 290 * Make JSON response object. 291 * 292 * @param ... varargs 293 * @return MHD response object 294 */ 295 #define TALER_MHD_MAKE_JSON_PACK(...) \ 296 TALER_MHD_make_json_steal (GNUNET_JSON_PACK (__VA_ARGS__)) 297 298 299 /** 300 * Pack Taler error code @a ec and associated hint into a 301 * JSON object. 302 * 303 * @param ec error code to pack 304 * @return packer array entries (two!) 305 */ 306 #define TALER_MHD_PACK_EC(ec) \ 307 GNUNET_JSON_pack_uint64 ("code", ec), \ 308 GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)) 309 310 /** 311 * Create a response indicating an internal error. 312 * 313 * @param ec error code to return 314 * @param detail additional optional detail about the error, can be NULL 315 * @return a MHD response object 316 */ 317 struct MHD_Response * 318 TALER_MHD_make_error (enum TALER_ErrorCode ec, 319 const char *detail); 320 321 322 /** 323 * Send a response indicating that the request was too big. 324 * 325 * @param connection the MHD connection to use 326 * @return a MHD result code 327 */ 328 MHD_RESULT 329 TALER_MHD_reply_request_too_large (struct MHD_Connection *connection); 330 331 332 /** 333 * Function to call to handle the request by sending 334 * back a redirect to the AGPL source code. 335 * 336 * @param connection the MHD connection to handle 337 * @param url where to redirect for the sources 338 * @return MHD result code 339 */ 340 MHD_RESULT 341 TALER_MHD_reply_agpl (struct MHD_Connection *connection, 342 const char *url); 343 344 345 /** 346 * Function to call to handle the request by sending 347 * back static data. 348 * 349 * @param connection the MHD connection to handle 350 * @param http_status status code to return 351 * @param mime_type content-type to use 352 * @param body response payload 353 * @param body_size number of bytes in @a body 354 * @return MHD result code 355 */ 356 MHD_RESULT 357 TALER_MHD_reply_static (struct MHD_Connection *connection, 358 unsigned int http_status, 359 const char *mime_type, 360 const char *body, 361 size_t body_size); 362 363 364 /** 365 * Process a POST request containing a JSON object. This 366 * function realizes an MHD POST processor that will 367 * (incrementally) process JSON data uploaded to the HTTP 368 * server. It will store the required state in the 369 * "connection_cls", which must be cleaned up using 370 * #TALER_MHD_parse_post_cleanup_callback(). 371 * 372 * @param connection the MHD connection 373 * @param con_cls the closure (points to a `struct Buffer *`) 374 * @param upload_data the POST data 375 * @param upload_data_size number of bytes in @a upload_data 376 * @param json the JSON object for a completed request 377 * @return 378 * #GNUNET_YES if json object was parsed or at least 379 * may be parsed in the future (call again); 380 * `*json` will be NULL if we need to be called again, 381 * and non-NULL if we are done. 382 * #GNUNET_NO is request incomplete or invalid 383 * (error message was generated) 384 * #GNUNET_SYSERR on internal error 385 * (we could not even queue an error message, 386 * close HTTP session with MHD_NO) 387 */ 388 enum GNUNET_GenericReturnValue 389 TALER_MHD_parse_post_json (struct MHD_Connection *connection, 390 void **con_cls, 391 const char *upload_data, 392 size_t *upload_data_size, 393 json_t **json); 394 395 396 /** 397 * Function called whenever we are done with a request 398 * to clean up our state. 399 * 400 * @param con_cls value as it was left by 401 * #TALER_MHD_parse_post_json(), to be cleaned up 402 */ 403 void 404 TALER_MHD_parse_post_cleanup_callback (void *con_cls); 405 406 407 /** 408 * Parse JSON object into components based on the given field 409 * specification. If parsing fails, we return an HTTP 410 * status code of 400 (#MHD_HTTP_BAD_REQUEST). 411 * 412 * @param connection the connection to send an error response to 413 * @param root the JSON node to start the navigation at. 414 * @param spec field specification for the parser 415 * @return 416 * #GNUNET_YES if navigation was successful (caller is responsible 417 * for freeing allocated variable-size data using 418 * GNUNET_JSON_parse_free() when done) 419 * #GNUNET_NO if json is malformed, error response was generated 420 * #GNUNET_SYSERR on internal error 421 */ 422 enum GNUNET_GenericReturnValue 423 TALER_MHD_parse_json_data (struct MHD_Connection *connection, 424 const json_t *root, 425 struct GNUNET_JSON_Specification *spec); 426 427 428 /** 429 * Parse JSON object that we (the server!) generated into components based on 430 * the given field specification. The difference to 431 * #TALER_MHD_parse_json_data() is that this function will fail 432 * with an HTTP failure of 500 (internal server error) in case 433 * parsing fails, instead of blaming it on the client with a 434 * 400 (#MHD_HTTP_BAD_REQUEST). 435 * 436 * @param connection the connection to send an error response to 437 * @param root the JSON node to start the navigation at. 438 * @param spec field specification for the parser 439 * @return 440 * #GNUNET_YES if navigation was successful (caller is responsible 441 * for freeing allocated variable-size data using 442 * GNUNET_JSON_parse_free() when done) 443 * #GNUNET_NO if json is malformed, error response was generated 444 * #GNUNET_SYSERR on internal error 445 */ 446 enum GNUNET_GenericReturnValue 447 TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection, 448 const json_t *root, 449 struct GNUNET_JSON_Specification *spec); 450 451 452 /** 453 * Parse JSON array into components based on the given field 454 * specification. Generates error response on parse errors. 455 * 456 * @param connection the connection to send an error response to 457 * @param root the JSON node to start the navigation at. 458 * @param[in,out] spec field specification for the parser 459 * @param ... -1-terminated list of array offsets of type 'int' 460 * @return 461 * #GNUNET_YES if navigation was successful (caller is responsible 462 * for freeing allocated variable-size data using 463 * GNUNET_JSON_parse_free() when done) 464 * #GNUNET_NO if json is malformed, error response was generated 465 * #GNUNET_SYSERR on internal error 466 */ 467 enum GNUNET_GenericReturnValue 468 TALER_MHD_parse_json_array (struct MHD_Connection *connection, 469 const json_t *root, 470 struct GNUNET_JSON_Specification *spec, 471 ...); 472 473 474 /** 475 * Extract optional relative time argument from request. 476 * 477 * @param connection the MHD connection 478 * @param label name of the argument to parse 479 * @param[out] duration set to #GNUNET_TIME_UNIT_ZERO if there was no duration argument given 480 * @return #GNUNET_OK on success, #GNUNET_NO if an 481 * error was returned on @a connection (caller should return #MHD_YES) and 482 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 483 */ 484 enum GNUNET_GenericReturnValue 485 TALER_MHD_parse_request_arg_rel_time (struct MHD_Connection *connection, 486 const char *label, 487 struct GNUNET_TIME_Relative *duration); 488 489 490 /** 491 * Extract optional relative time argument from request. 492 * Macro that *returns* #MHD_YES/#MHD_NO if the @a label 493 * argument existed but failed to parse. 494 * 495 * @param connection the MHD connection 496 * @param label label to check for 497 * @param[out] duration set to #GNUNET_TIME_UNIT_ZERO if there was no duration given 498 */ 499 #define TALER_MHD_parse_request_rel_time(connection,label,duration) \ 500 do { \ 501 switch (TALER_MHD_parse_request_arg_rel_time (connection, \ 502 label, \ 503 duration)) \ 504 { \ 505 case GNUNET_SYSERR: \ 506 GNUNET_break (0); \ 507 return MHD_NO; \ 508 case GNUNET_NO: \ 509 GNUNET_break_op (0); \ 510 return MHD_YES; \ 511 case GNUNET_OK: \ 512 break; \ 513 } \ 514 } while (0) 515 516 517 /** 518 * Extract optional "timeout_ms" argument from request. 519 * 520 * @param connection the MHD connection 521 * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, 522 * the current time plus the value given under "timeout_ms" otherwise 523 * @return #GNUNET_OK on success, #GNUNET_NO if an 524 * error was returned on @a connection (caller should return #MHD_YES) and 525 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 526 */ 527 enum GNUNET_GenericReturnValue 528 TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection, 529 struct GNUNET_TIME_Absolute *expiration); 530 531 532 /** 533 * Extract optional "timeout_ms" argument from request. 534 * Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms" 535 * argument existed but failed to parse. 536 * 537 * @param connection the MHD connection 538 * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, 539 * the current time plus the value given under "timeout_ms" otherwise 540 */ 541 #define TALER_MHD_parse_request_timeout(connection,expiration) \ 542 do { \ 543 switch (TALER_MHD_parse_request_arg_timeout (connection, \ 544 expiration)) \ 545 { \ 546 case GNUNET_SYSERR: \ 547 GNUNET_break (0); \ 548 return MHD_NO; \ 549 case GNUNET_NO: \ 550 GNUNET_break_op (0); \ 551 return MHD_YES; \ 552 case GNUNET_OK: \ 553 break; \ 554 } \ 555 } while (0) 556 557 558 /** 559 * Extract optional timestamp argument from request. 560 * 561 * @param connection the MHD connection 562 * @param fname name of the argument to parse 563 * @param[out] ts set to #GNUNET_TIME_UNIT_ZERO_TS if there was no timestamp 564 * @return #GNUNET_OK on success, #GNUNET_NO if an 565 * error was returned on @a connection (caller should return #MHD_YES) and 566 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 567 */ 568 enum GNUNET_GenericReturnValue 569 TALER_MHD_parse_request_arg_timestamp (struct MHD_Connection *connection, 570 const char *fname, 571 struct GNUNET_TIME_Timestamp *ts); 572 573 574 /** 575 * Extract optional timestamp argument from request. 576 * Macro that *returns* #MHD_YES/#MHD_NO if the timestamp 577 * argument existed but failed to parse. 578 * 579 * @param connection the MHD connection 580 * @param fname name of the argument 581 * @param[out] ts set to #GNUNET_TIME_UNIT_ZERO_TS if there was no timestamp 582 */ 583 #define TALER_MHD_parse_request_timestamp(connection,fname,ts) \ 584 do { \ 585 switch (TALER_MHD_parse_request_arg_timestamp (connection, \ 586 fname, \ 587 ts)) \ 588 { \ 589 case GNUNET_SYSERR: \ 590 GNUNET_break (0); \ 591 return MHD_NO; \ 592 case GNUNET_NO: \ 593 GNUNET_break_op (0); \ 594 return MHD_YES; \ 595 case GNUNET_OK: \ 596 break; \ 597 } \ 598 } while (0) 599 600 601 /** 602 * Extract optional "yes/no/all" argument from request. 603 * Macro that *returns* #MHD_YES/#MHD_NO if the 604 * argument existed but failed to parse. 605 * 606 * @param connection the MHD connection 607 * @param name name of the query parameter to parse 608 * @param def default value to set if absent 609 * @param[out] ret set to the yes/no/all value 610 */ 611 #define TALER_MHD_parse_request_yna(connection,name,def,ret) \ 612 do { \ 613 if (! (TALER_MHD_arg_to_yna (connection, \ 614 name, \ 615 def, \ 616 ret)) ) \ 617 { \ 618 GNUNET_break_op (0); \ 619 return TALER_MHD_reply_with_error ( \ 620 connection, \ 621 MHD_HTTP_BAD_REQUEST, \ 622 TALER_EC_GENERIC_PARAMETER_MALFORMED, \ 623 name); \ 624 } \ 625 } while (0) 626 627 /** 628 * Extract optional numeric limit argument from request. 629 * 630 * @param connection the MHD connection 631 * @param name name of the query parameter 632 * @param[out] off set to the offset, unchanged if the 633 * option was not given 634 * @return #GNUNET_OK on success, 635 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 636 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 637 */ 638 enum GNUNET_GenericReturnValue 639 TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection, 640 const char *name, 641 uint64_t *off); 642 643 644 /** 645 * Extract optional numeric argument from request. 646 * Macro that *returns* #MHD_YES/#MHD_NO if the 647 * requested argument existed but failed to parse. 648 * 649 * @param connection the MHD connection 650 * @param name name of the argument to parse 651 * @param[out] off set to the given numeric value, 652 * unchanged if value was not specified 653 */ 654 #define TALER_MHD_parse_request_number(connection,name,off) \ 655 do { \ 656 switch (TALER_MHD_parse_request_arg_number (connection, \ 657 name, \ 658 off)) \ 659 { \ 660 case GNUNET_SYSERR: \ 661 GNUNET_break (0); \ 662 return MHD_NO; \ 663 case GNUNET_NO: \ 664 GNUNET_break_op (0); \ 665 return MHD_YES; \ 666 case GNUNET_OK: \ 667 break; \ 668 } \ 669 } while (0) 670 671 672 /** 673 * Extract optional signed numeric limit argument from request. 674 * 675 * @param connection the MHD connection 676 * @param name name of the query parameter 677 * @param[out] val set to the signed value, unchanged if the 678 * option was not given 679 * @return #GNUNET_OK on success, 680 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 681 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 682 */ 683 enum GNUNET_GenericReturnValue 684 TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection, 685 const char *name, 686 int64_t *val); 687 688 689 /** 690 * Extract optional numeric argument from request. 691 * Macro that *returns* #MHD_YES/#MHD_NO if the 692 * requested argument existed but failed to parse. 693 * 694 * @param connection the MHD connection 695 * @param name name of the argument to parse 696 * @param[out] val set to the given numeric value, 697 * unchanged if value was not specified 698 */ 699 #define TALER_MHD_parse_request_snumber(connection,name,val) \ 700 do { \ 701 switch (TALER_MHD_parse_request_arg_snumber (connection, \ 702 name, \ 703 val)) \ 704 { \ 705 case GNUNET_SYSERR: \ 706 GNUNET_break (0); \ 707 return MHD_NO; \ 708 case GNUNET_NO: \ 709 GNUNET_break_op (0); \ 710 return MHD_YES; \ 711 case GNUNET_OK: \ 712 break; \ 713 } \ 714 } while (0) 715 716 717 /** 718 * Extract optional amount argument from request. 719 * 720 * @param connection the MHD connection 721 * @param name name of the query parameter 722 * @param[out] val set to the amount, unchanged if the 723 * option was not given 724 * @return #GNUNET_OK on success, 725 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 726 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 727 */ 728 enum GNUNET_GenericReturnValue 729 TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection, 730 const char *name, 731 struct TALER_Amount *val); 732 733 734 /** 735 * Extract optional amount argument from request. Macro that *returns* 736 * #MHD_YES/#MHD_NO if the requested argument existed but failed to parse. 737 * 738 * @param connection the MHD connection 739 * @param name name of the argument to parse 740 * @param[out] val set to the given amount, 741 * unchanged if value was not specified 742 */ 743 #define TALER_MHD_parse_request_amount(connection,name,val) \ 744 do { \ 745 switch (TALER_MHD_parse_request_arg_amount (connection, \ 746 name, \ 747 val)) \ 748 { \ 749 case GNUNET_SYSERR: \ 750 GNUNET_break (0); \ 751 return MHD_NO; \ 752 case GNUNET_NO: \ 753 GNUNET_break_op (0); \ 754 return MHD_YES; \ 755 case GNUNET_OK: \ 756 break; \ 757 } \ 758 } while (0) 759 760 761 /** 762 * Extract fixed-size base32crockford encoded data from request argument. 763 * 764 * Queues an error response to the connection if the parameter is missing or 765 * invalid. 766 * 767 * @param connection the MHD connection 768 * @param param_name the name of the parameter with the key 769 * @param[out] out_data pointer to store the result 770 * @param out_size expected size of @a out_data 771 * @param[out] present set to true if argument was found 772 * @return 773 * #GNUNET_YES if the the argument is present 774 * #GNUNET_NO if the argument is malformed 775 * #GNUNET_SYSERR on internal error (error response could not be sent) 776 */ 777 enum GNUNET_GenericReturnValue 778 TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, 779 const char *param_name, 780 void *out_data, 781 size_t out_size, 782 bool *present); 783 784 785 /** 786 * Extract fixed-size base32crockford encoded data from request header. 787 * 788 * Queues an error response to the connection if the parameter is missing or 789 * invalid. 790 * 791 * @param connection the MHD connection 792 * @param header_name the name of the HTTP header with the value 793 * @param[out] out_data pointer to store the result 794 * @param out_size expected size of @a out_data 795 * @param[out] present set to true if argument was found 796 * @return 797 * #GNUNET_YES if the the argument is present 798 * #GNUNET_NO if the argument is malformed 799 * #GNUNET_SYSERR on internal error (error response could not be sent) 800 */ 801 enum GNUNET_GenericReturnValue 802 TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, 803 const char *header_name, 804 void *out_data, 805 size_t out_size, 806 bool *present); 807 808 /** 809 * Extract fixed-size base32crockford encoded data from request. 810 * 811 * @param connection the MHD connection 812 * @param name the name of the parameter with the key 813 * @param[out] val pointer to store the result, type must determine size 814 * @param[in,out] required pass true to require presence of this argument; if 'false' 815 * set to true if the argument was found 816 * @return 817 * #GNUNET_YES if the the argument is present 818 * #GNUNET_NO if the argument is absent or malformed 819 * #GNUNET_SYSERR on internal error (error response could not be sent) 820 */ 821 #define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \ 822 do { \ 823 bool p; \ 824 switch (TALER_MHD_parse_request_arg_data (connection, name, \ 825 val, sizeof (*val), &p)) \ 826 { \ 827 case GNUNET_SYSERR: \ 828 GNUNET_break (0); \ 829 return MHD_NO; \ 830 case GNUNET_NO: \ 831 GNUNET_break_op (0); \ 832 return MHD_YES; \ 833 case GNUNET_OK: \ 834 if (required & (! p)) \ 835 { \ 836 GNUNET_break_op (0); \ 837 return TALER_MHD_reply_with_error ( \ 838 connection, \ 839 MHD_HTTP_BAD_REQUEST, \ 840 TALER_EC_GENERIC_PARAMETER_MISSING, \ 841 name); \ 842 } \ 843 required = p; \ 844 break; \ 845 } \ 846 } while (0) 847 848 849 /** 850 * Extract required fixed-size base32crockford encoded data from request. 851 * 852 * @param connection the MHD connection 853 * @param name the name of the parameter with the key 854 * @param[out] val pointer to store the result, type must determine size 855 * @return 856 * #GNUNET_YES if the the argument is present 857 * #GNUNET_NO if the argument is absent or malformed 858 * #GNUNET_SYSERR on internal error (error response could not be sent) 859 */ 860 #define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \ 861 do { \ 862 bool b = true; \ 863 TALER_MHD_parse_request_arg_auto (connection,name,val,b); \ 864 } while (0) 865 866 /** 867 * Extract fixed-size base32crockford encoded data from request. 868 * 869 * @param connection the MHD connection 870 * @param name the name of the header with the key 871 * @param[out] val pointer to store the result, type must determine size 872 * @param[in,out] required pass true to require presence of this argument; if 'false' 873 * set to true if the argument was found 874 * @return 875 * #GNUNET_YES if the the argument is present 876 * #GNUNET_NO if the argument is absent or malformed 877 * #GNUNET_SYSERR on internal error (error response could not be sent) 878 */ 879 #define TALER_MHD_parse_request_header_auto(connection,name,val,required) \ 880 do { \ 881 bool p; \ 882 switch (TALER_MHD_parse_request_header_data (connection, name, \ 883 val, sizeof (*val), \ 884 &p)) \ 885 { \ 886 case GNUNET_SYSERR: \ 887 GNUNET_break (0); \ 888 return MHD_NO; \ 889 case GNUNET_NO: \ 890 GNUNET_break_op (0); \ 891 return MHD_YES; \ 892 case GNUNET_OK: \ 893 if (required & (! p)) \ 894 return TALER_MHD_reply_with_error ( \ 895 connection, \ 896 MHD_HTTP_BAD_REQUEST, \ 897 TALER_EC_GENERIC_PARAMETER_MISSING, \ 898 name); \ 899 required = p; \ 900 break; \ 901 } \ 902 } while (0) 903 904 905 /** 906 * Extract required fixed-size base32crockford encoded data from request. 907 * 908 * @param connection the MHD connection 909 * @param name the name of the header with the key 910 * @param[out] val pointer to store the result, type must determine size 911 * @return 912 * #GNUNET_YES if the the argument is present 913 * #GNUNET_NO if the argument is absent or malformed 914 * #GNUNET_SYSERR on internal error (error response could not be sent) 915 */ 916 #define TALER_MHD_parse_request_header_auto_t(connection,name,val) \ 917 do { \ 918 bool b = true; \ 919 TALER_MHD_parse_request_header_auto (connection,name,val,b); \ 920 } while (0) 921 922 923 /** 924 * Check that the 'Content-Length' header is giving 925 * a length below @a max_len. If not, return an 926 * appropriate error response and return the 927 * correct #MHD_YES/#MHD_NO value from this function. 928 * 929 * @param connection the MHD connection 930 * @param max_len maximum allowed content length 931 * @return 932 * #GNUNET_YES if the the argument is present 933 * #GNUNET_NO if the argument is absent or malformed 934 * #GNUNET_SYSERR on internal error (error response could not be sent) 935 */ 936 enum GNUNET_GenericReturnValue 937 TALER_MHD_check_content_length_ (struct MHD_Connection *connection, 938 unsigned long long max_len); 939 940 941 /** 942 * Check that the 'Content-Length' header is giving 943 * a length below @a max_len. If not, return an 944 * appropriate error response and return the 945 * correct #MHD_YES/#MHD_NO value from this function. 946 * 947 * @param connection the MHD connection 948 * @param max_len maximum allowed content length 949 */ 950 #define TALER_MHD_check_content_length(connection,max_len) \ 951 do { \ 952 switch (TALER_MHD_check_content_length_ (connection, max_len)) \ 953 { \ 954 case GNUNET_SYSERR: \ 955 GNUNET_break (0); \ 956 return MHD_NO; \ 957 case GNUNET_NO: \ 958 GNUNET_break_op (0); \ 959 return MHD_YES; \ 960 case GNUNET_OK: \ 961 break; \ 962 } \ 963 } while (0) 964 965 966 /** 967 * Function called for logging by MHD. 968 * 969 * @param cls closure, NULL 970 * @param fm format string (`printf()`-style) 971 * @param ap arguments to @a fm 972 */ 973 void 974 TALER_MHD_handle_logs (void *cls, 975 const char *fm, 976 va_list ap); 977 978 979 /** 980 * Function called on each successfully bound listen 981 * socket by #TALER_MHD_listen_bind(). 982 * 983 * @param cls closure 984 * @param fd bound listen socket (must be used and eventually 985 * closed by the callee). Never -1. 986 */ 987 typedef void 988 (*TALER_MHD_ListenSocketCallback)(void *cls, 989 int fd); 990 991 992 /** 993 * Bind a listen socket to the UNIX domain path, 994 * or the TCP port(s) and IP address(es) configured, 995 * or return to @a cb the inherited sockets from systemd, 996 * all depending on what was specified in @a cfg in 997 * the section named @a section. 998 * 999 * @param cfg configuration to parse 1000 * @param section configuration section to use 1001 * @param cb function to call with each bound socket 1002 * @param cb_cls closure for @a cb 1003 * @return #GNUNET_OK on success (all configured sockets were bound) 1004 * #GNUNET_NO if some configured binding failed but the config is OK, 1005 * note that some listen sockets may have been created 1006 * #GNUNET_SYSERR if the configuration is invalid 1007 */ 1008 enum GNUNET_GenericReturnValue 1009 TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg, 1010 const char *section, 1011 TALER_MHD_ListenSocketCallback cb, 1012 void *cb_cls); 1013 1014 1015 /** 1016 * Start to run an event loop for @a daemon. 1017 * Only one daemon can be running per process 1018 * using this API. 1019 * 1020 * @param daemon the MHD service to run 1021 */ 1022 void 1023 TALER_MHD_daemon_start (struct MHD_Daemon *daemon); 1024 1025 1026 /** 1027 * Stop running the event loops of all MHD daemons. 1028 */ 1029 void 1030 TALER_MHD_daemons_halt (void); 1031 1032 1033 /** 1034 * Stop accepting new connections on all MHD daemons 1035 * (and close the listen sockets). 1036 */ 1037 void 1038 TALER_MHD_daemons_quiesce (void); 1039 1040 1041 /** 1042 * Destroy all state associated with all MHD daemons. 1043 */ 1044 void 1045 TALER_MHD_daemons_destroy (void); 1046 1047 /** 1048 * Trigger all MHD daemons that were running. Needed when 1049 * a connection was resumed. 1050 */ 1051 void 1052 TALER_MHD_daemon_trigger (void); 1053 1054 1055 /** 1056 * Prepared responses for legal documents 1057 * (terms of service, privacy policy). 1058 */ 1059 struct TALER_MHD_Legal; 1060 1061 1062 /** 1063 * Load set of legal documents as specified in @a cfg in section @a section 1064 * where the Etag is given under the @a tagoption and the directory under 1065 * the @a diroption. 1066 * 1067 * @param cfg configuration to use 1068 * @param section section to load values from 1069 * @param diroption name of the option with the 1070 * path to the legal documents 1071 * @param tagoption name of the files to use 1072 * for the legal documents and the Etag 1073 * @return NULL on error 1074 */ 1075 struct TALER_MHD_Legal * 1076 TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg, 1077 const char *section, 1078 const char *diroption, 1079 const char *tagoption); 1080 1081 1082 /** 1083 * Free set of legal documents 1084 * 1085 * @param legal legal documents to free 1086 */ 1087 void 1088 TALER_MHD_legal_free (struct TALER_MHD_Legal *legal); 1089 1090 1091 /** 1092 * Generate a response with a legal document in 1093 * the format and language of the user's choosing. 1094 * 1095 * @param conn HTTP connection to handle 1096 * @param legal legal document to serve 1097 * @return MHD result code 1098 */ 1099 MHD_RESULT 1100 TALER_MHD_reply_legal (struct MHD_Connection *conn, 1101 struct TALER_MHD_Legal *legal); 1102 1103 1104 /** 1105 * Send back a "204 No Content" response with headers 1106 * for the CORS pre-flight request. 1107 * 1108 * @param connection the MHD connection 1109 * @return MHD result code 1110 */ 1111 MHD_RESULT 1112 TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection); 1113 1114 1115 /** 1116 * Load SPA files from @a dir, relative to the project's 1117 * data directory. 1118 * 1119 * 1120 * @param pd project data to use to determine the parent directory 1121 * @param dir directory suffix to append to our data directory with the location of the files of the SPA 1122 * @return handle to serve static files from @a dir 1123 */ 1124 struct TALER_MHD_Spa * 1125 TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd, 1126 const char *dir); 1127 1128 /** 1129 * Load SPA files from absolute path to directory @a dn. 1130 * 1131 * 1132 * @param dn directory with the location of the files of the SPA, 1133 * should be an absolute path. 1134 * @return handle to serve static files from @a dir 1135 */ 1136 struct TALER_MHD_Spa * 1137 TALER_MHD_spa_load_dir (const char *dn); 1138 1139 1140 /** 1141 * Release resources used by SPA handler. 1142 * 1143 * @param[in] spa data structure to release 1144 */ 1145 void 1146 TALER_MHD_spa_free (struct TALER_MHD_Spa *spa); 1147 1148 1149 /** 1150 * Handle HTTP request for files in a @a spa. Generates 1151 * a 404 if no file at @a path does exists. 1152 * 1153 * @param spa the SPA to serve files from 1154 * @param connection HTTP connection to return data on 1155 * @param path request path to match against the @a spa 1156 * @return MHD status code to give to MHD 1157 */ 1158 MHD_RESULT 1159 TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa, 1160 struct MHD_Connection *connection, 1161 const char *path); 1162 1163 1164 /** 1165 * Information about a document #TALER_MHD_typst() should output. 1166 */ 1167 struct TALER_MHD_TypstDocument 1168 { 1169 /** 1170 * Form name, used to determine the Typst template to use. 1171 * NULL if @e data is a JSON string with a PDF to inline. 1172 */ 1173 const char *form_name; 1174 1175 /** 1176 * Form data. 1177 */ 1178 const json_t *data; 1179 }; 1180 1181 1182 /** 1183 * Context for generating PDF responses. 1184 */ 1185 struct TALER_MHD_TypstContext; 1186 1187 1188 /** 1189 * Result from a #TALER_MHD_typst() operation. 1190 */ 1191 struct TALER_MHD_TypstResponse 1192 { 1193 1194 /** 1195 * Error status of the operation. 1196 */ 1197 enum TALER_ErrorCode ec; 1198 1199 /** 1200 * Details depending on @e ec. 1201 */ 1202 union 1203 { 1204 /** 1205 * Hint if @e ec is not #TALER_EC_NONE. 1206 */ 1207 const char *hint; 1208 1209 /** 1210 * Filename with the result if @e ec is #TALER_EC_NONE. 1211 */ 1212 const char *filename; 1213 } details; 1214 1215 }; 1216 1217 /** 1218 * Function called with the result of a #TALER_MHD_typst() operation. 1219 * 1220 * @param cls closure 1221 * @param tr result of the operation 1222 */ 1223 typedef void 1224 (*TALER_MHD_TypstResultCallback) (void *cls, 1225 const struct TALER_MHD_TypstResponse *tr); 1226 1227 1228 /** 1229 * Generate PDFs using Typst and combine them using pdftk. The 1230 * file will be returned to @a cb and after @a cb returns all data 1231 * will be deleted from the local disk. 1232 * 1233 * @param cfg configuration to use (where to find Typst templates) 1234 * @param remove_on_exit should the directory be removed when done? 1235 * @param cfg_section_name name of the configuration section to use 1236 * @param num_documents length of the @a docs array 1237 * @param docs list of documents to combine into one large PDF 1238 * @param cb function to call with the resulting file(name) 1239 * @param cb_cls closure for @a cb 1240 * @return NULL on error 1241 */ 1242 struct TALER_MHD_TypstContext * 1243 TALER_MHD_typst ( 1244 const struct GNUNET_CONFIGURATION_Handle *cfg, 1245 bool remove_on_exit, 1246 const char *cfg_section_name, 1247 unsigned int num_documents, 1248 const struct TALER_MHD_TypstDocument docs[static num_documents], 1249 TALER_MHD_TypstResultCallback cb, 1250 void *cb_cls); 1251 1252 1253 /** 1254 * Abort all typst response generation processes. 1255 * To be used when the system is shutting down. 1256 */ 1257 void 1258 TALER_MHD_typst_cancel (struct TALER_MHD_TypstContext *tc); 1259 1260 1261 /** 1262 * Create HTTP response from the PDF file at @a filename 1263 * 1264 * @param filename file to return as PDF 1265 * @return NULL on error 1266 */ 1267 struct MHD_Response * 1268 TALER_MHD_response_from_pdf_file (const char *filename); 1269 1270 1271 #endif