exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler_mhd_lib.h (43785B)


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