summaryrefslogtreecommitdiff
path: root/src/reducer
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer')
-rw-r--r--src/reducer/Makefile.am1
-rw-r--r--src/reducer/anastasis_api_backup_redux.c248
-rw-r--r--src/reducer/anastasis_api_providers.c300
-rw-r--r--src/reducer/anastasis_api_recovery_redux.c306
-rw-r--r--src/reducer/anastasis_api_redux.c188
-rw-r--r--src/reducer/anastasis_api_redux.h36
6 files changed, 664 insertions, 415 deletions
diff --git a/src/reducer/Makefile.am b/src/reducer/Makefile.am
index 6dad659..1536d21 100644
--- a/src/reducer/Makefile.am
+++ b/src/reducer/Makefile.am
@@ -16,6 +16,7 @@ libanastasisredux_la_LDFLAGS = \
-no-undefined
libanastasisredux_la_SOURCES = \
anastasis_api_discovery.c \
+ anastasis_api_providers.c \
anastasis_api_redux.c anastasis_api_redux.h \
anastasis_api_recovery_redux.c \
anastasis_api_backup_redux.c \
diff --git a/src/reducer/anastasis_api_backup_redux.c b/src/reducer/anastasis_api_backup_redux.c
index 2e73587..6ca6de7 100644
--- a/src/reducer/anastasis_api_backup_redux.c
+++ b/src/reducer/anastasis_api_backup_redux.c
@@ -1,6 +1,6 @@
/*
This file is part of Anastasis
- Copyright (C) 2020, 2021 Anastasis SARL
+ Copyright (C) 2020-2023 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -32,7 +32,7 @@
* anastasis-httpd.h.
*/
#define ANASTASIS_FREE_STORAGE GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_YEARS, 5)
+ GNUNET_TIME_UNIT_YEARS, 5)
/**
* CPU limiter: do not evaluate more than 16k
@@ -353,7 +353,7 @@ add_authentication (json_t *state,
json_object_foreach (auth_providers, url, details)
{
- json_t *methods = NULL;
+ const json_t *methods = NULL;
json_t *method;
size_t index;
uint32_t size_limit_in_mb = 0;
@@ -371,8 +371,8 @@ add_authentication (json_t *state,
&http_status),
NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("methods",
- &methods),
+ GNUNET_JSON_spec_array_const ("methods",
+ &methods),
NULL),
GNUNET_JSON_spec_end ()
};
@@ -411,7 +411,6 @@ add_authentication (json_t *state,
break;
}
}
- GNUNET_JSON_parse_free (ispec);
if (! challenge_size_ok (size_limit_in_mb,
challenge_size))
{
@@ -767,17 +766,20 @@ free_costs (struct Costs *costs)
* Check if providers @a p1 and @a p2 have equivalent
* methods and cost structures.
*
+ * @param pb policy builder with list of providers
+ * @param p1 name of provider to compare
+ * @param p2 name of provider to compare
* @return true if the providers are fully equivalent
*/
static bool
-equiv_provider (struct PolicyBuilder *pb,
+equiv_provider (const struct PolicyBuilder *pb,
const char *p1,
const char *p2)
{
- json_t *j1;
- json_t *j2;
- json_t *m1;
- json_t *m2;
+ const json_t *j1;
+ const json_t *j2;
+ const json_t *m1;
+ const json_t *m2;
struct TALER_Amount uc1;
struct TALER_Amount uc2;
@@ -794,8 +796,8 @@ equiv_provider (struct PolicyBuilder *pb,
{
struct GNUNET_JSON_Specification s1[] = {
- GNUNET_JSON_spec_json ("methods",
- &m1),
+ GNUNET_JSON_spec_array_const ("methods",
+ &m1),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&uc1),
GNUNET_JSON_spec_end ()
@@ -813,8 +815,8 @@ equiv_provider (struct PolicyBuilder *pb,
{
struct GNUNET_JSON_Specification s2[] = {
- GNUNET_JSON_spec_json ("methods",
- &m2),
+ GNUNET_JSON_spec_array_const ("methods",
+ &m2),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&uc2),
GNUNET_JSON_spec_end ()
@@ -933,7 +935,7 @@ eval_provider_selection (struct PolicyBuilder *pb,
pb->m_idx[i]);
const json_t *provider_cfg = json_object_get (pb->providers,
prov_sel[i]);
- json_t *provider_methods;
+ const json_t *provider_methods;
const char *method_type;
json_t *md;
size_t index;
@@ -943,8 +945,8 @@ eval_provider_selection (struct PolicyBuilder *pb,
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
&size_limit_in_mb),
- GNUNET_JSON_spec_json ("methods",
- &provider_methods),
+ GNUNET_JSON_spec_array_const ("methods",
+ &provider_methods),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&upload_cost),
GNUNET_JSON_spec_end ()
@@ -1016,7 +1018,6 @@ eval_provider_selection (struct PolicyBuilder *pb,
GNUNET_break (0);
pb->ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID;
pb->hint = "'methods' of provider";
- GNUNET_JSON_parse_free (pspec);
for (unsigned int i = 0; i<pb->req_methods; i++)
free_costs (policy_ent[i].usage_fee);
return;
@@ -1038,14 +1039,12 @@ eval_provider_selection (struct PolicyBuilder *pb,
{
/* Provider does not OFFER this method, combination not possible.
Cost is basically 'infinite', but we simply then skip this. */
- GNUNET_JSON_parse_free (pspec);
GNUNET_JSON_parse_free (mspec);
for (unsigned int i = 0; i<pb->req_methods; i++)
free_costs (policy_ent[i].usage_fee);
return;
}
GNUNET_JSON_parse_free (mspec);
- GNUNET_JSON_parse_free (pspec);
}
/* calculate provider diversity by counting number of different
@@ -1666,7 +1665,8 @@ done_authentication (json_t *state,
pb.methods = json_object_get (state,
"authentication_methods");
if ( (NULL == pb.methods) ||
- (! json_is_array (pb.methods)) )
+ (! json_is_array (pb.methods)) ||
+ (json_array_size (pb.methods) > UINT_MAX) )
{
ANASTASIS_redux_fail_ (cb,
cb_cls,
@@ -1674,7 +1674,8 @@ done_authentication (json_t *state,
"'authentication_methods' must be provided");
return NULL;
}
- pb.num_methods = json_array_size (pb.methods);
+ pb.num_methods
+ = (unsigned int) json_array_size (pb.methods);
switch (pb.num_methods)
{
case 0:
@@ -1945,7 +1946,7 @@ add_policy (json_t *state,
const char *provider_url;
uint32_t method_idx;
const char *method_type;
- json_t *prov_methods;
+ const json_t *prov_methods;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("provider",
&provider_url),
@@ -1982,8 +1983,8 @@ add_policy (json_t *state,
NULL),
GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
&limit),
- GNUNET_JSON_spec_json ("methods",
- &prov_methods),
+ GNUNET_JSON_spec_array_const ("methods",
+ &prov_methods),
GNUNET_JSON_spec_end ()
};
@@ -2016,17 +2017,6 @@ add_policy (json_t *state,
json_decref (methods);
continue;
}
- if (! json_is_array (prov_methods))
- {
- GNUNET_break (0);
- json_decref (methods);
- json_decref (prov_methods);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "provider lacks authentication methods");
- return NULL;
- }
}
{
@@ -2038,7 +2028,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
@@ -2051,7 +2040,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
@@ -2083,7 +2071,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
@@ -2100,7 +2087,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
@@ -2111,7 +2097,6 @@ add_policy (json_t *state,
GNUNET_assert (0 ==
json_array_append (methods,
method));
- json_decref (prov_methods);
} /* end of json_array_foreach (arg_array, index, method) */
}
@@ -2970,6 +2955,62 @@ serialize_truth (struct UploadContext *uc)
/**
+ * Test if the given @a provider_url is used by any of the
+ * authentication methods and thus the provider should be
+ * considered mandatory for storing the policy.
+ *
+ * @param state state to inspect
+ * @param provider_url provider to test
+ * @return false if the provider can be removed from policy
+ * upload considerations without causing a problem
+ */
+static bool
+provider_required (const json_t *state,
+ const char *provider_url)
+{
+ json_t *policies
+ = json_object_get (state,
+ "policies");
+ size_t pidx;
+ json_t *policy;
+
+ json_array_foreach (policies, pidx, policy)
+ {
+ json_t *methods = json_object_get (policy,
+ "methods");
+ size_t midx;
+ json_t *method;
+
+ json_array_foreach (methods, midx, method)
+ {
+ const char *provider
+ = json_string_value (json_object_get (method,
+ "provider"));
+
+ if (NULL == provider)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcmp (provider,
+ provider_url))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * All truth uploads are done, begin with uploading the policy.
+ *
+ * @param[in,out] uc context for the operation
+ */
+static void
+share_secret (struct UploadContext *uc);
+
+
+/**
* Function called with the results of a #ANASTASIS_secret_share().
*
* @param cls closure with a `struct UploadContext *`
@@ -3053,7 +3094,8 @@ secret_share_result_cb (void *cls,
json_array_foreach (providers, off, provider)
{
const char *purl = json_string_value (json_object_get (provider,
- "provider_url"));
+ "provider_url")
+ );
if (NULL == purl)
{
@@ -3094,6 +3136,45 @@ secret_share_result_cb (void *cls,
{
json_t *details;
+ if (! provider_required (uc->state,
+ sr->details.provider_failure.provider_url))
+ {
+ /* try again without that provider */
+ json_t *provider;
+ json_t *providers;
+ size_t idx;
+
+ provider
+ = json_object_get (
+ json_object_get (uc->state,
+ "authentication_providers"),
+ sr->details.provider_failure.provider_url);
+ GNUNET_break (0 ==
+ json_object_set_new (provider,
+ "status",
+ json_string ("disabled")));
+ providers
+ = json_object_get (uc->state,
+ "policy_providers");
+ json_array_foreach (providers, idx, provider)
+ {
+ const char *url
+ = json_string_value (json_object_get (provider,
+ "provider_url"));
+
+ if ( (NULL != url) &&
+ (0 == strcmp (sr->details.provider_failure.provider_url,
+ url)) )
+ {
+ GNUNET_break (0 ==
+ json_array_remove (providers,
+ idx));
+ break;
+ }
+ }
+ share_secret (uc);
+ return;
+ }
details = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("http_status",
sr->details.provider_failure.http_status),
@@ -3130,23 +3211,25 @@ secret_share_result_cb (void *cls,
static void
share_secret (struct UploadContext *uc)
{
- json_t *user_id;
- json_t *core_secret;
- json_t *jpolicies;
- json_t *providers = NULL;
+ const json_t *user_id;
+ const json_t *core_secret;
+ const json_t *jpolicies;
+ const json_t *providers = NULL;
size_t policies_len;
const char *secret_name = NULL;
unsigned int pds_len;
struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("identity_attributes",
- &user_id),
- GNUNET_JSON_spec_json ("policies",
- &jpolicies),
- GNUNET_JSON_spec_json ("policy_providers",
- &providers),
- GNUNET_JSON_spec_json ("core_secret",
- &core_secret),
+ GNUNET_JSON_spec_object_const ("identity_attributes",
+ &user_id),
+ GNUNET_JSON_spec_array_const ("policies",
+ &jpolicies),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("policy_providers",
+ &providers),
+ NULL),
+ GNUNET_JSON_spec_object_const ("core_secret",
+ &core_secret),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("secret_name",
&secret_name),
@@ -3198,40 +3281,43 @@ share_secret (struct UploadContext *uc)
}
}
- if ( (! json_is_object (user_id)) ||
- (! json_is_array (jpolicies)) ||
- (0 == json_array_size (jpolicies)) ||
- ( (NULL != providers) &&
- (! json_is_array (providers)) ) )
+ policies_len = json_array_size (jpolicies);
+ if (0 == policies_len)
{
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"State parsing failed checks when preparing to share secret");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
- policies_len = json_array_size (jpolicies);
- pds_len = json_array_size (providers);
-
+ if (json_array_size (providers) > UINT_MAX)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (uc->cb,
+ uc->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "provider array excessively long");
+ upload_cancel_cb (uc);
+ return;
+ }
+ pds_len
+ = (unsigned int) json_array_size (providers);
if (0 == pds_len)
{
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"no workable providers in state");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
-
{
struct ANASTASIS_Policy *vpolicies[policies_len];
const struct ANASTASIS_Policy *policies[policies_len];
- struct ANASTASIS_ProviderDetails pds[GNUNET_NZL (pds_len)];
+ struct ANASTASIS_ProviderDetails pds[pds_len];
/* initialize policies/vpolicies arrays */
memset (pds,
@@ -3246,18 +3332,19 @@ share_secret (struct UploadContext *uc)
unsigned int methods_len;
if ( (! json_is_array (jmethods)) ||
- (0 == json_array_size (jmethods)) )
+ (0 == json_array_size (jmethods)) ||
+ (json_array_size (jmethods) > UINT_MAX) )
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'methods' must be an array");
- GNUNET_JSON_parse_free (spec);
+ "'methods' must be an array of sane length");
upload_cancel_cb (uc);
return;
}
- methods_len = json_array_size (jmethods);
+ methods_len
+ = (unsigned int) json_array_size (jmethods);
{
struct ANASTASIS_Policy *p;
struct ANASTASIS_Truth *truths[methods_len];
@@ -3267,13 +3354,13 @@ share_secret (struct UploadContext *uc)
{
const json_t *jmethod = json_array_get (jmethods,
j);
- json_t *jtruth = NULL;
+ const json_t *jtruth = NULL;
uint32_t truth_index;
const char *provider_url;
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("truth",
- &jtruth),
+ GNUNET_JSON_spec_object_const ("truth",
+ &jtruth),
NULL),
GNUNET_JSON_spec_string ("provider",
&provider_url),
@@ -3295,7 +3382,6 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
@@ -3312,8 +3398,6 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (ispec);
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
@@ -3351,13 +3435,10 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (ispec);
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
}
- GNUNET_JSON_parse_free (ispec);
ctruths[j] = truths[j];
}
p = ANASTASIS_policy_create (ctruths,
@@ -3401,7 +3482,6 @@ share_secret (struct UploadContext *uc)
for (unsigned int i = 0; i<policies_len; i++)
ANASTASIS_policy_destroy (vpolicies[i]);
upload_cancel_cb (uc);
- GNUNET_JSON_parse_free (spec);
return;
}
}
@@ -3432,7 +3512,6 @@ share_secret (struct UploadContext *uc)
for (unsigned int i = 0; i<policies_len; i++)
ANASTASIS_policy_destroy (vpolicies[i]);
}
- GNUNET_JSON_parse_free (spec);
if (NULL == uc->ss)
{
GNUNET_break (0);
@@ -4718,6 +4797,11 @@ ANASTASIS_backup_action_ (json_t *state,
},
{
ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
+ "poll_providers",
+ &ANASTASIS_REDUX_poll_providers_
+ },
+ {
+ ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
"back",
&ANASTASIS_back_generic_decrement_
},
diff --git a/src/reducer/anastasis_api_providers.c b/src/reducer/anastasis_api_providers.c
new file mode 100644
index 0000000..82243f5
--- /dev/null
+++ b/src/reducer/anastasis_api_providers.c
@@ -0,0 +1,300 @@
+/*
+ This file is part of Anastasis
+ 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 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 General Public License for more details.
+
+ 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/>
+*/
+/**
+ * @file reducer/anastasis_api_providers.c
+ * @brief anastasis provider synchronization logic
+ * @author Christian Grothoff
+ */
+
+#include <platform.h>
+#include <jansson.h>
+#include "anastasis_redux.h"
+#include "anastasis_error_codes.h"
+#include "anastasis_api_redux.h"
+
+
+/**
+ * Main data structure for sync_providers().
+ */
+struct MasterSync;
+
+
+/**
+ * Data structure for one provider we are syncing /config with.
+ */
+struct SyncEntry
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct SyncEntry *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct SyncEntry *prev;
+
+ /**
+ * Sync operation we are part of.
+ */
+ struct MasterSync *ms;
+
+ /**
+ * Redux action for this provider.
+ */
+ struct ANASTASIS_ReduxAction *ra;
+};
+
+
+/**
+ * Main data structure for sync_providers().
+ */
+struct MasterSync
+{
+ /**
+ * Our own sync action we expose externally.
+ */
+ struct ANASTASIS_ReduxAction ra;
+ /**
+ * Head of DLL with entries per provider.
+ */
+ struct SyncEntry *se_head;
+ /**
+ * Tail of DLL with entries per provider.
+ */
+ struct SyncEntry *se_tail;
+
+ /**
+ * Function to call with the result.
+ */
+ ANASTASIS_ActionCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * Free @a cls data structure.
+ *
+ * @param[in] cls data structure to free, must be a `struct MasterSync *`
+ */
+static void
+clean_sync (void *cls)
+{
+ struct MasterSync *ms = cls;
+ struct SyncEntry *se;
+
+ while (NULL != (se = ms->se_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ms->se_head,
+ ms->se_tail,
+ se);
+ se->ra->cleanup (se->ra->cleanup_cls);
+ GNUNET_free (se);
+ }
+ GNUNET_free (ms);
+}
+
+
+/**
+ * Function called when we have made progress on any of the
+ * providers we are trying to sync with.
+ *
+ * @param cls closure
+ * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state
+ * @param new_state the new state of the operation (client should json_incref() to keep an alias)
+ */
+static void
+sync_progress (void *cls,
+ enum TALER_ErrorCode error,
+ json_t *new_state)
+{
+ struct SyncEntry *se = cls;
+ struct MasterSync *ms = se->ms;
+
+ GNUNET_CONTAINER_DLL_remove (ms->se_head,
+ ms->se_tail,
+ se);
+ GNUNET_free (se);
+ ms->cb (ms->cb_cls,
+ error,
+ new_state);
+ clean_sync (ms);
+}
+
+
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_sync_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ json_t *rd;
+ json_t *cs_arr;
+ struct MasterSync *ms;
+
+ rd = json_object_get (state,
+ "recovery_document");
+ if (NULL == rd)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' missing");
+ return NULL;
+ }
+ cs_arr = json_object_get (rd,
+ "challenges");
+ if (! json_is_array (cs_arr))
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' must be an array");
+ return NULL;
+ }
+ ms = GNUNET_new (struct MasterSync);
+ ms->cb = cb;
+ ms->cb_cls = cb_cls;
+ {
+ json_t *cs;
+ unsigned int n_index;
+
+ json_array_foreach (cs_arr, n_index, cs)
+ {
+ const char *provider_url;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("url",
+ &provider_url),
+ GNUNET_JSON_spec_end ()
+ };
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+ struct SyncEntry *se;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' missing");
+ clean_sync (ms);
+ return NULL;
+ }
+ if (GNUNET_OK ==
+ ANASTASIS_reducer_lookup_salt (state,
+ provider_url,
+ &provider_salt))
+ continue; /* provider already ready */
+ se = GNUNET_new (struct SyncEntry);
+ se->ms = ms;
+ GNUNET_CONTAINER_DLL_insert (ms->se_head,
+ ms->se_tail,
+ se);
+ se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
+ state,
+ &sync_progress,
+ se);
+ }
+ }
+ if (NULL == ms->se_head)
+ {
+ /* everything already synced */
+ clean_sync (ms);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED,
+ "already in sync");
+ return NULL;
+ }
+ ms->ra.cleanup = &clean_sync;
+ ms->ra.cleanup_cls = ms;
+ return &ms->ra;
+}
+
+
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_poll_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ json_t *ap;
+ const char *url;
+ json_t *obj;
+ struct MasterSync *ms;
+
+ ap = json_object_get (state,
+ "authentication_providers");
+ if (NULL == ap)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'authentication_providers' missing");
+ return NULL;
+ }
+ ms = GNUNET_new (struct MasterSync);
+ ms->cb = cb;
+ ms->cb_cls = cb_cls;
+ json_object_foreach (ap, url, obj)
+ {
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+ struct SyncEntry *se;
+ struct ANASTASIS_ReduxAction *ra;
+
+ if (GNUNET_OK ==
+ ANASTASIS_reducer_lookup_salt (state,
+ url,
+ &provider_salt))
+ continue;
+ se = GNUNET_new (struct SyncEntry);
+ se->ms = ms;
+ GNUNET_CONTAINER_DLL_insert (ms->se_head,
+ ms->se_tail,
+ se);
+ ra = ANASTASIS_REDUX_add_provider_to_state_ (url,
+ state,
+ &sync_progress,
+ se);
+ if (NULL == ra)
+ return NULL; /* sync_progress already called! */
+ se->ra = ra;
+ }
+ if (NULL == ms->se_head)
+ {
+ /* everything already synced */
+ clean_sync (ms);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
+ "already in sync");
+ return NULL;
+ }
+ ms->ra.cleanup = &clean_sync;
+ ms->ra.cleanup_cls = ms;
+ return &ms->ra;
+}
diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c
index 22be38d..e795c55 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -2013,301 +2013,6 @@ select_challenge (json_t *state,
/**
- * Main data structure for sync_providers().
- */
-struct MasterSync;
-
-
-/**
- * Data structure for one provider we are syncing /config with.
- */
-struct SyncEntry
-{
- /**
- * Kept in a DLL.
- */
- struct SyncEntry *next;
-
- /**
- * Kept in a DLL.
- */
- struct SyncEntry *prev;
-
- /**
- * Sync operation we are part of.
- */
- struct MasterSync *ms;
-
- /**
- * Redux action for this provider.
- */
- struct ANASTASIS_ReduxAction *ra;
-};
-
-/**
- * Main data structure for sync_providers().
- */
-struct MasterSync
-{
- /**
- * Our own sync action we expose externally.
- */
- struct ANASTASIS_ReduxAction ra;
- /**
- * Head of DLL with entries per provider.
- */
- struct SyncEntry *se_head;
- /**
- * Tail of DLL with entries per provider.
- */
- struct SyncEntry *se_tail;
-
- /**
- * Function to call with the result.
- */
- ANASTASIS_ActionCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
-};
-
-
-/**
- * Free @a cls data structure.
- *
- * @param[in] cls data structure to free, must be a `struct MasterSync *`
- */
-static void
-clean_sync (void *cls)
-{
- struct MasterSync *ms = cls;
- struct SyncEntry *se;
-
- while (NULL != (se = ms->se_head))
- {
- GNUNET_CONTAINER_DLL_remove (ms->se_head,
- ms->se_tail,
- se);
- se->ra->cleanup (se->ra->cleanup_cls);
- GNUNET_free (se);
- }
- GNUNET_free (ms);
-}
-
-
-/**
- * Function called when we have made progress on any of the
- * providers we are trying to sync with.
- *
- * @param cls closure
- * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state
- * @param new_state the new state of the operation (client should json_incref() to keep an alias)
- */
-static void
-sync_progress (void *cls,
- enum TALER_ErrorCode error,
- json_t *new_state)
-{
- struct SyncEntry *se = cls;
- struct MasterSync *ms = se->ms;
-
- GNUNET_CONTAINER_DLL_remove (ms->se_head,
- ms->se_tail,
- se);
- GNUNET_free (se);
- ms->cb (ms->cb_cls,
- error,
- new_state);
- clean_sync (ms);
-}
-
-
-/**
- * Check if we have information on all providers involved in
- * a recovery procedure, and if not, try to obtain it. Upon
- * success, call @a cb with the updated provider status data.
- *
- * @param[in] state we are in
- * @param arguments our arguments with the solution
- * @param cb functiont o call with the new state
- * @param cb_cls closure for @a cb
- * @return handle to cancel challenge selection step
- */
-static struct ANASTASIS_ReduxAction *
-sync_providers (json_t *state,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls)
-{
- json_t *rd;
- json_t *cs_arr;
- struct MasterSync *ms;
-
- rd = json_object_get (state,
- "recovery_document");
- if (NULL == rd)
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'recovery_document' missing");
- return NULL;
- }
- cs_arr = json_object_get (rd,
- "challenges");
- if (! json_is_array (cs_arr))
- {
- GNUNET_break_op (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'recovery_document' must be an array");
- return NULL;
- }
- ms = GNUNET_new (struct MasterSync);
- ms->cb = cb;
- ms->cb_cls = cb_cls;
- {
- json_t *cs;
- unsigned int n_index;
-
- json_array_foreach (cs_arr, n_index, cs)
- {
- const char *provider_url;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("url",
- &provider_url),
- GNUNET_JSON_spec_end ()
- };
- struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
- struct SyncEntry *se;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (cs,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'recovery_document' missing");
- clean_sync (ms);
- return NULL;
- }
- if (GNUNET_OK ==
- ANASTASIS_reducer_lookup_salt (state,
- provider_url,
- &provider_salt))
- continue; /* provider already ready */
- se = GNUNET_new (struct SyncEntry);
- se->ms = ms;
- GNUNET_CONTAINER_DLL_insert (ms->se_head,
- ms->se_tail,
- se);
- se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
- state,
- &sync_progress,
- se);
- }
- }
- if (NULL == ms->se_head)
- {
- /* everything already synced */
- clean_sync (ms);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED,
- "already in sync");
- return NULL;
- }
- ms->ra.cleanup = &clean_sync;
- ms->ra.cleanup_cls = ms;
- return &ms->ra;
-}
-
-
-/**
- * Try to obtain configuration information on all configured
- * providers. Upon success, call @a cb with the updated provider
- * status data.
- *
- * @param[in] state we are in
- * @param arguments our arguments with the solution
- * @param cb functiont o call with the new state
- * @param cb_cls closure for @a cb
- * @return handle to cancel challenge selection step
- */
-static struct ANASTASIS_ReduxAction *
-poll_providers (json_t *state,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls)
-{
- json_t *ap;
- const char *url;
- json_t *obj;
- struct MasterSync *ms;
-
- ap = json_object_get (state,
- "authentication_providers");
- if (NULL == ap)
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'authentication_providers' missing");
- return NULL;
- }
- ms = GNUNET_new (struct MasterSync);
- ms->cb = cb;
- ms->cb_cls = cb_cls;
- json_object_foreach (ap, url, obj)
- {
- struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
- struct SyncEntry *se;
- struct ANASTASIS_ReduxAction *ra;
-
- if (GNUNET_OK ==
- ANASTASIS_reducer_lookup_salt (state,
- url,
- &provider_salt))
- continue;
- se = GNUNET_new (struct SyncEntry);
- se->ms = ms;
- GNUNET_CONTAINER_DLL_insert (ms->se_head,
- ms->se_tail,
- se);
- ra = ANASTASIS_REDUX_add_provider_to_state_ (url,
- state,
- &sync_progress,
- se);
- if (NULL == ra)
- return NULL; /* sync_progress already called! */
- se->ra = ra;
- }
- if (NULL == ms->se_head)
- {
- /* everything already synced */
- clean_sync (ms);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
- "already in sync");
- return NULL;
- }
- ms->ra.cleanup = &clean_sync;
- ms->ra.cleanup_cls = ms;
- return &ms->ra;
-}
-
-
-/**
* The user pressed "back" during challenge solving.
* Transition back to selecting another challenge.
*
@@ -2603,12 +2308,12 @@ done_secret_selecting (json_t *state,
void *cb_cls)
{
uint32_t mask;
- json_t *pa;
+ const json_t *pa;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint32 ("attribute_mask",
&mask),
- GNUNET_JSON_spec_json ("providers",
- &pa),
+ GNUNET_JSON_spec_array_const ("providers",
+ &pa),
GNUNET_JSON_spec_end ()
};
struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
@@ -2743,7 +2448,6 @@ done_secret_selecting (json_t *state,
pd->ra.cleanup_cls = pd;
return &pd->ra;
}
-
}
}
@@ -2833,7 +2537,7 @@ ANASTASIS_recovery_action_ (json_t *state,
{
ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
"poll_providers",
- &poll_providers
+ &ANASTASIS_REDUX_poll_providers_
},
{
ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
@@ -2853,7 +2557,7 @@ ANASTASIS_recovery_action_ (json_t *state,
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
"sync_providers",
- &sync_providers
+ &ANASTASIS_REDUX_sync_providers_
},
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c
index 2271942..4b5ad7b 100644
--- a/src/reducer/anastasis_api_redux.c
+++ b/src/reducer/anastasis_api_redux.c
@@ -1,6 +1,6 @@
/*
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 General Public License as published by the Free Software
@@ -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;
@@ -545,44 +566,70 @@ 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)
+ 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 == 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 == http_status) &&
+ 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;
@@ -591,26 +638,36 @@ config_cb (void *cls,
{
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->provider_salt = acfg->provider_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);
+ }
}
@@ -625,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;
@@ -650,12 +742,34 @@ 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,
@@ -663,6 +777,12 @@ check_config (const char *url)
}
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,
@@ -672,9 +792,6 @@ check_config (const char *url)
GNUNET_break (0);
return NULL;
}
- cr->tt = GNUNET_SCHEDULER_add_delayed (CONFIG_GENERIC_TIMEOUT,
- &config_request_timeout,
- cr);
return cr;
}
@@ -761,6 +878,9 @@ begin_provider_config_check (const char *cc,
cc)) )
{
/* skip */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Skipping provider restricted to country `%s'\n",
+ restricted);
continue;
}
if ( (NULL == restricted) &&
@@ -780,7 +900,8 @@ begin_provider_config_check (const char *cc,
json_object_set_new (pl,
url,
prov));
- check_config (url);
+ check_config (CONFIG_GENERIC_TIMEOUT,
+ url);
}
GNUNET_assert (0 ==
json_object_set_new (state,
@@ -1144,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);
@@ -1157,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;
}
diff --git a/src/reducer/anastasis_api_redux.h b/src/reducer/anastasis_api_redux.h
index cc59713..03eef33 100644
--- a/src/reducer/anastasis_api_redux.h
+++ b/src/reducer/anastasis_api_redux.h
@@ -108,6 +108,42 @@ ANASTASIS_REDUX_load_continents_ (void);
/**
+ * Try to obtain configuration information on all configured
+ * providers. Upon success, call @a cb with the updated provider
+ * status data.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_poll_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Check if we have information on all providers involved in
+ * a recovery procedure, and if not, try to obtain it. Upon
+ * success, call @a cb with the updated provider status data.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_sync_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls);
+
+
+/**
* Returns the enum value to a string value of a state.
*
* @param state_string string to convert