summaryrefslogtreecommitdiff
path: root/src/reducer/anastasis_api_redux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer/anastasis_api_redux.c')
-rw-r--r--src/reducer/anastasis_api_redux.c735
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 (&notify_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",
- &regexp)),
+ &regexp),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("validation-logic",
- &reglog)),
+ &reglog),
+ 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;
+}