diff options
Diffstat (limited to 'src/reducer/anastasis_api_redux.c')
-rw-r--r-- | src/reducer/anastasis_api_redux.c | 735 |
1 files changed, 585 insertions, 150 deletions
diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c index f55eece..4b5ad7b 100644 --- a/src/reducer/anastasis_api_redux.c +++ b/src/reducer/anastasis_api_redux.c @@ -1,16 +1,16 @@ /* This file is part of Anastasis - Copyright (C) 2020, 2021 Anastasis SARL + Copyright (C) 2020, 2021, 2022 Anastasis SARL Anastasis is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software + terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Anastasis 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. + A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU Affero General Public License along with + You should have received a copy of the GNU General Public License along with Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> */ /** @@ -36,6 +36,12 @@ */ #define CONFIG_GENERIC_TIMEOUT GNUNET_TIME_UNIT_MINUTES +/** + * How long do we wait in a more "synchronous" + * scenaro for a /config reply from an Anastasis provider. + */ +#define CONFIG_FAST_TIMEOUT GNUNET_TIME_UNIT_SECONDS + #define GENERATE_STRING(STRING) #STRING, static const char *generic_strings[] = { @@ -132,6 +138,21 @@ struct ConfigRequest struct ConfigReduxWaiting *w_tail; /** + * When did we start? + */ + struct GNUNET_TIME_Absolute start_time; + + /** + * When do we time out? + */ + struct GNUNET_TIME_Absolute timeout_at; + + /** + * How long do we wait before trying again? + */ + struct GNUNET_TIME_Relative backoff; + + /** * Obtained status code. */ unsigned int http_status; @@ -152,11 +173,6 @@ struct ConfigRequest char *business_name; /** - * currency used by the anastasis backend. - */ - char *currency; - - /** * Array of authorization methods supported by the server. */ struct AuthorizationMethodConfig *methods; @@ -188,9 +204,9 @@ struct ConfigRequest struct TALER_Amount liability_limit; /** - * Server salt. + * Provider salt. */ - struct ANASTASIS_CRYPTO_ProviderSaltP salt; + struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; /** * Task to timeout /config requests. @@ -234,6 +250,26 @@ static json_t *redux_countries; */ static json_t *provider_list; +/** + * External reducer binary or NULL + * to use internal reducer. + */ +static char *external_reducer_binary; + + +const char * +ANASTASIS_REDUX_probe_external_reducer (void) +{ + if (NULL != external_reducer_binary) + return external_reducer_binary; + external_reducer_binary = getenv ("ANASTASIS_EXTERNAL_REDUCER"); + if (NULL != external_reducer_binary) + unsetenv ("ANASTASIS_EXTERNAL_REDUCER"); + + return external_reducer_binary; + +} + /** * Extract the mode of a state from json @@ -262,7 +298,7 @@ ANASTASIS_generic_state_from_string_ (const char *state_string) if (0 == strcmp (state_string, generic_strings[i])) return i; - return ANASTASIS_GENERIC_STATE_ERROR; + return ANASTASIS_GENERIC_STATE_INVALID; } @@ -291,6 +327,8 @@ ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb, GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("detail", detail)), + GNUNET_JSON_pack_string ("reducer_type", + "error"), GNUNET_JSON_pack_uint64 ("code", ec), GNUNET_JSON_pack_string ("hint", @@ -344,7 +382,6 @@ free_config_request (struct ConfigRequest *cr) ANASTASIS_config_cancel (cr->co); if (NULL != cr->tt) GNUNET_SCHEDULER_cancel (cr->tt); - GNUNET_free (cr->currency); GNUNET_free (cr->url); GNUNET_free (cr->business_name); for (unsigned int i = 0; i<cr->methods_length; i++) @@ -471,13 +508,11 @@ notify_waiting (struct ConfigRequest *cr) "authentication_providers", provider_list = json_object ())); } - provider_list = json_object_get (w->state, - "authentication_providers"); - GNUNET_assert (NULL != provider_list); - if (TALER_EC_NONE != cr->ec) { prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "error"), GNUNET_JSON_pack_uint64 ("error_code", cr->ec), GNUNET_JSON_pack_uint64 ("http_status", @@ -503,6 +538,8 @@ notify_waiting (struct ConfigRequest *cr) mj)); } prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "ok"), GNUNET_JSON_pack_array_steal ("methods", methods_list), TALER_JSON_pack_amount ("annual_fee", @@ -511,14 +548,12 @@ notify_waiting (struct ConfigRequest *cr) &cr->truth_upload_fee), TALER_JSON_pack_amount ("liability_limit", &cr->liability_limit), - GNUNET_JSON_pack_string ("currency", - cr->currency), GNUNET_JSON_pack_string ("business_name", cr->business_name), GNUNET_JSON_pack_uint64 ("storage_limit_in_megabytes", cr->storage_limit_in_megabytes), - GNUNET_JSON_pack_data_auto ("salt", - &cr->salt), + GNUNET_JSON_pack_data_auto ("provider_salt", + &cr->provider_salt), GNUNET_JSON_pack_uint64 ("http_status", cr->http_status)); } @@ -531,68 +566,108 @@ notify_waiting (struct ConfigRequest *cr) w->state); abort_provider_config_cb (w); } +} + + +/** + * Notify anyone waiting on @a cr that the request is done + * (successful or failed). + * + * @param[in,out] cls request that completed + */ +static void +notify_waiting_cb (void *cls) +{ + struct ConfigRequest *cr = cls; + cr->tt = NULL; + notify_waiting (cr); } /** + * Function called when it is time to retry a + * failed /config request. + * + * @param cls the `struct ConfigRequest *` to retry. + */ +static void +retry_config (void *cls); + + +/** * Function called with the results of a #ANASTASIS_get_config(). * * @param cls closure - * @param http_status HTTP status of the request * @param acfg anastasis configuration */ static void config_cb (void *cls, - unsigned int http_status, const struct ANASTASIS_Config *acfg) { struct ConfigRequest *cr = cls; cr->co = NULL; - GNUNET_SCHEDULER_cancel (cr->tt); - cr->tt = NULL; - cr->http_status = http_status; - if (MHD_HTTP_OK != http_status) - cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; - if ( (MHD_HTTP_OK == http_status) && + if (NULL != cr->tt) + { + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = NULL; + } + cr->http_status = acfg->http_status; + if (MHD_HTTP_OK != acfg->http_status) + { + if (0 == acfg->http_status) + cr->ec = TALER_EC_ANASTASIS_GENERIC_PROVIDER_UNREACHABLE; + else + cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; + } + if ( (MHD_HTTP_OK == acfg->http_status) && (NULL == acfg) ) { cr->http_status = MHD_HTTP_NOT_FOUND; cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; } - else if (NULL != acfg) + else { - if (0 == acfg->storage_limit_in_megabytes) + if (0 == acfg->details.ok.storage_limit_in_megabytes) { cr->http_status = 0; cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG; } else { - GNUNET_free (cr->currency); - cr->currency = GNUNET_strdup (acfg->currency); + cr->ec = TALER_EC_NONE; GNUNET_free (cr->business_name); - cr->business_name = GNUNET_strdup (acfg->business_name); + cr->business_name = GNUNET_strdup (acfg->details.ok.business_name); for (unsigned int i = 0; i<cr->methods_length; i++) GNUNET_free (cr->methods[i].type); GNUNET_free (cr->methods); - cr->methods = GNUNET_new_array (acfg->methods_length, + cr->methods = GNUNET_new_array (acfg->details.ok.methods_length, struct AuthorizationMethodConfig); - for (unsigned int i = 0; i<acfg->methods_length; i++) + for (unsigned int i = 0; i<acfg->details.ok.methods_length; i++) { - cr->methods[i].type = GNUNET_strdup (acfg->methods[i].type); - cr->methods[i].usage_fee = acfg->methods[i].usage_fee; + cr->methods[i].type = GNUNET_strdup (acfg->details.ok.methods[i].type); + cr->methods[i].usage_fee = acfg->details.ok.methods[i].usage_fee; } - cr->methods_length = acfg->methods_length; - cr->storage_limit_in_megabytes = acfg->storage_limit_in_megabytes; - cr->annual_fee = acfg->annual_fee; - cr->truth_upload_fee = acfg->truth_upload_fee; - cr->liability_limit = acfg->liability_limit; - cr->salt = acfg->salt; + cr->methods_length = acfg->details.ok.methods_length; + cr->storage_limit_in_megabytes = + acfg->details.ok.storage_limit_in_megabytes; + cr->annual_fee = acfg->details.ok.annual_fee; + cr->truth_upload_fee = acfg->details.ok.truth_upload_fee; + cr->liability_limit = acfg->details.ok.liability_limit; + cr->provider_salt = acfg->details.ok.provider_salt; } } notify_waiting (cr); + if (MHD_HTTP_OK != acfg->http_status) + { + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + GNUNET_assert (NULL == cr->tt); + GNUNET_assert (NULL != cr->url); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); + } } @@ -607,22 +682,57 @@ config_request_timeout (void *cls) struct ConfigRequest *cr = cls; cr->tt = NULL; - ANASTASIS_config_cancel (cr->co); - cr->co = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } cr->http_status = 0; cr->ec = TALER_EC_GENERIC_TIMEOUT; notify_waiting (cr); + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + GNUNET_assert (NULL == cr->tt); + GNUNET_assert (NULL != cr->url); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); +} + + +static void +retry_config (void *cls) +{ + struct ConfigRequest *cr = cls; + + cr->tt = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } + cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT); + GNUNET_assert (NULL == cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); + cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, + cr->url, + &config_cb, + cr); + GNUNET_break (NULL != cr->co); } /** * Schedule job to obtain Anastasis provider configuration at @a url. * + * @param timeout how long to wait for a reply * @param url base URL of Anastasis provider * @return check config handle */ static struct ConfigRequest * -check_config (const char *url) +check_config (struct GNUNET_TIME_Relative timeout, + const char *url) { struct ConfigRequest *cr; @@ -632,17 +742,47 @@ check_config (const char *url) cr->url)) continue; if (NULL != cr->co) + { + struct GNUNET_TIME_Relative duration; + struct GNUNET_TIME_Relative left; + struct GNUNET_TIME_Relative xleft; + + duration = GNUNET_TIME_absolute_get_duration (cr->start_time); + left = GNUNET_TIME_relative_subtract (timeout, + duration); + xleft = GNUNET_TIME_absolute_get_remaining (cr->timeout_at); + if (GNUNET_TIME_relative_cmp (left, + <, + xleft)) + { + /* new timeout is shorter! */ + cr->timeout_at = GNUNET_TIME_relative_to_absolute (left); + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); + } return cr; /* already on it */ + } break; } if (NULL == cr) { cr = GNUNET_new (struct ConfigRequest); + cr->start_time = GNUNET_TIME_absolute_get (); cr->url = GNUNET_strdup (url); GNUNET_CONTAINER_DLL_insert (cr_head, cr_tail, cr); } + if (MHD_HTTP_OK == cr->http_status) + return cr; + cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout); + if (NULL != cr->tt) + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, cr->url, &config_cb, @@ -652,12 +792,6 @@ check_config (const char *url) GNUNET_break (0); return NULL; } - else - { - cr->tt = GNUNET_SCHEDULER_add_delayed (CONFIG_GENERIC_TIMEOUT, - &config_request_timeout, - cr); - } return cr; } @@ -665,12 +799,12 @@ check_config (const char *url) /** * Begin asynchronous check for provider configurations. * - * @param currencies the currencies to initiate the provider checks for + * @param cc country code that was selected * @param[in,out] state to set provider list for * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode -begin_provider_config_check (const json_t *currencies, +begin_provider_config_check (const char *cc, json_t *state) { if (NULL == provider_list) @@ -718,14 +852,17 @@ begin_provider_config_check (const json_t *currencies, json_array_foreach (provider_arr, index, provider) { const char *url; - const char *cur; + const char *restricted = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("url", &url), - GNUNET_JSON_spec_string ("currency", - &cur), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("restricted", + &restricted), + NULL), GNUNET_JSON_spec_end () }; + json_t *prov; if (GNUNET_OK != GNUNET_JSON_parse (provider, @@ -736,33 +873,35 @@ begin_provider_config_check (const json_t *currencies, json_decref (pl); return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED; } - + if ( (NULL != restricted) && + (0 != strcmp (restricted, + cc)) ) { - bool found = false; - json_t *cu; - size_t off; - - json_array_foreach (currencies, off, cu) - { - const char *currency; - - currency = json_string_value (cu); - if (NULL == currency) - { - json_decref (pl); - return TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID; - } - found = (0 == strcasecmp (currency, - cur)); - } - if (! found) - continue; + /* skip */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Skipping provider restricted to country `%s'\n", + restricted); + continue; + } + if ( (NULL == restricted) && + (0 == strcmp (cc, + "xx")) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Running in demo mode, skipping unrestricted providers\n"); + /* demo mode, skipping regular providers */ + continue; } + prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "not-contacted")); + GNUNET_assert (NULL != prov); GNUNET_assert (0 == json_object_set_new (pl, url, - json_object ())); - check_config (url); + prov)); + check_config (CONFIG_GENERIC_TIMEOUT, + url); } GNUNET_assert (0 == json_object_set_new (state, @@ -990,7 +1129,6 @@ select_country (json_t *state, { const json_t *required_attrs; const json_t *country_code; - const json_t *currencies; const json_t *redux_id_attr; if (NULL == arguments) @@ -1040,23 +1178,11 @@ select_country (json_t *state, } } - currencies = json_object_get (arguments, - "currencies"); - if ( (NULL == currencies) || - (! json_is_array (currencies)) ) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'currencies' missing"); - return NULL; - } - /* We now have an idea of the currency, begin fetching - provider /configs (we likely need them later) */ + /* Begin fetching provider /configs (we likely need them later) */ { enum TALER_ErrorCode ec; - ec = begin_provider_config_check (currencies, + ec = begin_provider_config_check (json_string_value (country_code), state); if (TALER_EC_NONE != ec) { @@ -1096,10 +1222,6 @@ select_country (json_t *state, (json_t *) country_code)); GNUNET_assert (0 == json_object_set (state, - "currencies", - (json_t *) currencies)); - GNUNET_assert (0 == - json_object_set (state, "required_attributes", (json_t *) required_attrs)); cb (cb_cls, @@ -1143,7 +1265,8 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, struct ConfigRequest *cr; struct ConfigReduxWaiting *w; - cr = check_config (url); + cr = check_config (CONFIG_FAST_TIMEOUT, + url); w = GNUNET_new (struct ConfigReduxWaiting); w->cr = cr; w->state = json_incref (state); @@ -1156,8 +1279,10 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, w); if (NULL == cr->co) { - notify_waiting (cr); - return NULL; + if (NULL != cr->tt) + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_now (¬ify_waiting_cb, + cr); } return &w->ra; } @@ -1228,19 +1353,22 @@ enter_user_attributes (json_t *state, const char *attribute_value; const char *regexp = NULL; const char *reglog = NULL; - int optional = false; + bool optional = false; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("name", &name), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("validation-regex", - ®exp)), + ®exp), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("validation-logic", - ®log)), + ®log), + NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_boolean ("optional", - &optional)), + GNUNET_JSON_spec_bool ("optional", + &optional), + NULL), GNUNET_JSON_spec_end () }; @@ -1378,9 +1506,16 @@ ANASTASIS_add_provider_ (json_t *state, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *urls; json_t *tlist; + if (NULL == arguments) + { + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "arguments missing"); + return true; /* cb was invoked */ + } tlist = json_object_get (state, "authentication_providers"); if (NULL == tlist) @@ -1392,47 +1527,19 @@ ANASTASIS_add_provider_ (json_t *state, "authentication_providers", tlist)); } - if (NULL == arguments) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "arguments missing"); - return true; - } - urls = json_object_get (arguments, - "urls"); - if (NULL == urls) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'urls' missing"); - return true; - } { - size_t index; - json_t *url; + json_t *params; + const char *url; - json_array_foreach (urls, index, url) + json_object_foreach (((json_t *) arguments), url, params) { - const char *url_str = json_string_value (url); - - if (NULL == url_str) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'urls' must be strings"); - return true; - } GNUNET_assert (0 == - json_object_set_new (tlist, - url_str, - json_object ())); + json_object_set (tlist, + url, + params)); } } - return false; + return false; /* cb not invoked */ } @@ -1502,6 +1609,256 @@ typedef struct ANASTASIS_ReduxAction * void *cb_cls); +/** + * Closure for read operations on the external reducer. + */ +struct ExternalReducerCls +{ + struct GNUNET_Buffer read_buffer; + struct GNUNET_SCHEDULER_Task *read_task; + struct GNUNET_DISK_PipeHandle *reducer_stdin; + struct GNUNET_DISK_PipeHandle *reducer_stdout; + struct GNUNET_OS_Process *reducer_process; + ANASTASIS_ActionCallback action_cb; + void *action_cb_cls; +}; + +/** + * Clean up and destroy the external reducer state. + * + * @param cls closure, a 'struct ExternalReducerCls *' + */ +static void +cleanup_external_reducer (void *cls) +{ + struct ExternalReducerCls *red_cls = cls; + + if (NULL != red_cls->read_task) + { + GNUNET_SCHEDULER_cancel (red_cls->read_task); + red_cls->read_task = NULL; + } + + GNUNET_buffer_clear (&red_cls->read_buffer); + if (NULL != red_cls->reducer_stdin) + { + GNUNET_DISK_pipe_close (red_cls->reducer_stdin); + red_cls->reducer_stdin = NULL; + } + if (NULL != red_cls->reducer_stdout) + { + GNUNET_DISK_pipe_close (red_cls->reducer_stdout); + red_cls->reducer_stdout = NULL; + } + + if (NULL != red_cls->reducer_process) + { + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + enum GNUNET_GenericReturnValue pwret; + + pwret = GNUNET_OS_process_wait_status (red_cls->reducer_process, + &type, + &code); + + GNUNET_assert (GNUNET_SYSERR != pwret); + if (GNUNET_NO == pwret) + { + GNUNET_OS_process_kill (red_cls->reducer_process, + SIGTERM); + GNUNET_assert (GNUNET_SYSERR != GNUNET_OS_process_wait ( + red_cls->reducer_process)); + } + + GNUNET_OS_process_destroy (red_cls->reducer_process); + red_cls->reducer_process = NULL; + } + + GNUNET_free (red_cls); +} + + +/** + * Task called when + * + * @param cls closure, a 'struct ExternalReducerCls *' + */ +static void +external_reducer_read_cb (void *cls) +{ + struct ExternalReducerCls *red_cls = cls; + ssize_t sret; + char buf[256]; + + red_cls->read_task = NULL; + + sret = GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle ( + red_cls->reducer_stdout, + GNUNET_DISK_PIPE_END_READ), + buf, + 256); + if (sret < 0) + { + GNUNET_break (0); + red_cls->action_cb (red_cls->action_cb_cls, + TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR, + NULL); + cleanup_external_reducer (red_cls); + return; + } + else if (0 == sret) + { + char *str = GNUNET_buffer_reap_str (&red_cls->read_buffer); + json_t *json; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got external reducer response: '%s'\n", + str); + + json = json_loads (str, 0, NULL); + + if (NULL == json) + { + GNUNET_break (0); + red_cls->action_cb (red_cls->action_cb_cls, + TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR, + NULL); + cleanup_external_reducer (red_cls); + return; + } + + { + enum TALER_ErrorCode ec; + ec = json_integer_value (json_object_get (json, "code")); + + red_cls->action_cb (red_cls->action_cb_cls, + ec, + json); + } + cleanup_external_reducer (red_cls); + return; + } + else + { + GNUNET_buffer_write (&red_cls->read_buffer, + buf, + sret); + + red_cls->read_task = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle ( + red_cls->reducer_stdout, + GNUNET_DISK_PIPE_END_READ), + external_reducer_read_cb, + red_cls); + } +} + + +/** + * Handle an action using an external reducer, i.e. + * by shelling out to another process. + */ +static struct ANASTASIS_ReduxAction * +redux_action_external (const char *ext_reducer, + const json_t *state, + const char *action, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls) +{ + char *arg_str; + char *state_str = json_dumps (state, JSON_COMPACT); + ssize_t sret; + struct ExternalReducerCls *red_cls = GNUNET_new (struct ExternalReducerCls); + + if (NULL == arguments) + arg_str = GNUNET_strdup ("{}"); + else + arg_str = json_dumps (arguments, JSON_COMPACT); + + red_cls->action_cb = cb; + red_cls->action_cb_cls = cb_cls; + + GNUNET_assert (NULL != (red_cls->reducer_stdin = GNUNET_DISK_pipe ( + GNUNET_DISK_PF_NONE))); + GNUNET_assert (NULL != (red_cls->reducer_stdout = GNUNET_DISK_pipe ( + GNUNET_DISK_PF_NONE))); + + /* By the time we're here, this variable should be unset, because + otherwise using anastasis-reducer as the external reducer + will lead to infinite recursion. */ + GNUNET_assert (NULL == getenv ("ANASTASIS_EXTERNAL_REDUCER")); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting external reducer with action '%s' and argument '%s'\n", + action, + arg_str); + + red_cls->reducer_process = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR, + red_cls->reducer_stdin, + red_cls->reducer_stdout, + NULL, + ext_reducer, + ext_reducer, + "-a", + arg_str, + action, + NULL); + + GNUNET_free (arg_str); + + if (NULL == red_cls->reducer_process) + { + GNUNET_break (0); + GNUNET_free (state_str); + cleanup_external_reducer (red_cls); + return NULL; + } + + /* Close pipe ends we don't use. */ + GNUNET_assert (GNUNET_OK == + GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin, + GNUNET_DISK_PIPE_END_READ)); + GNUNET_assert (GNUNET_OK == + GNUNET_DISK_pipe_close_end (red_cls->reducer_stdout, + GNUNET_DISK_PIPE_END_WRITE)); + + sret = GNUNET_DISK_file_write_blocking (GNUNET_DISK_pipe_handle ( + red_cls->reducer_stdin, + GNUNET_DISK_PIPE_END_WRITE), + state_str, + strlen (state_str)); + GNUNET_free (state_str); + if (sret <= 0) + { + GNUNET_break (0); + cleanup_external_reducer (red_cls); + return NULL; + } + + GNUNET_assert (GNUNET_OK == + GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin, + GNUNET_DISK_PIPE_END_WRITE)); + + red_cls->read_task = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle ( + red_cls->reducer_stdout, + GNUNET_DISK_PIPE_END_READ), + external_reducer_read_cb, + red_cls); + + { + struct ANASTASIS_ReduxAction *ra = GNUNET_new (struct + ANASTASIS_ReduxAction); + ra->cleanup_cls = red_cls; + ra->cleanup = cleanup_external_reducer; + return ra; + } +} + + struct ANASTASIS_ReduxAction * ANASTASIS_redux_action (const json_t *state, const char *action, @@ -1520,6 +1877,7 @@ ANASTASIS_redux_action (const json_t *state, "select_continent", &select_continent }, + /* Deprecated alias for "back" from that state, should be removed eventually. */ { ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, "unselect_continent", @@ -1527,6 +1885,11 @@ ANASTASIS_redux_action (const json_t *state, }, { ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, + "back", + &unselect_continent + }, + { + ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, "select_country", &select_country }, @@ -1536,11 +1899,6 @@ ANASTASIS_redux_action (const json_t *state, &select_continent }, { - ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, - "unselect_continent", - &unselect_continent - }, - { ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING, "enter_user_attributes", &enter_user_attributes @@ -1555,13 +1913,25 @@ ANASTASIS_redux_action (const json_t *state, "back", &ANASTASIS_back_generic_decrement_ }, - { ANASTASIS_GENERIC_STATE_ERROR, NULL, NULL } + { ANASTASIS_GENERIC_STATE_INVALID, NULL, NULL } }; bool recovery_mode = false; const char *s = json_string_value (json_object_get (state, "backup_state")); enum ANASTASIS_GenericState gs; + /* If requested, handle action with external reducer, used for testing. */ + { + const char *ext_reducer = ANASTASIS_REDUX_probe_external_reducer (); + if (NULL != ext_reducer) + return redux_action_external (ext_reducer, + state, + action, + arguments, + cb, + cb_cls); + } + if (NULL == s) { s = json_string_value (json_object_get (state, @@ -1583,7 +1953,7 @@ ANASTASIS_redux_action (const json_t *state, new_state = json_deep_copy (state); GNUNET_assert (NULL != new_state); - if (gs != ANASTASIS_GENERIC_STATE_ERROR) + if (gs != ANASTASIS_GENERIC_STATE_INVALID) { for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) { @@ -1737,3 +2107,68 @@ ANASTASIS_REDUX_load_continents_ () GNUNET_JSON_pack_array_steal ("continents", continents)); } + + +/** + * Lookup @a provider_salt of @a provider_url in @a state. + * + * @param state the state to inspect + * @param provider_url provider to look into + * @param[out] provider_salt value to extract + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_reducer_lookup_salt ( + const json_t *state, + const char *provider_url, + struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt) +{ + const json_t *aps; + const json_t *cfg; + uint32_t http_status = 0; + const char *status; + bool no_salt; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("status", + &status), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("http_status", + &http_status), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("provider_salt", + provider_salt), + &no_salt), + GNUNET_JSON_spec_end () + }; + + aps = json_object_get (state, + "authentication_providers"); + if (NULL == aps) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + cfg = json_object_get (aps, + provider_url); + if (NULL == cfg) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_JSON_parse (cfg, + spec, + NULL, NULL)) + { + /* provider not working */ + GNUNET_break_op (0); + return GNUNET_NO; + } + if (0 == strcmp (status, + "disabled")) + return GNUNET_NO; + if (no_salt) + return GNUNET_NO; + return GNUNET_OK; +} |