From d4fb0695ceb2f1c1ceb0ef1fc6fdded668396d4d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 7 Aug 2022 21:48:38 +0200 Subject: -start kyc-tester helper --- src/kyclogic/Makefile.am | 29 +- src/kyclogic/plugin_kyclogic_oauth2.c | 2 +- src/kyclogic/plugin_kyclogic_template.c | 17 +- src/kyclogic/taler-exchange-kyc-tester.c | 801 +++++++++++++++++++++++++++++++ 4 files changed, 837 insertions(+), 12 deletions(-) create mode 100644 src/kyclogic/taler-exchange-kyc-tester.c (limited to 'src/kyclogic') diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am index 4a4f60a48..7c754a806 100644 --- a/src/kyclogic/Makefile.am +++ b/src/kyclogic/Makefile.am @@ -16,10 +16,37 @@ EXTRA_DIST = \ kyclogic.conf \ kyclogic-oauth2.conf +bin_PROGRAMS = \ + taler-exchange-kyc-tester + +taler_exchange_kyc_tester_SOURCES = \ + taler-exchange-kyc-tester.c +taler_exchange_kyc_tester_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/mhd/libtalermhd.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lmicrohttpd \ + -lgnunetcurl \ + -lgnunetutil \ + -lgnunetjson \ + -ljansson \ + -lcurl \ + -lz \ + $(XLIB) + + + plugindir = $(libdir)/taler plugin_LTLIBRARIES = \ - libtaler_plugin_kyclogic_oauth2.la + libtaler_plugin_kyclogic_oauth2.la \ + libtaler_plugin_kyclogic_template.la + +libtaler_plugin_kyclogic_template_la_SOURCES = \ + plugin_kyclogic_template.c +libtaler_plugin_kyclogic_template_la_LIBADD = \ + $(LTLIBINTL) libtaler_plugin_kyclogic_oauth2_la_SOURCES = \ plugin_kyclogic_oauth2.c diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index ac13355e4..42f195be3 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -975,7 +975,7 @@ oauth2_webhook (void *cls, TALER_KYCLOGIC_ProviderLookupCallback plc, void *plc_cls, const char *http_method, - const char *url_path[], + const char *const url_path[], struct MHD_Connection *connection, const json_t *body, TALER_KYCLOGIC_WebhookCallback cb, diff --git a/src/kyclogic/plugin_kyclogic_template.c b/src/kyclogic/plugin_kyclogic_template.c index 1cc9b97c3..e59c15da1 100644 --- a/src/kyclogic/plugin_kyclogic_template.c +++ b/src/kyclogic/plugin_kyclogic_template.c @@ -164,10 +164,6 @@ struct TALER_KYCLOGIC_WebhookHandle */ const struct TALER_KYCLOGIC_ProviderDetails *pd; - /** - * Overall plugin state. - */ - struct PluginState *ps; }; @@ -210,6 +206,7 @@ template_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) * @param cls the @e cls of this struct with the plugin-specific state * @param pd provider configuration details * @param account_id which account to trigger process for + * @param legitimization_uuid unique ID for the legitimization process * @param cb function to call with the result * @param cb_cls closure for @a cb * @return handle to cancel operation early @@ -218,6 +215,7 @@ static struct TALER_KYCLOGIC_InitiateHandle * template_initiate (void *cls, const struct TALER_KYCLOGIC_ProviderDetails *pd, const struct TALER_PaytoHashP *account_id, + uint64_t legitimization_uuid, TALER_KYCLOGIC_InitiateCallback cb, void *cb_cls) { @@ -230,7 +228,7 @@ template_initiate (void *cls, ih->h_payto = *account_id; ih->pd = pd; GNUNET_break (0); // FIXME: add actual initiation logic! - return res; + return ih; } @@ -282,7 +280,7 @@ template_proof (void *cls, ph->cb_cls = cb_cls; GNUNET_break (0); // FIXME: start check! - return ps; + return ph; } @@ -321,10 +319,9 @@ template_webhook (void *cls, TALER_KYCLOGIC_ProviderLookupCallback plc, void *plc_cls, const char *http_method, - const char *url_path, + const char *const url_path[], struct MHD_Connection *connection, - size_t body_size, - const void *body, + const json_t *body, TALER_KYCLOGIC_WebhookCallback cb, void *cb_cls) { @@ -370,7 +367,7 @@ libtaler_plugin_kyclogic_template_init (void *cls) ps = GNUNET_new (struct PluginState); ps->cfg = cfg; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + GNUNET_CONFIGURATION_get_value_string (cfg, "exchange", "BASE_URL", &ps->exchange_base_url)) diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c new file mode 100644 index 000000000..24809a82d --- /dev/null +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -0,0 +1,801 @@ +/* + This file is part of TALER + Copyright (C) 2022 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file taler-exchange-kyc-tester.c + * @brief tool to test KYC integrations + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include +#include +#include "taler_mhd_lib.h" +#include "taler_json_lib.h" +#include "taler_crypto_lib.h" +#include "taler_kyclogic_plugin.h" +#include + + +/** + * @brief Context in which the exchange is processing + * all requests + */ +struct TEKT_RequestContext +{ + + /** + * Opaque parsing context. + */ + void *opaque_post_parsing_context; + + /** + * Request handler responsible for this request. + */ + const struct TEKT_RequestHandler *rh; + + /** + * Request URL (for logging). + */ + const char *url; + + /** + * Connection we are processing. + */ + struct MHD_Connection *connection; + + /** + * @e rh-specific cleanup routine. Function called + * upon completion of the request that should + * clean up @a rh_ctx. Can be NULL. + */ + void + (*rh_cleaner)(struct TEKT_RequestContext *rc); + + /** + * @e rh-specific context. Place where the request + * handler can associate state with this request. + * Can be NULL. + */ + void *rh_ctx; +}; + + +/** + * @brief Struct describing an URL and the handler for it. + */ +struct TEKT_RequestHandler +{ + + /** + * URL the handler is for (first part only). + */ + const char *url; + + /** + * Method the handler is for. + */ + const char *method; + + /** + * Callbacks for handling of the request. Which one is used + * depends on @e method. + */ + union + { + /** + * Function to call to handle a GET requests (and those + * with @e method NULL). + * + * @param rc context for the request + * @param mime_type the @e mime_type for the reply (hint, can be NULL) + * @param args array of arguments, needs to be of length @e args_expected + * @return MHD result code + */ + MHD_RESULT + (*get)(struct TEKT_RequestContext *rc, + const char *const args[]); + + + /** + * Function to call to handle a POST request. + * + * @param rc context for the request + * @param json uploaded JSON data + * @param args array of arguments, needs to be of length @e args_expected + * @return MHD result code + */ + MHD_RESULT + (*post)(struct TEKT_RequestContext *rc, + const json_t *root, + const char *const args[]); + + } handler; + + /** + * Number of arguments this handler expects in the @a args array. + */ + unsigned int nargs; + + /** + * Is the number of arguments given in @e nargs only an upper bound, + * and calling with fewer arguments could be OK? + */ + bool nargs_is_upper_bound; + + /** + * Mime type to use in reply (hint, can be NULL). + */ + const char *mime_type; + + /** + * Raw data for the @e handler, can be NULL for none provided. + */ + const void *data; + + /** + * Number of bytes in @e data, 0 for data is 0-terminated (!). + */ + size_t data_size; + + /** + * Default response code. 0 for none provided. + */ + unsigned int response_code; +}; + + +/** + * The exchange's configuration (global) + */ +static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg; + +/** + * Handle to the HTTP server. + */ +static struct MHD_Daemon *mhd; + +/** + * Our currency. + */ +static char *TEKT_currency; + +/** + * Our base URL. + */ +static char *TEKT_base_url; + +/** + * Value to return from main() + */ +static int global_ret; + +/** + * Port to run the daemon on. + */ +static uint16_t serve_port; + +/** + * Context for all CURL operations (useful to the event loop) + */ +static struct GNUNET_CURL_Context *TEKT_curl_ctx; + +/** + * Context for integrating #TEKT_curl_ctx with the + * GNUnet event loop. + */ +static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; + + +/** + * 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) +{ + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + details); +} + + +/** + * 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 TEKT_RequestContext *rc = *con_cls; + + (void) cls; + if (NULL == rc) + return; + if (NULL != rc->rh_cleaner) + rc->rh_cleaner (rc); + { +#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_free (rc); + *con_cls = NULL; +} + + +/** + * 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 TEKT_RequestContext *rc, + const char *url, + const char *upload_data, + size_t *upload_data_size) +{ + const struct TEKT_RequestHandler *rh = rc->rh; + // FIXME: handle '-1 == rh->nargs'!!! + 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 */ + 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 (NULL != root) + ret = rh->handler.post (rc, + root, + args); + else /* We also only have "POST" or "GET" in the API for at this point + (OPTIONS/HEAD are taken care of earlier) */ + ret = rh->handler.get (rc, + args); + } + json_decref (root); + return ret; +} + + +/** + * 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 TEKT_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 TEKT_RequestHandler handlers[] = { +#if FIXME + /* KYC endpoints */ + { + .url = "kyc-check", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEKT_handler_kyc_check, + .nargs = 1 + }, + { + .url = "kyc-proof", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEKT_handler_kyc_proof, + .nargs = 1 + }, + { + .url = "kyc-wallet", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &TEKT_handler_kyc_wallet, + .nargs = 0 + }, + { + .url = "kyc-webhook", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &TEKT_handler_kyc_webhook_post, + .nargs = -1 + }, + { + .url = "kyc-webhook", + .method = MHD_HTTP_METHOD_GET, + .handler.post = &TEKT_handler_kyc_webhook_get, + .nargs = -1 + }, +#endif + /* mark end of list */ + { + .url = NULL + } + }; + struct TEKT_RequestContext *rc = *con_cls; + + (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 TEKT_RequestContext); + rc->url = url; + rc->connection = connection; + } + + 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); + return ret; + } + 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 TEKT_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)) + { + 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); + 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 TEKT_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); + return ret; + } +} + + +/** + * Load configuration parameters for the exchange + * server into the corresponding global variables. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +exchange_serve_process_config (void) +{ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEKT_cfg, + "exchange", + "BASE_URL", + &TEKT_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL"); + return GNUNET_SYSERR; + } + if (! TALER_url_valid_charset (TEKT_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL", + "invalid URL"); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Function run on shutdown. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + struct MHD_Daemon *mhd; + (void) cls; + + mhd = TALER_MHD_daemon_stop (); + if (NULL != mhd) + MHD_stop_daemon (mhd); + if (NULL != TEKT_curl_ctx) + { + GNUNET_CURL_fini (TEKT_curl_ctx); + TEKT_curl_ctx = NULL; + } + if (NULL != exchange_curl_rc) + { + GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); + exchange_curl_rc = NULL; + } +} + + +/** + * 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; + + (void) cls; + (void) args; + (void ) cfgfile; + TALER_MHD_setup (TALER_MHD_GO_NONE); + TEKT_cfg = config; + + if (GNUNET_OK != + exchange_serve_process_config ()) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + TEKT_curl_ctx + = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &exchange_curl_rc); + if (NULL == TEKT_curl_ctx) + { + GNUNET_break (0); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + fh = TALER_MHD_bind (TEKT_cfg, + "exchange", + &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_SOCKET, + fh, + MHD_OPTION_EXTERNAL_LOGGER, + &TALER_MHD_handle_logs, + NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, + NULL, + 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); +} + + +/** + * The main function of the taler-exchange-httpd server ("the exchange"). + * + * @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) +{ + const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_help ( + "tool to test KYC provider integrations"), + GNUNET_GETOPT_OPTION_END + }; + enum GNUNET_GenericReturnValue ret; + + TALER_OS_init (); + ret = GNUNET_PROGRAM_run (argc, argv, + "taler-exchange-kyc-tester", + "tool to test KYC provider integrations", + options, + &run, NULL); + if (GNUNET_SYSERR == ret) + return EXIT_INVALIDARGUMENT; + if (GNUNET_NO == ret) + return EXIT_SUCCESS; + return global_ret; +} + + +/* end of taler-exchange-kyc-tester.c */ -- cgit v1.2.3