diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 1332 |
1 files changed, 907 insertions, 425 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index fefded578..36459fbd7 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-2023 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 @@ -27,19 +27,26 @@ #include <sched.h> #include <sys/resource.h> #include <limits.h> +#include "taler_kyclogic_lib.h" +#include "taler_templating_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd_age-withdraw.h" +#include "taler-exchange-httpd_age-withdraw_reveal.h" +#include "taler-exchange-httpd_aml-decision.h" #include "taler-exchange-httpd_auditors.h" #include "taler-exchange-httpd_batch-deposit.h" #include "taler-exchange-httpd_batch-withdraw.h" +#include "taler-exchange-httpd_coins_get.h" +#include "taler-exchange-httpd_config.h" #include "taler-exchange-httpd_contract.h" #include "taler-exchange-httpd_csr.h" -#include "taler-exchange-httpd_deposit.h" #include "taler-exchange-httpd_deposits_get.h" #include "taler-exchange-httpd_extensions.h" #include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_kyc-check.h" #include "taler-exchange-httpd_kyc-proof.h" #include "taler-exchange-httpd_kyc-wallet.h" +#include "taler-exchange-httpd_kyc-webhook.h" #include "taler-exchange-httpd_link.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_melt.h" @@ -48,19 +55,22 @@ #include "taler-exchange-httpd_purses_create.h" #include "taler-exchange-httpd_purses_deposit.h" #include "taler-exchange-httpd_purses_get.h" +#include "taler-exchange-httpd_purses_delete.h" #include "taler-exchange-httpd_purses_merge.h" #include "taler-exchange-httpd_recoup.h" #include "taler-exchange-httpd_recoup-refresh.h" #include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_refund.h" +#include "taler-exchange-httpd_reserves_attest.h" +#include "taler-exchange-httpd_reserves_close.h" #include "taler-exchange-httpd_reserves_get.h" +#include "taler-exchange-httpd_reserves_get_attest.h" #include "taler-exchange-httpd_reserves_history.h" +#include "taler-exchange-httpd_reserves_open.h" #include "taler-exchange-httpd_reserves_purse.h" -#include "taler-exchange-httpd_reserves_status.h" +#include "taler-exchange-httpd_spa.h" #include "taler-exchange-httpd_terms.h" #include "taler-exchange-httpd_transfers_get.h" -#include "taler-exchange-httpd_wire.h" -#include "taler-exchange-httpd_withdraw.h" #include "taler_exchangedb_lib.h" #include "taler_exchangedb_plugin.h" #include "taler_extensions.h" @@ -72,6 +82,11 @@ #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 ( \ @@ -96,14 +111,17 @@ static int allow_address_reuse; const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; /** - * Handle to the HTTP server. + * Configuration of age restriction + * + * Set after loading the library, enabled in database event handler. */ -static struct MHD_Daemon *mhd; +bool TEH_age_restriction_enabled = false; +struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0}; /** - * Our KYC configuration. + * Handle to the HTTP server. */ -struct TEH_KycOptions TEH_kyc_config; +static struct MHD_Daemon *mhd; /** * How long is caching /keys allowed at most? (global) @@ -122,24 +140,61 @@ struct GNUNET_TIME_Relative TEH_reserve_closing_delay; 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 TALER_EXCHANGEDB_Plugin *TEH_plugin; /** + * Absolute STEFAN parameter. + */ +struct TALER_Amount TEH_stefan_abs; + +/** + * Logarithmic STEFAN parameter. + */ +struct TALER_Amount TEH_stefan_log; + +/** + * Linear STEFAN parameter. + */ +float TEH_stefan_lin; + +/** + * Where to redirect users from "/"? + */ +static char *toplevel_redirect_url; + +/** * Our currency. */ char *TEH_currency; /** - * Our base URL. + * Name of the KYC-AML-trigger evaluation binary. */ -char *TEH_base_url; +char *TEH_kyc_aml_trigger; /** - * Age restriction flags and mask + * Option set to #GNUNET_YES if rewards are enabled. */ -bool TEH_age_restriction_enabled = true; +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. @@ -168,6 +223,7 @@ bool TEH_suicide; * TALER_SIGNATURE_MASTER_EXTENSION. */ struct TALER_MasterSignatureP TEH_extensions_sig; +bool TEH_extensions_signed = false; /** * Value to return from main() @@ -197,6 +253,22 @@ static unsigned long long active_connections; static unsigned long long req_max; /** + * Length of the cspecs array. + */ +static unsigned int num_cspecs; + +/** + * Rendering specs for currencies. + */ +static struct TALER_CurrencySpecification *cspecs; + +/** + * Rendering spec for our currency. + */ +const struct TALER_CurrencySpecification *TEH_cspec; + + +/** * Context for all CURL operations (useful to the event loop) */ struct GNUNET_CURL_Context *TEH_curl_ctx; @@ -268,10 +340,6 @@ handle_post_coins (struct TEH_RequestContext *rc, } h[] = { { - .op = "deposit", - .handler = &TEH_handler_deposit - }, - { .op = "melt", .handler = &TEH_handler_melt }, @@ -317,6 +385,317 @@ handle_post_coins (struct TEH_RequestContext *rc, /** + * Handle a GET "/coins/$COIN_PUB[/$OP]" request. Parses the "coin_pub" + * EdDSA key of the coin and demultiplexes based on $OP. + * + * @param rc request context + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_get_coins (struct TEH_RequestContext *rc, + const char *const args[2]) +{ + struct TALER_CoinSpendPublicKeyP coin_pub; + + if (NULL == args[0]) + { + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + rc->url); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &coin_pub, + sizeof (coin_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB, + args[0]); + } + if (NULL != args[1]) + { + if (0 == strcmp (args[1], + "history")) + return TEH_handler_coins_get (rc, + &coin_pub); + if (0 == strcmp (args[1], + "link")) + return TEH_handler_link (rc, + &coin_pub); + } + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + rc->url); +} + + +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpPostHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub" + * EdDSA key of the officer and demultiplexes based on $OP. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_aml (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[2]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpPostHandler handler; + + } h[] = { + { + .op = "decision", + .handler = &TEH_handler_post_aml_decision + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + root); + return r404 (rc->connection, + args[1]); +} + + +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param args remaining arguments + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpGetHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub" + * EdDSA key of the officer, checks the authentication signature, and + * demultiplexes based on $OP. + * + * @param rc request context + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_get_aml (struct TEH_RequestContext *rc, + const char *const args[]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpGetHandler handler; + + } h[] = { + { + .op = "decisions", + .handler = &TEH_handler_aml_decisions_get + }, + { + .op = "decision", + .handler = &TEH_handler_aml_decision_get + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + "argument missing"); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + if (NULL == args[1]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, + "AML GET operations must specify an operation identifier"); + } + { + const char *sig_hdr; + struct TALER_AmlOfficerSignatureP officer_sig; + + sig_hdr = MHD_lookup_connection_value (rc->connection, + MHD_HEADER_KIND, + TALER_AML_OFFICER_SIGNATURE_HEADER); + if ( (NULL == sig_hdr) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (sig_hdr, + strlen (sig_hdr), + &officer_sig, + sizeof (officer_sig))) || + (GNUNET_OK != + TALER_officer_aml_query_verify (&officer_pub, + &officer_sig)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, + sig_hdr); + } + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + } + + { + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->test_aml_officer (TEH_plugin->cls, + &officer_pub); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + &args[2]); + return r404 (rc->connection, + args[1]); +} + + +/** + * Handle a "/age-withdraw/$ACH/reveal" POST request. Parses the "ACH" + * hash of the commitment from a previous call to + * /reserves/$reserve_pub/age-withdraw + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_age_withdraw (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[2]) +{ + struct TALER_AgeWithdrawCommitmentHashP ach; + + if (0 != strcmp ("reveal", args[1])) + return r404 (rc->connection, + args[1]); + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &ach, + sizeof (ach))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_RESERVE_PUB_MALFORMED, + args[0]); + } + + return TEH_handler_age_withdraw_reveal (rc, + &ach, + root); +} + + +/** * Signature of functions that handle operations on reserves. * * @param rc request context @@ -359,26 +738,26 @@ handle_post_reserves (struct TEH_RequestContext *rc, } h[] = { { - .op = "withdraw", - .handler = &TEH_handler_withdraw - }, - { .op = "batch-withdraw", .handler = &TEH_handler_batch_withdraw }, { - .op = "status", - .handler = &TEH_handler_reserves_status - }, - { - .op = "history", - .handler = &TEH_handler_reserves_history + .op = "age-withdraw", + .handler = &TEH_handler_age_withdraw }, { .op = "purse", .handler = &TEH_handler_reserves_purse }, { + .op = "open", + .handler = &TEH_handler_reserves_open + }, + { + .op = "close", + .handler = &TEH_handler_reserves_close + }, + { .op = NULL, .handler = NULL }, @@ -393,7 +772,7 @@ handle_post_reserves (struct TEH_RequestContext *rc, GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_RESERVE_PUB_MALFORMED, + TALER_EC_GENERIC_RESERVE_PUB_MALFORMED, args[0]); } for (unsigned int i = 0; NULL != h[i].op; i++) @@ -408,6 +787,87 @@ handle_post_reserves (struct TEH_RequestContext *rc, /** + * Signature of functions that handle GET operations on reserves. + * + * @param rc request context + * @param reserve_pub the public key of the reserve + * @return MHD result code + */ +typedef MHD_RESULT +(*ReserveGetOpHandler)(struct TEH_RequestContext *rc, + const struct TALER_ReservePublicKeyP *reserve_pub); + + +/** + * Handle a "GET /reserves/$RESERVE_PUB[/$OP]" request. Parses the "reserve_pub" + * EdDSA key of the reserve and demultiplexes based on $OP. + * + * @param rc request context + * @param args NULL-terminated array of additional options, zero, one or two + * @return MHD result code + */ +static MHD_RESULT +handle_get_reserves (struct TEH_RequestContext *rc, + const char *const args[]) +{ + struct TALER_ReservePublicKeyP reserve_pub; + static const struct + { + /** + * Name of the operation (args[1]), optional + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + ReserveGetOpHandler handler; + + } h[] = { + { + .op = NULL, + .handler = &TEH_handler_reserves_get + }, + { + .op = "history", + .handler = &TEH_handler_reserves_history + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if ( (NULL == args[0]) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &reserve_pub, + sizeof (reserve_pub))) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_RESERVE_PUB_MALFORMED, + args[0]); + } + for (unsigned int i = 0; NULL != h[i].handler; i++) + { + if ( ( (NULL == args[1]) && + (NULL == h[i].op) ) || + ( (NULL != args[1]) && + (NULL != h[i].op) && + (0 == strcmp (h[i].op, + args[1])) ) ) + return h[i].handler (rc, + &reserve_pub); + } + return r404 (rc->connection, + args[1]); +} + + +/** * Signature of functions that handle operations on purses. * * @param connection HTTP request handle @@ -561,6 +1021,11 @@ handle_mhd_completion_callback (void *cls, TEH_check_invariants (); if (NULL != rc->rh_cleaner) rc->rh_cleaner (rc); + if (NULL != rc->root) + { + json_decref (rc->root); + rc->root = NULL; + } TEH_check_invariants (); { #if MHD_VERSION >= 0x00097304 @@ -627,7 +1092,6 @@ proceed_with_handler (struct TEH_RequestContext *rc, 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 @@ -648,8 +1112,9 @@ proceed_with_handler (struct TEH_RequestContext *rc, /* 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)) + if ( (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_POST)) && + (NULL == rc->root) ) { enum GNUNET_GenericReturnValue res; @@ -657,16 +1122,16 @@ proceed_with_handler (struct TEH_RequestContext *rc, &rc->opaque_post_parsing_context, upload_data, upload_data_size, - &root); + &rc->root); if (GNUNET_SYSERR == res) { - GNUNET_assert (NULL == root); + GNUNET_assert (NULL == rc->root); return MHD_NO; /* bad upload, could not even generate error */ } if ( (GNUNET_NO == res) || - (NULL == root) ) + (NULL == rc->root) ) { - GNUNET_assert (NULL == root); + GNUNET_assert (NULL == rc->root); return MHD_YES; /* so far incomplete upload or parser error */ } } @@ -678,9 +1143,9 @@ proceed_with_handler (struct TEH_RequestContext *rc, /* Parse command-line arguments */ /* make a copy of 'url' because 'strtok_r()' will modify */ - memcpy (d, - url, - ulen); + GNUNET_memcpy (d, + url, + ulen); i = 0; args[i++] = strtok_r (d, "/", &sp); while ( (NULL != args[i - 1]) && @@ -703,7 +1168,6 @@ proceed_with_handler (struct TEH_RequestContext *rc, 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, @@ -713,16 +1177,19 @@ proceed_with_handler (struct TEH_RequestContext *rc, /* 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) + if (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_POST)) ret = rh->handler.post (rc, - root, + 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) */ + 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; } @@ -765,6 +1232,20 @@ handler_seed (struct TEH_RequestContext *rc, /** + * Signature of functions that handle simple + * POST operations for the management API. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*ManagementPostHandler)( + struct MHD_Connection *connection, + const json_t *root); + + +/** * Handle POST "/management/..." requests. * * @param rc request context @@ -777,6 +1258,55 @@ handle_post_management (struct TEH_RequestContext *rc, const json_t *root, const char *const args[]) { + static const struct + { + const char *arg0; + const char *arg1; + ManagementPostHandler handler; + } plain_posts[] = { + { + .arg0 = "keys", + .handler = &TEH_handler_management_post_keys + }, + { + .arg0 = "wire", + .handler = &TEH_handler_management_post_wire + }, + { + .arg0 = "wire", + .arg1 = "disable", + .handler = &TEH_handler_management_post_wire_disable + }, + { + .arg0 = "wire-fee", + .handler = &TEH_handler_management_post_wire_fees + }, + { + .arg0 = "global-fee", + .handler = &TEH_handler_management_post_global_fees + }, + { + .arg0 = "extensions", + .handler = &TEH_handler_management_post_extensions + }, + { + .arg0 = "drain", + .handler = &TEH_handler_management_post_drain + }, + { + .arg0 = "aml-officers", + .handler = &TEH_handler_management_aml_officers + }, + { + .arg0 = "partners", + .handler = &TEH_handler_management_partners + }, + { + NULL, + NULL, + NULL + } + }; if (NULL == args[0]) { GNUNET_break_op (0); @@ -872,82 +1402,22 @@ handle_post_management (struct TEH_RequestContext *rc, &exchange_pub, root); } - if (0 == strcmp (args[0], - "keys")) - { - if (NULL != args[1]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/keys/*"); - } - return TEH_handler_management_post_keys (rc->connection, - root); - } - if (0 == strcmp (args[0], - "wire")) - { - if (NULL == args[1]) - return TEH_handler_management_post_wire (rc->connection, - root); - if ( (0 != strcmp (args[1], - "disable")) || - (NULL != args[2]) ) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/wire/disable"); - } - return TEH_handler_management_post_wire_disable (rc->connection, - root); - } - if (0 == strcmp (args[0], - "wire-fee")) - { - if (NULL != args[1]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/wire-fee/*"); - } - return TEH_handler_management_post_wire_fees (rc->connection, - root); - } - if (0 == strcmp (args[0], - "global-fee")) - { - if (NULL != args[1]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/global-fee/*"); - } - return TEH_handler_management_post_global_fees (rc->connection, - root); - } - if (0 == strcmp (args[0], - "extensions")) - { - if (NULL != args[1]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/extensions/*"); - } - return TEH_handler_management_post_extensions (rc->connection, - root); - } - if (0 == strcmp (args[0], - "drain")) - { - if (NULL != args[1]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/drain/*"); + for (unsigned int i = 0; + NULL != plain_posts[i].handler; + i++) + { + if (0 == strcmp (args[0], + plain_posts[i].arg0)) + { + if ( ( (NULL == args[1]) && + (NULL == plain_posts[i].arg1) ) || + ( (NULL != args[1]) && + (NULL != plain_posts[i].arg1) && + (0 == strcmp (args[1], + plain_posts[i].arg1)) ) ) + return plain_posts[i].handler (rc->connection, + root); } - return TEH_handler_management_post_drain (rc->connection, - root); } GNUNET_break_op (0); return r404 (rc->connection, @@ -956,7 +1426,7 @@ handle_post_management (struct TEH_RequestContext *rc, /** - * Handle a get "/management" request. + * Handle a GET "/management" request. * * @param rc request context * @param args array of additional options (must be [0] == "keys") @@ -1037,6 +1507,56 @@ handle_post_auditors (struct TEH_RequestContext *rc, /** + * Generates the response for "/", redirecting the + * client to the ``toplevel_redirect_url``. + * + * @param rc request context + * @param args remaining arguments (should be empty) + * @return MHD result code + */ +static MHD_RESULT +toplevel_redirect (struct TEH_RequestContext *rc, + const char *const args[]) +{ + const char *text = "Redirecting to /webui/"; + struct MHD_Response *response; + + response = MHD_create_response_from_buffer (strlen (text), + (void *) text, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + TALER_MHD_add_global_headers (response); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain")); + if (MHD_NO == + MHD_add_response_header (response, + MHD_HTTP_HEADER_LOCATION, + toplevel_redirect_url)) + { + GNUNET_break (0); + MHD_destroy_response (response); + return MHD_NO; + } + + { + MHD_RESULT ret; + + ret = MHD_queue_response (rc->connection, + MHD_HTTP_FOUND, + response); + MHD_destroy_response (response); + return ret; + } +} + + +/** * Handle incoming HTTP request. * * @param cls closure for MHD daemon (unused) @@ -1069,15 +1589,11 @@ handle_mhd_request (void *cls, .data = "User-agent: *\nDisallow: /\n", .response_code = MHD_HTTP_OK }, - /* Landing page, tell humans to go away. */ + /* Landing page, redirect to toplevel_redirect_url */ { .url = "", .method = MHD_HTTP_METHOD_GET, - .handler.get = TEH_handler_static_response, - .mime_type = "text/plain", - .data = - "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n", - .response_code = MHD_HTTP_OK + .handler.get = &toplevel_redirect }, /* 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 @@ -1093,6 +1609,12 @@ handle_mhd_request (void *cls, .method = MHD_HTTP_METHOD_GET, .handler.get = &handler_seed }, + /* Configuration */ + { + .url = "config", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEH_handler_config + }, /* Performance metrics */ { .url = "metrics", @@ -1117,12 +1639,6 @@ handle_mhd_request (void *cls, .method = MHD_HTTP_METHOD_GET, .handler.get = &TEH_keys_get_handler, }, - /* Requests for wiring information */ - { - .url = "wire", - .method = MHD_HTTP_METHOD_GET, - .handler.get = &TEH_handler_wire - }, { .url = "batch-deposit", .method = MHD_HTTP_METHOD_POST, @@ -1146,8 +1662,9 @@ handle_mhd_request (void *cls, { .url = "reserves", .method = MHD_HTTP_METHOD_GET, - .handler.get = &TEH_handler_reserves_get, - .nargs = 1 + .handler.get = &handle_get_reserves, + .nargs = 2, + .nargs_is_upper_bound = true }, { .url = "reserves", @@ -1155,6 +1672,24 @@ handle_mhd_request (void *cls, .handler.post = &handle_post_reserves, .nargs = 2 }, + { + .url = "age-withdraw", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &handle_post_age_withdraw, + .nargs = 2 + }, + { + .url = "reserves-attest", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEH_handler_reserves_get_attest, + .nargs = 1 + }, + { + .url = "reserves-attest", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &TEH_handler_reserves_attest, + .nargs = 1 + }, /* coins */ { .url = "coins", @@ -1165,8 +1700,9 @@ handle_mhd_request (void *cls, { .url = "coins", .method = MHD_HTTP_METHOD_GET, - .handler.get = TEH_handler_link, + .handler.get = &handle_get_coins, .nargs = 2, + .nargs_is_upper_bound = true }, /* refreshes/$RCH/reveal */ { @@ -1194,7 +1730,7 @@ handle_mhd_request (void *cls, .url = "purses", .method = MHD_HTTP_METHOD_POST, .handler.post = &handle_post_purses, - .nargs = 2 // ?? + .nargs = 2 }, /* Getting purse status */ { @@ -1203,6 +1739,13 @@ handle_mhd_request (void *cls, .handler.get = &TEH_handler_purses_get, .nargs = 2 }, + /* Deleting purse */ + { + .url = "purses", + .method = MHD_HTTP_METHOD_DELETE, + .handler.delete = &TEH_handler_purses_delete, + .nargs = 1 + }, /* Getting contracts */ { .url = "contracts", @@ -1215,7 +1758,7 @@ handle_mhd_request (void *cls, .url = "kyc-check", .method = MHD_HTTP_METHOD_GET, .handler.get = &TEH_handler_kyc_check, - .nargs = 1 + .nargs = 3 }, { .url = "kyc-proof", @@ -1229,6 +1772,20 @@ handle_mhd_request (void *cls, .handler.post = &TEH_handler_kyc_wallet, .nargs = 0 }, + { + .url = "kyc-webhook", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEH_handler_kyc_webhook_get, + .nargs = 16, /* more is not plausible */ + .nargs_is_upper_bound = true + }, + { + .url = "kyc-webhook", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &TEH_handler_kyc_webhook_post, + .nargs = 16, /* more is not plausible */ + .nargs_is_upper_bound = true + }, /* POST management endpoints */ { .url = "management", @@ -1252,6 +1809,28 @@ handle_mhd_request (void *cls, .nargs = 4, .nargs_is_upper_bound = true }, + /* AML endpoints */ + { + .url = "aml", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &handle_get_aml, + .nargs = 4, + .nargs_is_upper_bound = true + }, + { + .url = "aml", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &handle_post_aml, + .nargs = 2 + }, + { + .url = "webui", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEH_handler_spa, + .nargs = 1, + .nargs_is_upper_bound = true + }, + /* mark end of list */ { .url = NULL @@ -1292,30 +1871,8 @@ handle_mhd_request (void *cls, if (0 == strcasecmp (method, MHD_HTTP_METHOD_POST)) { - const char *cl; - - /* Maybe check for maximum upload size - and refuse requests if they are just too big. */ - cl = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_CONTENT_LENGTH); - if (NULL != cl) - { - unsigned long long cv; - char dummy; - - if (1 != sscanf (cl, - "%llu%c", - &cv, - &dummy)) - { - /* Not valid HTTP request, just close connection. */ - GNUNET_break_op (0); - return MHD_NO; - } - if (cv > TALER_MHD_REQUEST_BUFFER_MAX) - return TALER_MHD_reply_request_too_large (connection); - } + TALER_MHD_check_content_length (connection, + TALER_MHD_REQUEST_BUFFER_MAX); } } @@ -1395,7 +1952,8 @@ handle_mhd_request (void *cls, continue; found = true; /* The URL is a match! What we now do depends on the method. */ - if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS)) + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_OPTIONS)) { GNUNET_async_scope_restore (&old_scope); return TALER_MHD_reply_cors_preflight (connection); @@ -1493,276 +2051,178 @@ handle_mhd_request (void *cls, /** - * Load general KYC configuration parameters for the exchange server into the - * #TEH_kyc_config variable. + * Load configuration parameters for the exchange + * server into the corresponding global variables. * * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -parse_kyc_settings (void) +exchange_serve_process_config (void) { + static struct TALER_CurrencySpecification defspec = { + .num_fractional_input_digits = 2, + .num_fractional_normal_digits = 2, + .num_fractional_trailing_zero_digits = 2 + }; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - "exchange", - "KYC_WITHDRAW_PERIOD", - &TEH_kyc_config.withdraw_period)) + TALER_KYCLOGIC_kyc_init (TEH_cfg)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "KYC_WITHDRAW_PERIOD", - "valid relative time expected"); return GNUNET_SYSERR; } - if (GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) - return GNUNET_OK; if (GNUNET_OK != - TALER_config_get_amount (TEH_cfg, - "exchange", - "KYC_WITHDRAW_LIMIT", - &TEH_kyc_config.withdraw_limit)) - return GNUNET_SYSERR; - if (0 != strcasecmp (TEH_kyc_config.withdraw_limit.currency, - TEH_currency)) + GNUNET_CONFIGURATION_get_value_number (TEH_cfg, + "exchange", + "MAX_REQUESTS", + &req_max)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "KYC_WITHDRAW_LIMIT", - "currency mismatch"); - return GNUNET_SYSERR; + req_max = ULLONG_MAX; } - return GNUNET_OK; -} - - -/** - * Load OAuth2.0 configuration parameters for the exchange server into the - * #TEH_kyc_config variable. - * - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_kyc_oauth_cfg (void) -{ - char *s; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_OAUTH2_AUTH_URL", - &s)) + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "exchangedb", + "IDLE_RESERVE_EXPIRATION_TIME", + &TEH_reserve_closing_delay)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_AUTH_URL"); - return GNUNET_SYSERR; - } - if ( (! TALER_url_valid_charset (s)) || - ( (0 != strncasecmp (s, - "http://", - strlen ("http://"))) && - (0 != strncasecmp (s, - "https://", - strlen ("https://"))) ) ) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_AUTH_URL", - "not a valid URL"); - GNUNET_free (s); - return GNUNET_SYSERR; + "exchangedb", + "IDLE_RESERVE_EXPIRATION_TIME"); + /* use default */ + TEH_reserve_closing_delay + = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, + 4); } - TEH_kyc_config.details.oauth2.auth_url = s; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_OAUTH2_LOGIN_URL", - &s)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_LOGIN_URL"); - return GNUNET_SYSERR; - } - if ( (! TALER_url_valid_charset (s)) || - ( (0 != strncasecmp (s, - "http://", - strlen ("http://"))) && - (0 != strncasecmp (s, - "https://", - strlen ("https://"))) ) ) + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "exchange", + "MAX_KEYS_CACHING", + &TEH_max_keys_caching)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_LOGIN_URL", - "not a valid URL"); - GNUNET_free (s); + "exchange", + "MAX_KEYS_CACHING", + "valid relative time expected"); return GNUNET_SYSERR; } - TEH_kyc_config.details.oauth2.login_url = s; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_INFO_URL", - &s)) + "exchange", + "KYC_AML_TRIGGER", + &TEH_kyc_aml_trigger)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_INFO_URL"); - return GNUNET_SYSERR; - } - if ( (! TALER_url_valid_charset (s)) || - ( (0 != strncasecmp (s, - "http://", - strlen ("http://"))) && - (0 != strncasecmp (s, - "https://", - strlen ("https://"))) ) ) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_INFO_URL", - "not a valid URL"); - GNUNET_free (s); + "exchange", + "KYC_AML_TRIGGER"); return GNUNET_SYSERR; } - TEH_kyc_config.details.oauth2.info_url = s; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_OAUTH2_CLIENT_ID", - &s)) + "exchange", + "TOPLEVEL_REDIRECT_URL", + &toplevel_redirect_url)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_CLIENT_ID"); - return GNUNET_SYSERR; + toplevel_redirect_url = GNUNET_strdup ("/terms"); } - TEH_kyc_config.details.oauth2.client_id = s; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_OAUTH2_CLIENT_SECRET", - &s)) + TALER_config_get_currency (TEH_cfg, + &TEH_currency)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_CLIENT_SECRET"); + "taler", + "CURRENCY"); return GNUNET_SYSERR; } - TEH_kyc_config.details.oauth2.client_secret = s; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange-kyc-oauth2", - "KYC_OAUTH2_POST_URL", - &s)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-kyc-oauth2", - "KYC_OAUTH2_POST_URL"); + TALER_CONFIG_parse_currencies (TEH_cfg, + &num_cspecs, + &cspecs)) return GNUNET_SYSERR; - } - TEH_kyc_config.details.oauth2.post_kyc_redirect_url = s; - return GNUNET_OK; -} - - -/** - * 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) -{ + for (unsigned int i = 0; i<num_cspecs; i++) { - char *kyc_mode; + struct TALER_CurrencySpecification *cspec; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (TEH_cfg, - "exchange", - "KYC_MODE", - &kyc_mode)) + cspec = &cspecs[i]; + if (0 == strcmp (TEH_currency, + cspec->currency)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "KYC_MODE"); - return GNUNET_SYSERR; + TEH_cspec = cspec; + break; } - if (0 == strcasecmp (kyc_mode, - "NONE")) - { - TEH_kyc_config.mode = TEH_KYC_NONE; - } - else if (0 == strcasecmp (kyc_mode, - "OAUTH2")) - { - TEH_kyc_config.mode = TEH_KYC_OAUTH2; - if (GNUNET_OK != - parse_kyc_oauth_cfg ()) - { - GNUNET_free (kyc_mode); - return GNUNET_SYSERR; - } - } - else - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "KYC_MODE", - "Must be 'NONE' or 'OAUTH2'"); - GNUNET_free (kyc_mode); - return GNUNET_SYSERR; - } - GNUNET_free (kyc_mode); } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (TEH_cfg, - "exchange", - "MAX_REQUESTS", - &req_max)) + if (NULL == TEH_cspec) { - req_max = ULLONG_MAX; + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + "taler", + "CURRENCY", + "Lacking enabled currency specification for the given currency, using default"); + defspec.map_alt_unit_names + = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("0", + TEH_currency) + ); + defspec.name = TEH_currency; + GNUNET_assert (strlen (TEH_currency) < + sizeof (defspec.currency)); + strcpy (defspec.currency, + TEH_currency); + TEH_cspec = &defspec; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - "exchangedb", - "IDLE_RESERVE_EXPIRATION_TIME", - &TEH_reserve_closing_delay)) + TALER_config_get_amount (TEH_cfg, + "exchange", + "AML_THRESHOLD", + &TEH_aml_threshold)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchangedb", - "IDLE_RESERVE_EXPIRATION_TIME"); - /* use default */ - TEH_reserve_closing_delay - = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, - 4); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need amount in section `exchange' under `AML_THRESHOLD'\n"); + return GNUNET_SYSERR; } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - "exchange", - "MAX_KEYS_CACHING", - &TEH_max_keys_caching)) + TALER_config_get_amount (TEH_cfg, + "exchange", + "STEFAN_ABS", + &TEH_stefan_abs)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &TEH_stefan_abs)); + } + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, "exchange", - "MAX_KEYS_CACHING", - "valid relative time expected"); - return GNUNET_SYSERR; + "STEFAN_LOG", + &TEH_stefan_log)) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &TEH_stefan_log)); } if (GNUNET_OK != - TALER_config_get_currency (TEH_cfg, - &TEH_currency)) + GNUNET_CONFIGURATION_get_value_float (TEH_cfg, + "exchange", + "STEFAN_LIN", + &TEH_stefan_lin)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler", - "CURRENCY"); + TEH_stefan_lin = 0.0f; + } + + if (0 != strcmp (TEH_currency, + TEH_aml_threshold.currency)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n"); + return GNUNET_SYSERR; + } + TEH_enable_rewards + = GNUNET_CONFIGURATION_get_value_yesno ( + TEH_cfg, + "exchange", + "ENABLE_REWARDS"); + if (GNUNET_SYSERR == TEH_enable_rewards) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need YES or NO in section `exchange' under `ENABLE_REWARDS'\n"); return GNUNET_SYSERR; } if (GNUNET_OK != @@ -1785,35 +2245,6 @@ exchange_serve_process_config (void) return GNUNET_SYSERR; } - if (TEH_KYC_NONE != TEH_kyc_config.mode) - { - if (GNUNET_YES == - GNUNET_CONFIGURATION_have_value (TEH_cfg, - "exchange", - "KYC_WALLET_BALANCE_LIMIT")) - { - if ( (GNUNET_OK != - TALER_config_get_amount (TEH_cfg, - "exchange", - "KYC_WALLET_BALANCE_LIMIT", - &TEH_kyc_config.wallet_balance_limit)) || - (0 != strcasecmp (TEH_currency, - TEH_kyc_config.wallet_balance_limit.currency)) ) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "KYC_WALLET_BALANCE_LIMIT", - "valid amount expected"); - return GNUNET_SYSERR; - } - } - else - { - memset (&TEH_kyc_config.wallet_balance_limit, - 0, - sizeof (TEH_kyc_config.wallet_balance_limit)); - } - } { char *master_public_key_str; @@ -1829,11 +2260,10 @@ exchange_serve_process_config (void) 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_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, "exchange", @@ -1842,23 +2272,46 @@ exchange_serve_process_config (void) GNUNET_free (master_public_key_str); return GNUNET_SYSERR; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Launching exchange with public key `%s'...\n", + master_public_key_str); GNUNET_free (master_public_key_str); } - if (TEH_KYC_NONE != TEH_kyc_config.mode) + { + char *attr_enc_key_str; + if (GNUNET_OK != - parse_kyc_settings ()) + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "exchange", + "ATTRIBUTE_ENCRYPTION_KEY", + &attr_enc_key_str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "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); } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Launching exchange with public key `%s'...\n", - GNUNET_p2s (&TEH_master_public_key.eddsa_pub)); - if (NULL == - (TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg))) + for (unsigned int i = 0; i<MAX_DB_RETRIES; i++) + { + TEH_plugin = TALER_EXCHANGEDB_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\n"); + "Failed to initialize DB subsystem. Giving up.\n"); return GNUNET_SYSERR; } return GNUNET_OK; @@ -2003,7 +2456,9 @@ run_single_request (void) xfork = fork (); if (-1 == xfork) { - global_ret = EXIT_FAILURE; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "fork"); + global_ret = EXIT_NO_RESTART; GNUNET_SCHEDULER_shutdown (); return; } @@ -2090,12 +2545,17 @@ do_shutdown (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 (); @@ -2114,6 +2574,12 @@ do_shutdown (void *cls) GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); exchange_curl_rc = NULL; } + TALER_TEMPLATING_done (); + TEH_cspec = NULL; + TALER_CONFIG_free_currencies (num_cspecs, + cspecs); + num_cspecs = 0; + cspecs = NULL; } @@ -2151,31 +2617,48 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + if (GNUNET_OK != + TEH_spa_init ()) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + TALER_TEMPLATING_init ("exchange")) + { + global_ret = EXIT_NOTINSTALLED; + GNUNET_SCHEDULER_shutdown (); + return; + } if (GNUNET_SYSERR == TEH_plugin->preflight (TEH_plugin->cls)) { - global_ret = EXIT_FAILURE; + GNUNET_break (0); + global_ret = EXIT_NO_RESTART; GNUNET_SCHEDULER_shutdown (); return; } if (GNUNET_OK != TEH_extensions_init ()) { - global_ret = EXIT_FAILURE; + global_ret = EXIT_NOTINSTALLED; GNUNET_SCHEDULER_shutdown (); return; } if (GNUNET_OK != TEH_keys_init ()) { - global_ret = EXIT_FAILURE; + GNUNET_break (0); + global_ret = EXIT_NO_RESTART; GNUNET_SCHEDULER_shutdown (); return; } if (GNUNET_OK != TEH_wire_init ()) { - global_ret = EXIT_FAILURE; + GNUNET_break (0); + global_ret = EXIT_NO_RESTART; GNUNET_SCHEDULER_shutdown (); return; } @@ -2187,7 +2670,7 @@ run (void *cls, if (NULL == TEH_curl_ctx) { GNUNET_break (0); - global_ret = EXIT_FAILURE; + global_ret = EXIT_NO_RESTART; GNUNET_SCHEDULER_shutdown (); return; } @@ -2240,7 +2723,6 @@ run (void *cls, global_ret = EXIT_SUCCESS; TALER_MHD_daemon_start (mhd); atexit (&write_stats); - #if HAVE_DEVELOPER if (NULL != input_filename) run_single_request (); |