diff --git a/contrib/gana b/contrib/gana index ca56accac..8c7d9be40 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit ca56accac72b6ce050a38d36172390b14100a538 +Subproject commit 8c7d9be40ba627348da3e01b91b4f1d3cc78631f diff --git a/src/curl/curl.c b/src/curl/curl.c index 1410294e4..73fcf86a4 100644 --- a/src/curl/curl.c +++ b/src/curl/curl.c @@ -37,7 +37,7 @@ * @param body JSON body to add to @e ctx * @return #GNUNET_OK on success #GNUNET_SYSERR on failure */ -int +enum GNUNET_GenericReturnValue TALER_curl_easy_post (struct TALER_CURL_PostContext *ctx, CURL *eh, const json_t *body) diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index d199e4bc2..7779c38b1 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -123,6 +123,7 @@ taler_exchange_httpd_LDADD = \ -lgnunetutil \ -lgnunetjson \ -ljansson \ + -lcurl \ -lz \ $(XLIB) diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 590b9c39f..3bcea08fb 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -102,3 +102,7 @@ KYC_MODE = NONE # KYC Client secret used to obtain access tokens. # KYC_OAUTH2_CLIENT_SECRET = + +# Where to redirect clients after successful +# authorization? +# KYC_OAUTH_POST_URL = https://bank.com/ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 5491c3ef3..1feede1a8 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -102,6 +102,11 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ char *TEH_currency; +/** + * Our base URL. + */ +char *TEH_base_url; + /** * Default timeout in seconds for HTTP requests. */ @@ -134,6 +139,17 @@ static unsigned long long req_count; */ 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 #exchange_curl_ctx with the + * GNUnet event loop. + */ +static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; + /** * Signature of functions that handle operations on coins. @@ -1203,6 +1219,19 @@ parse_kyc_oauth_cfg (void) 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"); + return GNUNET_SYSERR; + } + TEH_kyc_config.details.oauth2.post_kyc_redirect_url = s; return GNUNET_OK; } @@ -1301,6 +1330,26 @@ exchange_serve_process_config (void) "CURRENCY"); return GNUNET_SYSERR; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "exchange", + "BASE_URL", + &TEH_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL"); + return GNUNET_SYSERR; + } + if (! TALER_url_valid_charset (TEH_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL", + "invalid URL"); + return GNUNET_SYSERR; + } + if (TEH_KYC_NONE != TEH_kyc_config.mode) { if (GNUNET_YES == @@ -1593,11 +1642,26 @@ do_shutdown (void *cls) mhd = TALER_MHD_daemon_stop (); TEH_resume_keys_requests (true); TEH_reserves_get_cleanup (); + TEH_kyc_proof_cleanup (); if (NULL != mhd) MHD_stop_daemon (mhd); TEH_WIRE_done (); TEH_keys_finished (); - TALER_EXCHANGEDB_plugin_unload (TEH_plugin); + if (NULL != TEH_plugin) + { + TALER_EXCHANGEDB_plugin_unload (TEH_plugin); + TEH_plugin = NULL; + } + if (NULL != TEH_curl_ctx) + { + GNUNET_CURL_fini (TEH_curl_ctx); + TEH_curl_ctx = NULL; + } + if (NULL != exchange_curl_rc) + { + GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); + exchange_curl_rc = NULL; + } } @@ -1655,6 +1719,17 @@ run (void *cls, } TEH_load_terms (TEH_cfg); + TEH_curl_ctx + = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &exchange_curl_rc); + if (NULL == TEH_curl_ctx) + { + GNUNET_break (0); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); fh = TALER_MHD_bind (TEH_cfg, diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 3f934dbd5..f66626b3d 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -105,6 +105,12 @@ struct TEH_KycOptions */ char *client_secret; + /** + * Where to redirect clients after the + * Web-based KYC process is done? + */ + char *post_kyc_redirect_url; + } oauth2; } details; @@ -162,11 +168,20 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; +/** + * Our (externally visible) base URL. + */ +extern char *TEH_base_url; + /** * Are we shutting down? */ extern volatile bool MHD_terminating; +/** + * Context for all CURL operations (useful to the event loop) + */ +extern struct GNUNET_CURL_Context *TEH_curl_ctx; /** * @brief Struct describing an URL and the handler for it. diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 7148b93fc..42162f8a4 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -381,7 +381,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, sizeof (deposit)); deposit.coin.coin_pub = *coin_pub; { - int res; + enum GNUNET_GenericReturnValue res; res = TALER_MHD_parse_json_data (connection, root, diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 99c727450..705900206 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1345,7 +1345,7 @@ get_date_string (struct GNUNET_TIME_Absolute at, * @param[in,out] response the response to modify * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue setup_general_response_headers (const struct TEH_KeyStateHandle *ksh, struct MHD_Response *response) { diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c index 13f5810b8..ae1ab34f2 100644 --- a/src/exchange/taler-exchange-httpd_kyc-check.c +++ b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -143,12 +143,31 @@ TEH_handler_kyc_check ( return res; if (! kcc.kyc.ok) { + char *url; + char *redirect_uri; + char *redirect_uri_encoded; + GNUNET_assert (TEH_KYC_OAUTH2 == TEH_kyc_config.mode); - return TALER_MHD_REPLY_JSON_PACK ( + GNUNET_asprintf (&redirect_uri, + "%s/kyc-proof/%llu", + TEH_base_url, + payment_target_uuid); + redirect_uri_encoded = TALER_urlencode (redirect_uri); + GNUNET_free (redirect_uri); + GNUNET_asprintf (&url, + "%s/login?client_id=%s&redirect_uri=%s", + TEH_kyc_config.details.oauth2.url, + TEH_kyc_config.details.oauth2.client_id, + redirect_uri_encoded); + GNUNET_free (redirect_uri_encoded); + + res = TALER_MHD_REPLY_JSON_PACK ( rc->connection, MHD_HTTP_ACCEPTED, GNUNET_JSON_pack_string ("kyc_url", - TEH_kyc_config.details.oauth2.url)); + url)); + GNUNET_free (url); + return res; } { struct TALER_ExchangePublicKeyP pub; diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index be7fc50fe..842e5dfd2 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -36,9 +36,99 @@ struct KycProofContext { + /** + * Kept in a DLL while suspended. + */ + struct KycProofContext *next; + + /** + * Kept in a DLL while suspended. + */ + struct KycProofContext *prev; + + /** + * Details about the connection we are processing. + */ + struct TEH_RequestContext *rc; + + /** + * Handle for the OAuth 2.0 CURL request. + */ + struct GNUNET_CURL_Job *job; + + /** + * OAuth 2.0 authorization code. + */ + const char *authorization_code; + + /** + * OAuth 2.0 token URL we are using for the + * request. + */ + char *token_url; + + /** + * Body of the POST request. + */ + char *post_body; + + /** + * Payment target this is about. + */ + unsigned long long payment_target_uuid; + + /** + * HTTP response to return. + */ + struct MHD_Response *response; + + /** + * HTTP response code to return. + */ + unsigned int response_code; + + /** + * #GNUNET_YES if we are suspended, + * #GNUNET_NO if not. + * #GNUNET_SYSERR if we had some error. + */ + enum GNUNET_GenericReturnValue suspended; + }; +/** + * Contexts are kept in a DLL while suspended. + */ +static struct KycProofContext *kpc_head; + +/** + * Contexts are kept in a DLL while suspended. + */ +static struct KycProofContext *kpc_tail; + + +void +TEH_kyc_proof_cleanup (void) +{ + struct KycProofContext *kpc; + + while (NULL != (kpc = kpc_head)) + { + if (NULL != kpc->job) + { + GNUNET_CURL_job_cancel (kpc->job); + kpc->job = NULL; + } + GNUNET_CONTAINER_DLL_remove (kpc_head, + kpc_tail, + kpc); + kpc->suspended = GNUNET_NO; + MHD_resume_connection (kpc->rc->connection); + } +} + + /** * Function implementing database transaction to check proof's KYC status. * Runs the transaction logic; IF it returns a non-error code, the transaction @@ -54,14 +144,185 @@ struct KycProofContext * @return transaction status */ static enum GNUNET_DB_QueryStatus -proof_kyc_check (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) +persist_kyc_ok (void *cls, + struct MHD_Connection *connection, + MHD_RESULT *mhd_ret) { struct KycProofContext *kpc = cls; - (void) kpc; // FIXME: do work here! - return -2; + return TEH_plugin->set_kyc_ok (TEH_plugin->cls, + kpc->payment_target_uuid); +} + + +/** + * After we are done with the CURL interaction we + * need to update our database state. + * + * @param cls our `struct KycProofContext` + * @param response_code HTTP response code from server, 0 on hard error + * @param response in JSON, NULL if response was not in JSON format + */ +static void +handle_curl_login_finished (void *cls, + long response_code, + const void *response) +{ + struct KycProofContext *kpc = cls; + const json_t *j = response; + + kpc->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + const char *access_token; + const char *token_type; + uint64_t expires_in_s; + const char *refresh_token; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("access_token", + &access_token), + GNUNET_JSON_spec_string ("token_type", + &token_type), + GNUNET_JSON_spec_uint64 ("expires_in", + &expires_in_s), + GNUNET_JSON_spec_string ("refresh_token", + &refresh_token), + GNUNET_JSON_spec_end () + }; + + { + enum GNUNET_GenericReturnValue res; + const char *emsg; + unsigned int line; + + res = GNUNET_JSON_parse (j, + spec, + &emsg, + &line); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + kpc->response + = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + "Unexpected response from KYC gateway"); + kpc->response_code + = MHD_HTTP_BAD_GATEWAY; + MHD_resume_connection (kpc->rc->connection); + TALER_MHD_daemon_trigger (); + return; + } + } + if (0 != strcasecmp (token_type, + "bearer")) + { + GNUNET_break_op (0); + kpc->response + = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + "Unexpected token type in response from KYC gateway"); + kpc->response_code + = MHD_HTTP_BAD_GATEWAY; + MHD_resume_connection (kpc->rc->connection); + TALER_MHD_daemon_trigger (); + return; + } + + /* TODO: Here we might want to keep something to persist in the DB, and + possibly use the access_token to download information we should + persist; then continue! */ + + MHD_resume_connection (kpc->rc->connection); + TALER_MHD_daemon_trigger (); + return; + } + default: + { + const char *msg; + const char *desc; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("error", + &msg), + GNUNET_JSON_spec_string ("error_description", + &desc), + GNUNET_JSON_spec_end () + }; + + { + enum GNUNET_GenericReturnValue res; + const char *emsg; + unsigned int line; + + res = GNUNET_JSON_parse (j, + spec, + &emsg, + &line); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + kpc->response + = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + "Unexpected response from KYC gateway"); + kpc->response_code + = MHD_HTTP_BAD_GATEWAY; + MHD_resume_connection (kpc->rc->connection); + TALER_MHD_daemon_trigger (); + return; + } + } + /* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED, + we MAY want to in the future look at the requested content type + and possibly respond in JSON if indicated. */ + { + char *reply; + + GNUNET_asprintf (&reply, + "%s

