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