donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit e028dbc25cabe76425fff32243e774b57ddf7d59
parent 2d465315b5eca681ad9c68441e341d83641d15ba
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date:   Thu,  7 Dec 2023 17:00:18 +0100

[donau] uncomment most of donau-httpd.c

Diffstat:
Msrc/donau/Makefile.am | 3+++
Msrc/donau/donau-httpd.c | 2690+++++++++++++++++++++++++++++++++++++++----------------------------------------
2 files changed, 1345 insertions(+), 1348 deletions(-)

diff --git a/src/donau/Makefile.am b/src/donau/Makefile.am @@ -30,6 +30,9 @@ donau_httpd_LDADD = \ $(top_builddir)/src/donaudb/libtalerdonaudb.la \ $(top_builddir)/src/util/libtalerdonauutil.la \ -lmicrohttpd \ + -ltalermhd \ + -ltalerutil \ + -ltalertemplating \ -lgnunetcurl \ -lgnunetutil \ -lgnunetjson \ diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -36,126 +36,126 @@ #include <gnunet/gnunet_mhd_compat.h> #include "donau_util.h" -// /** -// * Backlog for listen operation on unix domain sockets. -// */ -// #define UNIX_BACKLOG 50 - -// /** -// * How often will we try to connect to the database before giving up? -// */ -// #define MAX_DB_RETRIES 5 - -// /** -// * Above what request latency do we start to log? -// */ -/* #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \ +/** + * Backlog for listen operation on unix domain sockets. + */ +#define UNIX_BACKLOG 50 + +/** + * How often will we try to connect to the database before giving up? + */ +#define MAX_DB_RETRIES 5 + +/** + * Above what request latency do we start to log? + */ + #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \ GNUNET_TIME_UNIT_MILLISECONDS, 500) -*/ -// /** -// * Are clients allowed to request /keys for times other than the -// * current time? Allowing this could be abused in a DoS-attack -// * as building new /keys responses is expensive. Should only be -// * enabled for testcases, development and test systems. -// */ -// int TEH_allow_keys_timetravel; + +/** + * Are clients allowed to request /keys for times other than the + * current time? Allowing this could be abused in a DoS-attack + * as building new /keys responses is expensive. Should only be + * enabled for testcases, development and test systems. + */ +int TEH_allow_keys_timetravel; /** * Should we allow two HTTPDs to bind to the same port? */ static int allow_address_reuse; -// /** -// * The donau's configuration (global) -// */ -// const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; - -// /** -// * Configuration of age restriction -// * -// * Set after loading the library, enabled in database event handler. -// */ -// bool TEH_age_restriction_enabled = false; -// struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0}; - -// /** -// * Handle to the HTTP server. -// */ -// static struct MHD_Daemon *mhd; - -// /** -// * How long is caching /keys allowed at most? (global) -// */ -// struct GNUNET_TIME_Relative TEH_max_keys_caching; - -// /** -// * How long is the delay before we close reserves? -// */ -// struct GNUNET_TIME_Relative TEH_reserve_closing_delay; - -// /** -// * Master public key (according to the -// * configuration in the donau directory). (global) -// */ -// struct TALER_MasterPublicKeyP TEH_master_public_key; - -// /** -// * Key used to encrypt KYC attribute data in our database. -// */ -// struct TALER_AttributeEncryptionKeyP TEH_attribute_key; - -// /** -// * Our DB plugin. (global) -// */ -// struct DONAUDB_Plugin *TEH_plugin; - -// /** -// * Absolute STEFAN parameter. -// */ -// struct TALER_Amount TEH_stefan_abs; - -// /** -// * Logarithmic STEFAN parameter. -// */ -// struct TALER_Amount TEH_stefan_log; - -// /** -// * Linear STEFAN parameter. -// */ -// struct TALER_Amount TEH_stefan_lin; - -// /** -// * Default number of fractional digits to render -// * amounts with. -// */ -// unsigned int TEH_currency_fraction_digits; - -// /** -// * Our currency. -// */ -// char *TEH_currency; - -// /** -// * Name of the KYC-AML-trigger evaluation binary. -// */ -// char *TEH_kyc_aml_trigger; - -// /** -// * Option set to #GNUNET_YES if rewards are enabled. -// */ -// int TEH_enable_rewards; - -// /** -// * What is the largest amount we allow a peer to -// * merge into a reserve before always triggering -// * an AML check? -// */ -// struct TALER_Amount TEH_aml_threshold; - -// /** -// * Our base URL. -// */ -// char *TEH_base_url; +/** + * The donau's configuration (global) + */ +const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; + +/** + * Configuration of age restriction + * + * Set after loading the library, enabled in database event handler. + */ +bool TEH_age_restriction_enabled = false; +struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0}; + +/** + * Handle to the HTTP server. + */ +static struct MHD_Daemon *mhd; + +/** + * How long is caching /keys allowed at most? (global) + */ +struct GNUNET_TIME_Relative TEH_max_keys_caching; + +/** + * How long is the delay before we close reserves? + */ +struct GNUNET_TIME_Relative TEH_reserve_closing_delay; + +/** + * Master public key (according to the + * configuration in the donau directory). (global) + */ +struct TALER_MasterPublicKeyP TEH_master_public_key; + +/** + * Key used to encrypt KYC attribute data in our database. + */ +struct TALER_AttributeEncryptionKeyP TEH_attribute_key; + +/** + * Our DB plugin. (global) + */ +struct DONAUDB_Plugin *TEH_plugin; + +/** + * Absolute STEFAN parameter. + */ +struct TALER_Amount TEH_stefan_abs; + +/** + * Logarithmic STEFAN parameter. + */ +struct TALER_Amount TEH_stefan_log; + +/** + * Linear STEFAN parameter. + */ +struct TALER_Amount TEH_stefan_lin; + +/** + * Default number of fractional digits to render + * amounts with. + */ +unsigned int TEH_currency_fraction_digits; + +/** + * Our currency. + */ +char *TEH_currency; + +/** + * Name of the KYC-AML-trigger evaluation binary. + */ +char *TEH_kyc_aml_trigger; + +/** + * Option set to #GNUNET_YES if rewards are enabled. + */ +int TEH_enable_rewards; + +/** + * What is the largest amount we allow a peer to + * merge into a reserve before always triggering + * an AML check? + */ +struct TALER_Amount TEH_aml_threshold; + +/** + * Our base URL. + */ +char *TEH_base_url; /** * Default timeout in seconds for HTTP requests. @@ -167,84 +167,84 @@ static unsigned int connection_timeout = 30; */ static int connection_close; -// /** -// * -I command-line flag given? -// */ -// int TEH_check_invariants_flag; - -// /** -// * True if we should commit suicide once all active -// * connections are finished. -// */ -// bool TEH_suicide; - -// /** -// * Signature of the configuration of all enabled extensions, -// * signed by the donau's offline master key with purpose -// * TALER_SIGNATURE_MASTER_EXTENSION. -// */ -// struct TALER_MasterSignatureP TEH_extensions_sig; -// bool TEH_extensions_signed = false; - -// /** -// * Value to return from main() -// */ -// static int global_ret; - -// /** -// * Port to run the daemon on. -// */ -// static uint16_t serve_port; - -// /** -// * Counter for the number of requests this HTTP has processed so far. -// */ -// static unsigned long long req_count; - -// /** -// * Counter for the number of open connections. -// */ -// static unsigned long long active_connections; - -// /** -// * Limit for the number of requests this HTTP may process before restarting. -// * (This was added as one way of dealing with unavoidable memory fragmentation -// * happening slowly over time.) -// */ -// static unsigned long long req_max; - -// /** -// * Context for all CURL operations (useful to the event loop) -// */ -// struct GNUNET_CURL_Context *TEH_curl_ctx; - -// /** -// * Context for integrating #TEH_curl_ctx with the -// * GNUnet event loop. -// */ -// static struct GNUNET_CURL_RescheduleContext *donau_curl_rc; - -// /** -// * Signature of functions that handle operations on coins. -// * -// * @param connection the MHD connection to handle -// * @param coin_pub the public key of the coin -// * @param root uploaded JSON data -// * @return MHD result code -// */ -// typedef MHD_RESULT -// (*CoinOpHandler)(struct MHD_Connection *connection, -// const struct TALER_CoinSpendPublicKeyP *coin_pub, -// const json_t *root); - - -// /** -// * Generate a 404 "not found" reply on @a connection with -// * the hint @a details. -// * -// * @param connection where to send the reply on -// * @param details details for the error message, can be NULL -// */ +/** + * -I command-line flag given? + */ +int TEH_check_invariants_flag; + +/** + * True if we should commit suicide once all active + * connections are finished. + */ +bool TEH_suicide; + +/** + * Signature of the configuration of all enabled extensions, + * signed by the donau's offline master key with purpose + * TALER_SIGNATURE_MASTER_EXTENSION. + */ +struct TALER_MasterSignatureP TEH_extensions_sig; +bool TEH_extensions_signed = false; + +/** + * Value to return from main() + */ +static int global_ret; + +/** + * Port to run the daemon on. + */ +static uint16_t serve_port; + +/** + * Counter for the number of requests this HTTP has processed so far. + */ +//static unsigned long long req_count; + +/** + * Counter for the number of open connections. + */ +static unsigned long long active_connections; + +/** + * Limit for the number of requests this HTTP may process before restarting. + * (This was added as one way of dealing with unavoidable memory fragmentation + * happening slowly over time.) + */ +static unsigned long long req_max; + +/** + * Context for all CURL operations (useful to the event loop) + */ +struct GNUNET_CURL_Context *TEH_curl_ctx; + +/** + * Context for integrating #TEH_curl_ctx with the + * GNUnet event loop. + */ +static struct GNUNET_CURL_RescheduleContext *donau_curl_rc; + +/** + * Signature of functions that handle operations on coins. + * + * @param connection the MHD connection to handle + * @param coin_pub the public key of the coin + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*CoinOpHandler)(struct MHD_Connection *connection, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const json_t *root); + + +/** + * Generate a 404 "not found" reply on @a connection with + * the hint @a details. + * + * @param connection where to send the reply on + * @param details details for the error message, can be NULL + */ // static MHD_RESULT // r404 (struct MHD_Connection *connection, // const char *details) @@ -256,1195 +256,1190 @@ static int connection_close; // } -// /** -// * Function called whenever MHD is done with a request. If the -// * request was a POST, we may have stored a `struct Buffer *` in the -// * @a con_cls that might still need to be cleaned up. Call the -// * respective function to free the memory. -// * -// * @param cls client-defined closure -// * @param connection connection handle -// * @param con_cls value as set by the last call to -// * the #MHD_AccessHandlerCallback -// * @param toe reason for request termination -// * @see #MHD_OPTION_NOTIFY_COMPLETED -// * @ingroup request -// */ -// static void -// handle_mhd_completion_callback (void *cls, -// struct MHD_Connection *connection, -// void **con_cls, -// enum MHD_RequestTerminationCode toe) -// { -// struct TEH_RequestContext *rc = *con_cls; -// struct GNUNET_AsyncScopeSave old_scope; - -// (void) cls; -// if (NULL == rc) -// return; -// GNUNET_async_scope_enter (&rc->async_scope_id, -// &old_scope); -// check_suicide (); -// TEH_check_invariants (); -// if (NULL != rc->rh_cleaner) -// rc->rh_cleaner (rc); -// TEH_check_invariants (); -// { -// #if MHD_VERSION >= 0x00097304 -// const union MHD_ConnectionInfo *ci; -// unsigned int http_status = 0; - -// ci = MHD_get_connection_info (connection, -// MHD_CONNECTION_INFO_HTTP_STATUS); -// if (NULL != ci) -// http_status = ci->http_status; -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Request for `%s' completed with HTTP status %u (%d)\n", -// rc->url, -// http_status, -// toe); -// #else -// (void) connection; -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Request for `%s' completed (%d)\n", -// rc->url, -// toe); -// #endif -// } - -// TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context); -// /* Sanity-check that we didn't leave any transactions hanging */ -// // GNUNET_break (GNUNET_OK == -// // TEH_plugin->preflight (TEH_plugin->cls)); -// { -// struct GNUNET_TIME_Relative latency; - -// latency = GNUNET_TIME_absolute_get_duration (rc->start_time); -// if (latency.rel_value_us > -// WARN_LATENCY.rel_value_us) -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Request for `%s' took %s\n", -// rc->url, -// GNUNET_STRINGS_relative_time_to_string (latency, -// GNUNET_YES)); -// } -// GNUNET_free (rc); -// *con_cls = NULL; -// GNUNET_async_scope_restore (&old_scope); -// } +/** + * Function called whenever MHD is done with a request. If the + * request was a POST, we may have stored a `struct Buffer *` in the + * @a con_cls that might still need to be cleaned up. Call the + * respective function to free the memory. + * + * @param cls client-defined closure + * @param connection connection handle + * @param con_cls value as set by the last call to + * the #MHD_AccessHandlerCallback + * @param toe reason for request termination + * @see #MHD_OPTION_NOTIFY_COMPLETED + * @ingroup request + */ +static void +handle_mhd_completion_callback (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + struct TEH_RequestContext *rc = *con_cls; + struct GNUNET_AsyncScopeSave old_scope; + + (void) cls; + if (NULL == rc) + return; + GNUNET_async_scope_enter (&rc->async_scope_id, + &old_scope); + //check_suicide (); + //TEH_check_invariants (); + if (NULL != rc->rh_cleaner) + rc->rh_cleaner (rc); + //TEH_check_invariants (); + { +#if MHD_VERSION >= 0x00097304 + const union MHD_ConnectionInfo *ci; + unsigned int http_status = 0; + + ci = MHD_get_connection_info (connection, + MHD_CONNECTION_INFO_HTTP_STATUS); + if (NULL != ci) + http_status = ci->http_status; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Request for `%s' completed with HTTP status %u (%d)\n", + rc->url, + http_status, + toe); +#else + (void) connection; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Request for `%s' completed (%d)\n", + rc->url, + toe); +#endif + } + + TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context); + /* Sanity-check that we didn't leave any transactions hanging */ + GNUNET_break (GNUNET_OK == + TEH_plugin->preflight (TEH_plugin->cls)); + { + struct GNUNET_TIME_Relative latency; + + latency = GNUNET_TIME_absolute_get_duration (rc->start_time); + if (latency.rel_value_us > + WARN_LATENCY.rel_value_us) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Request for `%s' took %s\n", + rc->url, + GNUNET_STRINGS_relative_time_to_string (latency, + GNUNET_YES)); + } + GNUNET_free (rc); + *con_cls = NULL; + GNUNET_async_scope_restore (&old_scope); +} -// /** -// * We found a request handler responsible for handling a request. Parse the -// * @a upload_data (if applicable) and the @a url and call the -// * handler. -// * -// * @param rc request context -// * @param url rest of the URL to parse -// * @param upload_data upload data to parse (if available) -// * @param[in,out] upload_data_size number of bytes in @a upload_data -// * @return MHD result code -// */ -// static MHD_RESULT -// proceed_with_handler (struct TEH_RequestContext *rc, -// const char *url, -// const char *upload_data, -// size_t *upload_data_size) -// { -// const struct TEH_RequestHandler *rh = rc->rh; -// const char *args[rh->nargs + 2]; -// size_t ulen = strlen (url) + 1; -// json_t *root = NULL; -// MHD_RESULT ret; - -// /* We do check for "ulen" here, because we'll later stack-allocate a buffer -// of that size and don't want to enable malicious clients to cause us -// huge stack allocations. */ -// if (ulen > 512) -// { -// /* 512 is simply "big enough", as it is bigger than "6 * 54", -// which is the longest URL format we ever get (for -// /deposits/). The value should be adjusted if we ever define protocol -// endpoints with plausibly longer inputs. */ -// GNUNET_break_op (0); -// return TALER_MHD_reply_with_error (rc->connection, -// MHD_HTTP_URI_TOO_LONG, -// TALER_EC_GENERIC_URI_TOO_LONG, -// url); -// } - -// /* All POST endpoints come with a body in JSON format. So we parse -// the JSON here. */ -// if (0 == strcasecmp (rh->method, -// MHD_HTTP_METHOD_POST)) -// { -// enum GNUNET_GenericReturnValue res; - -// res = TALER_MHD_parse_post_json (rc->connection, -// &rc->opaque_post_parsing_context, -// upload_data, -// upload_data_size, -// &root); -// if (GNUNET_SYSERR == res) -// { -// GNUNET_assert (NULL == root); -// return MHD_NO; /* bad upload, could not even generate error */ -// } -// if ( (GNUNET_NO == res) || -// (NULL == root) ) -// { -// GNUNET_assert (NULL == root); -// return MHD_YES; /* so far incomplete upload or parser error */ -// } -// } - -// { -// char d[ulen]; -// unsigned int i; -// char *sp; - -// /* Parse command-line arguments */ -// /* make a copy of 'url' because 'strtok_r()' will modify */ -// GNUNET_memcpy (d, -// url, -// ulen); -// i = 0; -// args[i++] = strtok_r (d, "/", &sp); -// while ( (NULL != args[i - 1]) && -// (i <= rh->nargs + 1) ) -// args[i++] = strtok_r (NULL, "/", &sp); -// /* make sure above loop ran nicely until completion, and also -// that there is no excess data in 'd' afterwards */ -// if ( ( (rh->nargs_is_upper_bound) && -// (i - 1 > rh->nargs) ) || -// ( (! rh->nargs_is_upper_bound) && -// (i - 1 != rh->nargs) ) ) -// { -// char emsg[128 + 512]; - -// GNUNET_snprintf (emsg, -// sizeof (emsg), -// "Got %u+/%u segments for `%s' request (`%s')", -// i - 1, -// rh->nargs, -// rh->url, -// url); -// GNUNET_break_op (0); -// json_decref (root); -// return TALER_MHD_reply_with_error (rc->connection, -// MHD_HTTP_NOT_FOUND, -// TALER_EC_DONAU_GENERIC_WRONG_NUMBER_OF_SEGMENTS, -// emsg); -// } -// GNUNET_assert (NULL == args[i - 1]); - -// /* Above logic ensures that 'root' is exactly non-NULL for POST operations, -// so we test for 'root' to decide which handler to invoke. */ -// if (0 == strcasecmp (rh->method, -// MHD_HTTP_METHOD_POST)) -// ret = rh->handler.post (rc, -// root, -// args); -// else if (0 == strcasecmp (rh->method, -// MHD_HTTP_METHOD_DELETE)) -// ret = rh->handler.delete (rc, -// args); -// else /* Only GET left */ -// ret = rh->handler.get (rc, -// args); -// } -// json_decref (root); -// return ret; -// } +/** + * We found a request handler responsible for handling a request. Parse the + * @a upload_data (if applicable) and the @a url and call the + * handler. + * + * @param rc request context + * @param url rest of the URL to parse + * @param upload_data upload data to parse (if available) + * @param[in,out] upload_data_size number of bytes in @a upload_data + * @return MHD result code + */ +static MHD_RESULT +proceed_with_handler (struct TEH_RequestContext *rc, + const char *url, + const char *upload_data, + size_t *upload_data_size) +{ + const struct TEH_RequestHandler *rh = rc->rh; + const char *args[rh->nargs + 2]; + size_t ulen = strlen (url) + 1; + json_t *root = NULL; + MHD_RESULT ret; + + /* We do check for "ulen" here, because we'll later stack-allocate a buffer + of that size and don't want to enable malicious clients to cause us + huge stack allocations. */ + if (ulen > 512) + { + /* 512 is simply "big enough", as it is bigger than "6 * 54", + which is the longest URL format we ever get (for + /deposits/). The value should be adjusted if we ever define protocol + endpoints with plausibly longer inputs. */ + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_URI_TOO_LONG, + TALER_EC_GENERIC_URI_TOO_LONG, + url); + } + + /* All POST endpoints come with a body in JSON format. So we parse + the JSON here. */ + if (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_POST)) + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_post_json (rc->connection, + &rc->opaque_post_parsing_context, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + { + GNUNET_assert (NULL == root); + return MHD_NO; /* bad upload, could not even generate error */ + } + if ( (GNUNET_NO == res) || + (NULL == root) ) + { + GNUNET_assert (NULL == root); + return MHD_YES; /* so far incomplete upload or parser error */ + } + } + + { + char d[ulen]; + unsigned int i; + char *sp; + + /* Parse command-line arguments */ + /* make a copy of 'url' because 'strtok_r()' will modify */ + GNUNET_memcpy (d, + url, + ulen); + i = 0; + args[i++] = strtok_r (d, "/", &sp); + while ( (NULL != args[i - 1]) && + (i <= rh->nargs + 1) ) + args[i++] = strtok_r (NULL, "/", &sp); + /* make sure above loop ran nicely until completion, and also + that there is no excess data in 'd' afterwards */ + if ( ( (rh->nargs_is_upper_bound) && + (i - 1 > rh->nargs) ) || + ( (! rh->nargs_is_upper_bound) && + (i - 1 != rh->nargs) ) ) + { + char emsg[128 + 512]; + + GNUNET_snprintf (emsg, + sizeof (emsg), + "Got %u+/%u segments for `%s' request (`%s')", + i - 1, + rh->nargs, + rh->url, + url); + GNUNET_break_op (0); + json_decref (root); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, + emsg); + } + GNUNET_assert (NULL == args[i - 1]); + + /* Above logic ensures that 'root' is exactly non-NULL for POST operations, + so we test for 'root' to decide which handler to invoke. */ + if (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_POST)) + ret = rh->handler.post (rc, + root, + args); + else if (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_DELETE)) + ret = rh->handler.delete (rc, + args); + else /* Only GET left */ + ret = rh->handler.get (rc, + args); + } + json_decref (root); + return ret; +} -// /** -// * Handle a "/seed" request. -// * -// * @param rc request context -// * @param args array of additional options (must be empty for this function) -// * @return MHD result code -// */ -// static MHD_RESULT -// handler_seed (struct TEH_RequestContext *rc, -// const char *const args[]) -// { -// #define SEED_SIZE 32 -// char *body; -// MHD_RESULT ret; -// struct MHD_Response *resp; - -// (void) args; -// body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */ -// if (NULL == body) -// return MHD_NO; -// GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, -// body, -// SEED_SIZE); -// resp = MHD_create_response_from_buffer (SEED_SIZE, -// body, -// MHD_RESPMEM_MUST_FREE); -// TALER_MHD_add_global_headers (resp); -// ret = MHD_queue_response (rc->connection, -// MHD_HTTP_OK, -// resp); -// GNUNET_break (MHD_YES == ret); -// MHD_destroy_response (resp); -// return ret; -// #undef SEED_SIZE -// } +/** + * Handle a "/seed" request. + * + * @param rc request context + * @param args array of additional options (must be empty for this function) + * @return MHD result code + */ +static MHD_RESULT +handler_seed (struct TEH_RequestContext *rc, + const char *const args[]) +{ +#define SEED_SIZE 32 + char *body; + MHD_RESULT ret; + struct MHD_Response *resp; + + (void) args; + body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */ + if (NULL == body) + return MHD_NO; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + body, + SEED_SIZE); + resp = MHD_create_response_from_buffer (SEED_SIZE, + body, + MHD_RESPMEM_MUST_FREE); + TALER_MHD_add_global_headers (resp); + ret = MHD_queue_response (rc->connection, + MHD_HTTP_OK, + resp); + GNUNET_break (MHD_YES == ret); + MHD_destroy_response (resp); + return ret; +#undef SEED_SIZE +} -// /** -// * Handle incoming HTTP request. -// * -// * @param cls closure for MHD daemon (unused) -// * @param connection the connection -// * @param url the requested url -// * @param method the method (POST, GET, ...) -// * @param version HTTP version (ignored) -// * @param upload_data request data -// * @param upload_data_size size of @a upload_data in bytes -// * @param con_cls closure for request (a `struct TEH_RequestContext *`) -// * @return MHD result code -// */ -// static MHD_RESULT -// handle_mhd_request (void *cls, -// struct MHD_Connection *connection, -// const char *url, -// const char *method, -// const char *version, -// const char *upload_data, -// size_t *upload_data_size, -// void **con_cls) -// { -// static struct TEH_RequestHandler handlers[] = { +/** + * Handle incoming HTTP request. + * + * @param cls closure for MHD daemon (unused) + * @param connection the connection + * @param url the requested url + * @param method the method (POST, GET, ...) + * @param version HTTP version (ignored) + * @param upload_data request data + * @param upload_data_size size of @a upload_data in bytes + * @param con_cls closure for request (a `struct TEH_RequestContext *`) + * @return MHD result code + */ +static MHD_RESULT +handle_mhd_request (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + static struct TEH_RequestHandler handlers[] = { /* /robots.txt: disallow everything */ -// { -// .url = "robots.txt", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_static_response, -// .mime_type = "text/plain", -// .data = "User-agent: *\nDisallow: /\n", -// .response_code = MHD_HTTP_OK -// }, +{ + .url = "robots.txt", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_static_response, + .mime_type = "text/plain", + .data = "User-agent: *\nDisallow: /\n", + .response_code = MHD_HTTP_OK +}, /* Landing page, tell humans to go away. */ -// { -// .url = "", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = TEH_handler_static_response, -// .mime_type = "text/plain", -// .data = -// "Hello, I'm the Taler donau. This HTTP server is not for humans.\n", -// .response_code = MHD_HTTP_OK -// }, +{ + .url = "", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = TEH_handler_static_response, + .mime_type = "text/plain", + .data = + "Hello, I'm the Taler donau. This HTTP server is not for humans.\n", + .response_code = MHD_HTTP_OK +}, /* AGPL licensing page, redirect to source. As per the AGPL-license, every deployment is required to offer the user a download of the source of the actual deployment. We make this easy by including a redirect to the source here. */ -// { -// .url = "agpl", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_agpl_redirect -// }, -// { -// .url = "seed", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &handler_seed -// }, +{ + .url = "agpl", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_agpl_redirect +}, +{ + .url = "seed", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &handler_seed +}, /* Configuration */ -// { -// .url = "config", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_config -// }, +{ + .url = "config", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_config +}, /* Performance metrics */ -// { -// .url = "metrics", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_metrics -// }, +{ + .url = "metrics", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_metrics +}, /* Terms of service */ -// { -// .url = "terms", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_terms -// }, +{ + .url = "terms", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_terms +}, /* Privacy policy */ -// { -// .url = "privacy", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_handler_privacy -// }, +{ + .url = "privacy", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_handler_privacy +}, /* Return key material and fundamental properties for this donau */ -// { -// .url = "keys", -// .method = MHD_HTTP_METHOD_GET, -// .handler.get = &TEH_keys_get_handler, -// }, +{ + .url = "keys", + .method = MHD_HTTP_METHOD_GET, + //.handler.get = &TEH_keys_get_handler, +}, /* request R, used in clause schnorr withdraw and refresh */ -// { -// .url = "csr-melt", -// .method = MHD_HTTP_METHOD_POST, -// .handler.post = &TEH_handler_csr_melt, -// .nargs = 0 -// }, - - -// /* mark end of list */ -// { -// .url = NULL -// } -// }; -// struct TEH_RequestContext *rc = *con_cls; -// struct GNUNET_AsyncScopeSave old_scope; -// const char *correlation_id = NULL; - -// (void) cls; -// (void) version; -// if (NULL == rc) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Handling new request\n"); - -// /* We're in a new async scope! */ -// rc = *con_cls = GNUNET_new (struct TEH_RequestContext); -// rc->start_time = GNUNET_TIME_absolute_get (); -// GNUNET_async_scope_fresh (&rc->async_scope_id); -// TEH_check_invariants (); -// rc->url = url; -// rc->connection = connection; -// /* We only read the correlation ID on the first callback for every client */ -// correlation_id = MHD_lookup_connection_value (connection, -// MHD_HEADER_KIND, -// "Taler-Correlation-Id"); -// if ( (NULL != correlation_id) && -// (GNUNET_YES != -// GNUNET_CURL_is_valid_scope_id (correlation_id)) ) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -// "illegal incoming correlation ID\n"); -// correlation_id = NULL; -// } - -// /* Check if upload is in bounds */ -// if (0 == strcasecmp (method, -// MHD_HTTP_METHOD_POST)) -// { -// TALER_MHD_check_content_length (connection, -// TALER_MHD_REQUEST_BUFFER_MAX); -// } -// } - -// GNUNET_async_scope_enter (&rc->async_scope_id, -// &old_scope); -// TEH_check_invariants (); -// if (NULL != correlation_id) -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Handling request (%s) for URL '%s', correlation_id=%s\n", -// method, -// url, -// correlation_id); -// else -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Handling request (%s) for URL '%s'\n", -// method, -// url); -// /* on repeated requests, check our cache first */ -// if (NULL != rc->rh) -// { -// MHD_RESULT ret; -// const char *start; - -// if ('\0' == url[0]) -// /* strange, should start with '/', treat as just "/" */ -// url = "/"; -// start = strchr (url + 1, '/'); -// if (NULL == start) -// start = ""; -// ret = proceed_with_handler (rc, -// start, -// upload_data, -// upload_data_size); -// GNUNET_async_scope_restore (&old_scope); -// return ret; -// } - -// if ( (0 == strcasecmp (method, -// MHD_HTTP_METHOD_OPTIONS)) && -// (0 == strcmp ("*", -// url)) ) -// return TALER_MHD_reply_cors_preflight (connection); - -// if (0 == strcasecmp (method, -// MHD_HTTP_METHOD_HEAD)) -// method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ - -// /* parse first part of URL */ -// { -// bool found = false; -// size_t tok_size; -// const char *tok; -// const char *rest; - -// if ('\0' == url[0]) -// /* strange, should start with '/', treat as just "/" */ -// url = "/"; -// tok = url + 1; -// rest = strchr (tok, '/'); -// if (NULL == rest) -// { -// tok_size = strlen (tok); -// } -// else -// { -// tok_size = rest - tok; -// rest++; /* skip over '/' */ -// } -// for (unsigned int i = 0; NULL != handlers[i].url; i++) -// { -// struct TEH_RequestHandler *rh = &handlers[i]; - -// if ( (0 != strncmp (tok, -// rh->url, -// tok_size)) || -// (tok_size != strlen (rh->url) ) ) -// continue; -// found = true; -// /* The URL is a match! What we now do depends on the method. */ -// if (0 == strcasecmp (method, -// MHD_HTTP_METHOD_OPTIONS)) -// { -// GNUNET_async_scope_restore (&old_scope); -// return TALER_MHD_reply_cors_preflight (connection); -// } -// GNUNET_assert (NULL != rh->method); -// if (0 == strcasecmp (method, -// rh->method)) -// { -// MHD_RESULT ret; - -// /* cache to avoid the loop next time */ -// rc->rh = rh; -// /* run handler */ -// ret = proceed_with_handler (rc, -// url + tok_size + 1, -// upload_data, -// upload_data_size); -// GNUNET_async_scope_restore (&old_scope); -// return ret; -// } -// } - -// if (found) -// { -// /* we found a matching address, but the method is wrong */ -// struct MHD_Response *reply; -// MHD_RESULT ret; -// char *allowed = NULL; - -// GNUNET_break_op (0); -// for (unsigned int i = 0; NULL != handlers[i].url; i++) -// { -// struct TEH_RequestHandler *rh = &handlers[i]; - -// if ( (0 != strncmp (tok, -// rh->url, -// tok_size)) || -// (tok_size != strlen (rh->url) ) ) -// continue; -// if (NULL == allowed) -// { -// allowed = GNUNET_strdup (rh->method); -// } -// else -// { -// char *tmp; - -// GNUNET_asprintf (&tmp, -// "%s, %s", -// allowed, -// rh->method); -// GNUNET_free (allowed); -// allowed = tmp; -// } -// if (0 == strcasecmp (rh->method, -// MHD_HTTP_METHOD_GET)) -// { -// char *tmp; - -// GNUNET_asprintf (&tmp, -// "%s, %s", -// allowed, -// MHD_HTTP_METHOD_HEAD); -// GNUNET_free (allowed); -// allowed = tmp; -// } -// } -// reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, -// method); -// GNUNET_break (MHD_YES == -// MHD_add_response_header (reply, -// MHD_HTTP_HEADER_ALLOW, -// allowed)); -// GNUNET_free (allowed); -// ret = MHD_queue_response (connection, -// MHD_HTTP_METHOD_NOT_ALLOWED, -// reply); -// MHD_destroy_response (reply); -// return ret; -// } -// } - -// /* No handler matches, generate not found */ -// { -// MHD_RESULT ret; - -// ret = TALER_MHD_reply_with_error (connection, -// MHD_HTTP_NOT_FOUND, -// TALER_EC_GENERIC_ENDPOINT_UNKNOWN, -// url); -// GNUNET_async_scope_restore (&old_scope); -// return ret; -// } -// } +{ + .url = "csr-melt", + .method = MHD_HTTP_METHOD_POST, + //.handler.post = &TEH_handler_csr_melt, + .nargs = 0 +}, -// /** -// * Load configuration parameters for the donau -// * server into the corresponding global variables. -// * -// * @return #GNUNET_OK on success -// */ -// static enum GNUNET_GenericReturnValue -// donau_serve_process_config (void) -// { -// if (GNUNET_OK != -// TALER_KYCLOGIC_kyc_init (TEH_cfg)) -// { -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_number (TEH_cfg, -// "donau", -// "MAX_REQUESTS", -// &req_max)) -// { -// req_max = ULLONG_MAX; -// } -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_time (TEH_cfg, -// "donaudb", -// "IDLE_RESERVE_EXPIRATION_TIME", -// &TEH_reserve_closing_delay)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "donaudb", -// "IDLE_RESERVE_EXPIRATION_TIME"); -// /* use default */ -// TEH_reserve_closing_delay -// = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, -// 4); -// } - -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_time (TEH_cfg, -// "donau", -// "MAX_KEYS_CACHING", -// &TEH_max_keys_caching)) -// { -// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "MAX_KEYS_CACHING", -// "valid relative time expected"); -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_string (TEH_cfg, -// "donau", -// "KYC_AML_TRIGGER", -// &TEH_kyc_aml_trigger)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "KYC_AML_TRIGGER"); -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// TALER_config_get_currency (TEH_cfg, -// &TEH_currency)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "taler", -// "CURRENCY"); -// return GNUNET_SYSERR; -// } -// { -// unsigned long long cfd; - -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_number (TEH_cfg, -// "donau", -// "CURRENCY_FRACTION_DIGITS", -// &cfd)) -// cfd = 0; -// if (cfd > 8) -// { -// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, -// "taler", -// "CURRENCY_FRACTION_DIGITS", -// "Value must be below 8"); -// return GNUNET_SYSERR; -// } -// TEH_currency_fraction_digits = (unsigned int) cfd; -// } -// if (GNUNET_OK != -// TALER_config_get_amount (TEH_cfg, -// "donau", -// "AML_THRESHOLD", -// &TEH_aml_threshold)) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Need amount in section `donau' under `AML_THRESHOLD'\n"); -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// TALER_config_get_amount (TEH_cfg, -// "donau", -// "STEFAN_ABS", -// &TEH_stefan_abs)) -// { -// GNUNET_assert (GNUNET_OK == -// TALER_amount_set_zero (TEH_currency, -// &TEH_stefan_abs)); -// } -// if (GNUNET_OK != -// TALER_config_get_amount (TEH_cfg, -// "donau", -// "STEFAN_LOG", -// &TEH_stefan_log)) -// { -// GNUNET_assert (GNUNET_OK == -// TALER_amount_set_zero (TEH_currency, -// &TEH_stefan_log)); -// } -// if (GNUNET_OK != -// TALER_config_get_amount (TEH_cfg, -// "donau", -// "STEFAN_LIN", -// &TEH_stefan_lin)) -// { -// GNUNET_assert (GNUNET_OK == -// TALER_amount_set_zero (TEH_currency, -// &TEH_stefan_lin)); -// } - -// if (0 != strcmp (TEH_currency, -// TEH_aml_threshold.currency)) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Amount in section `donau' under `AML_THRESHOLD' uses the wrong currency!\n"); -// return GNUNET_SYSERR; -// } -// TEH_enable_rewards -// = GNUNET_CONFIGURATION_get_value_yesno ( -// TEH_cfg, -// "donau", -// "ENABLE_REWARDS"); -// if (GNUNET_SYSERR == TEH_enable_rewards) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Need YES or NO in section `donau' under `ENABLE_REWARDS'\n"); -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_string (TEH_cfg, -// "donau", -// "BASE_URL", -// &TEH_base_url)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "BASE_URL"); -// return GNUNET_SYSERR; -// } -// if (! TALER_url_valid_charset (TEH_base_url)) -// { -// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "BASE_URL", -// "invalid URL"); -// return GNUNET_SYSERR; -// } - -// { -// char *master_public_key_str; - -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_string (TEH_cfg, -// "donau", -// "MASTER_PUBLIC_KEY", -// &master_public_key_str)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "MASTER_PUBLIC_KEY"); -// return GNUNET_SYSERR; -// } -// if (GNUNET_OK != -// GNUNET_CRYPTO_eddsa_public_key_from_string (master_public_key_str, -// strlen ( -// master_public_key_str), -// &TEH_master_public_key. -// eddsa_pub)) -// { -// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "MASTER_PUBLIC_KEY", -// "invalid base32 encoding for a master public key"); -// GNUNET_free (master_public_key_str); -// return GNUNET_SYSERR; -// } -// GNUNET_log (GNUNET_ERROR_TYPE_INFO, -// "Launching donau with public key `%s'...\n", -// master_public_key_str); -// GNUNET_free (master_public_key_str); -// } - -// { -// char *attr_enc_key_str; - -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_string (TEH_cfg, -// "donau", -// "ATTRIBUTE_ENCRYPTION_KEY", -// &attr_enc_key_str)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// "donau", -// "ATTRIBUTE_ENCRYPTION_KEY"); -// return GNUNET_SYSERR; -// } -// GNUNET_CRYPTO_hash (attr_enc_key_str, -// strlen (attr_enc_key_str), -// &TEH_attribute_key.hash); -// GNUNET_free (attr_enc_key_str); -// } - -// for (unsigned int i = 0; i<MAX_DB_RETRIES; i++) -// { -// TEH_plugin = DONAUDB_plugin_load (TEH_cfg); -// if (NULL != TEH_plugin) -// break; -// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -// "Failed to connect to DB, will try again %u times\n", -// MAX_DB_RETRIES - i); -// sleep (1); -// } -// if (NULL == TEH_plugin) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Failed to initialize DB subsystem. Giving up.\n"); -// return GNUNET_SYSERR; -// } -// return GNUNET_OK; -// } + /* mark end of list */ + { + .url = NULL + } + }; + struct TEH_RequestContext *rc = *con_cls; + struct GNUNET_AsyncScopeSave old_scope; + const char *correlation_id = NULL; + + (void) cls; + (void) version; + if (NULL == rc) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling new request\n"); + + /* We're in a new async scope! */ + rc = *con_cls = GNUNET_new (struct TEH_RequestContext); + rc->start_time = GNUNET_TIME_absolute_get (); + GNUNET_async_scope_fresh (&rc->async_scope_id); + //TEH_check_invariants (); + rc->url = url; + rc->connection = connection; + /* We only read the correlation ID on the first callback for every client */ + correlation_id = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "Taler-Correlation-Id"); + if ( (NULL != correlation_id) && + (GNUNET_YES != + GNUNET_CURL_is_valid_scope_id (correlation_id)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "illegal incoming correlation ID\n"); + correlation_id = NULL; + } + + /* Check if upload is in bounds */ + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_POST)) + { + TALER_MHD_check_content_length (connection, + TALER_MHD_REQUEST_BUFFER_MAX); + } + } + + GNUNET_async_scope_enter (&rc->async_scope_id, + &old_scope); + //TEH_check_invariants (); + if (NULL != correlation_id) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling request (%s) for URL '%s', correlation_id=%s\n", + method, + url, + correlation_id); + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling request (%s) for URL '%s'\n", + method, + url); + /* on repeated requests, check our cache first */ + if (NULL != rc->rh) + { + MHD_RESULT ret; + const char *start; + + if ('\0' == url[0]) + /* strange, should start with '/', treat as just "/" */ + url = "/"; + start = strchr (url + 1, '/'); + if (NULL == start) + start = ""; + ret = proceed_with_handler (rc, + start, + upload_data, + upload_data_size); + GNUNET_async_scope_restore (&old_scope); + return ret; + } + + if ( (0 == strcasecmp (method, + MHD_HTTP_METHOD_OPTIONS)) && + (0 == strcmp ("*", + url)) ) + return TALER_MHD_reply_cors_preflight (connection); + + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_HEAD)) + method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ + + /* parse first part of URL */ + { + bool found = false; + size_t tok_size; + const char *tok; + const char *rest; + + if ('\0' == url[0]) + /* strange, should start with '/', treat as just "/" */ + url = "/"; + tok = url + 1; + rest = strchr (tok, '/'); + if (NULL == rest) + { + tok_size = strlen (tok); + } + else + { + tok_size = rest - tok; + rest++; /* skip over '/' */ + } + for (unsigned int i = 0; NULL != handlers[i].url; i++) + { + struct TEH_RequestHandler *rh = &handlers[i]; + + if ( (0 != strncmp (tok, + rh->url, + tok_size)) || + (tok_size != strlen (rh->url) ) ) + continue; + found = true; + /* The URL is a match! What we now do depends on the method. */ + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_OPTIONS)) + { + GNUNET_async_scope_restore (&old_scope); + return TALER_MHD_reply_cors_preflight (connection); + } + GNUNET_assert (NULL != rh->method); + if (0 == strcasecmp (method, + rh->method)) + { + MHD_RESULT ret; + + /* cache to avoid the loop next time */ + rc->rh = rh; + /* run handler */ + ret = proceed_with_handler (rc, + url + tok_size + 1, + upload_data, + upload_data_size); + GNUNET_async_scope_restore (&old_scope); + return ret; + } + } + + if (found) + { + /* we found a matching address, but the method is wrong */ + struct MHD_Response *reply; + MHD_RESULT ret; + char *allowed = NULL; + + GNUNET_break_op (0); + for (unsigned int i = 0; NULL != handlers[i].url; i++) + { + struct TEH_RequestHandler *rh = &handlers[i]; + + if ( (0 != strncmp (tok, + rh->url, + tok_size)) || + (tok_size != strlen (rh->url) ) ) + continue; + if (NULL == allowed) + { + allowed = GNUNET_strdup (rh->method); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s, %s", + allowed, + rh->method); + GNUNET_free (allowed); + allowed = tmp; + } + if (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_GET)) + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s, %s", + allowed, + MHD_HTTP_METHOD_HEAD); + GNUNET_free (allowed); + allowed = tmp; + } + } + reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, + method); + GNUNET_break (MHD_YES == + MHD_add_response_header (reply, + MHD_HTTP_HEADER_ALLOW, + allowed)); + GNUNET_free (allowed); + ret = MHD_queue_response (connection, + MHD_HTTP_METHOD_NOT_ALLOWED, + reply); + MHD_destroy_response (reply); + return ret; + } + } + + /* No handler matches, generate not found */ + { + MHD_RESULT ret; + + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + url); + GNUNET_async_scope_restore (&old_scope); + return ret; + } +} -// /** -// * Called when the main thread exits, writes out performance -// * stats if requested. -// */ -// static void -// write_stats (void) -// { -// struct GNUNET_DISK_FileHandle *fh; -// pid_t pid = getpid (); -// char *benchmark_dir; -// char *s; -// struct rusage usage; - -// benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR"); -// if (NULL == benchmark_dir) -// return; -// GNUNET_asprintf (&s, -// "%s/taler-donau-%llu.txt", -// benchmark_dir, -// (unsigned long long) pid); -// fh = GNUNET_DISK_file_open (s, -// (GNUNET_DISK_OPEN_WRITE -// | GNUNET_DISK_OPEN_TRUNCATE -// | GNUNET_DISK_OPEN_CREATE), -// (GNUNET_DISK_PERM_USER_READ -// | GNUNET_DISK_PERM_USER_WRITE)); -// GNUNET_free (s); -// if (NULL == fh) -// return; /* permission denied? */ - -// /* Collect stats, summed up for all threads */ -// GNUNET_assert (0 == -// getrusage (RUSAGE_SELF, -// &usage)); -// GNUNET_asprintf (&s, -// "time_donau sys %llu user %llu\n", -// (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000 -// + usage.ru_stime.tv_usec), -// (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000 -// + usage.ru_utime.tv_usec)); -// GNUNET_assert (GNUNET_SYSERR != -// GNUNET_DISK_file_write_blocking (fh, -// s, -// strlen (s))); -// GNUNET_free (s); -// GNUNET_assert (GNUNET_OK == -// GNUNET_DISK_file_close (fh)); -// } +/** + * Load configuration parameters for the donau + * server into the corresponding global variables. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +donau_serve_process_config (void) +{ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (TEH_cfg, + "donau", + "MAX_REQUESTS", + &req_max)) + { + req_max = ULLONG_MAX; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "donaudb", + "IDLE_RESERVE_EXPIRATION_TIME", + &TEH_reserve_closing_delay)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "donaudb", + "IDLE_RESERVE_EXPIRATION_TIME"); + /* use default */ + TEH_reserve_closing_delay + = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, + 4); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "donau", + "MAX_KEYS_CACHING", + &TEH_max_keys_caching)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "donau", + "MAX_KEYS_CACHING", + "valid relative time expected"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "donau", + "KYC_AML_TRIGGER", + &TEH_kyc_aml_trigger)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "donau", + "KYC_AML_TRIGGER"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_currency (TEH_cfg, + &TEH_currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler", + "CURRENCY"); + return GNUNET_SYSERR; + } + { + unsigned long long cfd; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (TEH_cfg, + "donau", + "CURRENCY_FRACTION_DIGITS", + &cfd)) + cfd = 0; + if (cfd > 8) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "taler", + "CURRENCY_FRACTION_DIGITS", + "Value must be below 8"); + return GNUNET_SYSERR; + } + TEH_currency_fraction_digits = (unsigned int) cfd; + } + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, + "donau", + "AML_THRESHOLD", + &TEH_aml_threshold)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need amount in section `donau' under `AML_THRESHOLD'\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, + "donau", + "STEFAN_ABS", + &TEH_stefan_abs)) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &TEH_stefan_abs)); + } + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, + "donau", + "STEFAN_LOG", + &TEH_stefan_log)) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &TEH_stefan_log)); + } + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, + "donau", + "STEFAN_LIN", + &TEH_stefan_lin)) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &TEH_stefan_lin)); + } + + if (0 != strcmp (TEH_currency, + TEH_aml_threshold.currency)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Amount in section `donau' under `AML_THRESHOLD' uses the wrong currency!\n"); + return GNUNET_SYSERR; + } + TEH_enable_rewards + = GNUNET_CONFIGURATION_get_value_yesno ( + TEH_cfg, + "donau", + "ENABLE_REWARDS"); + if (GNUNET_SYSERR == TEH_enable_rewards) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need YES or NO in section `donau' under `ENABLE_REWARDS'\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "donau", + "BASE_URL", + &TEH_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "donau", + "BASE_URL"); + return GNUNET_SYSERR; + } + if (! TALER_url_valid_charset (TEH_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "donau", + "BASE_URL", + "invalid URL"); + return GNUNET_SYSERR; + } + + { + char *master_public_key_str; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "donau", + "MASTER_PUBLIC_KEY", + &master_public_key_str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "donau", + "MASTER_PUBLIC_KEY"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_public_key_from_string (master_public_key_str, + strlen ( + master_public_key_str), + &TEH_master_public_key. + eddsa_pub)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "donau", + "MASTER_PUBLIC_KEY", + "invalid base32 encoding for a master public key"); + GNUNET_free (master_public_key_str); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Launching donau with public key `%s'...\n", + master_public_key_str); + GNUNET_free (master_public_key_str); + } + + { + char *attr_enc_key_str; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "donau", + "ATTRIBUTE_ENCRYPTION_KEY", + &attr_enc_key_str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "donau", + "ATTRIBUTE_ENCRYPTION_KEY"); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_hash (attr_enc_key_str, + strlen (attr_enc_key_str), + &TEH_attribute_key.hash); + GNUNET_free (attr_enc_key_str); + } + + for (unsigned int i = 0; i<MAX_DB_RETRIES; i++) + { + TEH_plugin = DONAUDB_plugin_load (TEH_cfg); + if (NULL != TEH_plugin) + break; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to DB, will try again %u times\n", + MAX_DB_RETRIES - i); + sleep (1); + } + if (NULL == TEH_plugin) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize DB subsystem. Giving up.\n"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Called when the main thread exits, writes out performance + * stats if requested. + */ +static void +write_stats (void) +{ + struct GNUNET_DISK_FileHandle *fh; + pid_t pid = getpid (); + char *benchmark_dir; + char *s; + struct rusage usage; + + benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR"); + if (NULL == benchmark_dir) + return; + GNUNET_asprintf (&s, + "%s/taler-donau-%llu.txt", + benchmark_dir, + (unsigned long long) pid); + fh = GNUNET_DISK_file_open (s, + (GNUNET_DISK_OPEN_WRITE + | GNUNET_DISK_OPEN_TRUNCATE + | GNUNET_DISK_OPEN_CREATE), + (GNUNET_DISK_PERM_USER_READ + | GNUNET_DISK_PERM_USER_WRITE)); + GNUNET_free (s); + if (NULL == fh) + return; /* permission denied? */ + + /* Collect stats, summed up for all threads */ + GNUNET_assert (0 == + getrusage (RUSAGE_SELF, + &usage)); + GNUNET_asprintf (&s, + "time_donau sys %llu user %llu\n", + (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000 + + usage.ru_stime.tv_usec), + (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000 + + usage.ru_utime.tv_usec)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_DISK_file_write_blocking (fh, + s, + strlen (s))); + GNUNET_free (s); + GNUNET_assert (GNUNET_OK == + GNUNET_DISK_file_close (fh)); +} -// /* Developer logic for supporting the `-f' option. */ -// #if HAVE_DEVELOPER +/* Developer logic for supporting the `-f' option. */ +#if HAVE_DEVELOPER /** * Option `-f' (specifies an input file to give to the HTTP server). */ static char *input_filename; -// /** -// * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename -// * as the input for the request. If launching the client worked, -// * run the #TEH_KS_loop() event loop as usual. -// * -// * @return child pid -// */ -// static pid_t -// run_fake_client (void) -// { -// pid_t cld; -// char ports[6]; -// int fd; - -// if (0 == strcmp (input_filename, -// "-")) -// fd = STDIN_FILENO; -// else -// fd = open (input_filename, -// O_RDONLY); -// if (-1 == fd) -// { -// fprintf (stderr, -// "Failed to open `%s': %s\n", -// input_filename, -// strerror (errno)); -// return -1; -// } -// /* Fake HTTP client request with #input_filename as input. -// We do this using the nc tool. */ -// GNUNET_snprintf (ports, -// sizeof (ports), -// "%u", -// serve_port); -// if (0 == (cld = fork ())) -// { -// GNUNET_break (0 == close (0)); -// GNUNET_break (0 == dup2 (fd, 0)); -// GNUNET_break (0 == close (fd)); -// if ( (0 != execlp ("nc", -// "nc", -// "localhost", -// ports, -// "-w", "30", -// NULL)) && -// (0 != execlp ("ncat", -// "ncat", -// "localhost", -// ports, -// "-i", "30", -// NULL)) ) -// { -// fprintf (stderr, -// "Failed to run both `nc' and `ncat': %s\n", -// strerror (errno)); -// } -// _exit (1); -// } -// /* parent process */ -// if (0 != strcmp (input_filename, -// "-")) -// GNUNET_break (0 == close (fd)); -// return cld; -// } +/** + * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename + * as the input for the request. If launching the client worked, + * run the #TEH_KS_loop() event loop as usual. + * + * @return child pid + */ +static pid_t +run_fake_client (void) +{ + pid_t cld; + char ports[6]; + int fd; + + if (0 == strcmp (input_filename, + "-")) + fd = STDIN_FILENO; + else + fd = open (input_filename, + O_RDONLY); + if (-1 == fd) + { + fprintf (stderr, + "Failed to open `%s': %s\n", + input_filename, + strerror (errno)); + return -1; + } + /* Fake HTTP client request with #input_filename as input. + We do this using the nc tool. */ + GNUNET_snprintf (ports, + sizeof (ports), + "%u", + serve_port); + if (0 == (cld = fork ())) + { + GNUNET_break (0 == close (0)); + GNUNET_break (0 == dup2 (fd, 0)); + GNUNET_break (0 == close (fd)); + if ( (0 != execlp ("nc", + "nc", + "localhost", + ports, + "-w", "30", + NULL)) && + (0 != execlp ("ncat", + "ncat", + "localhost", + ports, + "-i", "30", + NULL)) ) + { + fprintf (stderr, + "Failed to run both `nc' and `ncat': %s\n", + strerror (errno)); + } + _exit (1); + } + /* parent process */ + if (0 != strcmp (input_filename, + "-")) + GNUNET_break (0 == close (fd)); + return cld; +} -// /** -// * Run the donau to serve a single request only, without threads. -// * -// * @return #GNUNET_OK on success -// */ -// static void -// run_single_request (void) -// { -// pid_t xfork; - -// xfork = fork (); -// if (-1 == xfork) -// { -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// if (0 == xfork) -// { -// pid_t cld; - -// cld = run_fake_client (); -// if (-1 == cld) -// _exit (EXIT_FAILURE); -// _exit (EXIT_SUCCESS); -// } - -// { -// int status; - -// if (xfork != waitpid (xfork, -// &status, -// 0)) -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Waiting for `nc' child failed: %s\n", -// strerror (errno)); -// } -// } +/** + * Run the donau to serve a single request only, without threads. + * + * @return #GNUNET_OK on success + */ +static void +run_single_request (void) +{ + pid_t xfork; + + xfork = fork (); + if (-1 == xfork) + { + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (0 == xfork) + { + pid_t cld; + + cld = run_fake_client (); + if (-1 == cld) + _exit (EXIT_FAILURE); + _exit (EXIT_SUCCESS); + } + + { + int status; + + if (xfork != waitpid (xfork, + &status, + 0)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Waiting for `nc' child failed: %s\n", + strerror (errno)); + } +} +#endif +/* end of HAVE_DEVELOPER */ -// /* end of HAVE_DEVELOPER */ -// #endif - - -// /** -// * Signature of the callback used by MHD to notify the application -// * about completed connections. If we are running in test-mode with -// * an input_filename, this function is used to terminate the HTTPD -// * after the first request has been processed. -// * -// * @param cls client-defined closure, NULL -// * @param connection connection handle (ignored) -// * @param socket_context socket-specific pointer (ignored) -// * @param toe reason for connection notification -// */ -// static void -// connection_done (void *cls, -// struct MHD_Connection *connection, -// void **socket_context, -// enum MHD_ConnectionNotificationCode toe) -// { -// (void) cls; -// (void) connection; -// (void) socket_context; - -// switch (toe) -// { -// case MHD_CONNECTION_NOTIFY_STARTED: -// active_connections++; -// break; -// case MHD_CONNECTION_NOTIFY_CLOSED: -// active_connections--; -// if (TEH_suicide && -// (0 == active_connections) ) -// GNUNET_SCHEDULER_shutdown (); -// break; -// } -// #if HAVE_DEVELOPER -// /* We only act if the connection is closed. */ -// if (MHD_CONNECTION_NOTIFY_CLOSED != toe) -// return; -// if (NULL != input_filename) -// GNUNET_SCHEDULER_shutdown (); -// #endif -// } -// /** -// * Function run on shutdown. -// * -// * @param cls NULL -// */ -// static void -// do_shutdown (void *cls) -// { -// struct MHD_Daemon *mhd; -// (void) cls; - -// mhd = TALER_MHD_daemon_stop (); -// TEH_resume_keys_requests (true); -// TEH_deposits_get_cleanup (); -// TEH_reserves_get_cleanup (); -// TEH_purses_get_cleanup (); -// TEH_kyc_check_cleanup (); -// TEH_kyc_proof_cleanup (); -// TALER_KYCLOGIC_kyc_done (); -// if (NULL != mhd) -// { -// MHD_stop_daemon (mhd); -// mhd = NULL; -// } -// TEH_wire_done (); -// TEH_extensions_done (); -// TEH_keys_finished (); -// if (NULL != TEH_plugin) -// { -// DONAUDB_plugin_unload (TEH_plugin); -// TEH_plugin = NULL; -// } -// if (NULL != TEH_curl_ctx) -// { -// GNUNET_CURL_fini (TEH_curl_ctx); -// TEH_curl_ctx = NULL; -// } -// if (NULL != donau_curl_rc) -// { -// GNUNET_CURL_gnunet_rc_destroy (donau_curl_rc); -// donau_curl_rc = NULL; -// } -// TALER_TEMPLATING_done (); -// } +/** + * Signature of the callback used by MHD to notify the application + * about completed connections. If we are running in test-mode with + * an input_filename, this function is used to terminate the HTTPD + * after the first request has been processed. + * + * @param cls client-defined closure, NULL + * @param connection connection handle (ignored) + * @param socket_context socket-specific pointer (ignored) + * @param toe reason for connection notification + */ +static void +connection_done (void *cls, + struct MHD_Connection *connection, + void **socket_context, + enum MHD_ConnectionNotificationCode toe) +{ + (void) cls; + (void) connection; + (void) socket_context; + + switch (toe) + { + case MHD_CONNECTION_NOTIFY_STARTED: + active_connections++; + break; + case MHD_CONNECTION_NOTIFY_CLOSED: + active_connections--; + if (TEH_suicide && + (0 == active_connections) ) + GNUNET_SCHEDULER_shutdown (); + break; + } +#if HAVE_DEVELOPER + /* We only act if the connection is closed. */ + if (MHD_CONNECTION_NOTIFY_CLOSED != toe) + return; + if (NULL != input_filename) + GNUNET_SCHEDULER_shutdown (); +#endif +} -// /** -// * Main function that will be run by the scheduler. -// * -// * @param cls closure -// * @param args remaining command-line arguments -// * @param cfgfile name of the configuration file used (for saving, can be -// * NULL!) -// * @param config configuration -// */ -// static void -// run (void *cls, -// char *const *args, -// const char *cfgfile, -// const struct GNUNET_CONFIGURATION_Handle *config) -// { -// enum TALER_MHD_GlobalOptions go; -// int fh; - -// (void) cls; -// (void) args; -// (void ) cfgfile; -// go = TALER_MHD_GO_NONE; -// if (connection_close) -// go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; -// TALER_MHD_setup (go); -// TEH_cfg = config; - -// if (GNUNET_OK != -// donau_serve_process_config ()) -// { -// global_ret = EXIT_NOTCONFIGURED; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// if (GNUNET_OK != -// TALER_TEMPLATING_init ("donau")) -// { -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// // if (GNUNET_SYSERR == -// // TEH_plugin->preflight (TEH_plugin->cls)) -// // { -// // global_ret = EXIT_FAILURE; -// // GNUNET_SCHEDULER_shutdown (); -// // return; -// // } -// if (GNUNET_OK != -// TEH_extensions_init ()) -// { -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// if (GNUNET_OK != -// TEH_keys_init ()) -// { -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// if (GNUNET_OK != -// TEH_wire_init ()) -// { -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } - -// TEH_load_terms (TEH_cfg); -// TEH_curl_ctx -// = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, -// &donau_curl_rc); -// if (NULL == TEH_curl_ctx) -// { -// GNUNET_break (0); -// global_ret = EXIT_FAILURE; -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// donau_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx); -// GNUNET_SCHEDULER_add_shutdown (&do_shutdown, -// NULL); -// fh = TALER_MHD_bind (TEH_cfg, -// "donau", -// &serve_port); -// if ( (0 == serve_port) && -// (-1 == fh) ) -// { -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME -// | MHD_USE_PIPE_FOR_SHUTDOWN -// | MHD_USE_DEBUG | MHD_USE_DUAL_STACK -// | MHD_USE_TCP_FASTOPEN, -// (-1 == fh) ? serve_port : 0, -// NULL, NULL, -// &handle_mhd_request, NULL, -// MHD_OPTION_LISTEN_BACKLOG_SIZE, -// (unsigned int) 1024, -// MHD_OPTION_LISTEN_SOCKET, -// fh, -// MHD_OPTION_EXTERNAL_LOGGER, -// &TALER_MHD_handle_logs, -// NULL, -// MHD_OPTION_NOTIFY_COMPLETED, -// &handle_mhd_completion_callback, -// NULL, -// MHD_OPTION_NOTIFY_CONNECTION, -// &connection_done, -// NULL, -// MHD_OPTION_CONNECTION_TIMEOUT, -// connection_timeout, -// (0 == allow_address_reuse) -// ? MHD_OPTION_END -// : MHD_OPTION_LISTENING_ADDRESS_REUSE, -// (unsigned int) allow_address_reuse, -// MHD_OPTION_END); -// if (NULL == mhd) -// { -// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -// "Failed to launch HTTP service. Is the port in use?\n"); -// GNUNET_SCHEDULER_shutdown (); -// return; -// } -// global_ret = EXIT_SUCCESS; -// TALER_MHD_daemon_start (mhd); -// atexit (&write_stats); - -// #if HAVE_DEVELOPER -// if (NULL != input_filename) -// run_single_request (); -// #endif -// } +/** + * Function run on shutdown. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + struct MHD_Daemon *mhd; + (void) cls; + + mhd = TALER_MHD_daemon_stop (); + // TEH_resume_keys_requests (true); + // TEH_deposits_get_cleanup (); + // TEH_reserves_get_cleanup (); + // TEH_purses_get_cleanup (); + // TEH_kyc_check_cleanup (); + // TEH_kyc_proof_cleanup (); + // TALER_KYCLOGIC_kyc_done (); + if (NULL != mhd) + { + MHD_stop_daemon (mhd); + mhd = NULL; + } + // TEH_wire_done (); + // TEH_extensions_done (); + // TEH_keys_finished (); + if (NULL != TEH_plugin) + { + DONAUDB_plugin_unload (TEH_plugin); + TEH_plugin = NULL; + } + if (NULL != TEH_curl_ctx) + { + GNUNET_CURL_fini (TEH_curl_ctx); + TEH_curl_ctx = NULL; + } + if (NULL != donau_curl_rc) + { + GNUNET_CURL_gnunet_rc_destroy (donau_curl_rc); + donau_curl_rc = NULL; + } + TALER_TEMPLATING_done (); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be + * NULL!) + * @param config configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + enum TALER_MHD_GlobalOptions go; + int fh; + + (void) cls; + (void) args; + (void ) cfgfile; + go = TALER_MHD_GO_NONE; + if (connection_close) + go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; + TALER_MHD_setup (go); + TEH_cfg = config; + + if (GNUNET_OK != + donau_serve_process_config ()) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + TALER_TEMPLATING_init ("donau")) + { + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_SYSERR == + TEH_plugin->preflight (TEH_plugin->cls)) + { + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + // if (GNUNET_OK != + // TEH_extensions_init ()) + // { + // global_ret = EXIT_FAILURE; + // GNUNET_SCHEDULER_shutdown (); + // return; + // } + // if (GNUNET_OK != + // TEH_keys_init ()) + // { + // global_ret = EXIT_FAILURE; + // GNUNET_SCHEDULER_shutdown (); + // return; + // } + // if (GNUNET_OK != + // TEH_wire_init ()) + // { + // global_ret = EXIT_FAILURE; + // GNUNET_SCHEDULER_shutdown (); + // return; + // } + + //TEH_load_terms (TEH_cfg); + TEH_curl_ctx + = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &donau_curl_rc); + if (NULL == TEH_curl_ctx) + { + GNUNET_break (0); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + donau_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + fh = TALER_MHD_bind (TEH_cfg, + "donau", + &serve_port); + if ( (0 == serve_port) && + (-1 == fh) ) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME + | MHD_USE_PIPE_FOR_SHUTDOWN + | MHD_USE_DEBUG | MHD_USE_DUAL_STACK + | MHD_USE_TCP_FASTOPEN, + (-1 == fh) ? serve_port : 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_BACKLOG_SIZE, + (unsigned int) 1024, + MHD_OPTION_LISTEN_SOCKET, + fh, + MHD_OPTION_EXTERNAL_LOGGER, + &TALER_MHD_handle_logs, + NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, + NULL, + MHD_OPTION_NOTIFY_CONNECTION, + &connection_done, + NULL, + MHD_OPTION_CONNECTION_TIMEOUT, + connection_timeout, + (0 == allow_address_reuse) + ? MHD_OPTION_END + : MHD_OPTION_LISTENING_ADDRESS_REUSE, + (unsigned int) allow_address_reuse, + MHD_OPTION_END); + if (NULL == mhd) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to launch HTTP service. Is the port in use?\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + global_ret = EXIT_SUCCESS; + TALER_MHD_daemon_start (mhd); + atexit (&write_stats); + +#if HAVE_DEVELOPER + if (NULL != input_filename) + run_single_request (); +#endif +} /** * The main function of the taler-donau-httpd server ("the donau"). @@ -1488,17 +1483,16 @@ main (int argc, (void)options[0]; // delete me ret = 1; // delete me DONAU_OS_init (); - // ret = GNUNET_PROGRAM_run (argc, argv, - // "taler-donau-httpd", - // "Taler donau HTTP service", - // options, - // &run, NULL); + ret = GNUNET_PROGRAM_run (argc, argv, + "taler-donau-httpd", + "Taler donau HTTP service", + options, + &run, NULL); if (GNUNET_SYSERR == ret) return EXIT_INVALIDARGUMENT; if (GNUNET_NO == ret) return EXIT_SUCCESS; - // return global_ret; - return -1; + return global_ret; } /* end of taler-donau-httpd.c */ \ No newline at end of file