%s

%s

", + msg, + msg, + desc); + kpc->response + = MHD_create_response_from_buffer (strlen (reply), + reply, + MHD_RESPMEM_MUST_COPY); + GNUNET_assert (NULL != kpc->response); + GNUNET_free (reply); + } + kpc->response_code = MHD_HTTP_FORBIDDEN; + MHD_resume_connection (kpc->rc->connection); + TALER_MHD_daemon_trigger (); + } + break; + } +} + + +/** + * Function called to clean up a context. + * + * @param rc request context + */ +static void +clean_kpc (struct TEH_RequestContext *rc) +{ + struct KycProofContext *kpc = rc->rh_ctx; + + if (NULL != kpc->job) + { + GNUNET_CURL_job_cancel (kpc->job); + kpc->job = NULL; + } + if (NULL != kpc->response) + { + MHD_destroy_response (kpc->response); + kpc->response = NULL; + } + GNUNET_free (kpc->post_body); + GNUNET_free (kpc->token_url); + GNUNET_free (kpc); } @@ -70,44 +331,188 @@ TEH_handler_kyc_proof ( struct TEH_RequestContext *rc, const char *const args[]) { - struct KycProofContext kpc; - MHD_RESULT res; - enum GNUNET_GenericReturnValue ret; - unsigned long long payment_target_uuid; - char dummy; + struct KycProofContext *kpc = rc->rh_ctx; - if (1 != - sscanf (args[0], - "%llu%c", - &payment_target_uuid, - &dummy)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "payment_target_uuid"); + if (NULL == kpc) + { /* first time */ + char dummy; + + kpc = GNUNET_new (struct KycProofContext); + kpc->rc = rc; + rc->rh_ctx = kpc; + rc->rh_cleaner = &clean_kpc; + + if (1 != + sscanf (args[0], + "%llu%c", + &kpc->payment_target_uuid, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "payment_target_uuid"); + } + kpc->authorization_code + = MHD_lookup_connection_value (rc->connection, + MHD_GET_ARGUMENT_KIND, + "code"); + if (NULL == kpc->authorization_code) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "code"); + } + if (TEH_KYC_NONE == TEH_kyc_config.mode) + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + + { + CURL *eh; + + eh = curl_easy_init (); + if (NULL == eh) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + "curl_easy_init"); + } + GNUNET_asprintf (&kpc->token_url, + "%s/token", + TEH_kyc_config.details.oauth2.url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + kpc->token_url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POST, + 1)); + { + char *client_id; + char *redirect_uri; + char *client_secret; + char *authorization_code; + + client_id = curl_easy_escape (eh, + TEH_kyc_config.details.oauth2.client_id, + 0); + GNUNET_assert (NULL != client_id); + { + char *request_uri; + + GNUNET_asprintf (&request_uri, + "%s/login?client_id=%s", + TEH_kyc_config.details.oauth2.url, + TEH_kyc_config.details.oauth2.client_id); + redirect_uri = curl_easy_escape (eh, + request_uri, + 0); + GNUNET_free (request_uri); + } + GNUNET_assert (NULL != redirect_uri); + client_secret = curl_easy_escape (eh, + TEH_kyc_config.details.oauth2. + client_secret, + 0); + GNUNET_assert (NULL != client_secret); + authorization_code = curl_easy_escape (eh, + kpc->authorization_code, + 0); + GNUNET_assert (NULL != authorization_code); + GNUNET_asprintf (&kpc->post_body, + "client_id=%s&redirect_uri=%s&client_secret=%s&code=%s&grant_type=authorization_code", + client_id, + redirect_uri, + client_secret, + authorization_code); + curl_free (authorization_code); + curl_free (client_secret); + curl_free (redirect_uri); + curl_free (client_id); + } + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + kpc->post_body)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_FOLLOWLOCATION, + 1L)); + /* limit MAXREDIRS to 5 as a simple security measure against + a potential infinite loop caused by a malicious target */ + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_MAXREDIRS, + 5L)); + + kpc->job = GNUNET_CURL_job_add (TEH_curl_ctx, + eh, + &handle_curl_login_finished, + kpc); + kpc->suspended = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (kpc_head, + kpc_tail, + kpc); + MHD_suspend_connection (rc->connection); + return MHD_YES; + } } - if (1 || (TEH_KYC_NONE == TEH_kyc_config.mode)) - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - ret = TEH_DB_run_transaction (rc->connection, - "check proof kyc", - &res, - &proof_kyc_check, - &kpc); - if (GNUNET_SYSERR == ret) + if (NULL != kpc->response) + { + /* handle _failed_ resumed cases */ + return MHD_queue_response (rc->connection, + kpc->response_code, + kpc->response); + } + + /* _successfully_ resumed case */ + { + MHD_RESULT res; + enum GNUNET_GenericReturnValue ret; + + ret = TEH_DB_run_transaction (kpc->rc->connection, + "check proof kyc", + &res, + &persist_kyc_ok, + kpc); + if (GNUNET_SYSERR == ret) + return res; + } + + { + struct MHD_Response *response; + MHD_RESULT res; + + response = MHD_create_response_from_buffer (0, + "", + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + GNUNET_break (MHD_YES == + MHD_add_response_header ( + response, + MHD_HTTP_HEADER_LOCATION, + TEH_kyc_config.details.oauth2.post_kyc_redirect_url)); + res = MHD_queue_response (rc->connection, + MHD_HTTP_SEE_OTHER, + response); + MHD_destroy_response (response); return res; - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_uint64 ("42", - 42)); + } } diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.h b/src/exchange/taler-exchange-httpd_kyc-proof.h index 9cf1963c7..c075b2424 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.h +++ b/src/exchange/taler-exchange-httpd_kyc-proof.h @@ -25,6 +25,14 @@ #include "taler-exchange-httpd.h" +/** + * Shutdown kyc-proof subsystem. Resumes all suspended long-polling clients + * and cleans up data structures. + */ +void +TEH_kyc_proof_cleanup (void); + + /** * Handle a "/kyc-proof" request. * diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 0026829da..4b3ae19d7 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -360,6 +360,12 @@ prepare_statements (struct PostgresClosure *pg) " LIMIT 1;", 1), #if FIXME_DD23 + /* Used in #postgres_set_kyc_ok() */ + GNUNET_PQ_make_prepare ("set_kyc_ok", + "UPDATE wire_targets" + " SET kyc_ok=TRUE" + " WHERE wire_target_serial_id=$1", + 1), /* Used in #postgres_get_kyc_status() */ GNUNET_PQ_make_prepare ("get_kyc_status", "SELECT" @@ -3555,6 +3561,31 @@ postgres_reserves_get (void *cls, } +/** + * Set the KYC status to "OK" for a bank account. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param payment_target_uuid which account has been checked + * @param ... possibly additional data to persist (TODO) + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_set_kyc_ok (void *cls, + uint64_t payment_target_uuid, + ...) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&payment_target_uuid), + GNUNET_PQ_query_param_end + }; + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "set_kyc_ok", + params); +} + + /** * Get the KYC status for a bank account. * @@ -11261,6 +11292,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->iterate_auditor_denominations = &postgres_iterate_auditor_denominations; plugin->reserves_get = &postgres_reserves_get; + plugin->set_kyc_ok = &postgres_set_kyc_ok; plugin->get_kyc_status = &postgres_get_kyc_status; plugin->select_kyc_status = &postgres_select_kyc_status; plugin->inselect_wallet_kyc_status = &postgres_inselect_wallet_kyc_status; diff --git a/src/include/taler_curl_lib.h b/src/include/taler_curl_lib.h index 42d7f9d1f..5151f4cf6 100644 --- a/src/include/taler_curl_lib.h +++ b/src/include/taler_curl_lib.h @@ -59,7 +59,7 @@ struct TALER_CURL_PostContext * @param body JSON body to add to @e ctx * @return #GNUNET_OK on success #GNUNET_SYSERR on failure */ -int +enum GNUNET_GenericReturnValue TALER_curl_easy_post (struct TALER_CURL_PostContext *ctx, CURL *eh, const json_t *body); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index b8c504063..5a3313ca7 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -2360,6 +2360,20 @@ struct TALER_EXCHANGEDB_Plugin struct TALER_EXCHANGEDB_KycStatus *kyc); + /** + * Set the KYC status to "OK" for a bank account. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param payment_target_uuid which account has been checked + * @param ... possibly additional data to persist (TODO) + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*set_kyc_ok)(void *cls, + uint64_t payment_target_uuid, + ...); + + /** * Get the KYC status for a bank account. * diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c index 7f29b3b8c..ceb318841 100644 --- a/src/lib/exchange_api_link.c +++ b/src/lib/exchange_api_link.c @@ -82,7 +82,7 @@ struct TALER_EXCHANGE_LinkHandle * @param[out] pub where to return the public key for the coin * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ -static int +static enum GNUNET_GenericReturnValue parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, const json_t *json, uint32_t coin_num, @@ -175,7 +175,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, * @param json json reply with the data for one coin * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ -static int +static enum GNUNET_GenericReturnValue parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh, const json_t *json) {