summaryrefslogtreecommitdiff
path: root/src/backend/anastasis-httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/anastasis-httpd.c')
-rw-r--r--src/backend/anastasis-httpd.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c
new file mode 100644
index 0000000..56bd7c9
--- /dev/null
+++ b/src/backend/anastasis-httpd.c
@@ -0,0 +1,943 @@
+/*
+ This file is part of TALER
+ (C) 2020 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backup/anastasis-httpd.c
+ * @brief HTTP serving layer intended to provide basic backup operations
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ * @author Dominik Meister
+ */
+#include "platform.h"
+#include "anastasis-httpd.h"
+#include "anastasis_util_lib.h"
+#include "anastasis-httpd_mhd.h"
+#include "anastasis_database_lib.h"
+#include "anastasis-httpd_policy.h"
+#include "anastasis-httpd_truth.h"
+#include "anastasis-httpd_terms.h"
+#include "anastasis-httpd_config.h"
+
+
+/**
+ * Backlog for listen operation on unix-domain sockets.
+ */
+#define UNIX_BACKLOG 500
+
+/**
+ * Upload limit to the service, in megabytes.
+ */
+unsigned long long int AH_upload_limit_mb;
+
+/**
+ * Annual fee for the backup account.
+ */
+struct TALER_Amount AH_annual_fee;
+
+/**
+ * Fee for a truth upload.
+ */
+struct TALER_Amount AH_truth_upload_fee;
+
+/**
+ * Amount of insurance.
+ */
+struct TALER_Amount AH_insurance;
+
+/**
+ * Cost for secure question truth download.
+ */
+struct TALER_Amount AH_question_cost;
+
+/**
+ * Our configuration.
+ */
+const struct GNUNET_CONFIGURATION_Handle *AH_cfg;
+
+/**
+ * Our Taler backend to process payments.
+ */
+char *AH_backend_url;
+
+/**
+ * Taler currency.
+ */
+char *AH_currency;
+
+/**
+ * Our fulfillment URL.
+ */
+char *AH_fulfillment_url;
+
+/**
+ * Our business name.
+ */
+char *AH_business_name;
+
+/**
+ * Our server salt.
+ */
+struct ANASTASIS_CRYPTO_ProviderSaltP AH_server_salt;
+
+/**
+ * Number of policy uploads permitted per annual fee payment.
+ */
+unsigned long long AH_post_counter = 64LLU;
+
+/**
+ * Our context for making HTTP requests.
+ */
+struct GNUNET_CURL_Context *AH_ctx;
+
+/**
+ * Should a "Connection: close" header be added to each HTTP response?
+ */
+static int AH_connection_close;
+
+/**
+ * Task running the HTTP server.
+ */
+static struct GNUNET_SCHEDULER_Task *mhd_task;
+
+/**
+ * Global return code
+ */
+static int global_result;
+
+/**
+ * The MHD Daemon
+ */
+static struct MHD_Daemon *mhd;
+
+/**
+ * Connection handle to the our database
+ */
+struct ANASTASIS_DatabasePlugin *db;
+
+/**
+ * Reschedule context for #SH_ctx.
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+/**
+ * Set if we should immediately #MHD_run again.
+ */
+static int triggered;
+
+/**
+ * Username and password to use for client authentication
+ * (optional).
+ */
+static char *userpass;
+
+/**
+ * Type of the client's TLS certificate (optional).
+ */
+static char *certtype;
+
+/**
+ * File with the client's TLS certificate (optional).
+ */
+static char *certfile;
+
+/**
+ * File with the client's TLS private key (optional).
+ */
+static char *keyfile;
+
+/**
+ * This value goes in the Authorization:-header.
+ */
+static char *apikey;
+
+/**
+ * Passphrase to decrypt client's TLS private key file (optional).
+ */
+static char *keypass;
+
+
+/**
+ * Function that queries MHD's select sets and
+ * starts the task waiting for them.
+ */
+static struct GNUNET_SCHEDULER_Task *
+prepare_daemon (void);
+
+
+/**
+ * Call MHD to process pending requests and then go back
+ * and schedule the next run.
+ *
+ * @param cls the `struct MHD_Daemon` of the HTTP server to run
+ */
+static void
+run_daemon (void *cls)
+{
+ (void) cls;
+ mhd_task = NULL;
+ do {
+ triggered = 0;
+ GNUNET_assert (MHD_YES == MHD_run (mhd));
+ } while (0 != triggered);
+ mhd_task = prepare_daemon ();
+}
+
+
+/**
+ * Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
+ *
+ * @param cls NULL
+ */
+void
+AH_trigger_daemon (void *cls)
+{
+ (void) cls;
+ if (NULL != mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (mhd_task);
+ mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
+ NULL);
+ }
+ else
+ {
+ triggered = 1;
+ }
+}
+
+
+/**
+ * Kick GNUnet Curl scheduler to begin curl interactions.
+ */
+void
+AH_trigger_curl (void)
+{
+ GNUNET_CURL_gnunet_scheduler_reschedule (&rc);
+}
+
+
+/**
+ * A client has requested the given url using the given method
+ * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
+ * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
+ * must call MHD callbacks to provide content to give back to the
+ * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
+ * #MHD_HTTP_NOT_FOUND, etc.).
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param url the requested url
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ * #MHD_HTTP_METHOD_PUT, etc.)
+ * @param version the HTTP version string (i.e.
+ * #MHD_HTTP_VERSION_1_1)
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of #MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * @a upload_data)
+ * @param upload_data_size set initially to the size of the
+ * @a upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param con_cls pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global #MHD_RequestCompletedCallback (which
+ * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, `*con_cls` will be NULL.
+ * @return #MHD_YES if the connection was handled successfully,
+ * #MHD_NO if the socket must be closed due to a serious
+ * error while handling the request
+ */
+static MHD_RESULT
+url_handler (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 AH_RequestHandler handlers[] = {
+ /* Landing page, tell humans to go away. */
+ { "/", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, I'm Anastasis. This HTTP server is not for humans.\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK },
+ { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
+ NULL, 0,
+ &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
+ { "/terms", MHD_HTTP_METHOD_GET, NULL,
+ NULL, 0,
+ &AH_handler_terms, MHD_HTTP_OK },
+ { "/privacy", MHD_HTTP_METHOD_GET, NULL,
+ NULL, 0,
+ &AH_handler_terms, MHD_HTTP_OK },
+ { "/config", MHD_HTTP_METHOD_GET, "text/json",
+ NULL, 0,
+ &AH_handler_config, MHD_HTTP_OK },
+ {NULL, NULL, NULL, NULL, 0, 0 }
+ };
+ static struct AH_RequestHandler h404 = {
+ "", NULL, "text/html",
+ "<html><title>404: not found</title></html>", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
+ };
+ static struct AH_RequestHandler h405 = {
+ "", NULL, "text/html",
+ "<html><title>405: method not allowed</title></html>", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_METHOD_NOT_ALLOWED
+ };
+ struct TM_HandlerContext *hc = *con_cls;
+ const char *correlation_id = NULL;
+ bool path_matched;
+
+ if (NULL == hc)
+ {
+ struct GNUNET_AsyncScopeId aid;
+
+ GNUNET_async_scope_fresh (&aid);
+ /* We only read the correlation ID on the first callback for every client */
+ correlation_id = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "Anastasis-Correlation-Id");
+ if ((NULL != correlation_id) &&
+ (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid incoming correlation ID\n");
+ correlation_id = NULL;
+ }
+ hc = GNUNET_new (struct TM_HandlerContext);
+ *con_cls = hc;
+ hc->async_scope_id = aid;
+ }
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET; /* MHD will throw away the body */
+
+ GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
+ if (NULL != correlation_id)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling request for (%s) 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);
+ if (0 == strncmp (url,
+ "/policy/",
+ strlen ("/policy/")))
+ {
+ const char *account = url + strlen ("/policy/");
+ struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (
+ account,
+ strlen (account),
+ &account_pub,
+ sizeof (struct ANASTASIS_CRYPTO_AccountPublicKeyP)))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "account public key");
+ }
+ if (0 == strcmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ return AH_policy_get (connection,
+ &account_pub);
+ }
+ if (0 == strcmp (method,
+ MHD_HTTP_METHOD_POST))
+ {
+ return AH_handler_policy_post (connection,
+ hc,
+ &account_pub,
+ upload_data,
+ upload_data_size);
+ }
+ return TMH_MHD_handler_static_response (&h405,
+ connection);
+ }
+ if (0 == strncmp (url,
+ "/truth/",
+ strlen ("/truth/")))
+ {
+ struct ANASTASIS_CRYPTO_TruthUUIDP tu;
+ const char *pub_key_str;
+
+ pub_key_str = &url[strlen ("/truth/")];
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (
+ pub_key_str,
+ strlen (pub_key_str),
+ &tu,
+ sizeof(tu)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "truth UUID");
+ }
+ if (0 == strcmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ return AH_handler_truth_get (connection,
+ &tu,
+ hc);
+ }
+ if (0 == strcmp (method,
+ MHD_HTTP_METHOD_POST))
+ {
+ return AH_handler_truth_post (connection,
+ hc,
+ &tu,
+ upload_data,
+ upload_data_size);
+ }
+ return TMH_MHD_handler_static_response (&h405,
+ connection);
+ }
+ path_matched = false;
+ for (unsigned int i = 0; NULL != handlers[i].url; i++)
+ {
+ struct AH_RequestHandler *rh = &handlers[i];
+
+ if (0 == strcmp (url,
+ rh->url))
+ {
+ path_matched = true;
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_OPTIONS))
+ {
+ return TALER_MHD_reply_cors_preflight (connection);
+ }
+ if ( (NULL == rh->method) ||
+ (0 == strcasecmp (method,
+ rh->method)) )
+ {
+ return rh->handler (rh,
+ connection);
+ }
+ }
+ }
+ if (path_matched)
+ return TMH_MHD_handler_static_response (&h405,
+ connection);
+ return TMH_MHD_handler_static_response (&h404,
+ connection);
+}
+
+
+/**
+ * Shutdown task (magically invoked when the application is being
+ * quit)
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ (void) cls;
+ AH_resume_all_bc ();
+ AH_truth_shutdown ();
+ AH_truth_upload_shutdown ();
+ if (NULL != mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (mhd_task);
+ mhd_task = NULL;
+ }
+ if (NULL != AH_ctx)
+ {
+ GNUNET_CURL_fini (AH_ctx);
+ AH_ctx = NULL;
+ }
+ if (NULL != rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (rc);
+ rc = NULL;
+ }
+ if (NULL != mhd)
+ {
+ MHD_stop_daemon (mhd);
+ mhd = NULL;
+ }
+ if (NULL != db)
+ {
+ ANASTASIS_DB_plugin_unload (db);
+ db = NULL;
+ }
+}
+
+
+/**
+ * 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 TM_HandlerContext *hc = *con_cls;
+
+ (void) cls;
+ (void) connection;
+ if (NULL == hc)
+ return;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Finished handling request with status %d\n",
+ (int) toe);
+ if (NULL != hc->cc)
+ hc->cc (hc);
+ GNUNET_free (hc);
+ *con_cls = NULL;
+}
+
+
+/**
+ * Function that queries MHD's select sets and
+ * starts the task waiting for them.
+ *
+ * @param daemon_handle HTTP server to prepare to run
+ */
+static struct GNUNET_SCHEDULER_Task *
+prepare_daemon (void)
+{
+ struct GNUNET_SCHEDULER_Task *ret;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ struct GNUNET_NETWORK_FDSet *wrs;
+ struct GNUNET_NETWORK_FDSet *wws;
+ int max;
+ MHD_UNSIGNED_LONG_LONG timeout;
+ int haveto;
+ struct GNUNET_TIME_Relative tv;
+
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ wrs = GNUNET_NETWORK_fdset_create ();
+ wws = GNUNET_NETWORK_fdset_create ();
+ max = -1;
+ GNUNET_assert (MHD_YES ==
+ MHD_get_fdset (mhd,
+ &rs,
+ &ws,
+ &es,
+ &max));
+ haveto = MHD_get_timeout (mhd, &timeout);
+ if (haveto == MHD_YES)
+ tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ tv = GNUNET_TIME_UNIT_FOREVER_REL;
+ GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
+ GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding run_daemon select task\n");
+ ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+ tv,
+ wrs,
+ wws,
+ &run_daemon,
+ NULL);
+ GNUNET_NETWORK_fdset_destroy (wrs);
+ GNUNET_NETWORK_fdset_destroy (wws);
+ return ret;
+}
+
+
+/**
+ * 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)
+{
+ int fh;
+ uint16_t port;
+ enum TALER_MHD_GlobalOptions go;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting anastasis-httpd\n");
+ go = TALER_MHD_GO_NONE;
+ if (AH_connection_close)
+ go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
+ AH_load_terms (config);
+ TALER_MHD_setup (go);
+ AH_cfg = config;
+ global_result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "anastasis",
+ "UPLOAD_LIMIT_MB",
+ &AH_upload_limit_mb))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "UPLOAD_LIMIT_MB");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_amount (config,
+ "anastasis",
+ "INSURANCE",
+ &AH_insurance))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "INSURANCE");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_amount (config,
+ "authorization-question",
+ "COST",
+ &AH_question_cost))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "authorization-question",
+ "COST");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_amount (config,
+ "anastasis",
+ "ANNUAL_FEE",
+ &AH_annual_fee))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "ANNUAL_FEE");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_amount (config,
+ "anastasis",
+ "TRUTH_UPLOAD_FEE",
+ &AH_truth_upload_fee))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "TRUTH_UPLOAD_FEE");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_currency (config,
+ &AH_currency))
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (0 != strcasecmp (AH_currency,
+ AH_annual_fee.currency))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "ANNUAL_FEE",
+ "currency mismatch");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_cmp_currency (&AH_insurance,
+ &AH_annual_fee))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "INSURANCE",
+ "currency mismatch");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "anastasis",
+ "PAYMENT_BACKEND_URL",
+ &AH_backend_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "PAYMENT_BACKEND_URL");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if ( (0 != strncasecmp ("https://",
+ AH_backend_url,
+ strlen ("https://"))) &&
+ (0 != strncasecmp ("http://",
+ AH_backend_url,
+ strlen ("http://"))) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "PAYMENT_BACKEND_URL",
+ "Must be HTTP(S) URL");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ if ( (0 == strcasecmp ("https://",
+ AH_backend_url)) ||
+ (0 == strcasecmp ("http://",
+ AH_backend_url)) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "PAYMENT_BACKEND_URL",
+ "Must have domain name");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "anastasis",
+ "FULFILLMENT_URL",
+ &AH_fulfillment_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "FULFILLMENT_URL");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "anastasis",
+ "ANNUAL_POLICY_UPLOAD_LIMIT",
+ &AH_post_counter))
+ {
+ /* only warn, we will use the default */
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ "anastasis",
+ "ANNUAL_POLICY_UPLOAD_LIMIT");
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "anastasis",
+ "BUSINESS_NAME",
+ &AH_business_name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "BUSINESS_NAME");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ {
+ char *server_salt;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "anastasis",
+ "SERVER_SALT",
+ &server_salt))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "anastasis",
+ "SERVER_SALT");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (&AH_server_salt,
+ sizeof (AH_server_salt),
+ "anastasis-server-salt",
+ strlen ("anastasis-server-salt"),
+ server_salt,
+ strlen (server_salt),
+ NULL,
+ 0));
+ GNUNET_free (server_salt);
+ }
+
+ /* setup HTTP client event loop */
+ AH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &rc);
+ rc = GNUNET_CURL_gnunet_rc_create (AH_ctx);
+ if (NULL != userpass)
+ GNUNET_CURL_set_userpass (AH_ctx,
+ userpass);
+ if (NULL != keyfile)
+ GNUNET_CURL_set_tlscert (AH_ctx,
+ certtype,
+ certfile,
+ keyfile,
+ keypass);
+ if (NULL != apikey)
+ {
+ char *auth_header;
+
+ GNUNET_asprintf (&auth_header,
+ "%s: %s",
+ MHD_HTTP_HEADER_AUTHORIZATION,
+ apikey);
+ if (GNUNET_OK !=
+ GNUNET_CURL_append_header (AH_ctx,
+ auth_header))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed so set %s header, trying without\n",
+ MHD_HTTP_HEADER_AUTHORIZATION);
+ }
+ GNUNET_free (auth_header);
+ }
+
+ if (NULL ==
+ (db = ANASTASIS_DB_plugin_load (config)))
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ fh = TALER_MHD_bind (config,
+ "anastasis",
+ &port);
+ if ( (0 == port) &&
+ (-1 == fh) )
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
+ port,
+ NULL, NULL,
+ &url_handler, NULL,
+ MHD_OPTION_LISTEN_SOCKET, fh,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
+ int) 10 /* 10s */,
+ MHD_OPTION_END);
+ if (NULL == mhd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch HTTP service (port %u in use?), exiting.\n",
+ port);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ global_result = GNUNET_OK;
+ mhd_task = prepare_daemon ();
+}
+
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ enum GNUNET_GenericReturnValue res;
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_string ('A',
+ "auth",
+ "USERNAME:PASSWORD",
+ "use the given USERNAME and PASSWORD for client authentication",
+ &userpass),
+ GNUNET_GETOPT_option_flag ('C',
+ "connection-close",
+ "force HTTP connections to be closed after each request",
+ &AH_connection_close),
+ GNUNET_GETOPT_option_string ('k',
+ "key",
+ "KEYFILE",
+ "file with the private TLS key for TLS client authentication",
+ &keyfile),
+ GNUNET_GETOPT_option_string ('p',
+ "pass",
+ "KEYFILEPASSPHRASE",
+ "passphrase needed to decrypt the TLS client private key file",
+ &keypass),
+ GNUNET_GETOPT_option_string ('K',
+ "apikey",
+ "APIKEY",
+ "API key to use in the HTTP request to the merchant backend",
+ &apikey),
+ GNUNET_GETOPT_option_string ('t',
+ "type",
+ "CERTTYPE",
+ "type of the TLS client certificate, defaults to PEM if not specified",
+ &certtype),
+
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ /* FIRST get the libtalerutil initialization out
+ of the way. Then throw that one away, and force
+ the ANASTASIS defaults to be used! */
+ (void) TALER_project_data_default ();
+ GNUNET_OS_init (ANASTASIS_project_data_default ());
+ res = GNUNET_PROGRAM_run (argc, argv,
+ "anastasis-httpd",
+ "Anastasis HTTP interface",
+ options, &run, NULL);
+ if (GNUNET_SYSERR == res)
+ return 3;
+ if (GNUNET_NO == res)
+ return 0;
+ return (GNUNET_OK == global_result) ? 0 : 1;
+}