anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

commit 0c2c6ac10d3c5a1f2294fa7f8d7f0f967f7fa793
parent 4fefdf6262bdffa6a4cfedd5fd25e24821787def
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 29 Jul 2026 12:09:18 +0200

use new typed C structs for the reducer

Diffstat:
Msrc/include/anastasis.h | 14++++++++++++++
Msrc/lib/anastasis_recovery.c | 9+++++++++
Msrc/reducer/anastasis_api_backup_redux.c | 3009+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/reducer/anastasis_api_discovery.c | 297+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/reducer/anastasis_api_providers.c | 157+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/reducer/anastasis_api_recovery_redux.c | 1873++++++++++++++++++++++++++++---------------------------------------------------
Msrc/reducer/anastasis_api_redux.c | 972++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/reducer/anastasis_api_redux.h | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/reducer/anastasis_api_redux_free.c | 166++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/reducer/anastasis_api_redux_parse.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/reducer/anastasis_api_redux_serialize.c | 4++++
Msrc/reducer/anastasis_api_redux_state.c | 15+++++++++++++++
Msrc/reducer/anastasis_api_redux_state.h | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 3032 insertions(+), 3885 deletions(-)

diff --git a/src/include/anastasis.h b/src/include/anastasis.h @@ -122,6 +122,20 @@ ANASTASIS_challenge_set_async (struct ANASTASIS_Challenge *challenge, /** + * Remember the @a answer_pin the user supplied for @a challenge without + * declaring the challenge asynchronous. Used when an answer was given + * up front, so that it is still around if the provider later turns the + * challenge into an asynchronous one. + * + * @param[in,out] challenge the challenge to update + * @param answer_pin answer code to remember + */ +void +ANASTASIS_challenge_set_answer_pin (struct ANASTASIS_Challenge *challenge, + uint64_t answer_pin); + + +/** * Possible outcomes of trying to start a challenge operation. */ enum ANASTASIS_ChallengeStartStatus diff --git a/src/lib/anastasis_recovery.c b/src/lib/anastasis_recovery.c @@ -471,6 +471,15 @@ ANASTASIS_challenge_set_async (struct ANASTASIS_Challenge *challenge, uint64_t answer_pin) { challenge->ci.async = true; + ANASTASIS_challenge_set_answer_pin (challenge, + answer_pin); +} + + +void +ANASTASIS_challenge_set_answer_pin (struct ANASTASIS_Challenge *challenge, + uint64_t answer_pin) +{ challenge->ci.have_answer_pin = true; challenge->ci.answer_pin = answer_pin; } diff --git a/src/reducer/anastasis_api_backup_redux.c b/src/reducer/anastasis_api_backup_redux.c @@ -24,6 +24,7 @@ #include "platform.h" #include "anastasis_redux.h" #include "anastasis_api_redux.h" +#include "anastasis_api_redux_state.h" #include <taler/taler_merchant_service.h> /** @@ -149,21 +150,17 @@ ANASTASIS_backup_state_to_string_ (enum ANASTASIS_BackupState bs) /** - * Update the 'backup_state' field of @a state to @a new_backup_state. + * Transition @a rs to @a new_backup_state. * - * @param[in,out] state the state to transition + * @param[in,out] rs the state to transition * @param new_backup_state the state to transition to */ static void -set_state (json_t *state, +set_state (struct ANASTASIS_ReduxState *rs, enum ANASTASIS_BackupState new_backup_state) { - GNUNET_assert ( - 0 == - json_object_set_new ( - state, - "backup_state", - json_string (ANASTASIS_backup_state_to_string_ (new_backup_state)))); + GNUNET_assert (ANASTASIS_RT_BACKUP == rs->type); + rs->details.backup.state = new_backup_state; } @@ -254,8 +251,14 @@ ANASTASIS_backup_start (const struct GNUNET_CONFIGURATION_Handle *cfg) json_object_set_new (initial_state, "reducer_type", json_string ("backup"))); - set_state (initial_state, - ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING); + GNUNET_assert ( + 0 == + json_object_set_new ( + initial_state, + "backup_state", + json_string ( + ANASTASIS_backup_state_to_string_ ( + ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING)))); return initial_state; } @@ -292,31 +295,38 @@ challenge_size_ok (uint32_t size_limit_in_mb, * @return NULL */ static struct ANASTASIS_ReduxAction * -add_authentication (json_t *state, +add_authentication (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *auth_providers; - json_t *method; + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + const json_t *method; const char *method_type; + const char *instructions; + const char *mime_type = NULL; void *challenge; size_t challenge_size; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("type", &method_type), + GNUNET_JSON_spec_string ("instructions", + &instructions), GNUNET_JSON_spec_varsize ("challenge", &challenge, &challenge_size), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("mime_type", + &mime_type), + NULL), GNUNET_JSON_spec_end () }; - auth_providers = json_object_get (state, - "authentication_providers"); - if (NULL == auth_providers) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' missing"); @@ -328,7 +338,8 @@ add_authentication (json_t *state, if (NULL == method) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'authentication_method' required"); @@ -340,10 +351,11 @@ add_authentication (json_t *state, NULL, NULL)) { GNUNET_break (0); - json_dumpf (method, + json_dumpf ((json_t *) method, stderr, JSON_INDENT (2)); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'authentication_method' content malformed"); @@ -353,70 +365,34 @@ add_authentication (json_t *state, { bool found = false; bool too_big = false; - json_t *details; - const char *url; - json_object_foreach (auth_providers, url, details) + for (unsigned int i = 0; i < rs->common.providers_len; i++) { - const json_t *methods = NULL; - json_t *imethod; - size_t index; - uint32_t size_limit_in_mb = 0; - const char *status; - uint32_t http_status = 0; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("status", - &status), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", - &size_limit_in_mb), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("http_status", - &http_status), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("methods", - &methods), - NULL), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i]; + const struct ANASTASIS_ReduxProviderConfig *cfg = &p->config; - if (GNUNET_OK != - GNUNET_JSON_parse (details, - ispec, - NULL, NULL)) + if (ANASTASIS_RPS_OK != p->status) + continue; + if (! p->have_config) { GNUNET_break (0); continue; } - if (0 != strcmp (status, - "ok")) - continue; - if (MHD_HTTP_OK != http_status) + if (MHD_HTTP_OK != cfg->http_status) continue; /* skip providers that are down */ - if ( (NULL == methods) || - (0 == size_limit_in_mb) ) + if (0 == cfg->storage_limit_in_megabytes) { GNUNET_break (0); continue; } - json_array_foreach (methods, index, imethod) - { - const char *type; - - type = json_string_value (json_object_get (imethod, - "type")); - GNUNET_break (NULL != type); - if ( (NULL != type) && - (0 == strcmp (type, - method_type)) ) + for (unsigned int j = 0; j < cfg->methods_len; j++) + if (0 == strcmp (cfg->methods[j].type, + method_type)) { found = true; break; } - } - if (! challenge_size_ok (size_limit_in_mb, + if (! challenge_size_ok (cfg->storage_limit_in_megabytes, challenge_size)) { /* Challenge data too big for this provider. Try to find another one. @@ -430,55 +406,42 @@ add_authentication (json_t *state, } if (! found) { - if (too_big) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG, - method_type); - } - else - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED, - method_type); - } + enum TALER_ErrorCode ec + = too_big + ? TALER_EC_ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG + : TALER_EC_ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED; + + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + ec, + method_type); GNUNET_JSON_parse_free (spec); return NULL; } } - GNUNET_JSON_parse_free (spec); /* append provided method to our array */ { - json_t *auth_method_arr; + struct ANASTASIS_ReduxAuthMethod am = { + .type = GNUNET_strdup (method_type), + .instructions = GNUNET_strdup (instructions), + .mime_type = (NULL == mime_type) ? NULL : GNUNET_strdup (mime_type), + .challenge = GNUNET_memdup (challenge, + challenge_size), + .challenge_size = challenge_size + }; - auth_method_arr = json_object_get (state, - "authentication_methods"); - if (NULL == auth_method_arr) - { - auth_method_arr = json_array (); - GNUNET_assert (0 == json_object_set_new (state, - "authentication_methods", - auth_method_arr)); - } - if (! json_is_array (auth_method_arr)) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'authentication_methods' must be an array"); - return NULL; - } - GNUNET_assert (0 == - json_array_append (auth_method_arr, - method)); - cb (cb_cls, - TALER_EC_NONE, - state); + GNUNET_array_append (b->authentication_methods, + b->authentication_methods_len, + am); + b->have_authentication_methods = true; } + GNUNET_JSON_parse_free (spec); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -495,19 +458,19 @@ add_authentication (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -del_authentication (json_t *state, +del_authentication (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *idx; - json_t *auth_method_arr; + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + const json_t *idx; + json_int_t index; - auth_method_arr = json_object_get (state, - "authentication_methods"); - if (! json_is_array (auth_method_arr)) + if (! b->have_authentication_methods) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_methods' must be an array"); @@ -515,7 +478,8 @@ del_authentication (json_t *state, } if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -526,29 +490,41 @@ del_authentication (json_t *state, if ( (NULL == idx) || (! json_is_integer (idx)) ) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'authentication_method' must be a number"); return NULL; } - + index = json_integer_value (idx); + if ( (index < 0) || + (index >= (json_int_t) b->authentication_methods_len) ) { - size_t index = (size_t) json_integer_value (idx); - - if (0 != json_array_remove (auth_method_arr, - index)) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, - "removal failed"); - return NULL; - } + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, + "removal failed"); + return NULL; + } + { + struct ANASTASIS_ReduxAuthMethod *am + = &b->authentication_methods[index]; + + GNUNET_free (am->type); + GNUNET_free (am->instructions); + GNUNET_free (am->challenge); + GNUNET_free (am->mime_type); + memmove (am, + am + 1, + sizeof (*am) * (b->authentication_methods_len - index - 1)); + b->authentication_methods_len--; } - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -645,14 +621,16 @@ struct Policy struct PolicyBuilder { /** - * Authentication providers available overall, from our state. + * State we are working on; provides the authentication providers + * available overall. */ - json_t *providers; + const struct ANASTASIS_ReduxCommon *common; /** - * Authentication methods available overall, from our state. + * Backup state we are working on; provides the authentication + * methods the user entered, and receives the computed policies. */ - const json_t *methods; + struct ANASTASIS_ReduxBackup *backup; /** * Head of DLL of all possible policies. @@ -665,11 +643,6 @@ struct PolicyBuilder struct Policy *p_tail; /** - * Array of authentication policies to be computed. - */ - json_t *policies; - - /** * Array of length @e req_methods. */ unsigned int *m_idx; @@ -781,137 +754,58 @@ equiv_provider (const struct PolicyBuilder *pb, const char *p1, const char *p2) { - const json_t *j1; - const json_t *j2; - const json_t *m1; - const json_t *m2; - struct TALER_Amount uc1; - struct TALER_Amount uc2; - - j1 = json_object_get (pb->providers, - p1); - j2 = json_object_get (pb->providers, - p2); + const struct ANASTASIS_ReduxProvider *j1; + const struct ANASTASIS_ReduxProvider *j2; + const struct ANASTASIS_ReduxProviderConfig *c1; + const struct ANASTASIS_ReduxProviderConfig *c2; + + j1 = ANASTASIS_REDUX_provider_find_ (pb->common, + p1); + j2 = ANASTASIS_REDUX_provider_find_ (pb->common, + p2); if ( (NULL == j1) || - (NULL == j2) ) + (NULL == j2) || + (! j1->have_config) || + (! j2->have_config) ) { GNUNET_break (0); return false; } - - { - struct GNUNET_JSON_Specification s1[] = { - GNUNET_JSON_spec_array_const ("methods", - &m1), - TALER_JSON_spec_amount_any ("truth_upload_fee", - &uc1), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j1, - s1, - NULL, NULL)) - { - GNUNET_break (0); - return false; - } - } - - { - struct GNUNET_JSON_Specification s2[] = { - GNUNET_JSON_spec_array_const ("methods", - &m2), - TALER_JSON_spec_amount_any ("truth_upload_fee", - &uc2), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j2, - s2, - NULL, NULL)) - { - GNUNET_break (0); - return false; - } - } - + c1 = &j1->config; + c2 = &j2->config; if ( (GNUNET_OK != - TALER_amount_cmp_currency (&uc1, - &uc2)) || + TALER_amount_cmp_currency (&c1->truth_upload_fee, + &c2->truth_upload_fee)) || (0 != - TALER_amount_cmp (&uc1, - &uc2)) ) + TALER_amount_cmp (&c1->truth_upload_fee, + &c2->truth_upload_fee)) ) return false; - if (json_array_size (m1) != json_array_size (m2)) + if (c1->methods_len != c2->methods_len) return false; + for (unsigned int i = 0; i < c1->methods_len; i++) { - size_t idx1; - json_t *e1; + const struct ANASTASIS_ReduxMethodSpec *m1 = &c1->methods[i]; + bool matched = false; - json_array_foreach (m1, idx1, e1) + for (unsigned int j = 0; j < c2->methods_len; j++) { - const char *type1; - struct TALER_Amount fee1; - struct GNUNET_JSON_Specification s1[] = { - GNUNET_JSON_spec_string ("type", - &type1), - TALER_JSON_spec_amount_any ("usage_fee", - &fee1), - GNUNET_JSON_spec_end () - }; - bool matched = false; - - if (GNUNET_OK != - GNUNET_JSON_parse (e1, - s1, - NULL, NULL)) - { - GNUNET_break (0); - return false; - } + const struct ANASTASIS_ReduxMethodSpec *m2 = &c2->methods[j]; + + if ( (0 == strcmp (m1->type, + m2->type)) && + (GNUNET_OK == + TALER_amount_cmp_currency (&m1->usage_fee, + &m2->usage_fee)) && + (0 == TALER_amount_cmp (&m1->usage_fee, + &m2->usage_fee)) ) { - size_t idx2; - json_t *e2; - - json_array_foreach (m2, idx2, e2) - { - const char *type2; - struct TALER_Amount fee2; - struct GNUNET_JSON_Specification s2[] = { - GNUNET_JSON_spec_string ("type", - &type2), - TALER_JSON_spec_amount_any ("usage_fee", - &fee2), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (e2, - s2, - NULL, NULL)) - { - GNUNET_break (0); - return false; - } - if ( (0 == strcmp (type1, - type2)) && - (GNUNET_OK == - TALER_amount_cmp_currency (&fee1, - &fee2)) && - (0 == TALER_amount_cmp (&fee1, - &fee2)) ) - { - matched = true; - break; - } - } + matched = true; + break; } - if (! matched) - return false; } + if (! matched) + return false; } return true; } @@ -936,110 +830,48 @@ eval_provider_selection (struct PolicyBuilder *pb, sizeof (policy_ent)); for (unsigned int i = 0; i < pb->req_methods; i++) { - const json_t *method_obj = json_array_get (pb->methods, - pb->m_idx[i]); - const json_t *provider_cfg = json_object_get (pb->providers, - prov_sel[i]); - const json_t *provider_methods; - const char *method_type; - json_t *md; - size_t index; + const struct ANASTASIS_ReduxAuthMethod *am + = &pb->backup->authentication_methods[pb->m_idx[i]]; + const struct ANASTASIS_ReduxProvider *p; + const struct ANASTASIS_ReduxProviderConfig *cfg; bool found = false; - uint32_t size_limit_in_mb; - struct TALER_Amount upload_cost; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", - &size_limit_in_mb), - GNUNET_JSON_spec_array_const ("methods", - &provider_methods), - TALER_JSON_spec_amount_any ("truth_upload_fee", - &upload_cost), - GNUNET_JSON_spec_end () - }; - void *challenge; - size_t challenge_size; - struct GNUNET_JSON_Specification mspec[] = { - GNUNET_JSON_spec_string ("type", - &method_type), - GNUNET_JSON_spec_varsize ("challenge", - &challenge, - &challenge_size), - GNUNET_JSON_spec_end () - }; policy_ent[i].provider_name = prov_sel[i]; - if (GNUNET_OK != - GNUNET_JSON_parse (method_obj, - mspec, - NULL, NULL)) - { - GNUNET_break (0); - pb->ec = TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID; - pb->hint = "'authentication_method' content malformed"; - goto cleanup; - } - - if (MHD_HTTP_OK != - json_integer_value (json_object_get (provider_cfg, - "http_status"))) - { - GNUNET_JSON_parse_free (mspec); - goto cleanup; - } - if (GNUNET_OK != - GNUNET_JSON_parse (provider_cfg, - pspec, - NULL, NULL)) + p = ANASTASIS_REDUX_provider_find_ (pb->common, + prov_sel[i]); + if ( (NULL == p) || + (! p->have_config) || + (MHD_HTTP_OK != p->config.http_status) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Skipping provider %s: no suitable configuration\n", prov_sel[i]); - GNUNET_JSON_parse_free (mspec); goto cleanup; } - json_array_foreach (provider_methods, index, md) + cfg = &p->config; + for (unsigned int j = 0; j < cfg->methods_len; j++) { - const char *type; - struct TALER_Amount method_cost; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("type", - &type), - TALER_JSON_spec_amount_any ("usage_fee", - &method_cost), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxMethodSpec *ms = &cfg->methods[j]; - if (GNUNET_OK != - GNUNET_JSON_parse (md, - spec, - NULL, NULL)) - { - GNUNET_break (0); - pb->ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID; - pb->hint = "'methods' of provider"; - goto cleanup; - } - if ( (0 == strcmp (type, - method_type)) && - (challenge_size_ok (size_limit_in_mb, - challenge_size) ) ) + if ( (0 == strcmp (ms->type, + am->type)) && + (challenge_size_ok (cfg->storage_limit_in_megabytes, + am->challenge_size) ) ) { found = true; add_cost (&policy_ent[i].usage_fee, - &method_cost); + &ms->usage_fee); add_cost (&policy_ent[i].usage_fee, - &upload_cost); + &cfg->truth_upload_fee); } } if (! found) { /* Provider does not OFFER this method, combination not possible. Cost is basically 'infinite', but we simply then skip this. */ - GNUNET_JSON_parse_free (mspec); goto cleanup; } - GNUNET_JSON_parse_free (mspec); } /* calculate provider diversity by counting number of different @@ -1159,40 +991,15 @@ provider_candidate (struct PolicyBuilder *pb, const char *prov_sel[], unsigned int i) { - const char *url; - json_t *pconfig; - - json_object_foreach (pb->providers, url, pconfig) + for (unsigned int k = 0; k < pb->common->providers_len; k++) { - const char *status; - uint32_t http_status = 0; - 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_end () - }; + const struct ANASTASIS_ReduxProvider *p = &pb->common->providers[k]; - if (GNUNET_OK != - GNUNET_JSON_parse (pconfig, - spec, - NULL, NULL)) - { - GNUNET_break (0); - continue; - } - if ( (MHD_HTTP_OK != http_status) || - (0 == strcmp (status, - "disabled")) ) - { - GNUNET_JSON_parse_free (spec); + if ( (ANASTASIS_RPS_DISABLED == p->status) || + (! p->have_config) || + (MHD_HTTP_OK != p->config.http_status) ) continue; - } - GNUNET_JSON_parse_free (spec); - prov_sel[i] = url; + prov_sel[i] = p->url.url; if (i == pb->req_methods - 1) { eval_provider_selection (pb, @@ -1510,8 +1317,8 @@ find_best_map (struct PolicyBuilder *pb, /** - * Select cheapest policy combinations and add them to the JSON ``policies`` - * array in @a pb + * Select cheapest policy combinations and store them in the ``policies`` + * of the backup state @a pb builds. * * @param[in,out] pb policy builder with our state */ @@ -1545,7 +1352,11 @@ select_policies (struct PolicyBuilder *pb) p = p->next) { struct PolicyMap *pm = &best[off++]; - json_t *method_arr; + struct ANASTASIS_ReduxPolicy pol = { + .methods_len = pb->req_methods, + .methods = GNUNET_new_array (pb->req_methods, + struct ANASTASIS_ReduxPolicyMethod) + }; #if DEBUG fprintf (stderr, @@ -1560,31 +1371,19 @@ select_policies (struct PolicyBuilder *pb) } fprintf (stderr, "\n"); #endif - /* Convert "best" selection into 'policies' array */ - method_arr = json_array (); - GNUNET_assert (NULL != method_arr); + /* Convert "best" selection into the 'policies' array */ for (unsigned int i = 0; i < pb->req_methods; i++) { - json_t *policy_method = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("authentication_method", - p->challenges[i]), - GNUNET_JSON_pack_string ("provider", - pm->providers[i].provider_name)); - - GNUNET_assert (0 == - json_array_append_new (method_arr, - policy_method)); - } - { - json_t *policy = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ( - "methods", - method_arr)); - - GNUNET_assert (0 == - json_array_append_new (pb->policies, - policy)); + struct ANASTASIS_ReduxPolicyMethod *pm2 = &pol.methods[i]; + + pm2->authentication_method.idx = p->challenges[i]; + ANASTASIS_REDUX_provider_url_set_ (&pm2->provider, + pm->providers[i].provider_name); } + GNUNET_array_append (pb->backup->policies, + pb->backup->policies_len, + pol); + pb->backup->have_policies = true; } } } @@ -1637,52 +1436,50 @@ clean_pb (struct PolicyBuilder *pb) * @return NULL */ static struct ANASTASIS_ReduxAction * -done_authentication (json_t *state, +done_authentication (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; struct PolicyBuilder pb = { - .ec = TALER_EC_NONE + .ec = TALER_EC_NONE, + .common = &rs->common, + .backup = b }; - json_t *providers; - json_t *policy_providers; + const json_t *providers; - pb.providers = json_object_get (state, - "authentication_providers"); - if ( (NULL == pb.providers) || - (! json_is_object (pb.providers) ) ) + if (! rs->common.have_providers) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' must be provided"); return NULL; } - pb.methods = json_object_get (state, - "authentication_methods"); - if ( (NULL == pb.methods) || - (! json_is_array (pb.methods)) || - (json_array_size (pb.methods) > UINT_MAX) ) + if (! b->have_authentication_methods) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_methods' must be provided"); return NULL; } - pb.num_methods - = (unsigned int) json_array_size (pb.methods); + pb.num_methods = b->authentication_methods_len; switch (pb.num_methods) { case 0: - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_methods' must not be empty"); return NULL; case 1: - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "Two factor authentication (2-FA) is required"); @@ -1716,106 +1513,94 @@ done_authentication (json_t *state, method_candidate (&pb, 0); } - pb.policies = json_array (); + /* the computed policies replace whatever was there before */ + ANASTASIS_REDUX_policies_clear_ (b); + b->have_policies = true; select_policies (&pb); clean_pb (&pb); if (TALER_EC_NONE != pb.ec) { - json_decref (pb.policies); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, pb.ec, pb.hint); return NULL; } - GNUNET_assert (0 == - json_object_set_new (state, - "policies", - pb.policies)); + ANASTASIS_REDUX_policy_providers_clear_ (b); + b->have_policy_providers = true; providers = json_object_get (arguments, "providers"); if (NULL == providers) { /* Setup a providers array from all working providers */ - json_t *available = json_object_get (state, - "authentication_providers"); - const char *url; - json_t *details; - - policy_providers = json_array (); - GNUNET_assert (NULL != policy_providers); - json_object_foreach (available, url, details) + for (unsigned int i = 0; i < rs->common.providers_len; i++) { - json_t *provider; + const char *url = rs->common.providers[i].url.url; + struct ANASTASIS_ReduxPolicyProvider pp = { 0 }; struct ANASTASIS_CRYPTO_ProviderSaltP salt; if (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (state, - url, - &salt)) + ANASTASIS_REDUX_lookup_salt_ (&rs->common, + url, + &salt)) continue; /* skip providers that are down */ - provider = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("provider_url", - url)); - GNUNET_assert (NULL != provider); - GNUNET_assert (0 == - json_array_append_new (policy_providers, - provider)); + ANASTASIS_REDUX_provider_url_set_ (&pp.provider_url, + url); + GNUNET_array_append (b->policy_providers, + b->policy_providers_len, + pp); } } else { - /* Setup a providers array from all working providers */ + /* Setup a providers array from the requested providers */ size_t off; json_t *url; - policy_providers = json_array (); json_array_foreach (providers, off, url) { - json_t *provider; + struct ANASTASIS_ReduxPolicyProvider pp = { 0 }; struct ANASTASIS_CRYPTO_ProviderSaltP salt; const char *url_str; url_str = json_string_value (url); if ( (NULL == url_str) || (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (state, - url_str, - &salt)) ) + ANASTASIS_REDUX_lookup_salt_ (&rs->common, + url_str, + &salt)) ) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "unworkable provider requested"); return NULL; } - provider = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("provider_url", - url_str)); - GNUNET_assert (0 == - json_array_append_new (policy_providers, - provider)); + ANASTASIS_REDUX_provider_url_set_ (&pp.provider_url, + url_str); + GNUNET_array_append (b->policy_providers, + b->policy_providers_len, + pp); } } - if (0 == json_array_size (policy_providers)) + if (0 == b->policy_providers_len) { - json_decref (policy_providers); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "no workable providers in state"); return NULL; } - GNUNET_assert (0 == - json_object_set_new (state, - "policy_providers", - policy_providers)); - set_state (state, + set_state (rs, ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -1834,17 +1619,17 @@ done_authentication (json_t *state, * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -add_provider (json_t *state, +add_provider (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - if (ANASTASIS_add_provider_ (state, + if (ANASTASIS_add_provider_ (rs, arguments, cb, cb_cls)) return NULL; - return ANASTASIS_REDUX_backup_begin_ (state, + return ANASTASIS_REDUX_backup_begin_ (rs, NULL, cb, cb_cls); @@ -1865,21 +1650,20 @@ add_provider (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -add_policy (json_t *state, +add_policy (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; const json_t *arg_array; - json_t *policies; - const json_t *auth_providers; - const json_t *auth_methods; - json_t *methods; + struct ANASTASIS_ReduxPolicy pol = { 0 }; if (NULL == arguments) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -1890,50 +1674,45 @@ add_policy (json_t *state, if (! json_is_array (arg_array)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'policy' not an array"); return NULL; } - policies = json_object_get (state, - "policies"); - if (! json_is_array (policies)) + if (! b->have_policies) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'policies' not an array"); return NULL; } - auth_providers = json_object_get (state, - "authentication_providers"); - if (! json_is_object (auth_providers)) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'auth_providers' not an object"); return NULL; } - auth_methods = json_object_get (state, - "authentication_methods"); - if (! json_is_array (auth_methods)) + if (! b->have_authentication_methods) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'auth_methods' not an array"); return NULL; } - methods = json_array (); - GNUNET_assert (NULL != methods); - - /* Add all methods from 'arg_array' to 'methods' */ + /* Add all methods from 'arg_array' to the new policy */ { size_t aindex; json_t *method; @@ -1943,7 +1722,8 @@ add_policy (json_t *state, const char *provider_url; uint32_t method_idx; const char *method_type; - const json_t *prov_methods; + const struct ANASTASIS_ReduxProvider *p; + const struct ANASTASIS_ReduxProviderConfig *cfg; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("provider", &provider_url), @@ -1958,174 +1738,115 @@ add_policy (json_t *state, NULL, NULL)) { GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_policy_clear_ (&pol); + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'method' details malformed"); return NULL; } + p = ANASTASIS_REDUX_provider_find_ (&rs->common, + provider_url); + if (NULL == p) { - const json_t *prov_cfg; - uint32_t limit; - const char *status; - uint32_t http_status = 0; - 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_uint32 ("storage_limit_in_megabytes", - &limit), - GNUNET_JSON_spec_array_const ("methods", - &prov_methods), - GNUNET_JSON_spec_end () - }; - - prov_cfg = json_object_get (auth_providers, - provider_url); - if (NULL == prov_cfg) - { - GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "provider URL unknown"); - return NULL; - } - if (GNUNET_OK != - GNUNET_JSON_parse (prov_cfg, - spec, - NULL, NULL)) - { - /* skip provider, likely was down */ - json_decref (methods); - continue; - } - if ( (MHD_HTTP_OK != http_status) || - (0 != strcmp (status, - "ok")) ) - { - /* skip provider, disabled or down */ - json_decref (methods); - continue; - } + GNUNET_break (0); + ANASTASIS_REDUX_policy_clear_ (&pol); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "provider URL unknown"); + return NULL; } + if ( (! p->have_config) || + (ANASTASIS_RPS_OK != p->status) || + (MHD_HTTP_OK != p->config.http_status) ) + continue; /* skip provider, disabled or down */ + cfg = &p->config; + if (method_idx >= b->authentication_methods_len) { - const json_t *auth_method; - - auth_method = json_array_get (auth_methods, - method_idx); - if (NULL == auth_method) - { - GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "authentication method unknown"); - return NULL; - } - method_type = json_string_value (json_object_get (auth_method, - "type")); - if (NULL == method_type) - { - GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "authentication method must be a string"); - return NULL; - } + GNUNET_break (0); + ANASTASIS_REDUX_policy_clear_ (&pol); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "authentication method unknown"); + return NULL; } + method_type = b->authentication_methods[method_idx].type; { bool found = false; - size_t mindex; - json_t *pm; - json_array_foreach (prov_methods, mindex, pm) - { - struct TALER_Amount method_cost; - const char *type; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("type", - &type), - TALER_JSON_spec_amount_any ("usage_fee", - &method_cost), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (pm, - spec, - NULL, NULL)) + + for (unsigned int j = 0; j < cfg->methods_len; j++) + if (0 == strcmp (cfg->methods[j].type, + method_type)) { - GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "provider authentication method specification invalid"); - return NULL; + found = true; + break; } - if (0 != strcmp (type, - method_type)) - continue; - found = true; - break; - } if (! found) { GNUNET_break (0); - json_decref (methods); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "selected provider does not support authentication method"); + ANASTASIS_REDUX_policy_clear_ (&pol); + ANASTASIS_REDUX_fail_ ( + rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "selected provider does not support authentication method"); return NULL; } } - GNUNET_assert (0 == - json_array_append (methods, - method)); + { + struct ANASTASIS_ReduxPolicyMethod pm = { + .authentication_method.idx = method_idx + }; + + ANASTASIS_REDUX_provider_url_set_ (&pm.provider, + provider_url); + GNUNET_array_append (pol.methods, + pol.methods_len, + pm); + } } /* end of json_array_foreach (arg_array, mindex, method) */ } /* add new policy to array of existing policies */ { - json_t *policy; - json_t *idx; + const json_t *idx; + unsigned int at; - policy = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("methods", - methods)); idx = json_object_get (arguments, "policy_index"); if ( (NULL == idx) || (! json_is_integer (idx)) ) - { - GNUNET_assert (0 == - json_array_append_new (policies, - policy)); - } + at = b->policies_len; else + at = GNUNET_MIN ((unsigned int) json_integer_value (idx), + b->policies_len); + GNUNET_array_append (b->policies, + b->policies_len, + pol); + if (at != b->policies_len - 1) { - GNUNET_assert (0 == - json_array_insert_new (policies, - json_integer_value (idx), - policy)); + /* shift the tail one to the right and drop the new entry in */ + memmove (&b->policies[at + 1], + &b->policies[at], + sizeof (struct ANASTASIS_ReduxPolicy) + * (b->policies_len - at - 1)); + b->policies[at] = pol; } } - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -2144,19 +1865,20 @@ add_policy (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -update_policy (json_t *state, +update_policy (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; const json_t *idx; - size_t index; - json_t *policy_arr; + json_int_t index; if (NULL == arguments) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -2167,35 +1889,42 @@ update_policy (json_t *state, if (! json_is_integer (idx)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'policy_index' must be an integer"); return NULL; } index = json_integer_value (idx); - policy_arr = json_object_get (state, - "policies"); - if (! json_is_array (policy_arr)) + if (! b->have_policies) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'policies' must be an array"); return NULL; } - if (0 != json_array_remove (policy_arr, - index)) + if ( (index < 0) || + (index >= (json_int_t) b->policies_len) ) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "removal failed"); return NULL; } - return add_policy (state, + ANASTASIS_REDUX_policy_clear_ (&b->policies[index]); + memmove (&b->policies[index], + &b->policies[index + 1], + sizeof (struct ANASTASIS_ReduxPolicy) + * (b->policies_len - index - 1)); + b->policies_len--; + return add_policy (rs, arguments, cb, cb_cls); @@ -2216,18 +1945,19 @@ update_policy (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -del_policy (json_t *state, +del_policy (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; const json_t *idx; - size_t index; - json_t *policy_arr; + json_int_t index; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -2237,35 +1967,43 @@ del_policy (json_t *state, "policy_index"); if (! json_is_integer (idx)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'policy_index' must be an integer"); return NULL; } index = json_integer_value (idx); - policy_arr = json_object_get (state, - "policies"); - if (! json_is_array (policy_arr)) + if (! b->have_policies) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'policies' must be an array"); return NULL; } - if (0 != json_array_remove (policy_arr, - index)) + if ( (index < 0) || + (index >= (json_int_t) b->policies_len) ) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "removal failed"); return NULL; } - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_policy_clear_ (&b->policies[index]); + memmove (&b->policies[index], + &b->policies[index + 1], + sizeof (struct ANASTASIS_ReduxPolicy) + * (b->policies_len - index - 1)); + b->policies_len--; + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -2284,21 +2022,21 @@ del_policy (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -del_challenge (json_t *state, +del_challenge (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; const json_t *pidx; const json_t *cidx; - size_t index; - json_t *policy_arr; - json_t *policy; - json_t *method_arr; + json_int_t index; + struct ANASTASIS_ReduxPolicy *policy; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -2310,7 +2048,8 @@ del_challenge (json_t *state, "challenge_index"); if (! json_is_integer (pidx)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'policy_index' must be an integer"); @@ -2318,56 +2057,60 @@ del_challenge (json_t *state, } if (! json_is_integer (cidx)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'challenge_index' must be an integer"); return NULL; } index = json_integer_value (pidx); - policy_arr = json_object_get (state, - "policies"); - if (! json_is_array (policy_arr)) + if (! b->have_policies) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'policies' must be an array"); return NULL; } - policy = json_array_get (policy_arr, - index); - if (NULL == policy) + if ( (index < 0) || + (index >= (json_int_t) b->policies_len) ) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'policy_index' out of range"); return NULL; } - method_arr = json_object_get (policy, - "methods"); - if (NULL == method_arr) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, - "methods missing in policy"); - return NULL; - } + policy = &b->policies[index]; index = json_integer_value (cidx); - if (0 != json_array_remove (method_arr, - index)) + if ( (index < 0) || + (index >= (json_int_t) policy->methods_len) ) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "removal failed"); return NULL; } - cb (cb_cls, - TALER_EC_NONE, - state); + { + struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[index]; + + ANASTASIS_REDUX_provider_url_clear_ (&pm->provider); + if (NULL != pm->truth) + ANASTASIS_truth_free (pm->truth); + memmove (pm, + pm + 1, + sizeof (*pm) * (policy->methods_len - index - 1)); + policy->methods_len--; + } + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -2408,17 +2151,15 @@ expiration_to_years (struct GNUNET_TIME_Timestamp expiration) * #GNUNET_SYSERR if the state is invalid */ static enum GNUNET_GenericReturnValue -update_expiration_cost (json_t *state, +update_expiration_cost (struct ANASTASIS_ReduxState *rs, struct GNUNET_TIME_Timestamp expiration) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; struct Costs *costs = NULL; unsigned int years; - json_t *providers; bool is_free = true; - providers = json_object_get (state, - "authentication_providers"); - if (! json_is_object (providers)) + if (! rs->common.have_providers) { GNUNET_break (0); return GNUNET_SYSERR; @@ -2427,51 +2168,26 @@ update_expiration_cost (json_t *state, years = expiration_to_years (expiration); /* go over all providers and add up cost */ - { - const char *url; - json_t *provider; - - json_object_foreach (providers, url, provider) + for (unsigned int i = 0; i < rs->common.providers_len; i++) + { + const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i]; + struct TALER_Amount fee; + + if ( (ANASTASIS_RPS_OK != p->status) || + (! p->have_config) || + (MHD_HTTP_OK != p->config.http_status) ) + continue; /* skip providers that are down or disabled */ + if (0 > + TALER_amount_multiply (&fee, + &p->config.annual_fee, + years)) { - struct TALER_Amount annual_fee; - const char *status; - uint32_t http_status = 0; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_string ("status", - &status), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("http_status", - &http_status), - NULL), - TALER_JSON_spec_amount_any ("annual_fee", - &annual_fee), - GNUNET_JSON_spec_end () - }; - struct TALER_Amount fee; - - if (GNUNET_OK != - GNUNET_JSON_parse (provider, - pspec, - NULL, NULL)) - { - /* likely down, skip */ - continue; - } - if ( (MHD_HTTP_OK != http_status) || - (0 != strcmp (status, - "ok")) ) - continue; /* skip providers that are down or disabled */ - if (0 > - TALER_amount_multiply (&fee, - &annual_fee, - years)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - add_cost (&costs, - &fee); + GNUNET_break (0); + free_costs (costs); + return GNUNET_SYSERR; } + add_cost (&costs, + &fee); } /* go over all truths and add up cost */ @@ -2480,44 +2196,21 @@ update_expiration_cost (json_t *state, unsigned int len = 0; struct AlreadySeen { - uint32_t method; + unsigned int method; const char *provider_url; } *seen = NULL; - json_t *policies; - size_t pidx; - json_t *policy; - policies = json_object_get (state, - "policies"); - json_array_foreach (policies, pidx, policy) + for (unsigned int pidx = 0; pidx < b->policies_len; pidx++) { - json_t *methods; - json_t *method; - size_t midx; + const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pidx]; - methods = json_object_get (policy, - "methods"); - json_array_foreach (methods, midx, method) + for (unsigned int midx = 0; midx < policy->methods_len; midx++) { - const char *provider_url; - uint32_t method_idx; - - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("provider", - &provider_url), - GNUNET_JSON_spec_uint32 ("authentication_method", - &method_idx), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[midx]; + const char *provider_url = pm->provider.url; + unsigned int method_idx = pm->authentication_method.idx; + const struct ANASTASIS_ReduxProvider *p; - if (GNUNET_OK != - GNUNET_JSON_parse (method, - spec, - NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } /* check if we have seen this one before */ { bool found = false; @@ -2539,47 +2232,33 @@ update_expiration_cost (json_t *state, seen[off].method = method_idx; seen[off].provider_url = provider_url; off++; + p = ANASTASIS_REDUX_provider_find_ (&rs->common, + provider_url); + if ( (NULL == p) || + (! p->have_config) || + (ANASTASIS_RPS_OK != p->status) || + (MHD_HTTP_OK != p->config.http_status) ) + { + GNUNET_break (0); + GNUNET_array_grow (seen, + len, + 0); + free_costs (costs); + return GNUNET_SYSERR; + } { - struct TALER_Amount upload_cost; - const char *status; - uint32_t http_status = 0; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_string ("status", - &status), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("http_status", - &http_status), - NULL), - TALER_JSON_spec_amount_any ("truth_upload_fee", - &upload_cost), - GNUNET_JSON_spec_end () - }; struct TALER_Amount fee; - const json_t *provider_cfg - = json_object_get (providers, - provider_url); - - if (GNUNET_OK != - GNUNET_JSON_parse (provider_cfg, - pspec, - NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ( (MHD_HTTP_OK != http_status) || - (0 != strcmp (status, - "ok")) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + if (0 > TALER_amount_multiply (&fee, - &upload_cost, + &p->config.truth_upload_fee, years)) { GNUNET_break (0); + GNUNET_array_grow (seen, + len, + 0); + free_costs (costs); return GNUNET_SYSERR; } add_cost (&costs, @@ -2593,50 +2272,28 @@ update_expiration_cost (json_t *state, } /* convert 'costs' into state */ + GNUNET_free (b->upload_fees); + b->upload_fees_len = 0; + b->have_upload_fees = true; + while (NULL != costs) { - json_t *arr; + struct Costs *nxt = costs->next; - arr = json_array (); - GNUNET_assert (NULL != arr); - while (NULL != costs) + if (! TALER_amount_is_zero (&costs->cost)) { - struct Costs *nxt = costs->next; - - if (! TALER_amount_is_zero (&costs->cost)) - { - json_t *ao; - - ao = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("fee", - &costs->cost)); - GNUNET_assert (0 == - json_array_append_new (arr, - ao)); - is_free = false; - } - GNUNET_free (costs); - costs = nxt; + GNUNET_array_append (b->upload_fees, + b->upload_fees_len, + costs->cost); + is_free = false; } - GNUNET_assert (0 == - json_object_set_new (state, - "upload_fees", - arr)); + GNUNET_free (costs); + costs = nxt; } if (is_free) expiration = GNUNET_TIME_relative_to_timestamp (ANASTASIS_FREE_STORAGE); - /* update 'expiration' in state */ - { - json_t *eo; - - eo = GNUNET_JSON_from_timestamp (expiration); - GNUNET_assert (0 == - json_object_set_new (state, - "expiration", - eo)); - } - - + b->expiration = expiration; + b->have_expiration = true; return GNUNET_OK; } @@ -2652,63 +2309,45 @@ update_expiration_cost (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -done_policy_review (json_t *state, +done_policy_review (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - const json_t *policy_arr; + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + struct GNUNET_TIME_Timestamp exp; - policy_arr = json_object_get (state, - "policies"); - if (0 == json_array_size (policy_arr)) + if (0 == b->policies_len) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "no policies specified"); return NULL; } + exp = b->have_expiration + ? b->expiration + : GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS); + if (GNUNET_TIME_absolute_is_zero (exp.abs_time)) + exp = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS); + if (GNUNET_OK != + update_expiration_cost (rs, + exp)) { - struct GNUNET_TIME_Timestamp exp - = GNUNET_TIME_UNIT_ZERO_TS; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("expiration", - &exp), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (state, - spec, - NULL, NULL)) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, - "invalid expiration specified"); - return NULL; - } - if (GNUNET_TIME_absolute_is_zero (exp.abs_time)) - exp = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS); - if (GNUNET_OK != - update_expiration_cost (state, - exp)) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, - "could not calculate expiration cost"); - return NULL; - } + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, + "could not calculate expiration cost"); + return NULL; } - set_state (state, + set_state (rs, ANASTASIS_BACKUP_STATE_SECRET_EDITING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -2830,9 +2469,9 @@ struct UploadContext void *cb_cls; /** - * Our state. + * Our state; we own it. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; /** * Master secret sharing operation, NULL if not yet running. @@ -2898,25 +2537,90 @@ upload_cancel_cb (void *cls) ANASTASIS_secret_share_cancel (uc->ss); uc->ss = NULL; } - json_decref (uc->state); + ANASTASIS_REDUX_state_free_ (uc->rs); GNUNET_free (uc); } /** - * Take all of the ongoing truth uploads and serialize them into the @a uc - * state. + * Return the state of @a uc to the application and dispose of @a uc. + * + * @param[in] uc context to finish + * @param ec error code to report alongside the state + */ +static void +uc_return (struct UploadContext *uc, + enum TALER_ErrorCode ec) +{ + struct ANASTASIS_ReduxState *rs = uc->rs; + ANASTASIS_ActionCallback cb = uc->cb; + void *cb_cls = uc->cb_cls; + + uc->rs = NULL; + upload_cancel_cb (uc); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + ec); +} + + +/** + * Report an error to the application and dispose of @a uc. + * + * @param[in] uc context to finish + * @param ec error to report + * @param detail human-readable detail, may be NULL + */ +static void +uc_fail (struct UploadContext *uc, + enum TALER_ErrorCode ec, + const char *detail) +{ + ANASTASIS_ActionCallback cb = uc->cb; + void *cb_cls = uc->cb_cls; + + upload_cancel_cb (uc); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + detail); +} + + +/** + * Duplicate the truth @a t. Truths are shared between the policies + * that use them, but each `struct ANASTASIS_Truth` has a single owner, + * so the state gets its own copy. + * + * @param t truth to copy + * @return a copy of @a t + */ +static struct ANASTASIS_Truth * +truth_dup (const struct ANASTASIS_Truth *t) +{ + json_t *jt = ANASTASIS_truth_to_json (t); + struct ANASTASIS_Truth *r; + + GNUNET_assert (NULL != jt); + r = ANASTASIS_truth_from_json (jt); + GNUNET_assert (NULL != r); + json_decref (jt); + return r; +} + + +/** + * Take all of the ongoing truth uploads and store them in the @a uc + * state, so that they survive a round-trip through the application. * * @param[in,out] uc context to take truth uploads from and to update state of */ static void serialize_truth (struct UploadContext *uc) { - json_t *policies; + struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup; - policies = json_object_get (uc->state, - "policies"); - GNUNET_assert (json_is_array (policies)); for (struct TruthUpload *tue = uc->tues_head; NULL != tue; tue = tue->next) @@ -2926,26 +2630,17 @@ serialize_truth (struct UploadContext *uc) for (unsigned int i = 0; i<tue->policies_length; i++) { const struct PolicyMethodReference *pmr = &tue->policies[i]; - json_t *policy = json_array_get (policies, - pmr->policy_index); - json_t *methods = json_object_get (policy, - "methods"); - json_t *auth_method = json_array_get (methods, - pmr->method_index); - json_t *truth = ANASTASIS_truth_to_json (tue->t); - - GNUNET_assert (0 == - json_object_set_new (truth, - "upload_status", - json_integer (tue->us))); - GNUNET_assert (NULL != policy); - GNUNET_assert (NULL != methods); - GNUNET_assert (NULL != auth_method); - GNUNET_assert (NULL != truth); - GNUNET_assert (0 == - json_object_set_new (auth_method, - "truth", - truth)); + struct ANASTASIS_ReduxPolicy *policy; + struct ANASTASIS_ReduxPolicyMethod *pm; + + GNUNET_assert (pmr->policy_index < b->policies_len); + policy = &b->policies[pmr->policy_index]; + GNUNET_assert (pmr->method_index < policy->methods_len); + pm = &policy->methods[pmr->method_index]; + if (NULL != pm->truth) + ANASTASIS_truth_free (pm->truth); + pm->truth = truth_dup (tue->t); + pm->upload_status = tue->us; } } } @@ -2962,37 +2657,19 @@ serialize_truth (struct UploadContext *uc) * upload considerations without causing a problem */ static bool -provider_required (const json_t *state, +provider_required (const struct ANASTASIS_ReduxState *rs, const char *provider_url) { - json_t *policies - = json_object_get (state, - "policies"); - size_t pidx; - json_t *policy; + const struct ANASTASIS_ReduxBackup *b = &rs->details.backup; - json_array_foreach (policies, pidx, policy) + for (unsigned int pidx = 0; pidx < b->policies_len; pidx++) { - 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")); + const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pidx]; - if (NULL == provider) - { - GNUNET_break (0); - continue; - } - if (0 == strcmp (provider, + for (unsigned int midx = 0; midx < policy->methods_len; midx++) + if (0 == strcmp (policy->methods[midx].provider.url, provider_url)) return true; - } } return false; } @@ -3018,6 +2695,7 @@ secret_share_result_cb (void *cls, const struct ANASTASIS_ShareResult *sr) { struct UploadContext *uc = cls; + struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup; uc->ss = NULL; switch (sr->ss) @@ -3025,147 +2703,103 @@ secret_share_result_cb (void *cls, case ANASTASIS_SHARE_STATUS_SUCCESS: /* Just to be safe, delete the "core_secret" so that it is not accidentally preserved anywhere */ - (void) json_object_del (uc->state, - "core_secret"); + json_decref (b->core_secret); + b->core_secret = NULL; + for (unsigned int i = 0; i < b->success_details_len; i++) + ANASTASIS_REDUX_provider_url_clear_ ( + &b->success_details[i].provider_url); + GNUNET_free (b->success_details); + b->success_details_len = 0; + b->have_success_details = true; + for (unsigned int i = 0; i<sr->details.success.num_providers; i++) { - json_t *sa = json_object (); + const struct ANASTASIS_ProviderSuccessStatus *pssi + = &sr->details.success.pss[i]; + struct ANASTASIS_ReduxSuccessDetail d = { + .policy_version = pssi->policy_version, + .policy_expiration = pssi->policy_expiration + }; - GNUNET_assert (NULL != sa); - for (unsigned int i = 0; i<sr->details.success.num_providers; i++) - { - const struct ANASTASIS_ProviderSuccessStatus *pssi - = &sr->details.success.pss[i]; - json_t *d; - - d = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("policy_version", - pssi->policy_version), - GNUNET_JSON_pack_timestamp ("policy_expiration", - pssi->policy_expiration)); - GNUNET_assert (NULL != d); - GNUNET_assert (0 == - json_object_set_new (sa, - pssi->provider_url, - d)); - } - GNUNET_assert (0 == - json_object_set_new (uc->state, - "success_details", - sa)); + ANASTASIS_REDUX_provider_url_set_ (&d.provider_url, + pssi->provider_url); + GNUNET_array_append (b->success_details, + b->success_details_len, + d); } - set_state (uc->state, + set_state (uc->rs, ANASTASIS_BACKUP_STATE_BACKUP_FINISHED); - uc->cb (uc->cb_cls, - TALER_EC_NONE, - uc->state); - break; + uc_return (uc, + TALER_EC_NONE); + return; case ANASTASIS_SHARE_STATUS_PAYMENT_REQUIRED: + set_state (uc->rs, + ANASTASIS_BACKUP_STATE_POLICIES_PAYING); + serialize_truth (uc); + ANASTASIS_REDUX_policy_payment_requests_clear_ (b); + b->have_policy_payment_requests = true; + for (unsigned int i = 0; i< + sr->details.payment_required.payment_requests_length; i++) { - json_t *ra; - json_t *providers; - - providers = json_object_get (uc->state, - "policy_providers"); - set_state (uc->state, - ANASTASIS_BACKUP_STATE_POLICIES_PAYING); - serialize_truth (uc); - ra = json_array (); - GNUNET_assert (NULL != ra); - for (unsigned int i = 0; i< - sr->details.payment_required.payment_requests_length; i++) + const struct ANASTASIS_SharePaymentRequest *spr; + struct ANASTASIS_ReduxPolicyPaymentRequest ppr = { 0 }; + + spr = &sr->details.payment_required.payment_requests[i]; + ppr.payto = GNUNET_strdup (spr->payment_request_url); + ANASTASIS_REDUX_provider_url_set_ (&ppr.provider, + spr->provider_url); + GNUNET_array_append (b->policy_payment_requests, + b->policy_payment_requests_len, + ppr); + for (unsigned int off = 0; off < b->policy_providers_len; off++) { - const struct ANASTASIS_SharePaymentRequest *spr; - json_t *pr; - size_t off; - json_t *provider; - - spr = &sr->details.payment_required.payment_requests[i]; - pr = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("payto", - spr->payment_request_url), - GNUNET_JSON_pack_string ("provider", - spr->provider_url)); - GNUNET_assert (0 == - json_array_append_new (ra, - pr)); - json_array_foreach (providers, off, provider) - { - const char *purl = json_string_value (json_object_get (provider, - "provider_url") - ); + struct ANASTASIS_ReduxPolicyProvider *pp = &b->policy_providers[off]; - if (NULL == purl) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "policy_providers array contents are invalid"); - json_decref (ra); - return; - } - if (0 == strcmp (purl, - spr->provider_url)) - { - json_t *psj; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Remembering payment secret for provider `%s'\n", - spr->provider_url); - psj = GNUNET_JSON_from_data_auto (&spr->payment_secret); - GNUNET_assert (0 == - json_object_set_new (provider, - "payment_secret", - psj)); - } + if (0 == strcmp (pp->provider_url.url, + spr->provider_url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Remembering payment secret for provider `%s'\n", + spr->provider_url); + pp->payment_secret = spr->payment_secret; + pp->have_payment_secret = true; } } - GNUNET_assert (0 == - json_object_set_new (uc->state, - "policy_payment_requests", - ra)); } - uc->cb (uc->cb_cls, - TALER_EC_NONE, - uc->state); - break; + uc_return (uc, + TALER_EC_NONE); + return; case ANASTASIS_SHARE_STATUS_PROVIDER_FAILED: { json_t *details; + ANASTASIS_ActionCallback cb; + void *cb_cls; - if (! provider_required (uc->state, + if (! provider_required (uc->rs, 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) + struct ANASTASIS_ReduxProvider *p; + + p = ANASTASIS_REDUX_provider_find_ ( + &uc->rs->common, + sr->details.provider_failure.provider_url); + if (NULL == p) + GNUNET_break (0); + else + p->status = ANASTASIS_RPS_DISABLED; + for (unsigned int idx = 0; idx < b->policy_providers_len; idx++) { - const char *url - = json_string_value (json_object_get (provider, - "provider_url")); + struct ANASTASIS_ReduxPolicyProvider *pp + = &b->policy_providers[idx]; - if ( (NULL != url) && - (0 == strcmp (sr->details.provider_failure.provider_url, - url)) ) + if (0 == strcmp (sr->details.provider_failure.provider_url, + pp->provider_url.url)) { - GNUNET_break (0 == - json_array_remove (providers, - idx)); + ANASTASIS_REDUX_provider_url_clear_ (&pp->provider_url); + memmove (pp, + pp + 1, + sizeof (*pp) * (b->policy_providers_len - idx - 1)); + b->policy_providers_len--; break; } } @@ -3182,21 +2816,22 @@ secret_share_result_cb (void *cls, sr->details.provider_failure.ec)), GNUNET_JSON_pack_string ("provider_url", sr->details.provider_failure.provider_url)); - uc->cb (uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED, - details); + cb = uc->cb; + cb_cls = uc->cb_cls; + upload_cancel_cb (uc); + cb (cb_cls, + TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED, + details); json_decref (details); } - break; + return; default: GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unexpected share result"); - break; + uc_fail (uc, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected share result"); + return; } - upload_cancel_cb (uc); } @@ -3208,106 +2843,35 @@ secret_share_result_cb (void *cls, static void share_secret (struct UploadContext *uc) { - 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 ANASTASIS_ReduxBackup *b = &uc->rs->details.backup; + unsigned int policies_len = b->policies_len; + unsigned int pds_len = b->policy_providers_len; struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO; - struct GNUNET_JSON_Specification spec[] = { - 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), - NULL), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (uc->state, - spec, - NULL, NULL)) + if ( (NULL == uc->rs->common.identity_attributes) || + (NULL == b->core_secret) || + (! b->have_policies) ) { - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "State parsing failed when preparing to share secret"); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "State parsing failed when preparing to share secret"); return; } - - { - json_t *args; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_relative_time ("timeout", - &timeout), - NULL), - GNUNET_JSON_spec_end () - }; - - args = json_object_get (uc->state, - "pay_arguments"); - if ( (NULL != args) && - (GNUNET_OK != - GNUNET_JSON_parse (args, - pspec, - NULL, NULL)) ) - { - json_dumpf (args, - stderr, - JSON_INDENT (2)); - GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - NULL); - upload_cancel_cb (uc); - return; - } - } - - policies_len = json_array_size (jpolicies); + if (b->have_pay_arguments && + b->pay_arguments.have_timeout) + timeout = b->pay_arguments.timeout; 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"); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "State parsing failed checks when preparing to share secret"); return; } - - 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"); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "no workable providers in state"); return; } @@ -3320,28 +2884,21 @@ share_secret (struct UploadContext *uc) memset (pds, 0, sizeof (pds)); - for (size_t i = 0; i<policies_len; i++) + for (unsigned int i = 0; i<policies_len; i++) { - const json_t *policy = json_array_get (jpolicies, - i); - const json_t *jmethods = json_object_get (policy, - "methods"); - unsigned int methods_len; - - if ( (! json_is_array (jmethods)) || - (0 == json_array_size (jmethods)) || - (json_array_size (jmethods) > UINT_MAX) ) + const struct ANASTASIS_ReduxPolicy *policy = &b->policies[i]; + unsigned int methods_len = policy->methods_len; + + if (0 == methods_len) { GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'methods' must be an array of sane length"); - upload_cancel_cb (uc); + for (unsigned int k = 0; k<i; k++) + ANASTASIS_policy_destroy (vpolicies[k]); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'methods' must be an array of sane length"); return; } - methods_len - = (unsigned int) json_array_size (jmethods); { struct ANASTASIS_Policy *p; struct ANASTASIS_Truth *truths[methods_len]; @@ -3349,76 +2906,27 @@ share_secret (struct UploadContext *uc) for (unsigned int j = 0; j<methods_len; j++) { - const json_t *jmethod = json_array_get (jmethods, - j); - 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_object_const ("truth", - &jtruth), - NULL), - GNUNET_JSON_spec_string ("provider", - &provider_url), - GNUNET_JSON_spec_uint32 ("authentication_method", - &truth_index), - GNUNET_JSON_spec_end () - }; - - GNUNET_break (NULL != jmethod); - if (GNUNET_OK != - GNUNET_JSON_parse (jmethod, - ispec, - NULL, NULL)) - { - GNUNET_break (0); - for (unsigned int k = 0; k<j; k++) - ANASTASIS_truth_free (truths[k]); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'truth' failed to decode"); - upload_cancel_cb (uc); - return; - } - if (NULL != jtruth) + const struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[j]; + + if (NULL != pm->truth) { - /* Get truth by deserializing from state */ - truths[j] = ANASTASIS_truth_from_json (jtruth); - if (NULL == truths[j]) - { - GNUNET_break (0); - for (unsigned int k = 0; k<j; k++) - ANASTASIS_truth_free (truths[k]); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'truth' failed to decode"); - upload_cancel_cb (uc); - return; - } + /* Truth we already have in the state */ + truths[j] = truth_dup (pm->truth); } else { bool found = false; - /* Maybe we never serialized the truth; find it in our DLL */ + /* Maybe we never stored the truth; find it in our DLL */ for (struct TruthUpload *tue = uc->tues_head; NULL != tue; tue = tue->next) { GNUNET_break (NULL != tue->t); - if ( (tue->am_idx == truth_index) && - (0 == strcmp (provider_url, + if ( (tue->am_idx == pm->authentication_method.idx) && + (0 == strcmp (pm->provider.url, tue->provider_url)) ) { - /* Duplicate truth object */ - json_t *jt = ANASTASIS_truth_to_json (tue->t); - - GNUNET_assert (NULL != jt); - truths[j] = ANASTASIS_truth_from_json (jt); - GNUNET_assert (NULL != truths[j]); - json_decref (jt); + truths[j] = truth_dup (tue->t); found = true; break; } @@ -3428,11 +2936,11 @@ share_secret (struct UploadContext *uc) GNUNET_break (0); for (unsigned int k = 0; k<j; k++) ANASTASIS_truth_free (truths[k]); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'truth' failed to decode"); - upload_cancel_cb (uc); + for (unsigned int k = 0; k<i; k++) + ANASTASIS_policy_destroy (vpolicies[k]); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'truth' failed to decode"); return; } } @@ -3450,35 +2958,23 @@ share_secret (struct UploadContext *uc) /* initialize 'pds' array */ for (unsigned int i = 0; i<pds_len; i++) { - json_t *pdj = json_array_get (providers, - i); - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("payment_secret", - &pds[i].payment_secret), - NULL), - GNUNET_JSON_spec_string ("provider_url", - &pds[i].provider_url), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxPolicyProvider *pp + = &b->policy_providers[i]; - if ( (GNUNET_OK != - GNUNET_JSON_parse (pdj, - ispec, - NULL, NULL)) || - (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (uc->state, - pds[i].provider_url, - &pds[i].provider_salt)) ) + pds[i].provider_url = pp->provider_url.url; + if (pp->have_payment_secret) + pds[i].payment_secret = pp->payment_secret; + if (GNUNET_OK != + ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common, + pds[i].provider_url, + &pds[i].provider_salt)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'providers' entry malformed"); for (unsigned int p = 0; p<policies_len; p++) ANASTASIS_policy_destroy (vpolicies[p]); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'providers' entry malformed"); return; } } @@ -3487,12 +2983,12 @@ share_secret (struct UploadContext *uc) char *secret; size_t secret_size; - secret = json_dumps (core_secret, + secret = json_dumps (b->core_secret, JSON_COMPACT | JSON_SORT_KEYS); GNUNET_assert (NULL != secret); secret_size = strlen (secret); uc->ss = ANASTASIS_secret_share (ANASTASIS_REDUX_ctx_, - user_id, + uc->rs->common.identity_attributes, pds, pds_len, policies, @@ -3501,7 +2997,7 @@ share_secret (struct UploadContext *uc) timeout, &secret_share_result_cb, uc, - secret_name, + b->secret_name, secret, secret_size); GNUNET_free (secret); @@ -3512,11 +3008,9 @@ share_secret (struct UploadContext *uc) if (NULL == uc->ss) { GNUNET_break (0); - ANASTASIS_redux_fail_ (uc->cb, - uc->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Failed to begin secret sharing"); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Failed to begin secret sharing"); return; } } @@ -3531,33 +3025,28 @@ share_secret (struct UploadContext *uc) static void request_truth_payment (struct UploadContext *uc) { - json_t *payments; + struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup; - payments = json_array (); - GNUNET_assert (NULL != payments); serialize_truth (uc); + ANASTASIS_REDUX_payments_clear_ (b); + b->have_payments = true; for (struct TruthUpload *tue = uc->tues_head; NULL != tue; tue = tue->next) { + char *pr; + if (NULL == tue->payment_request) continue; - GNUNET_assert ( - 0 == - json_array_append_new (payments, - json_string ( - tue->payment_request))); + pr = GNUNET_strdup (tue->payment_request); + GNUNET_array_append (b->payments, + b->payments_len, + pr); } - GNUNET_assert (0 == - json_object_set_new (uc->state, - "payments", - payments)); - set_state (uc->state, + set_state (uc->rs, ANASTASIS_BACKUP_STATE_TRUTHS_PAYING); - uc->cb (uc->cb_cls, - TALER_EC_NONE, - uc->state); - upload_cancel_cb (uc); + uc_return (uc, + TALER_EC_NONE); } @@ -3579,13 +3068,17 @@ check_upload_finished (struct UploadContext *uc) { if (TALER_EC_NONE != tue->ec) { + ANASTASIS_ActionCallback cb = uc->cb; + void *cb_cls = uc->cb_cls; + enum TALER_ErrorCode ec = tue->ec; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Truth upload failed with error %d\n", - (int) tue->ec); - uc->cb (uc->cb_cls, - tue->ec, - NULL); + (int) ec); upload_cancel_cb (uc); + cb (cb_cls, + ec, + NULL); return; } if (NULL != tue->tu) @@ -3657,13 +3150,13 @@ add_truth_object (struct UploadContext *uc, const struct PolicyMethodReference *pmr, const char *provider_url, uint32_t am_idx, - json_t *truth, + const struct ANASTASIS_ReduxPolicyMethod *pm, unsigned int *async_truth, - json_t *auth_method) + const struct ANASTASIS_ReduxAuthMethod *auth_method) { /* check if we are already uploading this truth */ struct TruthUpload *tue; - bool must_upload = true; + bool must_upload; for (tue = uc->tues_head; NULL != tue; @@ -3696,36 +3189,10 @@ add_truth_object (struct UploadContext *uc, tue->policies_length = 1; } - { - uint32_t status = UINT32_MAX; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("upload_status", - &status), - NULL), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (truth, - spec, - NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - must_upload = (ANASTASIS_US_SUCCESS != status); - } + must_upload = (ANASTASIS_US_SUCCESS != pm->upload_status); if (NULL == tue->t) - { - tue->t = ANASTASIS_truth_from_json (truth); - if (NULL == tue->t) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } + tue->t = truth_dup (pm->truth); if ( (NULL != tue->tu) && (! must_upload) ) @@ -3741,58 +3208,33 @@ add_truth_object (struct UploadContext *uc, { struct ANASTASIS_CRYPTO_ProviderSaltP salt; struct ANASTASIS_CRYPTO_UserIdentifierP id; - void *truth_data; - size_t truth_data_size; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_varsize ("challenge", - &truth_data, - &truth_data_size), - GNUNET_JSON_spec_end () - }; if (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (uc->state, - provider_url, - &salt)) + ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common, + provider_url, + &salt)) { GNUNET_break (0); return GNUNET_SYSERR; } - if (GNUNET_OK != - GNUNET_JSON_parse (auth_method, - spec, - NULL, NULL)) + if (NULL == uc->rs->common.identity_attributes) { - json_dumpf (auth_method, - stderr, - JSON_INDENT (2)); GNUNET_break (0); return GNUNET_SYSERR; } - { - json_t *user_id; - - user_id = json_object_get (uc->state, - "identity_attributes"); - if (! json_is_object (user_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - ANASTASIS_CRYPTO_user_identifier_derive (user_id, - &salt, - &id); - } + ANASTASIS_CRYPTO_user_identifier_derive ( + uc->rs->common.identity_attributes, + &salt, + &id); tue->tu = ANASTASIS_truth_upload3 (ANASTASIS_REDUX_ctx_, &id, tue->t, - truth_data, - truth_data_size, + auth_method->challenge, + auth_method->challenge_size, uc->years, uc->timeout, &truth_upload_cb, tue); - GNUNET_JSON_parse_free (spec); tue->t = NULL; (*async_truth)++; } @@ -3820,7 +3262,8 @@ add_truth_object (struct UploadContext *uc, * the truth to, used to check for existing entries * @param am_idx index of the authentication method, used to check for existing entries * @param auth_method object with the challenge details, to generate the truth - * @return #GNUNET_SYSERR on error requiring abort, + * @return #GNUNET_SYSERR on error requiring abort, in which case @a uc + * has already been disposed of, * #GNUNET_NO if no new truth upload was generated (@a pmr was appended) * #GNUNET_OK if a new truth upload was initiated */ @@ -3829,17 +3272,15 @@ check_truth_upload (struct UploadContext *uc, const struct PolicyMethodReference *pmr, const char *provider_url, uint32_t am_idx, - json_t *auth_method) + const struct ANASTASIS_ReduxAuthMethod *auth_method) { - json_t *user_id; - json_t *jtruth; + const json_t *user_id = uc->rs->common.identity_attributes; struct TruthUpload *tue; - user_id = json_object_get (uc->state, - "identity_attributes"); - if (! json_is_object (user_id)) + if (NULL == user_id) { GNUNET_break (0); + upload_cancel_cb (uc); return GNUNET_SYSERR; } @@ -3862,56 +3303,9 @@ check_truth_upload (struct UploadContext *uc, /* need new upload */ tue = GNUNET_new (struct TruthUpload); { - json_t *policies = json_object_get (uc->state, - "policies"); - json_t *policy = json_array_get (policies, - pmr->policy_index); - json_t *methods = json_object_get (policy, - "methods"); - json_t *method = json_array_get (methods, - pmr->method_index); - - jtruth = json_object_get (method, - "truth"); - } - - { - const char *type; - const char *mime_type = NULL; - const char *instructions = NULL; - void *truth_data; - size_t truth_data_size; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("type", - &type), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("mime_type", - &mime_type), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("instructions", - &instructions), - NULL), - GNUNET_JSON_spec_varsize ("challenge", - &truth_data, - &truth_data_size), - GNUNET_JSON_spec_end () - }; struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; struct ANASTASIS_CRYPTO_UserIdentifierP id; - if (GNUNET_OK != - GNUNET_JSON_parse (auth_method, - spec, - NULL, NULL)) - { - json_dumpf (auth_method, - stderr, - JSON_INDENT (2)); - GNUNET_break (0); - GNUNET_free (tue); - return GNUNET_SYSERR; - } GNUNET_CONTAINER_DLL_insert (uc->tues_head, uc->tues_tail, tue); @@ -3922,87 +3316,36 @@ check_truth_upload (struct UploadContext *uc, tue->am_idx = am_idx; tue->policies_length = 1; if (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (uc->state, - provider_url, - &provider_salt)) + ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common, + provider_url, + &provider_salt)) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); upload_cancel_cb (uc); return GNUNET_SYSERR; } ANASTASIS_CRYPTO_user_identifier_derive (user_id, &provider_salt, &id); - { - struct ANASTASIS_CRYPTO_TruthUUIDP uuid; - struct ANASTASIS_CRYPTO_QuestionSaltP question_salt; - struct ANASTASIS_CRYPTO_TruthKeyP truth_key; - struct ANASTASIS_CRYPTO_KeyShareP key_share; - struct ANASTASIS_CRYPTO_NonceP nonce; - struct GNUNET_JSON_Specification jspec[] = { - GNUNET_JSON_spec_fixed_auto ("salt", - &question_salt), - GNUNET_JSON_spec_fixed_auto ("truth_key", - &truth_key), - GNUNET_JSON_spec_fixed_auto ("nonce", - &nonce), - GNUNET_JSON_spec_fixed_auto ("uuid", - &uuid), - GNUNET_JSON_spec_fixed_auto ("key_share", - &key_share), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jtruth, - jspec, - NULL, NULL)) - { - tue->tu = ANASTASIS_truth_upload (ANASTASIS_REDUX_ctx_, - &id, - provider_url, - type, - instructions, - mime_type, - &provider_salt, - truth_data, - truth_data_size, - uc->years, - uc->timeout, - &truth_upload_cb, - tue); - } - else - { - tue->tu = ANASTASIS_truth_upload2 (ANASTASIS_REDUX_ctx_, - &id, - provider_url, - type, - instructions, - mime_type, - &provider_salt, - truth_data, - truth_data_size, - uc->years, - uc->timeout, - &nonce, - &uuid, - &question_salt, - &truth_key, - &key_share, - &truth_upload_cb, - tue); - } - } + tue->tu = ANASTASIS_truth_upload (ANASTASIS_REDUX_ctx_, + &id, + provider_url, + auth_method->type, + auth_method->instructions, + auth_method->mime_type, + &provider_salt, + auth_method->challenge, + auth_method->challenge_size, + uc->years, + uc->timeout, + &truth_upload_cb, + tue); if (NULL == tue->tu) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); upload_cancel_cb (uc); return GNUNET_SYSERR; } - GNUNET_JSON_parse_free (spec); return GNUNET_OK; } } @@ -4018,48 +3361,35 @@ check_truth_upload (struct UploadContext *uc, * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -upload (json_t *state, +upload (struct ANASTASIS_ReduxState *rs, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; struct UploadContext *uc; - json_t *auth_methods; - json_t *policies; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("expiration", - &expiration), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (state, - spec, - NULL, NULL)) + if (! b->have_expiration) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'expiration' missing"); return NULL; } - auth_methods = json_object_get (state, - "authentication_methods"); - if ( (! json_is_array (auth_methods)) || - (0 == json_array_size (auth_methods)) ) + if (0 == b->authentication_methods_len) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_methods' must be non-empty array"); return NULL; } - policies = json_object_get (state, - "policies"); - if ( (! json_is_array (policies)) || - (0 == json_array_size (policies)) ) + if (0 == b->policies_len) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'policies' must be non-empty array"); @@ -4071,154 +3401,83 @@ upload (json_t *state, uc->ra.cleanup_cls = uc; uc->cb = cb; uc->cb_cls = cb_cls; - uc->state = json_incref (state); - uc->years = expiration_to_years (expiration); - - { - json_t *args; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_relative_time ("timeout", - &uc->timeout), - NULL), - GNUNET_JSON_spec_end () - }; - - args = json_object_get (uc->state, - "pay_arguments"); - if ( (NULL != args) && - (GNUNET_OK != - GNUNET_JSON_parse (args, - pspec, - NULL, NULL)) ) - { - json_dumpf (args, - stderr, - JSON_INDENT (2)); - GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'timeout' must be valid delay"); - - return NULL; - } - } + uc->rs = rs; + uc->years = expiration_to_years (b->expiration); + if (b->have_pay_arguments && + b->pay_arguments.have_timeout) + uc->timeout = b->pay_arguments.timeout; { - json_t *policy; - size_t pindex; unsigned int async_truth = 0; - json_array_foreach (policies, pindex, policy) + for (unsigned int pindex = 0; pindex < b->policies_len; pindex++) { - json_t *methods = json_object_get (policy, - "methods"); - json_t *auth_method; - size_t mindex; + const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pindex]; - if ( (! json_is_array (methods)) || - (0 == json_array_size (policies)) ) + if (0 == policy->methods_len) { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'policies' must be non-empty array"); - upload_cancel_cb (uc); + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'policies' must be non-empty array"); return NULL; } - json_array_foreach (methods, mindex, auth_method) + for (unsigned int mindex = 0; mindex < policy->methods_len; mindex++) { - uint32_t am_idx; - const char *provider_url; - json_t *truth = NULL; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("provider", - &provider_url), - GNUNET_JSON_spec_uint32 ("authentication_method", - &am_idx), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("truth", - &truth), - NULL), - GNUNET_JSON_spec_end () + const struct ANASTASIS_ReduxPolicyMethod *pm + = &policy->methods[mindex]; + unsigned int am_idx = pm->authentication_method.idx; + struct PolicyMethodReference pmr = { + .policy_index = pindex, + .method_index = mindex }; + const struct ANASTASIS_ReduxAuthMethod *amj; + int ret; - if (GNUNET_OK != - GNUNET_JSON_parse (auth_method, - ispec, - NULL, NULL)) + if (am_idx >= b->authentication_methods_len) { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'method' data malformed"); - upload_cancel_cb (uc); + uc_fail ( + uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'authentication_method' refers to invalid authorization index malformed"); return NULL; } + amj = &b->authentication_methods[am_idx]; + if (NULL == pm->truth) { - struct PolicyMethodReference pmr = { - .policy_index = pindex, - .method_index = mindex - }; - json_t *amj; - - amj = json_array_get (auth_methods, - am_idx); - if (NULL == amj) + ret = check_truth_upload (uc, + &pmr, + pm->provider.url, + am_idx, + amj); + if (GNUNET_SYSERR == ret) { + /* check_truth_upload() already disposed of @a uc */ ANASTASIS_redux_fail_ (cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'authentication_method' refers to invalid authorization index malformed"); - upload_cancel_cb (uc); - GNUNET_JSON_parse_free (spec); + NULL); return NULL; } - if (NULL == truth) - { - int ret; - - ret = check_truth_upload (uc, - &pmr, - provider_url, - am_idx, - amj); - if (GNUNET_SYSERR == ret) - { - GNUNET_JSON_parse_free (spec); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - NULL); - return NULL; - } - if (GNUNET_OK == ret) - async_truth++; - } - else + if (GNUNET_OK == ret) + async_truth++; + } + else + { + ret = add_truth_object (uc, + &pmr, + pm->provider.url, + am_idx, + pm, + &async_truth, + amj); + if (GNUNET_SYSERR == ret) { - int ret; - - ret = add_truth_object (uc, - &pmr, - provider_url, - am_idx, - truth, - &async_truth, - amj); - if (GNUNET_SYSERR == ret) - { - GNUNET_JSON_parse_free (spec); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - NULL); - return NULL; - } + uc_fail (uc, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + NULL); + return NULL; } } - GNUNET_JSON_parse_free (spec); } /* end for all methods of policy */ } /* end for all policies */ if (async_truth > 0) @@ -4246,16 +3505,17 @@ upload (json_t *state, * leaves 512 kb for the @a secret_size, which should be plenty (given * that this is supposed to be for a master key, and not the actual data). * - * @param state our state, could be used in the future to calculate the + * @param rs our state, could be used in the future to calculate the * size of the recovery document without the core secret * @param secret_size size of the core secret * @param min_limit_in_mb minimum upload size of all providers */ static bool -core_secret_fits (const json_t *state, +core_secret_fits (const struct ANASTASIS_ReduxState *rs, size_t secret_size, uint32_t min_limit_in_mb) { + (void) rs; return (min_limit_in_mb * 1024LL * 1024LL > 512LLU * 1024LLU + secret_size); } @@ -4264,21 +3524,17 @@ core_secret_fits (const json_t *state, /** * Check if the upload size limit is satisfied. * - * @param state our state + * @param rs our state * @param jsecret the uploaded secret * @return #GNUNET_OK if @a secret_size works for all providers, * #GNUNET_NO if the @a secret_size is too big, * #GNUNET_SYSERR if a provider has a limit of 0 */ static enum GNUNET_GenericReturnValue -check_upload_size_limit (json_t *state, +check_upload_size_limit (const struct ANASTASIS_ReduxState *rs, const json_t *jsecret) { uint32_t min_limit = UINT32_MAX; - json_t *aps = json_object_get (state, - "authentication_providers"); - const char *url; - json_t *ap; size_t secret_size; { @@ -4295,44 +3551,20 @@ check_upload_size_limit (json_t *state, this is under the (simplified) assumption that we store the recovery document at all providers; this may be changed later, see #6760. */ - json_object_foreach (aps, url, ap) + for (unsigned int i = 0; i < rs->common.providers_len; i++) { - uint32_t limit = 0; - const char *status; - uint32_t http_status = 0; - 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_uint32 ("storage_limit_in_megabytes", - &limit), - NULL), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i]; - if (GNUNET_OK != - GNUNET_JSON_parse (ap, - spec, - NULL, NULL)) - { - /* skip malformed provider, likely /config failed */ - GNUNET_break_op (0); - continue; - } - if ( (MHD_HTTP_OK != http_status) || - (0 != strcmp (status, - "ok")) ) + if ( (ANASTASIS_RPS_OK != p->status) || + (! p->have_config) || + (MHD_HTTP_OK != p->config.http_status) ) continue; - if (0 == limit) + if (0 == p->config.storage_limit_in_megabytes) return GNUNET_SYSERR; min_limit = GNUNET_MIN (min_limit, - limit); + p->config.storage_limit_in_megabytes); } - if (! core_secret_fits (state, + if (! core_secret_fits (rs, secret_size, min_limit)) return GNUNET_NO; @@ -4351,17 +3583,18 @@ check_upload_size_limit (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -enter_secret (json_t *state, +enter_secret (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *jsecret; + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + const json_t *jsecret; struct GNUNET_TIME_Timestamp expiration = GNUNET_TIME_UNIT_ZERO_TS; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("secret", - &jsecret), + GNUNET_JSON_spec_object_const ("secret", + &jsecret), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("expiration", &expiration), @@ -4371,7 +3604,8 @@ enter_secret (json_t *state, if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -4382,7 +3616,8 @@ enter_secret (json_t *state, spec, NULL, NULL)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'secret' argument required"); @@ -4390,50 +3625,46 @@ enter_secret (json_t *state, } /* check upload size limit */ + switch (check_upload_size_limit (rs, + jsecret)) { - enum GNUNET_GenericReturnValue ret; - - ret = check_upload_size_limit (state, - jsecret); - switch (ret) - { - case GNUNET_SYSERR: - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "provider has an upload limit of 0"); - return NULL; - case GNUNET_NO: - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, - NULL); - return NULL; - default: - break; - } + case GNUNET_SYSERR: + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "provider has an upload limit of 0"); + return NULL; + case GNUNET_NO: + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, + NULL); + return NULL; + default: + break; } if (! GNUNET_TIME_absolute_is_zero (expiration.abs_time)) { if (GNUNET_OK != - update_expiration_cost (state, + update_expiration_cost (rs, expiration)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "could not calculate expiration cost"); return NULL; } } - GNUNET_assert (0 == - json_object_set (state, - "core_secret", - jsecret)); - cb (cb_cls, - TALER_EC_NONE, - state); - GNUNET_JSON_parse_free (spec); + json_decref (b->core_secret); + b->core_secret = json_incref ((json_t *) jsecret); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -4449,24 +3680,28 @@ enter_secret (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -clear_secret (json_t *state, +clear_secret (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - if (0 != - json_object_del (state, - "core_secret")) + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + + if (NULL == b->core_secret) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'core_secret' not set"); return NULL; } - cb (cb_cls, - TALER_EC_NONE, - state); + json_decref (b->core_secret); + b->core_secret = NULL; + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -4482,11 +3717,12 @@ clear_secret (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -enter_secret_name (json_t *state, +enter_secret_name (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; const char *secret_name = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("name", @@ -4496,7 +3732,8 @@ enter_secret_name (json_t *state, if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -4507,21 +3744,19 @@ enter_secret_name (json_t *state, spec, NULL, NULL)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'name' argument required"); return NULL; } - - GNUNET_assert (0 == - json_object_set_new (state, - "secret_name", - json_string (secret_name))); - cb (cb_cls, - TALER_EC_NONE, - state); - GNUNET_JSON_parse_free (spec); + GNUNET_free (b->secret_name); + b->secret_name = GNUNET_strdup (secret_name); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -4539,7 +3774,7 @@ enter_secret_name (json_t *state, * @return NULL (synchronous operation) */ static struct ANASTASIS_ReduxAction * -update_expiration (json_t *state, +update_expiration (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) @@ -4554,7 +3789,8 @@ update_expiration (json_t *state, if (NULL == arguments) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -4566,26 +3802,29 @@ update_expiration (json_t *state, NULL, NULL)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'expiration' argument required"); return NULL; } if (GNUNET_OK != - update_expiration_cost (state, + update_expiration_cost (rs, expiration)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, "could not calculate expiration cost"); return NULL; } - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -4601,24 +3840,17 @@ update_expiration (json_t *state, * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -finish_secret (json_t *state, +finish_secret (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *core_secret; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("core_secret", - &core_secret), - GNUNET_JSON_spec_end () - }; + const struct ANASTASIS_ReduxBackup *b = &rs->details.backup; - if (GNUNET_OK != - GNUNET_JSON_parse (state, - spec, - NULL, NULL)) + if (NULL == b->core_secret) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "State parsing failed: 'core_secret' is missing"); @@ -4626,64 +3858,107 @@ finish_secret (json_t *state, } /* check upload size limit */ + switch (check_upload_size_limit (rs, + b->core_secret)) { - enum GNUNET_GenericReturnValue ret; - - ret = check_upload_size_limit (state, - core_secret); - switch (ret) - { - case GNUNET_SYSERR: - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "provider has an upload limit of 0"); - GNUNET_JSON_parse_free (spec); - return NULL; - case GNUNET_NO: - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, - NULL); - GNUNET_JSON_parse_free (spec); - return NULL; - default: - break; - } + case GNUNET_SYSERR: + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "provider has an upload limit of 0"); + return NULL; + case GNUNET_NO: + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, + NULL); + return NULL; + default: + break; } - - GNUNET_JSON_parse_free (spec); - return upload (state, + return upload (rs, cb, cb_cls); } /** + * Remember the arguments of a "pay" action in @a rs, so that they are + * still around when the upload is retried after the payment. + * + * @param[in,out] rs state to update + * @param arguments arguments of the action, may be NULL + * @param cb callback to report a failure to + * @param cb_cls closure for @a cb + * @return false if @a arguments were malformed, in which case @a cb has + * been invoked and @a rs freed + */ +static bool +set_pay_arguments (struct ANASTASIS_ReduxState *rs, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls) +{ + struct ANASTASIS_ReduxBackup *b = &rs->details.backup; + bool no_timeout; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_relative_time ("timeout", + &b->pay_arguments.timeout), + &no_timeout), + GNUNET_JSON_spec_end () + }; + + if (NULL == arguments) + return true; + if (GNUNET_OK != + GNUNET_JSON_parse (arguments, + spec, + NULL, NULL)) + { + json_dumpf ((json_t *) arguments, + stderr, + JSON_INDENT (2)); + GNUNET_break (0); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'timeout' must be valid delay"); + return false; + } + b->have_pay_arguments = true; + b->pay_arguments.have_timeout = ! no_timeout; + return true; +} + + +/** * DispatchHandler/Callback function which is called for a * "pay" action. * Returns an #ANASTASIS_ReduxAction as operation is async. * - * @param state state to operate on + * @param[in] rs state to operate on * @param arguments arguments to use for operation on state * @param cb callback to call during/after operation * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -pay_truths_backup (json_t *state, +pay_truths_backup (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { /* Clear 'payments' if it exists */ - (void) json_object_del (state, - "payments"); - if (NULL != arguments) - GNUNET_assert (0 == - json_object_set (state, - "pay_arguments", - (json_t *) arguments)); - return upload (state, + ANASTASIS_REDUX_payments_clear_ (&rs->details.backup); + if (! set_pay_arguments (rs, + arguments, + cb, + cb_cls)) + return NULL; + return upload (rs, cb, cb_cls); } @@ -4700,20 +3975,19 @@ pay_truths_backup (json_t *state, * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -pay_policies_backup (json_t *state, +pay_policies_backup (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { /* Clear 'policy_payment_requests' if it exists */ - (void) json_object_del (state, - "policy_payment_requests"); - if (NULL != arguments) - GNUNET_assert (0 == - json_object_set (state, - "pay_arguments", - (json_t *) arguments)); - return upload (state, + ANASTASIS_REDUX_policy_payment_requests_clear_ (&rs->details.backup); + if (! set_pay_arguments (rs, + arguments, + cb, + cb_cls)) + return NULL; + return upload (rs, cb, cb_cls); } @@ -4730,16 +4004,17 @@ pay_policies_backup (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -back_finished (json_t *state, +back_finished (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - set_state (state, + set_state (rs, ANASTASIS_BACKUP_STATE_SECRET_EDITING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -4747,20 +4022,20 @@ back_finished (json_t *state, /** * Signature of callback function that implements a state transition. * - * @param state current state + * @param[in] rs current state * @param arguments arguments for the state transition * @param cb function to call when done * @param cb_cls closure for @a cb */ typedef struct ANASTASIS_ReduxAction * -(*DispatchHandler)(json_t *state, +(*DispatchHandler)(struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); struct ANASTASIS_ReduxAction * -ANASTASIS_backup_action_ (json_t *state, +ANASTASIS_backup_action_ (struct ANASTASIS_ReduxState *rs, const char *action, const json_t *arguments, ANASTASIS_ActionCallback cb, @@ -4879,33 +4154,23 @@ ANASTASIS_backup_action_ (json_t *state, }, { ANASTASIS_BACKUP_STATE_INVALID, NULL, NULL } }; - const char *s = json_string_value (json_object_get (state, - "backup_state")); - enum ANASTASIS_BackupState bs; + enum ANASTASIS_BackupState bs = rs->details.backup.state; - GNUNET_assert (NULL != s); /* holds as per invariant of caller */ - bs = ANASTASIS_backup_state_from_string_ (s); - if (ANASTASIS_BACKUP_STATE_INVALID == bs) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "unknown 'backup_state'"); - return NULL; - } + GNUNET_assert (ANASTASIS_RT_BACKUP == rs->type); for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) { if ( (bs == dispatchers[i].backup_state) && (0 == strcmp (action, dispatchers[i].backup_action)) ) { - return dispatchers[i].fun (state, + return dispatchers[i].fun (rs, arguments, cb, cb_cls); } } - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, action); @@ -4941,11 +4206,6 @@ struct BackupStartStateProviderEntry struct BackupStartState *bss; /** - * Resulting provider information, NULL if not (yet) available. - */ - json_t *istate; - - /** * Ongoing reducer action to obtain /config, NULL if completed. */ struct ANASTASIS_ReduxAction *ra; @@ -4970,9 +4230,10 @@ struct BackupStartState struct BackupStartStateProviderEntry *pe_tail; /** - * State we are updating. + * State we are updating; we own it. All our entries update it in + * place, so there is nothing to merge once they are done. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; /** * Function to call when we are done. @@ -5015,117 +4276,85 @@ abort_backup_begin_cb (void *cls) pe); if (NULL != pe->ra) pe->ra->cleanup (pe->ra->cleanup_cls); - json_decref (pe->istate); GNUNET_free (pe); } - json_decref (bss->state); + ANASTASIS_REDUX_state_free_ (bss->rs); GNUNET_free (bss); } /** - * We finished downloading /config from all providers, merge - * into the main state, trigger the continuation and free our - * state. + * We finished downloading /config from all providers, trigger the + * continuation and free our state. * - * @param[in] bss main state to merge into + * @param[in] bss main state to return */ static void providers_complete (struct BackupStartState *bss) { - struct BackupStartStateProviderEntry *pe; - json_t *tlist; - - tlist = json_object_get (bss->state, - "authentication_providers"); - if (NULL == tlist) - { - tlist = json_object (); - GNUNET_assert (NULL != tlist); - GNUNET_assert (0 == - json_object_set_new (bss->state, - "authentication_providers", - tlist)); - } - while (NULL != (pe = bss->pe_head)) - { - json_t *provider_list; - - GNUNET_CONTAINER_DLL_remove (bss->pe_head, - bss->pe_tail, - pe); - provider_list = json_object_get (pe->istate, - "authentication_providers"); - /* merge provider_list into tlist (overriding existing entries) */ - if (NULL != provider_list) - { - const char *url; - json_t *value; - - json_object_foreach (provider_list, url, value) { - GNUNET_assert (0 == - json_object_set (tlist, - url, - value)); - } - } - json_decref (pe->istate); - GNUNET_free (pe); - } - bss->cb (bss->cb_cls, - TALER_EC_NONE, - bss->state); - json_decref (bss->state); - GNUNET_free (bss); + struct ANASTASIS_ReduxState *rs = bss->rs; + ANASTASIS_ActionCallback cb = bss->cb; + void *cb_cls = bss->cb_cls; + + bss->rs = NULL; + abort_backup_begin_cb (bss); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); } /** * Function called when the complete information about a provider - * was added to @a new_state. + * was added to the state. * * @param cls a `struct BackupStartStateProviderEntry` * @param error error code - * @param new_state resulting new state + * @param rs the state, updated in place */ static void provider_added_cb (void *cls, enum TALER_ErrorCode error, - json_t *new_state) + struct ANASTASIS_ReduxState *rs) { struct BackupStartStateProviderEntry *pe = cls; + struct BackupStartState *bss = pe->bss; + GNUNET_assert (rs == bss->rs); + /* our waiter unlinks and frees itself right after this call */ pe->ra = NULL; - pe->istate = json_incref (new_state); + GNUNET_CONTAINER_DLL_remove (bss->pe_head, + bss->pe_tail, + pe); pe->ec = error; - pe->bss->pending--; - if (0 == pe->bss->pending) - providers_complete (pe->bss); + GNUNET_free (pe); + bss->pending--; + if (0 == bss->pending) + providers_complete (bss); } struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_backup_begin_ (json_t *state, +ANASTASIS_REDUX_backup_begin_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *provider_list; struct BackupStartState *bss; - provider_list = json_object_get (state, - "authentication_providers"); - if (NULL == provider_list) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' missing"); return NULL; } bss = GNUNET_new (struct BackupStartState); - bss->state = json_incref (state); + bss->rs = rs; bss->cb = cb; bss->cb_cls = cb_cls; bss->ra.cleanup_cls = bss; @@ -5133,45 +4362,35 @@ ANASTASIS_REDUX_backup_begin_ (json_t *state, bss->pending = 1; /* decremented after initialization loop */ { - json_t *prov; - const char *url; - json_object_foreach (provider_list, url, prov) { + /* The /config replies add providers to the state, so take a + snapshot of the URLs to query before starting any of them. */ + unsigned int len = rs->common.providers_len; + char *urls[GNUNET_NZL (len)]; + + for (unsigned int i = 0; i < len; i++) + urls[i] = (ANASTASIS_RPS_DISABLED == rs->common.providers[i].status) + ? NULL + : GNUNET_strdup (rs->common.providers[i].url.url); + for (unsigned int i = 0; i < len; i++) + { struct BackupStartStateProviderEntry *pe; - json_t *istate; - const char *status; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("status", - &status), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (prov, - spec, - NULL, NULL)) - { - /* skip malformed provider entry */ - GNUNET_break_op (0); - continue; - } - if (0 == strcmp (status, - "disabled")) + if (NULL == urls[i]) continue; pe = GNUNET_new (struct BackupStartStateProviderEntry); pe->bss = bss; - istate = json_object (); - GNUNET_assert (NULL != istate); GNUNET_CONTAINER_DLL_insert (bss->pe_head, bss->pe_tail, pe); - pe->ra = ANASTASIS_REDUX_add_provider_to_state_ (url, - istate, + bss->pending++; + pe->ra = ANASTASIS_REDUX_add_provider_to_state_ (urls[i], + rs, &provider_added_cb, pe); - json_decref (istate); - if (NULL != pe->ra) - bss->pending++; + GNUNET_assert (NULL != pe->ra); } + for (unsigned int i = 0; i < len; i++) + GNUNET_free (urls[i]); } bss->pending--; if (0 == bss->pending) diff --git a/src/reducer/anastasis_api_discovery.c b/src/reducer/anastasis_api_discovery.c @@ -24,6 +24,7 @@ #include "anastasis_error_codes.h" #include <taler/taler_json_lib.h> #include "anastasis_api_redux.h" +#include "anastasis_api_redux_state.h" #include <dlfcn.h> @@ -61,7 +62,7 @@ struct ProviderOperation * Attribute mask applied to the identity attributes * for this operation. */ - json_int_t attribute_mask; + struct ANASTASIS_AttributeMask attribute_mask; }; @@ -100,16 +101,65 @@ struct ANASTASIS_PolicyDiscovery /** * State we are operating on. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; /** * Number of optional fields in our identity attributes. */ - json_int_t opt_cnt; + unsigned int opt_cnt; }; /** + * Return a copy of @a master_id with the optional attributes selected + * by @a mask removed. The internal, typed counterpart of + * #ANASTASIS_mask_id_data(). + * + * @param common state describing which attributes exist + * @param master_id the user's identity attributes + * @param mask which of the optional attributes to drop + * @return NULL if @a master_id does not match @a common + */ +static json_t * +mask_id_data (const struct ANASTASIS_ReduxCommon *common, + const json_t *master_id, + struct ANASTASIS_AttributeMask mask) +{ + json_t *ret = json_deep_copy (master_id); + unsigned int bit = 0; + + GNUNET_assert (NULL != ret); + for (unsigned int i = 0; i < common->required_attributes_len; i++) + { + const struct ANASTASIS_ReduxAttributeSpec *a + = &common->required_attributes[i]; + bool present; + + present = (NULL != + json_object_get (master_id, + a->name)); + if ( (! present) && (! a->optional) ) + { + GNUNET_break (0); + json_decref (ret); + return NULL; + } + if (present && a->optional) + { + if (0 != ((1LLU << bit) & mask.mask)) + { + GNUNET_assert (0 == + json_object_del (ret, + a->name)); + } + bit++; + } + } + return ret; +} + + +/** * Callback which passes back meta data about one of the * recovery documents available at the provider. * @@ -175,7 +225,7 @@ meta_cb (void *cls, recdoc_id, po->provider_url, version, - po->attribute_mask, + (json_int_t) po->attribute_mask.mask, server_time, secret_name, pa); @@ -195,11 +245,10 @@ meta_cb (void *cls, static void start_po (struct ANASTASIS_PolicyDiscovery *pd, const json_t *id_data, - json_int_t mask, + struct ANASTASIS_AttributeMask mask, const char *provider_url, const json_t *cursor) { - const json_t *state = pd->state; struct ProviderOperation *po; uint32_t max_version = UINT32_MAX; struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; @@ -236,7 +285,7 @@ start_po (struct ANASTASIS_PolicyDiscovery *pd, JSON_INDENT (2)); return; } - if ( (cmask == mask) && + if ( (cmask == mask.mask) && (0 == strcmp (url, provider_url)) ) { @@ -247,9 +296,9 @@ start_po (struct ANASTASIS_PolicyDiscovery *pd, } if (GNUNET_OK != - ANASTASIS_reducer_lookup_salt (state, - provider_url, - &provider_salt)) + ANASTASIS_REDUX_lookup_salt_ (&pd->rs->common, + provider_url, + &provider_salt)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "No /config for `%s', skipping provider\n", @@ -287,90 +336,61 @@ ANASTASIS_policy_discovery_start (const json_t *state, void *cb_cls) { struct ANASTASIS_PolicyDiscovery *pd; - json_t *master_id = json_object_get (state, - "identity_attributes"); - json_t *providers = json_object_get (state, - "authentication_providers"); - json_t *required_attributes = json_object_get (state, - "required_attributes"); + struct ANASTASIS_ReduxState *rs; + const json_t *master_id; unsigned int opt_cnt; - if ( (NULL == master_id) || - (! json_is_object (master_id)) ) { - GNUNET_break (0); - json_dumpf (state, - stderr, - JSON_INDENT (2)); - return NULL; - } - if ( (NULL == providers) || - (! json_is_object (providers)) ) - { - GNUNET_break (0); - json_dumpf (state, - stderr, - JSON_INDENT (2)); - return NULL; + enum TALER_ErrorCode ec; + const char *detail; + + rs = ANASTASIS_REDUX_state_parse_ (state, + &ec, + &detail); + if (NULL == rs) + { + GNUNET_break (0); + json_dumpf ((json_t *) state, + stderr, + JSON_INDENT (2)); + return NULL; + } } - if ( (NULL == required_attributes) || - (! json_is_array (required_attributes)) ) + master_id = rs->common.identity_attributes; + if ( (NULL == master_id) || + (! rs->common.have_providers) || + (! rs->common.have_required_attributes) ) { GNUNET_break (0); - json_dumpf (required_attributes, + json_dumpf ((json_t *) state, stderr, JSON_INDENT (2)); + ANASTASIS_REDUX_state_free_ (rs); return NULL; } /* count optional attributes present in 'master_id' */ opt_cnt = 0; + for (unsigned int i = 0; i < rs->common.required_attributes_len; i++) { - size_t index; - json_t *required_attribute; + const struct ANASTASIS_ReduxAttributeSpec *a + = &rs->common.required_attributes[i]; + bool present; - json_array_foreach (required_attributes, - index, - required_attribute) + present = (NULL != + json_object_get (master_id, + a->name)); + if ((! present) && (! a->optional)) { - const char *name; - int optional = false; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("name", - &name), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_boolean ("optional", - &optional), - NULL), - GNUNET_JSON_spec_end () - }; - bool present; - - if (GNUNET_OK != - GNUNET_JSON_parse (required_attribute, - spec, - NULL, NULL)) - { - GNUNET_break (0); - json_dumpf (required_attribute, - stderr, - JSON_INDENT (2)); - return NULL; - } - present = (NULL != - json_object_get (master_id, - name)); - if ((! present) && (! optional)) - { - GNUNET_break (0); - json_dumpf (master_id, - stderr, - JSON_INDENT (2)); - return NULL; - } - if (present && optional) - opt_cnt++; + GNUNET_break (0); + json_dumpf ((json_t *) master_id, + stderr, + JSON_INDENT (2)); + ANASTASIS_REDUX_state_free_ (rs); + return NULL; } + if (present && a->optional) + opt_cnt++; } pd = GNUNET_new (struct ANASTASIS_PolicyDiscovery); @@ -379,26 +399,25 @@ ANASTASIS_policy_discovery_start (const json_t *state, pd->cb = cb; pd->cb_cls = cb_cls; pd->opt_cnt = opt_cnt; - pd->state = json_deep_copy (state); + pd->rs = rs; /* Compute 'id_data' for all possible masks, and then start downloads at all providers for 'id_data' */ - for (json_int_t mask = 0; mask < (1LL << opt_cnt); mask++) + for (uint64_t m = 0; m < (1LLU << opt_cnt); m++) { - json_t *id_data = ANASTASIS_mask_id_data (state, - master_id, - mask); - json_t *value; - const char *url; + struct ANASTASIS_AttributeMask mask = { + .mask = m + }; + json_t *id_data = mask_id_data (&rs->common, + master_id, + mask); - json_object_foreach (providers, url, value) - { + for (unsigned int i = 0; i < rs->common.providers_len; i++) start_po (pd, id_data, mask, - url, + rs->common.providers[i].url.url, cursor); - } json_decref (id_data); } return pd; @@ -410,24 +429,29 @@ ANASTASIS_policy_discovery_more (struct ANASTASIS_PolicyDiscovery *pd, const char *provider_url, json_t *provider_state) { - json_t *master_id = json_object_get (pd->state, - "identity_attributes"); - json_t *providers = json_object_get (pd->state, - "authentication_providers"); + const json_t *master_id = pd->rs->common.identity_attributes; + const char *detail; GNUNET_assert (NULL != master_id); - GNUNET_assert (NULL != providers); - GNUNET_assert (0 == - json_object_set (providers, - provider_url, - provider_state)); + if (GNUNET_OK != + ANASTASIS_REDUX_provider_set_ (&pd->rs->common, + provider_url, + provider_state, + &detail)) + { + GNUNET_break_op (0); + return; + } /* Compute 'id_data' for all possible masks, and then start downloads at provider for 'id_data' */ - for (json_int_t mask = 0; mask < (1LL << pd->opt_cnt); mask++) + for (uint64_t m = 0; m < (1LLU << pd->opt_cnt); m++) { - json_t *id_data = ANASTASIS_mask_id_data (pd->state, - master_id, - mask); + struct ANASTASIS_AttributeMask mask = { + .mask = m + }; + json_t *id_data = mask_id_data (&pd->rs->common, + master_id, + mask); start_po (pd, id_data, @@ -479,7 +503,7 @@ ANASTASIS_policy_discovery_stop (struct ANASTASIS_PolicyDiscovery *pd) &free_dd_json, NULL); GNUNET_CONTAINER_multihashmap_destroy (pd->dd_map); - json_decref (pd->state); + ANASTASIS_REDUX_state_free_ (pd->rs); GNUNET_free (pd); } @@ -489,61 +513,34 @@ ANASTASIS_mask_id_data (const json_t *state, const json_t *master_id, json_int_t mask) { - json_t *required_attributes = json_object_get (state, - "required_attributes"); - size_t index; - json_t *required_attribute; - json_t *ret = json_deep_copy (master_id); - unsigned int bit = 0; + struct ANASTASIS_ReduxState *rs; + struct ANASTASIS_AttributeMask m = { + .mask = (uint64_t) mask + }; + json_t *ret; - if ( (NULL == required_attributes) || - (! json_is_array (required_attributes)) ) { - GNUNET_break (0); - return NULL; - } + enum TALER_ErrorCode ec; + const char *detail; - json_array_foreach (required_attributes, index, required_attribute) - { - const char *name; - int optional = false; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("name", - &name), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_boolean ("optional", - &optional), - NULL), - GNUNET_JSON_spec_end () - }; - bool present; - - if (GNUNET_OK != - GNUNET_JSON_parse (required_attribute, - spec, - NULL, NULL)) - { - GNUNET_break (0); - return NULL; - } - present = (NULL != - json_object_get (master_id, - name)); - if ((! present) && (! optional)) + rs = ANASTASIS_REDUX_state_parse_ (state, + &ec, + &detail); + if (NULL == rs) { GNUNET_break (0); return NULL; } - if (present && optional) - { - if (0 != ((1LL << bit) & mask)) - { - GNUNET_assert (0 == - json_object_del (ret, - name)); - } - bit++; - } } + if (! rs->common.have_required_attributes) + { + GNUNET_break (0); + ANASTASIS_REDUX_state_free_ (rs); + return NULL; + } + ret = mask_id_data (&rs->common, + master_id, + m); + ANASTASIS_REDUX_state_free_ (rs); return ret; } diff --git a/src/reducer/anastasis_api_providers.c b/src/reducer/anastasis_api_providers.c @@ -24,6 +24,7 @@ #include "anastasis_redux.h" #include "anastasis_error_codes.h" #include "anastasis_api_redux.h" +#include "anastasis_api_redux_state.h" /** @@ -87,6 +88,12 @@ struct MasterSync */ void *cb_cls; + /** + * State all our entries share. We own it until the first entry + * reports back, at which point it is handed to @e cb. + */ + struct ANASTASIS_ReduxState *rs; + }; @@ -109,121 +116,99 @@ clean_sync (void *cls) se->ra->cleanup (se->ra->cleanup_cls); GNUNET_free (se); } + ANASTASIS_REDUX_state_free_ (ms->rs); GNUNET_free (ms); } /** * Function called when we have made progress on any of the - * providers we are trying to sync with. + * providers we are trying to sync with. The first one to report wins: + * we return its result and drop the rest. * * @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) + * @param error error code, #TALER_EC_NONE if the state is the new successful state + * @param[in] rs the state, updated for the provider that reported */ static void sync_progress (void *cls, enum TALER_ErrorCode error, - json_t *new_state) + struct ANASTASIS_ReduxState *rs) { struct SyncEntry *se = cls; struct MasterSync *ms = se->ms; + ANASTASIS_ActionCallback cb = ms->cb; + void *cb_cls = ms->cb_cls; + GNUNET_assert (rs == ms->rs); GNUNET_CONTAINER_DLL_remove (ms->se_head, ms->se_tail, se); GNUNET_free (se); - ms->cb (ms->cb_cls, - error, - new_state); + /* The remaining entries are cancelled below; none of them touches the + state once it has been unlinked, so handing it over here is safe. */ + ms->rs = NULL; clean_sync (ms); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + error); } struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_sync_providers_ (json_t *state, +ANASTASIS_REDUX_sync_providers_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *rd; - json_t *cs_arr; + const struct ANASTASIS_RecoveryInformation *ri; struct MasterSync *ms; - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) + if (NULL == rs->details.recovery.r) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + 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; - } + ri = ANASTASIS_recovery_get_info (rs->details.recovery.r); ms = GNUNET_new (struct MasterSync); ms->cb = cb; ms->cb_cls = cb_cls; + ms->rs = rs; + for (unsigned int i = 0; i < ri->cs_len; i++) { - 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; + const struct ANASTASIS_ChallengeDetails *cd; + 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); - } + cd = ANASTASIS_challenge_get_details (ri->cs[i]); + if (GNUNET_OK == + ANASTASIS_REDUX_lookup_salt_ (&rs->common, + cd->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_ (cd->provider_url, + rs, + &sync_progress, + se); } if (NULL == ms->se_head) { /* everything already synced */ + ms->rs = NULL; clean_sync (ms); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED, "already in sync"); @@ -236,22 +221,18 @@ ANASTASIS_REDUX_sync_providers_ (json_t *state, struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_poll_providers_ (json_t *state, +ANASTASIS_REDUX_poll_providers_ (struct ANASTASIS_ReduxState *rs, 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) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' missing"); @@ -260,35 +241,35 @@ ANASTASIS_REDUX_poll_providers_ (json_t *state, ms = GNUNET_new (struct MasterSync); ms->cb = cb; ms->cb_cls = cb_cls; - json_object_foreach (ap, url, obj) + ms->rs = rs; + for (unsigned int i = 0; i < rs->common.providers_len; i++) { + const char *url = rs->common.providers[i].url.url; struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; struct SyncEntry *se; - struct ANASTASIS_ReduxAction *ra; if (GNUNET_OK == - ANASTASIS_reducer_lookup_salt (state, - url, - &provider_salt)) + ANASTASIS_REDUX_lookup_salt_ (&rs->common, + 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; + se->ra = ANASTASIS_REDUX_add_provider_to_state_ (url, + rs, + &sync_progress, + se); } if (NULL == ms->se_head) { /* everything already synced */ + ms->rs = NULL; clean_sync (ms); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, "already in sync"); diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c @@ -25,6 +25,7 @@ #include "anastasis_redux.h" #include "anastasis_error_codes.h" #include "anastasis_api_redux.h" +#include "anastasis_api_redux_state.h" #define GENERATE_STRING(STRING) #STRING, @@ -60,16 +61,18 @@ ANASTASIS_recovery_state_to_string_ (enum ANASTASIS_RecoveryState rs) } +/** + * Transition @a rs to @a new_recovery_state. + * + * @param[in,out] rs state to transition + * @param new_recovery_state state to transition to + */ static void -set_state (json_t *state, +set_state (struct ANASTASIS_ReduxState *rs, enum ANASTASIS_RecoveryState new_recovery_state) { - GNUNET_assert ( - 0 == - json_object_set_new ( - state, - "recovery_state", - json_string (ANASTASIS_recovery_state_to_string_ (new_recovery_state)))); + GNUNET_assert (ANASTASIS_RT_RECOVERY == rs->type); + rs->details.recovery.state = new_recovery_state; } @@ -158,8 +161,14 @@ ANASTASIS_recovery_start (const struct GNUNET_CONFIGURATION_Handle *cfg) json_object_set_new (initial_state, "reducer_type", json_string ("recovery"))); - set_state (initial_state, - ANASTASIS_RECOVERY_STATE_CONTINENT_SELECTING); + GNUNET_assert ( + 0 == + json_object_set_new ( + initial_state, + "recovery_state", + json_string ( + ANASTASIS_recovery_state_to_string_ ( + ANASTASIS_RECOVERY_STATE_CONTINENT_SELECTING)))); return initial_state; } @@ -185,11 +194,6 @@ struct SelectChallengeContext struct GNUNET_TIME_Relative timeout; /** - * Overall recovery action. - */ - struct ANASTASIS_Recovery *r; - - /** * Function to call with the next state. */ ANASTASIS_ActionCallback cb; @@ -200,9 +204,11 @@ struct SelectChallengeContext void *cb_cls; /** - * Our state. + * Our state; we own it. The recovery operation we drive lives in + * `rs->details.recovery.r`, so there is no second handle to keep in + * sync here. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; /** * Our arguments (like answers to the challenge, if already provided). @@ -241,12 +247,9 @@ sctx_free (void *cls) { struct SelectChallengeContext *sctx = cls; - if (NULL != sctx->r) - { - ANASTASIS_recovery_abort (sctx->r); - sctx->r = NULL; - } - json_decref (sctx->state); + /* Freeing the state also aborts the recovery operation, if one is + still running. */ + ANASTASIS_REDUX_state_free_ (sctx->rs); json_decref (sctx->args); if (NULL != sctx->delayed_report) { @@ -258,72 +261,114 @@ sctx_free (void *cls) /** - * Call the action callback with an error result + * Hand our state back to the application and dispose of @a sctx. * - * @param cb action callback to call - * @param cb_cls closure for @a cb - * @param rc error code to translate to JSON + * @param[in] sctx context to finish + * @param ec error code to report alongside the state + */ +static void +sctx_return (struct SelectChallengeContext *sctx, + enum TALER_ErrorCode ec) +{ + struct ANASTASIS_ReduxState *rs = sctx->rs; + ANASTASIS_ActionCallback cb = sctx->cb; + void *cb_cls = sctx->cb_cls; + + sctx->rs = NULL; + sctx_free (sctx); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + ec); +} + + +/** + * Report an error to the application and dispose of @a sctx. + * + * @param[in] sctx context to finish + * @param ec error to report + * @param detail human-readable detail, may be NULL */ static void -fail_by_error (ANASTASIS_ActionCallback cb, - void *cb_cls, - enum ANASTASIS_RecoveryStatus rc) +sctx_fail (struct SelectChallengeContext *sctx, + enum TALER_ErrorCode ec, + const char *detail) +{ + ANASTASIS_ActionCallback cb = sctx->cb; + void *cb_cls = sctx->cb_cls; + + sctx_free (sctx); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + detail); +} + + +/** + * Translate a recovery failure into an error code and message. + * + * @param rc status to translate + * @param[out] msg set to a human-readable, translatable message + * @return the error code to report + */ +static enum TALER_ErrorCode +error_by_status (enum ANASTASIS_RecoveryStatus rc, + const char **msg) { - const char *msg = NULL; enum TALER_ErrorCode ec = TALER_EC_INVALID; + *msg = NULL; switch (rc) { case ANASTASIS_RS_SUCCESS: GNUNET_assert (0); break; case ANASTASIS_RS_POLICY_DOWNLOAD_FAILED: - msg = gettext_noop ("download failed due to unexpected network issue"); + *msg = gettext_noop ("download failed due to unexpected network issue"); ec = TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED; break; case ANASTASIS_RS_POLICY_DOWNLOAD_NO_POLICY: GNUNET_break (0); - msg = gettext_noop ("policy document returned was malformed"); + *msg = gettext_noop ("policy document returned was malformed"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED; break; case ANASTASIS_RS_POLICY_DOWNLOAD_TOO_BIG: GNUNET_break (0); - msg = gettext_noop ("policy document too large for client memory"); + *msg = gettext_noop ("policy document too large for client memory"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED; break; case ANASTASIS_RS_POLICY_DOWNLOAD_INVALID_COMPRESSION: GNUNET_break (0); - msg = gettext_noop ("failed to decompress policy document"); + *msg = gettext_noop ("failed to decompress policy document"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED; break; case ANASTASIS_RS_POLICY_DOWNLOAD_NO_JSON: GNUNET_break (0); - msg = gettext_noop ("policy document returned was not in JSON format"); + *msg = gettext_noop ("policy document returned was not in JSON format"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED; break; case ANASTASIS_RS_POLICY_MALFORMED_JSON: GNUNET_break (0); - msg = gettext_noop ( + *msg = gettext_noop ( "policy document returned was not in required JSON format"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED; break; case ANASTASIS_RS_POLICY_SERVER_ERROR: - msg = gettext_noop ("Anastasis server reported transient internal error"); + *msg = gettext_noop ("Anastasis server reported transient internal error"); ec = TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED; break; case ANASTASIS_RS_POLICY_GONE: - msg = gettext_noop ("policy document no longer exists"); + *msg = gettext_noop ("policy document no longer exists"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED; break; case ANASTASIS_RS_POLICY_UNKNOWN: - msg = gettext_noop ("account unknown to Anastasis server"); + *msg = gettext_noop ("account unknown to Anastasis server"); ec = TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED; break; } - ANASTASIS_redux_fail_ (cb, - cb_cls, - ec, - msg); + return ec; } @@ -343,8 +388,10 @@ core_secret_cb (void *cls, size_t secret_size) { struct SelectChallengeContext *sctx = cls; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; - sctx->r = NULL; + /* the library disposed of the operation before calling us */ + rr->r = NULL; if (ANASTASIS_RS_SUCCESS == rc) { json_t *jsecret; @@ -355,29 +402,29 @@ core_secret_cb (void *cls, NULL); if (NULL == jsecret) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_SECRET_MALFORMED, - NULL); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_SECRET_MALFORMED, + NULL); return; } - GNUNET_assert (0 == - json_object_set_new (sctx->state, - "core_secret", - jsecret)); - set_state (sctx->state, + json_decref (rr->core_secret); + rr->core_secret = jsecret; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_RECOVERY_FINISHED); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; } - fail_by_error (sctx->cb, - sctx->cb_cls, - rc); - sctx_free (sctx); + { + const char *msg; + enum TALER_ErrorCode ec; + + ec = error_by_status (rc, + &msg); + sctx_fail (sctx, + ec, + msg); + } } @@ -393,114 +440,33 @@ report_solved (void *cls) struct SelectChallengeContext *sctx = cls; sctx->delayed_report = NULL; - set_state (sctx->state, + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); } /** - * Find challenge of @a uuid in @a state under "recovery_information". + * Obtain the feedback entry for challenge @a uuid, reset to a fresh + * variant of kind @a status. * - * @param state the state to search - * @param uuid the UUID to search for - * @return NULL on error, otherwise challenge entry; RC is NOT incremented + * @param[in,out] sctx context whose state to update + * @param uuid challenge the feedback is about + * @param status which variant the caller is about to fill in + * @return the entry to fill in */ -static json_t * -find_challenge_in_ri (json_t *state, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid) +static struct ANASTASIS_ReduxFeedback * +set_feedback (struct SelectChallengeContext *sctx, + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + enum ANASTASIS_ReduxFeedbackStatus status) { - struct ANASTASIS_CRYPTO_TruthUUIDP u; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("uuid", - &u), - GNUNET_JSON_spec_end () - }; - json_t *ri; - json_t *challenges; - json_t *challenge; - size_t index; + struct ANASTASIS_ReduxFeedback *fb; - ri = json_object_get (state, - "recovery_information"); - if (NULL == ri) - { - GNUNET_break (0); - return NULL; - } - challenges = json_object_get (ri, - "challenges"); - if (NULL == challenges) - { - GNUNET_break (0); - return NULL; - } - json_array_foreach (challenges, index, challenge) - { - if (GNUNET_OK != - GNUNET_JSON_parse (challenge, - spec, - NULL, NULL)) - { - GNUNET_break (0); - return NULL; - } - if (0 == - GNUNET_memcmp (&u, - uuid)) - { - return challenge; - } - } - return NULL; -} - - -/** - * Find challenge of @a uuid in @a state under "challenges". - * - * @param state the state to search - * @param uuid the UUID to search for - * @return NULL on error, otherwise challenge entry; RC is NOT incremented - */ -static json_t * -find_challenge_in_cs (json_t *state, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid) -{ - json_t *rd = json_object_get (state, - "recovery_document"); - json_t *cs = json_object_get (rd, - "challenges"); - json_t *c; - size_t off; - - json_array_foreach (cs, off, c) - { - struct ANASTASIS_CRYPTO_TruthUUIDP u; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("uuid", - &u), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (c, - spec, - NULL, NULL)) - { - GNUNET_break (0); - continue; - } - if (0 != - GNUNET_memcmp (uuid, - &u)) - continue; - return c; - } - return NULL; + fb = ANASTASIS_REDUX_feedback_get_ (&sctx->rs->details.recovery, + uuid); + fb->status = status; + return fb; } @@ -517,286 +483,142 @@ start_feedback_cb ( const struct ANASTASIS_ChallengeStartResponse *csr) { struct SelectChallengeContext *sctx = cls; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; const struct ANASTASIS_ChallengeDetails *cd; - char uuid[sizeof (cd->uuid) * 2]; - char *end; - json_t *feedback; + struct ANASTASIS_ReduxFeedback *fb; cd = ANASTASIS_challenge_get_details (csr->challenge); - end = GNUNET_STRINGS_data_to_string (&cd->uuid, - sizeof (cd->uuid), - uuid, - sizeof (uuid)); - GNUNET_assert (NULL != end); - *end = '\0'; - feedback = json_object_get (sctx->state, - "challenge_feedback"); - if (NULL == feedback) - { - feedback = json_object (); - GNUNET_assert (0 == - json_object_set_new (sctx->state, - "challenge_feedback", - feedback)); - } switch (csr->cs) { case ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED: - { - json_t *instructions; - char *hint; - - GNUNET_asprintf (&hint, - _ ("Required TAN can be found in `%s'"), - csr->details.tan_filename); - instructions = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "code-in-file"), - GNUNET_JSON_pack_string ("filename", - csr->details.tan_filename), - GNUNET_JSON_pack_string ("display_hint", - hint)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - instructions)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_CODE_IN_FILE); + fb->details.code_in_file.filename + = GNUNET_strdup (csr->details.tan_filename); + GNUNET_asprintf (&fb->display_hint, + _ ("Required TAN can be found in `%s'"), + csr->details.tan_filename); + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED: - { - json_t *instructions; - char *hint; - - GNUNET_asprintf (&hint, - _ ("TAN code was sent to `%s'"), - csr->details.tan_address_hint); - instructions = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "send-to-address"), - GNUNET_JSON_pack_string ("address_hint", - csr->details.tan_address_hint), - GNUNET_JSON_pack_string ("display_hint", - hint)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - instructions)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_SEND_TO_ADDRESS); + fb->details.send_to_address.address_hint + = GNUNET_strdup (csr->details.tan_address_hint); + GNUNET_asprintf (&fb->display_hint, + _ ("TAN code was sent to `%s'"), + csr->details.tan_address_hint); + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_START_STATUS_TAN_ALREADY_SENT: - { - json_t *instructions; - char *hint; - - GNUNET_asprintf (&hint, - _ ("TAN code already sent.")); - instructions = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "send-to-address"), - GNUNET_JSON_pack_string ("display_hint", - hint)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - instructions)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_SEND_TO_ADDRESS); + GNUNET_asprintf (&fb->display_hint, + _ ("TAN code already sent.")); + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED: - { - json_t *pay; - char *hint; - - GNUNET_asprintf (&hint, - _ ("Taler payment to `%s' required"), - csr->details.payment_required.taler_pay_uri); - pay = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "taler-payment"), - GNUNET_JSON_pack_string ( - "taler_pay_uri", - csr->details.payment_required.taler_pay_uri), - GNUNET_JSON_pack_string ("provider", - cd->provider_url), - GNUNET_JSON_pack_string ("display_hint", - hint), - GNUNET_JSON_pack_data_auto ( - "payment_secret", - &csr->details.payment_required.payment_secret)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - pay)); - } + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_TALER_PAYMENT); + fb->details.taler_payment.taler_pay_uri + = GNUNET_strdup (csr->details.payment_required.taler_pay_uri); + ANASTASIS_REDUX_provider_url_set_ (&fb->details.taler_payment.provider, + cd->provider_url); + fb->details.taler_payment.payment_secret + = csr->details.payment_required.payment_secret; + GNUNET_asprintf (&fb->display_hint, + _ ("Taler payment to `%s' required"), + csr->details.payment_required.taler_pay_uri); /* Remember payment secret for later (once application claims it paid) */ { - json_t *challenge = find_challenge_in_ri (sctx->state, - &cd->uuid); + struct ANASTASIS_ReduxChallengeInfo *ci; - GNUNET_assert (NULL != challenge); - GNUNET_assert (0 == - json_object_set_new ( - challenge, - "payment_secret", - GNUNET_JSON_from_data_auto ( - &csr->details.payment_required.payment_secret))); + ci = ANASTASIS_REDUX_challenge_find_ (rr, + &cd->uuid); + GNUNET_assert (NULL != ci); + ci->payment_secret = csr->details.payment_required.payment_secret; + ci->have_payment_secret = true; } - set_state (sctx->state, + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE: - { - json_t *err; - - err = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "server-failure"), - GNUNET_JSON_pack_uint64 ("http_status", - csr->http_status), - GNUNET_JSON_pack_uint64 ("error_code", - csr->ec)); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - err)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_SERVER_FAILURE); + fb->details.server_failure.http_status = csr->http_status; + fb->details.server_failure.ec = csr->ec; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - csr->ec, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + csr->ec); return; case ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN: - { - json_t *err; - - err = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "truth-unknown"), - GNUNET_JSON_pack_uint64 ("http_status", - csr->http_status), - GNUNET_JSON_pack_uint64 ("error_code", - TALER_EC_ANASTASIS_TRUTH_UNKNOWN)); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - err)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_TRUTH_UNKNOWN); + fb->details.server_failure.http_status = csr->http_status; + fb->details.server_failure.ec = TALER_EC_ANASTASIS_TRUTH_UNKNOWN; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - TALER_EC_ANASTASIS_TRUTH_UNKNOWN, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_ANASTASIS_TRUTH_UNKNOWN); return; case ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED: - { - json_t *reply; - json_t *c; - char *hint; - - c = find_challenge_in_cs (sctx->state, - &cd->uuid); - if (NULL == c) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - sctx_free (sctx); - return; - } - GNUNET_assert (0 == - json_object_set_new (c, - "async", - json_true ())); - GNUNET_assert ( - 0 == - json_object_set_new ( - c, - "answer-pin", - json_integer ( - csr->details.bank_transfer_required.answer_code))); - GNUNET_asprintf (&hint, - _ ("Wire %s to %s (%s) with subject %s\n"), - TALER_amount2s ( - &csr->details.bank_transfer_required.amount), - csr->details.bank_transfer_required.target_iban, - csr->details.bank_transfer_required.target_business_name, - csr->details.bank_transfer_required.wire_transfer_subject - ); - reply = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "iban-instructions"), - GNUNET_JSON_pack_string ( - "target_iban", - csr->details.bank_transfer_required.target_iban), - GNUNET_JSON_pack_string ( - "display_hint", - hint), - GNUNET_JSON_pack_string ( - "target_business_name", - csr->details.bank_transfer_required.target_business_name), - GNUNET_JSON_pack_string ( - "wire_transfer_subject", - csr->details.bank_transfer_required.wire_transfer_subject), - TALER_JSON_pack_amount ( - "challenge_amount", - &csr->details.bank_transfer_required.amount)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - reply)); - } - GNUNET_assert (0 == - json_object_set_new (sctx->state, - "selected_challenge_uuid", - GNUNET_JSON_from_data_auto ( - &cd->uuid))); - set_state (sctx->state, + /* The provider turned this into an asynchronous challenge: remember + the answer code so that a later "poll" can pick it up again. */ + ANASTASIS_challenge_set_async ( + csr->challenge, + csr->details.bank_transfer_required.answer_code); + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_IBAN_INSTRUCTIONS); + fb->details.iban_instructions.target_iban + = GNUNET_strdup (csr->details.bank_transfer_required.target_iban); + fb->details.iban_instructions.target_business_name + = GNUNET_strdup ( + csr->details.bank_transfer_required.target_business_name); + fb->details.iban_instructions.wire_transfer_subject + = GNUNET_strdup ( + csr->details.bank_transfer_required.wire_transfer_subject); + fb->details.iban_instructions.amount + = csr->details.bank_transfer_required.amount; + GNUNET_asprintf (&fb->display_hint, + _ ("Wire %s to %s (%s) with subject %s\n"), + TALER_amount2s ( + &csr->details.bank_transfer_required.amount), + csr->details.bank_transfer_required.target_iban, + csr->details.bank_transfer_required.target_business_name, + csr->details.bank_transfer_required.wire_transfer_subject); + rr->selected_challenge = cd->uuid; + rr->have_selected_challenge = true; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; } GNUNET_break (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + NULL); } @@ -813,61 +635,17 @@ answer_feedback_cb ( const struct ANASTASIS_ChallengeAnswerResponse *csr) { struct SelectChallengeContext *sctx = cls; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; const struct ANASTASIS_ChallengeDetails *cd; - char uuid[sizeof (cd->uuid) * 2]; - char *end; - json_t *feedback; + struct ANASTASIS_ReduxFeedback *fb; cd = ANASTASIS_challenge_get_details (csr->challenge); - end = GNUNET_STRINGS_data_to_string (&cd->uuid, - sizeof (cd->uuid), - uuid, - sizeof (uuid)); - GNUNET_assert (NULL != end); - *end = '\0'; - feedback = json_object_get (sctx->state, - "challenge_feedback"); - if (NULL == feedback) - { - feedback = json_object (); - GNUNET_assert (0 == - json_object_set_new (sctx->state, - "challenge_feedback", - feedback)); - } switch (csr->cs) { case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED: - { - json_t *rd; - - rd = ANASTASIS_recovery_serialize (sctx->r); - if (NULL == rd) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unable to serialize recovery state"); - sctx_free (sctx); - return; - } - GNUNET_assert (0 == - json_object_set_new (sctx->state, - "recovery_document", - rd)); - } - { - json_t *solved; - - solved = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "solved")); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - solved)); - } + (void) set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_SOLVED); /* Delay reporting challenge success, as we MAY still also see a secret recovery success (and we can only call the callback once) */ @@ -875,168 +653,92 @@ answer_feedback_cb ( sctx); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER: - { - json_t *instructions; - - instructions = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "incorrect-answer"), - GNUNET_JSON_pack_uint64 ("error_code", - csr->ec)); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - instructions)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_INCORRECT_ANSWER); + fb->details.incorrect_answer.ec = csr->ec; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED: - { - json_t *pay; - char *hint; - - GNUNET_asprintf (&hint, - _ ("Taler payment to `%s' required"), - csr->details.payment_required.taler_pay_uri); - pay = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "taler-payment"), - GNUNET_JSON_pack_string ( - "taler_pay_uri", - csr->details.payment_required.taler_pay_uri), - GNUNET_JSON_pack_string ( - "display_hint", - hint), - GNUNET_JSON_pack_string ("provider", - cd->provider_url), - GNUNET_JSON_pack_data_auto ( - "payment_secret", - &csr->details.payment_required.payment_secret)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - pay)); - } + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_TALER_PAYMENT); + fb->details.taler_payment.taler_pay_uri + = GNUNET_strdup (csr->details.payment_required.taler_pay_uri); + ANASTASIS_REDUX_provider_url_set_ (&fb->details.taler_payment.provider, + cd->provider_url); + fb->details.taler_payment.payment_secret + = csr->details.payment_required.payment_secret; + GNUNET_asprintf (&fb->display_hint, + _ ("Taler payment to `%s' required"), + csr->details.payment_required.taler_pay_uri); /* Remember payment secret for later (once application claims it paid) */ { - json_t *challenge = find_challenge_in_ri (sctx->state, - &cd->uuid); + struct ANASTASIS_ReduxChallengeInfo *ci; - GNUNET_assert (NULL != challenge); - GNUNET_assert (0 == - json_object_set_new ( - challenge, - "payment_secret", - GNUNET_JSON_from_data_auto ( - &csr->details.payment_required.payment_secret))); + ci = ANASTASIS_REDUX_challenge_find_ (rr, + &cd->uuid); + GNUNET_assert (NULL != ci); + ci->payment_secret = csr->details.payment_required.payment_secret; + ci->have_payment_secret = true; } - set_state (sctx->state, + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE: - { - json_t *err; - - err = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "server-failure"), - GNUNET_JSON_pack_uint64 ("http_status", - csr->http_status), - GNUNET_JSON_pack_uint64 ("error_code", - csr->ec)); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - err)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_SERVER_FAILURE); + fb->details.server_failure.http_status = csr->http_status; + fb->details.server_failure.ec = csr->ec; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - csr->ec, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + csr->ec); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN: - { - json_t *err; - - err = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("state", - "truth-unknown"), - GNUNET_JSON_pack_uint64 ("http_status", - csr->http_status), - GNUNET_JSON_pack_uint64 ("error_code", - TALER_EC_ANASTASIS_TRUTH_UNKNOWN)); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - err)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_TRUTH_UNKNOWN); + fb->details.server_failure.http_status = csr->http_status; + fb->details.server_failure.ec = TALER_EC_ANASTASIS_TRUTH_UNKNOWN; + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - TALER_EC_ANASTASIS_TRUTH_UNKNOWN, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_ANASTASIS_TRUTH_UNKNOWN); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED: - { - json_t *err; - char *hint; - - GNUNET_asprintf ( - &hint, - _ ("exceeded limit of %llu attempts in %s"), - (unsigned long long) csr->details.rate_limit_exceeded.request_limit, - GNUNET_TIME_relative2s ( - csr->details.rate_limit_exceeded.request_frequency, - true)); - err = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ( - "state", - "rate-limit-exceeded"), - GNUNET_JSON_pack_string ( - "display_hint", - hint), - GNUNET_JSON_pack_uint64 ( - "request_limit", - csr->details.rate_limit_exceeded.request_limit), - GNUNET_JSON_pack_time_rel ( - "request_frequency", - csr->details.rate_limit_exceeded.request_frequency), - GNUNET_JSON_pack_uint64 ( - "error_code", - TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED)); - GNUNET_free (hint); - GNUNET_assert (0 == - json_object_set_new (feedback, - uuid, - err)); - } - set_state (sctx->state, + fb = set_feedback (sctx, + &cd->uuid, + ANASTASIS_RFS_RATE_LIMIT_EXCEEDED); + fb->details.rate_limit_exceeded.ec + = TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED; + fb->details.rate_limit_exceeded.request_limit + = csr->details.rate_limit_exceeded.request_limit; + fb->details.rate_limit_exceeded.request_frequency + = csr->details.rate_limit_exceeded.request_frequency; + GNUNET_asprintf ( + &fb->display_hint, + _ ("exceeded limit of %llu attempts in %s"), + (unsigned long long) csr->details.rate_limit_exceeded.request_limit, + GNUNET_TIME_relative2s ( + csr->details.rate_limit_exceeded.request_frequency, + true)); + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - sctx->cb (sctx->cb_cls, - TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED); return; } GNUNET_break (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + NULL); } @@ -1056,6 +758,7 @@ solve_challenge_cb (void *cls, const struct ANASTASIS_RecoveryInformation *ri) { struct SelectChallengeContext *sctx = cls; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; const struct ANASTASIS_PaymentSecretP *psp = NULL; struct ANASTASIS_PaymentSecretP ps; struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO; @@ -1071,15 +774,14 @@ solve_challenge_cb (void *cls, &ps), GNUNET_JSON_spec_end () }; + struct ANASTASIS_ReduxChallengeInfo *sci; if (NULL == ri) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "recovery information could not be deserialized"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "recovery information could not be deserialized"); return; } @@ -1090,11 +792,9 @@ solve_challenge_cb (void *cls, NULL, NULL)) ) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'timeout' malformed"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'timeout' malformed"); return; } @@ -1106,51 +806,30 @@ solve_challenge_cb (void *cls, { struct ANASTASIS_Challenge *ci = ri->cs[i]; const struct ANASTASIS_ChallengeDetails *cd; - json_t *challenge; - json_t *pin; cd = ANASTASIS_challenge_get_details (ci); if (cd->solved || (! cd->async) ) continue; - - challenge = find_challenge_in_cs (sctx->state, - &cd->uuid); - if (NULL == challenge) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "challenge not found"); - sctx_free (sctx); - return; - } - pin = json_object_get (challenge, - "answer-pin"); - if (! json_is_integer (pin)) + if (! cd->have_answer_pin) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "async challenge 'answer-pin' not found"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "async challenge 'answer-pin' not found"); return; } if (GNUNET_OK != ANASTASIS_challenge_answer2 (ci, psp, timeout, - json_integer_value (pin), + cd->answer_pin, &answer_feedback_cb, sctx)) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Failed to begin answering asynchronous challenge"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Failed to begin answering asynchronous challenge"); return; } poll_started = true; @@ -1173,61 +852,37 @@ solve_challenge_cb (void *cls, } /* end resuming async challenges */ /* Check if we got a payment_secret */ + sci = ANASTASIS_REDUX_challenge_find_ (rr, + &sctx->uuid); + if (NULL == sci) { - json_t *challenge; - - challenge = find_challenge_in_ri (sctx->state, - &sctx->uuid); - if (NULL == challenge) + GNUNET_break_op (0); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "challenge not found"); + return; + } + if (NULL != + json_object_get (sctx->args, + "payment_secret")) + { + /* check if we got payment secret in args */ + if (GNUNET_OK != + GNUNET_JSON_parse (sctx->args, + pspec, + NULL, NULL)) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "challenge not found"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'payment_secret' malformed"); return; } - - if (NULL != - json_object_get (sctx->args, - "payment_secret")) - { - /* check if we got payment secret in args */ - if (GNUNET_OK != - GNUNET_JSON_parse (sctx->args, - pspec, - NULL, NULL)) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'payment_secret' malformed"); - sctx_free (sctx); - return; - } - psp = &ps; - } - else if (NULL != - json_object_get (challenge, - "payment_secret")) - { - if (GNUNET_OK != - GNUNET_JSON_parse (challenge, - pspec, - NULL, NULL)) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'payment_secret' malformed"); - sctx_free (sctx); - return; - } - psp = &ps; - } + psp = &ps; + } + else if (sci->have_payment_secret) + { + psp = &sci->payment_secret; } /* start or solve selected challenge */ @@ -1236,7 +891,6 @@ solve_challenge_cb (void *cls, struct ANASTASIS_Challenge *ci = ri->cs[i]; const struct ANASTASIS_ChallengeDetails *cd; int ret; - json_t *c; cd = ANASTASIS_challenge_get_details (ci); if (cd->async) @@ -1247,38 +901,28 @@ solve_challenge_cb (void *cls, continue; if (cd->solved) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "Selected challenge already solved"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "Selected challenge already solved"); return; } - c = find_challenge_in_cs (sctx->state, - &cd->uuid); - GNUNET_assert (NULL != c); if (0 == strcmp ("question", cd->type)) { /* security question, answer must be a string */ - json_t *janswer = json_object_get (sctx->args, - "answer"); - const char *answer = json_string_value (janswer); + const char *answer = json_string_value (json_object_get (sctx->args, + "answer")); if (NULL == answer) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'answer' missing"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'answer' missing"); return; } /* persist answer, in case payment is required */ - GNUNET_assert (0 == - json_object_set (c, - "answer", - janswer)); + GNUNET_free (sci->answer); + sci->answer = GNUNET_strdup (answer); ret = ANASTASIS_challenge_answer (ci, psp, timeout, @@ -1299,10 +943,8 @@ solve_challenge_cb (void *cls, /* persist answer, in case async processing happens via poll */ - GNUNET_assert (0 == - json_object_set (c, - "answer-pin", - pin)); + ANASTASIS_challenge_set_answer_pin (ci, + ianswer); ret = ANASTASIS_challenge_answer2 (ci, psp, timeout, @@ -1324,11 +966,9 @@ solve_challenge_cb (void *cls, spec, NULL, NULL)) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'answer' malformed"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'answer' malformed"); return; } ret = ANASTASIS_challenge_answer3 (ci, @@ -1349,20 +989,16 @@ solve_challenge_cb (void *cls, } if (GNUNET_OK != ret) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Failed to begin answering challenge"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Failed to begin answering challenge"); return; } return; /* await answer feedback */ } - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'uuid' not in list of challenges"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'uuid' not in list of challenges"); } @@ -1382,37 +1018,31 @@ pay_challenge_cb (void *cls, const struct ANASTASIS_RecoveryInformation *ri) { struct SelectChallengeContext *sctx = cls; - json_t *challenge; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; + struct ANASTASIS_ReduxChallengeInfo *sci; if (NULL == ri) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "recovery information could not be deserialized"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "recovery information could not be deserialized"); return; } - challenge = find_challenge_in_ri (sctx->state, - &sctx->uuid); - if (NULL == challenge) + sci = ANASTASIS_REDUX_challenge_find_ (rr, + &sctx->uuid); + if (NULL == sci) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "challenge not found"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "challenge not found"); return; } /* persist payment, in case we need to run the request again */ - GNUNET_assert ( - 0 == - json_object_set_new (challenge, - "payment_secret", - GNUNET_JSON_from_data_auto (&sctx->ps))); + sci->payment_secret = sctx->ps; + sci->have_payment_secret = true; for (unsigned int i = 0; i<ri->cs_len; i++) { @@ -1427,11 +1057,9 @@ pay_challenge_cb (void *cls, continue; if (cd->solved) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "Selected challenge already solved"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "Selected challenge already solved"); return; } @@ -1439,23 +1067,17 @@ pay_challenge_cb (void *cls, cd->type)) { /* security question, answer must be a string and already ready */ - json_t *janswer = json_object_get (challenge, - "answer"); - const char *answer = json_string_value (janswer); - - if (NULL == answer) + if (NULL == sci->answer) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'answer' missing"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'answer' missing"); return; } ret = ANASTASIS_challenge_answer (ci, &sctx->ps, sctx->timeout, - answer, + sci->answer, &answer_feedback_cb, sctx); } @@ -1468,20 +1090,16 @@ pay_challenge_cb (void *cls, } if (GNUNET_OK != ret) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Failed to begin answering challenge"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Failed to begin answering challenge"); return; } return; /* await answer feedback */ } - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'uuid' not in list of challenges"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'uuid' not in list of challenges"); } @@ -1496,77 +1114,59 @@ pay_challenge_cb (void *cls, * @return handle to cancel challenge selection step */ static struct ANASTASIS_ReduxAction * -solve_challenge (json_t *state, +solve_challenge (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - struct SelectChallengeContext *sctx - = GNUNET_new (struct SelectChallengeContext); - json_t *rd; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid", - &sctx->uuid), - GNUNET_JSON_spec_end () - }; + struct ANASTASIS_ReduxRecovery *rr = &rs->details.recovery; + struct SelectChallengeContext *sctx; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); return NULL; } - if (GNUNET_OK != - GNUNET_JSON_parse (state, - spec, - NULL, NULL)) + if (! rr->have_selected_challenge) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'selected_challenge_uuid' missing"); return NULL; } - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) + if (NULL == rr->r) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "solve_challenge"); return NULL; } + sctx = GNUNET_new (struct SelectChallengeContext); + sctx->uuid = rr->selected_challenge; sctx->cb = cb; sctx->cb_cls = cb_cls; - sctx->state = json_incref (state); + sctx->rs = rs; sctx->args = json_incref ((json_t*) arguments); - sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_, - rd); - if ( (NULL != sctx->r) && - (GNUNET_OK != - ANASTASIS_recovery_resume (sctx->r, - &solve_challenge_cb, - sctx, - &core_secret_cb, - sctx)) ) - { - ANASTASIS_recovery_abort (sctx->r); - sctx->r = NULL; - } - if (NULL == sctx->r) + if (GNUNET_OK != + ANASTASIS_recovery_resume (rr->r, + &solve_challenge_cb, + sctx, + &core_secret_cb, + sctx)) { - json_decref (sctx->state); - json_decref (sctx->args); - GNUNET_free (sctx); GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' invalid"); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' invalid"); return NULL; } sctx->ra.cleanup = &sctx_free; @@ -1587,54 +1187,41 @@ solve_challenge (json_t *state, * @return handle to cancel challenge selection step */ static struct ANASTASIS_ReduxAction * -poll_challenges (json_t *state, +poll_challenges (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - struct SelectChallengeContext *sctx - = GNUNET_new (struct SelectChallengeContext); - json_t *rd; + struct ANASTASIS_ReduxRecovery *rr = &rs->details.recovery; + struct SelectChallengeContext *sctx; - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) + if (NULL == rr->r) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "poll_challenges"); return NULL; } + sctx = GNUNET_new (struct SelectChallengeContext); sctx->poll_only = true; sctx->cb = cb; sctx->cb_cls = cb_cls; - sctx->state = json_incref (state); + sctx->rs = rs; sctx->args = json_incref ((json_t*) arguments); - sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_, - rd); - if ( (NULL != sctx->r) && - (GNUNET_OK != - ANASTASIS_recovery_resume (sctx->r, - &solve_challenge_cb, - sctx, - &core_secret_cb, - sctx)) ) - { - ANASTASIS_recovery_abort (sctx->r); - sctx->r = NULL; - } - if (NULL == sctx->r) + if (GNUNET_OK != + ANASTASIS_recovery_resume (rr->r, + &solve_challenge_cb, + sctx, + &core_secret_cb, + sctx)) { - json_decref (sctx->state); - json_decref (sctx->args); - GNUNET_free (sctx); GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' invalid"); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' invalid"); return NULL; } sctx->ra.cleanup = &sctx_free; @@ -1654,33 +1241,29 @@ poll_challenges (json_t *state, * @return handle to cancel challenge selection step */ static struct ANASTASIS_ReduxAction * -pay_challenge (json_t *state, +pay_challenge (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - struct SelectChallengeContext *sctx - = GNUNET_new (struct SelectChallengeContext); - json_t *rd; + struct ANASTASIS_ReduxRecovery *rr = &rs->details.recovery; + struct SelectChallengeContext *sctx; struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid", - &sctx->uuid), - GNUNET_JSON_spec_end () - }; + struct ANASTASIS_PaymentSecretP ps; struct GNUNET_JSON_Specification aspec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_relative_time ("timeout", &timeout), NULL), GNUNET_JSON_spec_fixed_auto ("payment_secret", - &sctx->ps), + &ps), GNUNET_JSON_spec_end () }; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -1691,62 +1274,51 @@ pay_challenge (json_t *state, aspec, NULL, NULL)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'payment_secret' missing"); return NULL; } - if (GNUNET_OK != - GNUNET_JSON_parse (state, - spec, - NULL, NULL)) + if (! rr->have_selected_challenge) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'selected_challenge_uuid' missing"); return NULL; } - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) + if (NULL == rr->r) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "pay_challenge"); return NULL; } + sctx = GNUNET_new (struct SelectChallengeContext); + sctx->uuid = rr->selected_challenge; + sctx->ps = ps; sctx->timeout = timeout; sctx->cb = cb; sctx->cb_cls = cb_cls; - sctx->state = json_incref (state); + sctx->rs = rs; sctx->args = json_incref ((json_t*) arguments); - sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_, - rd); - if ( (NULL != sctx->r) && - (GNUNET_OK != - ANASTASIS_recovery_resume (sctx->r, - &pay_challenge_cb, - sctx, - &core_secret_cb, - sctx)) ) - { - ANASTASIS_recovery_abort (sctx->r); - sctx->r = NULL; - } - if (NULL == sctx->r) + if (GNUNET_OK != + ANASTASIS_recovery_resume (rr->r, + &pay_challenge_cb, + sctx, + &core_secret_cb, + sctx)) { - json_decref (sctx->state); - json_decref (sctx->args); - GNUNET_free (sctx); GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' invalid"); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' invalid"); return NULL; } sctx->ra.cleanup = &sctx_free; @@ -1771,6 +1343,7 @@ select_challenge_cb (void *cls, const struct ANASTASIS_RecoveryInformation *ri) { struct SelectChallengeContext *sctx = cls; + struct ANASTASIS_ReduxRecovery *rr = &sctx->rs->details.recovery; const struct ANASTASIS_PaymentSecretP *psp = NULL; struct ANASTASIS_PaymentSecretP ps; struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO; @@ -1787,15 +1360,12 @@ select_challenge_cb (void *cls, GNUNET_JSON_spec_end () }; - if (NULL == ri) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "recovery information could not be deserialized"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "recovery information could not be deserialized"); return; } @@ -1805,11 +1375,9 @@ select_challenge_cb (void *cls, NULL, NULL)) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'timeout' malformed"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'timeout' malformed"); return; } @@ -1825,11 +1393,9 @@ select_challenge_cb (void *cls, NULL, NULL)) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'payment_secret' malformed"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'payment_secret' malformed"); return; } psp = &ps; @@ -1837,36 +1403,21 @@ select_challenge_cb (void *cls, else { /* Check if we got a payment_secret in state */ - json_t *challenge = find_challenge_in_ri (sctx->state, - &sctx->uuid); + const struct ANASTASIS_ReduxChallengeInfo *sci; - if (NULL == challenge) + sci = ANASTASIS_REDUX_challenge_find_ (rr, + &sctx->uuid); + if (NULL == sci) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "challenge not found"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "challenge not found"); return; } - if (NULL != - json_object_get (challenge, - "payment_secret")) + if (sci->have_payment_secret) { - if (GNUNET_OK != - GNUNET_JSON_parse (challenge, - pspec, - NULL, NULL)) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'payment_secret' malformed"); - sctx_free (sctx); - return; - } + ps = sci->payment_secret; psp = &ps; } } @@ -1884,18 +1435,13 @@ select_challenge_cb (void *cls, continue; if (cd->solved) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "Selected challenge already solved"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "Selected challenge already solved"); return; } - GNUNET_assert ( - 0 == - json_object_set_new (sctx->state, - "selected_challenge_uuid", - GNUNET_JSON_from_data_auto (&cd->uuid))); + rr->selected_challenge = cd->uuid; + rr->have_selected_challenge = true; if ( (0 == strcmp ("question", cd->type)) || (0 == strcmp ("totp", @@ -1903,56 +1449,37 @@ select_challenge_cb (void *cls, { /* security question or TOTP: immediately request user to answer it */ - set_state (sctx->state, + set_state (sctx->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); - sctx->cb (sctx->cb_cls, - TALER_EC_NONE, - sctx->state); - sctx_free (sctx); + sctx_return (sctx, + TALER_EC_NONE); return; } /* trigger challenge */ - { - json_t *c = find_challenge_in_cs (sctx->state, - &cd->uuid); - json_t *pin = json_object_get (c, - "answer-pin"); - - if (NULL != pin) - { - uint64_t ianswer = json_integer_value (pin); - - ret = ANASTASIS_challenge_answer2 (ci, - psp, - timeout, - ianswer, - &answer_feedback_cb, - sctx); - } - else - { - ret = ANASTASIS_challenge_start (ci, + if (cd->have_answer_pin) + ret = ANASTASIS_challenge_answer2 (ci, psp, - &start_feedback_cb, + timeout, + cd->answer_pin, + &answer_feedback_cb, sctx); - } - } + else + ret = ANASTASIS_challenge_start (ci, + psp, + &start_feedback_cb, + sctx); if (GNUNET_OK != ret) { - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "Failed to begin answering challenge"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "Failed to begin answering challenge"); return; } return; /* await answer feedback */ } - ANASTASIS_redux_fail_ (sctx->cb, - sctx->cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, - "'uuid' not in list of challenges"); - sctx_free (sctx); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + "'uuid' not in list of challenges"); } @@ -1967,23 +1494,24 @@ select_challenge_cb (void *cls, * @return handle to cancel challenge selection step */ static struct ANASTASIS_ReduxAction * -select_challenge (json_t *state, +select_challenge (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - struct SelectChallengeContext *sctx - = GNUNET_new (struct SelectChallengeContext); - json_t *rd; + struct ANASTASIS_ReduxRecovery *rr = &rs->details.recovery; + struct SelectChallengeContext *sctx; + struct ANASTASIS_CRYPTO_TruthUUIDP uuid; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("uuid", - &sctx->uuid), + &uuid), GNUNET_JSON_spec_end () }; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -1994,50 +1522,40 @@ select_challenge (json_t *state, spec, NULL, NULL)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'uuid' missing"); return NULL; } - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) + if (NULL == rr->r) { GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "select_challenge"); return NULL; } + sctx = GNUNET_new (struct SelectChallengeContext); + sctx->uuid = uuid; sctx->cb = cb; sctx->cb_cls = cb_cls; - sctx->state = json_incref (state); + sctx->rs = rs; sctx->args = json_incref ((json_t*) arguments); - sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_, - rd); - if ( (NULL != sctx->r) && - (GNUNET_OK != - ANASTASIS_recovery_resume (sctx->r, - &select_challenge_cb, - sctx, - &core_secret_cb, - sctx)) ) - { - ANASTASIS_recovery_abort (sctx->r); - sctx->r = NULL; - } - if (NULL == sctx->r) + if (GNUNET_OK != + ANASTASIS_recovery_resume (rr->r, + &select_challenge_cb, + sctx, + &core_secret_cb, + sctx)) { - json_decref (sctx->state); - json_decref (sctx->args); - GNUNET_free (sctx); GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' invalid"); + sctx_fail (sctx, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' invalid"); return NULL; } sctx->ra.cleanup = &sctx_free; @@ -2057,20 +1575,19 @@ select_challenge (json_t *state, * @return NULL (synchronous operation) */ static struct ANASTASIS_ReduxAction * -back_challenge_solving (json_t *state, +back_challenge_solving (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { (void) arguments; - GNUNET_assert (0 == - json_object_del (state, - "selected_challenge_uuid")); - set_state (state, + rs->details.recovery.have_selected_challenge = false; + set_state (rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -2107,9 +1624,9 @@ struct PolicyDownloadEntry void *cb_cls; /** - * State we are using. + * State we are using; we own it. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; }; @@ -2130,7 +1647,7 @@ free_pd (void *cls) pd->recovery = NULL; } GNUNET_free (pd->backend_url); - json_decref (pd->state); + ANASTASIS_REDUX_state_free_ (pd->rs); GNUNET_free (pd); } @@ -2152,22 +1669,16 @@ return_no_policy (struct PolicyDownloadEntry *pd, const char *detail = (offline) ? "could not contact provider (offline)" : "provider does not know this policy"; - json_t *estate; + ANASTASIS_ActionCallback cb = pd->cb; + void *cb_cls = pd->cb_cls; GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Provider offline!\n"); - estate = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("detail", - detail)), - GNUNET_JSON_pack_uint64 ("code", - ec), - GNUNET_JSON_pack_string ("hint", - TALER_ErrorCode_get_hint (ec))); - pd->cb (pd->cb_cls, - ec, - estate); free_pd (pd); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + detail); } @@ -2188,111 +1699,75 @@ policy_lookup_cb (void *cls, const struct ANASTASIS_RecoveryInformation *ri) { struct PolicyDownloadEntry *pd = cls; - json_t *policies; - json_t *challenges; - json_t *recovery_information; + struct ANASTASIS_ReduxRecovery *rr = &pd->rs->details.recovery; + struct ANASTASIS_ReduxRecoveryInfo *nri; + struct ANASTASIS_ReduxState *rs; + ANASTASIS_ActionCallback cb; + void *cb_cls; if (NULL == ri) { /* Woopsie, failed hard. */ - ANASTASIS_recovery_abort (pd->recovery); - GNUNET_free (pd->backend_url); - GNUNET_free (pd); return_no_policy (pd, false); return; } - policies = json_array (); - GNUNET_assert (NULL != policies); + nri = GNUNET_new (struct ANASTASIS_ReduxRecoveryInfo); + nri->challenges_len = ri->cs_len; + nri->challenges = GNUNET_new_array (nri->challenges_len, + struct ANASTASIS_ReduxChallengeInfo); + for (unsigned int i = 0; i<ri->cs_len; i++) + { + struct ANASTASIS_ReduxChallengeInfo *ci = &nri->challenges[i]; + const struct ANASTASIS_ChallengeDetails *cd; + + cd = ANASTASIS_challenge_get_details (ri->cs[i]); + ci->uuid = cd->uuid; + ci->type = GNUNET_strdup (cd->type); + ci->instructions = GNUNET_strdup (cd->instructions); + } /* end for all challenges */ + nri->policies_len = ri->dps_len; + nri->policies = GNUNET_new_array (nri->policies_len, + struct ANASTASIS_ReduxRecoveryPolicy); for (unsigned int i = 0; i<ri->dps_len; i++) { - struct ANASTASIS_DecryptionPolicy *dps = ri->dps[i]; - json_t *pchallenges; + const struct ANASTASIS_DecryptionPolicy *dps = ri->dps[i]; + struct ANASTASIS_ReduxRecoveryPolicy *p = &nri->policies[i]; - pchallenges = json_array (); - GNUNET_assert (NULL != pchallenges); + p->uuids_len = dps->challenges_length; + p->uuids = GNUNET_new_array (p->uuids_len, + struct ANASTASIS_CRYPTO_TruthUUIDP); for (unsigned int j = 0; j<dps->challenges_length; j++) { - struct ANASTASIS_Challenge *c = dps->challenges[j]; const struct ANASTASIS_ChallengeDetails *cd; - json_t *cj; - - cd = ANASTASIS_challenge_get_details (c); - cj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("uuid", - &cd->uuid)); - GNUNET_assert (0 == - json_array_append_new (pchallenges, - cj)); + cd = ANASTASIS_challenge_get_details (dps->challenges[j]); + p->uuids[j] = cd->uuid; } - GNUNET_assert (0 == - json_array_append_new (policies, - pchallenges)); } /* end for all policies */ - challenges = json_array (); - GNUNET_assert (NULL != challenges); - for (unsigned int i = 0; i<ri->cs_len; i++) - { - struct ANASTASIS_Challenge *c = ri->cs[i]; - const struct ANASTASIS_ChallengeDetails *cd; - json_t *cj; - - cd = ANASTASIS_challenge_get_details (c); - cj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("uuid", - &cd->uuid), - GNUNET_JSON_pack_string ("type", - cd->type), - GNUNET_JSON_pack_string ("uuid-display", - ANASTASIS_CRYPTO_uuid2s (&cd->uuid)), - GNUNET_JSON_pack_string ("instructions", - cd->instructions)); - GNUNET_assert (0 == - json_array_append_new (challenges, - cj)); - } /* end for all challenges */ - recovery_information = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("challenges", - challenges), - GNUNET_JSON_pack_array_steal ("policies", - policies), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("secret_name", - ri->secret_name)), - GNUNET_JSON_pack_string ("provider_url", - pd->backend_url), - GNUNET_JSON_pack_uint64 ("version", - ri->version)); - GNUNET_assert (0 == - json_object_set_new (pd->state, - "recovery_information", - recovery_information)); - { - json_t *rd; - - rd = ANASTASIS_recovery_serialize (pd->recovery); - if (NULL == rd) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (pd->cb, - pd->cb_cls, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unable to serialize recovery state"); - free_pd (pd); - return; - } - GNUNET_assert (0 == - json_object_set_new (pd->state, - "recovery_document", - rd)); - } - set_state (pd->state, + if (NULL != ri->secret_name) + nri->secret_name = GNUNET_strdup (ri->secret_name); + ANASTASIS_REDUX_provider_url_set_ (&nri->provider_url, + pd->backend_url); + nri->version = ri->version; + ANASTASIS_REDUX_recovery_info_free_ (rr->ri); + rr->ri = nri; + /* The state takes over the operation: it is what gets serialized as + `recovery_document`, and it is aborted when the state is freed. */ + GNUNET_assert (NULL == rr->r); + rr->r = pd->recovery; + pd->recovery = NULL; + set_state (pd->rs, ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING); - pd->cb (pd->cb_cls, - TALER_EC_NONE, - pd->state); + rs = pd->rs; + cb = pd->cb; + cb_cls = pd->cb_cls; + pd->rs = NULL; free_pd (pd); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); } @@ -2314,14 +1789,21 @@ core_early_secret_cb (void *cls, size_t secret_size) { struct PolicyDownloadEntry *pd = cls; + ANASTASIS_ActionCallback cb = pd->cb; + void *cb_cls = pd->cb_cls; + const char *msg; + enum TALER_ErrorCode ec; pd->recovery = NULL; GNUNET_assert (NULL == secret); GNUNET_assert (ANASTASIS_RS_SUCCESS != rc); - fail_by_error (pd->cb, - pd->cb_cls, - rc); + ec = error_by_status (rc, + &msg); free_pd (pd); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + msg); } @@ -2336,7 +1818,7 @@ core_early_secret_cb (void *cls, * @return NULL */ static struct ANASTASIS_ReduxAction * -done_secret_selecting (json_t *state, +done_secret_selecting (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) @@ -2350,15 +1832,6 @@ done_secret_selecting (json_t *state, &pa), GNUNET_JSON_spec_end () }; - struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_fixed_auto ("provider_salt", - &provider_salt), - GNUNET_JSON_spec_end () - }; - json_t *p_cfg; - json_t *id_data; - const json_t *providers; if (GNUNET_OK != GNUNET_JSON_parse (arguments, @@ -2369,19 +1842,18 @@ done_secret_selecting (json_t *state, json_dumpf (arguments, stderr, JSON_INDENT (2)); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, NULL); return NULL; } - providers = json_object_get (state, - "authentication_providers"); - if ( (NULL == providers) || - (! json_is_object (providers)) ) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' missing"); @@ -2396,6 +1868,7 @@ done_secret_selecting (json_t *state, json_array_foreach (pa, poff, pe) { + const struct ANASTASIS_ReduxProvider *p; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_uint64 ("version", &version), @@ -2413,37 +1886,25 @@ done_secret_selecting (json_t *state, json_dumpf (pe, stderr, JSON_INDENT (2)); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, NULL); return NULL; } - p_cfg = json_object_get (providers, - provider_url); - if (MHD_HTTP_OK != - json_integer_value (json_object_get (p_cfg, - "http_status"))) + p = ANASTASIS_REDUX_provider_find_ (&rs->common, + provider_url); + if ( (NULL == p) || + (! p->have_config) || + (MHD_HTTP_OK != p->config.http_status) ) continue; - if (GNUNET_OK != - GNUNET_JSON_parse (p_cfg, - pspec, - NULL, NULL)) - { - GNUNET_break (0); /* should be impossible for well-formed state */ - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "Salt unknown for selected provider"); - return NULL; - } - id_data = json_object_get (state, - "identity_attributes"); - if (NULL == id_data) + if (NULL == rs->common.identity_attributes) { GNUNET_break (0); /* should be impossible for well-formed state */ - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'identity_attributes' missing"); @@ -2455,27 +1916,26 @@ done_secret_selecting (json_t *state, pd->cb = cb; pd->cb_cls = cb_cls; - pd->state = json_incref (state); + pd->rs = rs; pd->backend_url = GNUNET_strdup (provider_url); - pd->recovery = ANASTASIS_recovery_begin (ANASTASIS_REDUX_ctx_, - id_data, - version, - pd->backend_url, - &provider_salt, - &policy_lookup_cb, - pd, - &core_early_secret_cb, - pd); + pd->recovery = ANASTASIS_recovery_begin ( + ANASTASIS_REDUX_ctx_, + rs->common.identity_attributes, + version, + pd->backend_url, + &p->config.provider_salt, + &policy_lookup_cb, + pd, + &core_early_secret_cb, + pd); if (NULL == pd->recovery) { GNUNET_break (0); + free_pd (pd); ANASTASIS_redux_fail_ (cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR, NULL); - GNUNET_free (pd->backend_url); - json_decref (pd->state); - GNUNET_free (pd); return NULL; } pd->ra.cleanup = &free_pd; @@ -2486,7 +1946,8 @@ done_secret_selecting (json_t *state, } /* no provider worked */ - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "selected provider is not online"); @@ -2504,7 +1965,7 @@ done_secret_selecting (json_t *state, * @return handle to cancel challenge selection step */ static struct ANASTASIS_ReduxAction * -add_provider (json_t *state, +add_provider (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) @@ -2522,36 +1983,37 @@ add_provider (json_t *state, NULL, NULL)) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, NULL); return NULL; } - return ANASTASIS_REDUX_add_provider_to_state_ (provider_url, - state, - cb, - cb_cls); + return ANASTASIS_REDUX_provider_add_ (provider_url, + rs, + cb, + cb_cls); } /** * Signature of callback function that implements a state transition. * - * @param state current state + * @param[in] rs current state * @param arguments arguments for the state transition * @param cb function to call when done * @param cb_cls closure for @a cb */ typedef struct ANASTASIS_ReduxAction * -(*DispatchHandler)(json_t *state, +(*DispatchHandler)(struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); struct ANASTASIS_ReduxAction * -ANASTASIS_recovery_action_ (json_t *state, +ANASTASIS_recovery_action_ (struct ANASTASIS_ReduxState *rs, const char *action, const json_t *arguments, ANASTASIS_ActionCallback cb, @@ -2625,33 +2087,23 @@ ANASTASIS_recovery_action_ (json_t *state, }, { ANASTASIS_RECOVERY_STATE_INVALID, NULL, NULL } }; - const char *s = json_string_value (json_object_get (state, - "recovery_state")); - enum ANASTASIS_RecoveryState rs; + enum ANASTASIS_RecoveryState state = rs->details.recovery.state; - GNUNET_assert (NULL != s); - rs = ANASTASIS_recovery_state_from_string_ (s); - if (ANASTASIS_RECOVERY_STATE_INVALID == rs) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_state' field invalid"); - return NULL; - } + GNUNET_assert (ANASTASIS_RT_RECOVERY == rs->type); for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) { - if ( (rs == dispatchers[i].recovery_state) && + if ( (state == dispatchers[i].recovery_state) && (0 == strcmp (action, dispatchers[i].recovery_action)) ) { - return dispatchers[i].fun (state, + return dispatchers[i].fun (rs, arguments, cb, cb_cls); } } - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, action); @@ -2660,21 +2112,18 @@ ANASTASIS_recovery_action_ (json_t *state, struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state, +ANASTASIS_REDUX_recovery_challenge_begin_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - const json_t *providers; json_t *attributes; - providers = json_object_get (state, - "authentication_providers"); - if ( (NULL == providers) || - (! json_is_object (providers)) ) + if (! rs->common.have_providers) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'authentication_providers' missing"); @@ -2686,20 +2135,20 @@ ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state, (! json_is_object (attributes)) ) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'identity_attributes' missing"); return NULL; } - GNUNET_assert (0 == - json_object_set (state, - "identity_attributes", - attributes)); - set_state (state, + json_decref (rs->common.identity_attributes); + rs->common.identity_attributes = json_incref (attributes); + set_state (rs, ANASTASIS_RECOVERY_STATE_SECRET_SELECTING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c @@ -26,6 +26,7 @@ #include "anastasis_error_codes.h" #include <taler/taler_json_lib.h> #include "anastasis_api_redux.h" +#include "anastasis_api_redux_state.h" #include <dlfcn.h> @@ -77,14 +78,16 @@ struct ConfigReduxWaiting struct ConfigRequest *cr; /** - * State we are processing. + * State we are processing. Borrowed, not owned: the operation that + * started us keeps the state alive until it has cancelled us. See + * #ANASTASIS_REDUX_add_provider_to_state_. */ - json_t *state; + struct ANASTASIS_ReduxState *rs; /** - * Function to call with updated @e state. + * Function to call with updated @e rs. */ - ANASTASIS_ActionCallback cb; + ANASTASIS_REDUX_StateCallback cb; /** * Closure for @e cb. @@ -272,20 +275,36 @@ ANASTASIS_REDUX_probe_external_reducer (void) /** - * Extract the mode of a state from json + * Extract the generic part of the step @a rs is at. + * + * The generic states are the common prefix of the backup and the + * recovery state enumerations (see #ANASTASIS_GENERIC_STATES), so a + * value that is in range for #ANASTASIS_GenericState means the same + * thing in either variant. * - * @param state the state to operate on - * @return "backup_state" or "recovery_state" + * @param rs the state to inspect + * @return #ANASTASIS_GENERIC_STATE_INVALID if @a rs is past the generic + * steps, or is an `error` state */ -static const char * -get_state_mode (const json_t *state) +static enum ANASTASIS_GenericState +get_generic_state (const struct ANASTASIS_ReduxState *rs) { - if (json_object_get (state, "backup_state")) - return "backup_state"; - if (json_object_get (state, "recovery_state")) - return "recovery_state"; - GNUNET_assert (0); - return NULL; + unsigned int s; + + switch (rs->type) + { + case ANASTASIS_RT_BACKUP: + s = (unsigned int) rs->details.backup.state; + break; + case ANASTASIS_RT_RECOVERY: + s = (unsigned int) rs->details.recovery.state; + break; + case ANASTASIS_RT_ERROR: + return ANASTASIS_GENERIC_STATE_INVALID; + } + if (s >= sizeof (generic_strings) / sizeof(*generic_strings)) + return ANASTASIS_GENERIC_STATE_INVALID; + return (enum ANASTASIS_GenericState) s; } @@ -341,24 +360,27 @@ ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb, /** - * Transition the @a state to @a gs. + * Transition @a rs to the generic state @a gs. * - * @param[in,out] state to transition + * @param[in,out] rs state to transition * @param gs state to transition to */ static void -redux_transition (json_t *state, +redux_transition (struct ANASTASIS_ReduxState *rs, enum ANASTASIS_GenericState gs) { - const char *s_mode = get_state_mode (state); - - GNUNET_assert (0 == - json_object_set_new ( - state, - s_mode, - json_string ( - ANASTASIS_generic_state_to_string_ (gs)))); - + switch (rs->type) + { + case ANASTASIS_RT_BACKUP: + rs->details.backup.state = (enum ANASTASIS_BackupState) gs; + return; + case ANASTASIS_RT_RECOVERY: + rs->details.recovery.state = (enum ANASTASIS_RecoveryState) gs; + return; + case ANASTASIS_RT_ERROR: + break; + } + GNUNET_assert (0); } @@ -480,7 +502,7 @@ abort_provider_config_cb (void *cls) GNUNET_CONTAINER_DLL_remove (cr->w_head, cr->w_tail, w); - json_decref (w->state); + /* w->rs is borrowed; freeing it is the caller's business. */ GNUNET_free (w); } @@ -498,73 +520,47 @@ notify_waiting (struct ConfigRequest *cr) while (NULL != (w = cr->w_head)) { - json_t *apl; - json_t *prov; + struct ANASTASIS_ReduxProvider *p; - if (NULL == (apl = json_object_get (w->state, - "authentication_providers"))) - { - GNUNET_assert (0 == - json_object_set_new (w->state, - "authentication_providers", - apl = json_object ())); - } + p = ANASTASIS_REDUX_provider_get_ (&w->rs->common, + cr->url); + if (p->have_config) + ANASTASIS_REDUX_provider_config_clear_ (&p->config); + p->have_config = false; + memset (&p->error, + 0, + sizeof (p->error)); 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", - cr->http_status)); + p->status = ANASTASIS_RPS_ERROR; + p->error.ec = cr->ec; + p->error.http_status = cr->http_status; } else { - json_t *methods_list; + struct ANASTASIS_ReduxProviderConfig *cfg = &p->config; - methods_list = json_array (); - GNUNET_assert (NULL != methods_list); + p->status = ANASTASIS_RPS_OK; + p->have_config = true; + cfg->methods_len = cr->methods_length; + cfg->methods = GNUNET_new_array (cfg->methods_len, + struct ANASTASIS_ReduxMethodSpec); for (unsigned int i = 0; i<cr->methods_length; i++) { - struct AuthorizationMethodConfig *method = &cr->methods[i]; - json_t *mj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("type", - method->type), - TALER_JSON_pack_amount ("usage_fee", - &method->usage_fee)); - - GNUNET_assert (0 == - json_array_append_new (methods_list, - mj)); + cfg->methods[i].type = GNUNET_strdup (cr->methods[i].type); + cfg->methods[i].usage_fee = cr->methods[i].usage_fee; } - prov = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("status", - "ok"), - GNUNET_JSON_pack_array_steal ("methods", - methods_list), - TALER_JSON_pack_amount ("annual_fee", - &cr->annual_fee), - TALER_JSON_pack_amount ("truth_upload_fee", - &cr->truth_upload_fee), - TALER_JSON_pack_amount ("liability_limit", - &cr->liability_limit), - 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 ("provider_salt", - &cr->provider_salt), - GNUNET_JSON_pack_uint64 ("http_status", - cr->http_status)); + cfg->annual_fee = cr->annual_fee; + cfg->truth_upload_fee = cr->truth_upload_fee; + cfg->liability_limit = cr->liability_limit; + cfg->provider_salt = cr->provider_salt; + cfg->business_name = GNUNET_strdup (cr->business_name); + cfg->storage_limit_in_megabytes = cr->storage_limit_in_megabytes; + cfg->http_status = cr->http_status; } - GNUNET_assert (0 == - json_object_set_new (apl, - cr->url, - prov)); w->cb (w->cb_cls, cr->ec, - w->state); + w->rs); abort_provider_config_cb (w); } } @@ -792,12 +788,12 @@ check_config (struct GNUNET_TIME_Relative timeout, * Begin asynchronous check for provider configurations. * * @param cc country code that was selected - * @param[in,out] state to set provider list for + * @param[in,out] common state to set the provider list for * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode begin_provider_config_check (const char *cc, - json_t *state) + struct ANASTASIS_ReduxCommon *common) { if (NULL == provider_list) { @@ -839,9 +835,11 @@ begin_provider_config_check (const char *cc, json_t *provider; const json_t *provider_arr = json_object_get (provider_list, "anastasis_provider"); - json_t *pl; - pl = json_object (); + /* The applicable providers depend on the country, so start over + rather than merge into whatever an earlier selection left. */ + ANASTASIS_REDUX_providers_clear_ (common); + common->have_providers = true; json_array_foreach (provider_arr, index, provider) { const char *url; @@ -855,7 +853,6 @@ begin_provider_config_check (const char *cc, NULL), GNUNET_JSON_spec_end () }; - json_t *prov; if (GNUNET_OK != GNUNET_JSON_parse (provider, @@ -863,7 +860,7 @@ begin_provider_config_check (const char *cc, NULL, NULL)) { GNUNET_break (0); - json_decref (pl); + ANASTASIS_REDUX_providers_clear_ (common); return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED; } if ( (NULL != restricted) && @@ -885,21 +882,11 @@ begin_provider_config_check (const char *cc, /* 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, - prov)); + (void) ANASTASIS_REDUX_provider_get_ (common, + url); check_config (CONFIG_GENERIC_TIMEOUT, url); } - GNUNET_assert (0 == - json_object_set_new (state, - "authentication_providers", - pl)); } return TALER_EC_NONE; } @@ -1015,14 +1002,14 @@ redux_id_attr_init (const char *country_code) * DispatchHandler/Callback function which is called for a * "select_continent" action. * - * @param state state to operate on + * @param[in] rs state to operate on * @param arguments arguments to use for operation on state * @param cb callback to call during/after operation * @param cb_cls callback closure * @return NULL */ static struct ANASTASIS_ReduxAction * -select_continent (json_t *state, +select_continent (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) @@ -1031,11 +1018,13 @@ select_continent (json_t *state, const json_t *root = json_object_get (rc, "countries"); const json_t *continent; + const char *cname; json_t *countries; if (NULL == root) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, "'countries' missing"); @@ -1043,7 +1032,8 @@ select_continent (json_t *state, } if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -1053,54 +1043,71 @@ select_continent (json_t *state, "continent"); if (NULL == continent) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'continent' missing"); return NULL; } + cname = json_string_value (continent); countries = json_array (); GNUNET_assert (NULL != countries); { size_t index; const json_t *country; - bool found = false; json_array_foreach (root, index, country) { - json_t *temp_continent = json_object_get (country, - "continent"); - if (1 == json_equal (continent, - temp_continent)) - { + const char *cc = json_string_value (json_object_get (country, + "continent")); + + if ( (NULL != cname) && + (NULL != cc) && + (0 == strcmp (cname, + cc)) ) GNUNET_assert (0 == json_array_append (countries, (json_t *) country)); - found = true; - } } - if (! found) + if (0 == json_array_size (countries)) { - ANASTASIS_redux_fail_ (cb, + json_decref (countries); + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'continent' unknown"); return NULL; } } - redux_transition (state, + { + const char *detail; + + if (GNUNET_OK != + ANASTASIS_REDUX_countries_set_ (&rs->common, + countries, + &detail)) + { + GNUNET_break (0); + json_decref (countries); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, + detail); + return NULL; + } + } + json_decref (countries); + redux_transition (rs, ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING); - GNUNET_assert (0 == - json_object_set (state, - "selected_continent", - (json_t *) continent)); - GNUNET_assert (0 == - json_object_set_new (state, - "countries", - countries)); - cb (cb_cls, - TALER_EC_NONE, - state); + GNUNET_free (rs->common.selected_continent); + rs->common.selected_continent = GNUNET_strdup (cname); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -1116,54 +1123,51 @@ select_continent (json_t *state, * @return #ANASTASIS_ReduxAction */ static struct ANASTASIS_ReduxAction * -select_country (json_t *state, +select_country (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { const json_t *required_attrs; - const json_t *country_code; + const char *country_code; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); return NULL; } - country_code = json_object_get (arguments, - "country_code"); - if (NULL == country_code) + if (NULL == json_object_get (arguments, + "country_code")) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'country_code' missing"); return NULL; } + country_code = json_string_value (json_object_get (arguments, + "country_code")); { - json_t *countries = json_object_get (state, - "countries"); - size_t index; - json_t *country; bool found = false; - json_array_foreach (countries, index, country) - { - json_t *cc = json_object_get (country, - "code"); - if (1 == json_equal (country_code, - cc)) + for (unsigned int i = 0; i < rs->common.countries_len; i++) + if ( (NULL != country_code) && + (0 == strcmp (rs->common.countries[i].code, + country_code)) ) { found = true; break; } - } if (! found) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "specified country not on selected continent"); @@ -1175,12 +1179,13 @@ select_country (json_t *state, { enum TALER_ErrorCode ec; - ec = begin_provider_config_check (json_string_value (country_code), - state); + ec = begin_provider_config_check (country_code, + &rs->common); if (TALER_EC_NONE != ec) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, ec, NULL); @@ -1191,14 +1196,15 @@ select_country (json_t *state, { const json_t *ria; - ria = redux_id_attr_init (json_string_value (country_code)); + ria = redux_id_attr_init (country_code); if (NULL == ria) { GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_RESOURCE_MISSING, - json_string_value (country_code)); + country_code); return NULL; } required_attrs = json_object_get (ria, @@ -1206,25 +1212,38 @@ select_country (json_t *state, } if (NULL == required_attrs) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, "'required_attributes' missing"); return NULL; } - redux_transition (state, + { + const char *detail; + + if (GNUNET_OK != + ANASTASIS_REDUX_required_attributes_set_ (&rs->common, + required_attrs, + &detail)) + { + GNUNET_break (0); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, + detail); + return NULL; + } + } + redux_transition (rs, ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING); - GNUNET_assert (0 == - json_object_set (state, - "selected_country", - (json_t *) country_code)); - GNUNET_assert (0 == - json_object_set (state, - "required_attributes", - (json_t *) required_attrs)); - cb (cb_cls, - TALER_EC_NONE, - state); + GNUNET_free (rs->common.selected_country); + rs->common.selected_country = GNUNET_strdup (country_code); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -1240,24 +1259,25 @@ select_country (json_t *state, * @return NULL */ static struct ANASTASIS_ReduxAction * -unselect_continent (json_t *state, +unselect_continent (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - redux_transition (state, + redux_transition (rs, ANASTASIS_GENERIC_STATE_CONTINENT_SELECTING); - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } struct ANASTASIS_ReduxAction * ANASTASIS_REDUX_add_provider_to_state_ (const char *url, - json_t *state, - ANASTASIS_ActionCallback cb, + struct ANASTASIS_ReduxState *rs, + ANASTASIS_REDUX_StateCallback cb, void *cb_cls) { struct ConfigRequest *cr; @@ -1267,7 +1287,7 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, url); w = GNUNET_new (struct ConfigReduxWaiting); w->cr = cr; - w->state = json_incref (state); + w->rs = rs; w->cb = cb; w->cb_cls = cb_cls; w->ra.cleanup = &abort_provider_config_cb; @@ -1287,6 +1307,107 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, /** + * Context for #ANASTASIS_REDUX_provider_add_(). + */ +struct ProviderAddContext +{ + /** + * Handle we returned for cancellation of the operation. + */ + struct ANASTASIS_ReduxAction ra; + + /** + * The /config request we are waiting for, NULL once it completed. + */ + struct ANASTASIS_ReduxAction *inner; + + /** + * State we own until we hand it to @e cb. + */ + struct ANASTASIS_ReduxState *rs; + + /** + * Function to call with the new state. + */ + ANASTASIS_ActionCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; +}; + + +/** + * Free @a cls. + * + * @param[in] cls a `struct ProviderAddContext *` + */ +static void +provider_add_cleanup (void *cls) +{ + struct ProviderAddContext *pac = cls; + + if (NULL != pac->inner) + pac->inner->cleanup (pac->inner->cleanup_cls); + ANASTASIS_REDUX_state_free_ (pac->rs); + GNUNET_free (pac); +} + + +/** + * The /config request completed; return the updated state. + * + * @param cls a `struct ProviderAddContext *` + * @param ec status of the request + * @param[in] rs the updated state + */ +static void +provider_add_done (void *cls, + enum TALER_ErrorCode ec, + struct ANASTASIS_ReduxState *rs) +{ + struct ProviderAddContext *pac = cls; + ANASTASIS_ActionCallback cb = pac->cb; + void *cb_cls = pac->cb_cls; + + GNUNET_assert (rs == pac->rs); + /* our waiter unlinks and frees itself right after this call */ + pac->inner = NULL; + pac->rs = NULL; + provider_add_cleanup (pac); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + ec); +} + + +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_provider_add_ (const char *url, + struct ANASTASIS_ReduxState *rs, + ANASTASIS_ActionCallback cb, + void *cb_cls) +{ + struct ProviderAddContext *pac; + + pac = GNUNET_new (struct ProviderAddContext); + pac->rs = rs; + pac->cb = cb; + pac->cb_cls = cb_cls; + pac->inner = ANASTASIS_REDUX_add_provider_to_state_ (url, + rs, + &provider_add_done, + pac); + /* the /config request never completes synchronously */ + GNUNET_assert (NULL != pac->inner); + pac->ra.cleanup = &provider_add_cleanup; + pac->ra.cleanup_cls = pac; + return &pac->ra; +} + + +/** * DispatchHandler/Callback function which is called for a * "enter_user_attributes" action. * Returns an #ANASTASIS_ReduxAction if operation is async. @@ -1298,17 +1419,17 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, * @return NULL */ static struct ANASTASIS_ReduxAction * -enter_user_attributes (json_t *state, +enter_user_attributes (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { const json_t *attributes; - const json_t *required_attributes; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "arguments missing"); @@ -1318,155 +1439,106 @@ enter_user_attributes (json_t *state, "identity_attributes"); if (NULL == attributes) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, "'identity_attributes' missing"); return NULL; } - GNUNET_assert (0 == - json_object_set (state, - "identity_attributes", - (json_t *) attributes)); + json_decref (rs->common.identity_attributes); + rs->common.identity_attributes = json_incref ((json_t *) attributes); - /* Verify required attributes are present and well-formed */ - required_attributes = json_object_get (state, - "required_attributes"); - if ( (NULL == required_attributes) || - (! json_is_array (required_attributes)) ) + if (! rs->common.have_required_attributes) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, "'required_attributes' must be an array"); return NULL; } + /* Verify required attributes are present and well-formed */ + for (unsigned int i = 0; i < rs->common.required_attributes_len; i++) { - size_t index; - json_t *required_attribute; + const struct ANASTASIS_ReduxAttributeSpec *a + = &rs->common.required_attributes[i]; + const char *attribute_value; - json_array_foreach (required_attributes, index, required_attribute) + attribute_value = json_string_value (json_object_get (attributes, + a->name)); + if (NULL == attribute_value) { - const char *name; - const char *attribute_value; - const char *regexp = NULL; - const char *reglog = NULL; - 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), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("validation-logic", - &reglog), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("optional", - &optional), - NULL), - GNUNET_JSON_spec_end () - }; + if (a->optional) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Request is missing required attribute `%s'\n", + a->name); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_GENERIC_PARAMETER_MISSING, + a->name); + return NULL; + } + if ( (NULL != a->validation_regex) && + (! validate_regex (attribute_value, + a->validation_regex)) ) + { + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_REGEX_FAILED, + a->name); + return NULL; + } + if (NULL != a->validation_logic) + { + bool (*regfun)(const char *); - if (GNUNET_OK != - GNUNET_JSON_parse (required_attribute, - spec, - NULL, NULL)) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'required_attributes' lacks required fields"); - return NULL; - } - attribute_value = json_string_value (json_object_get (attributes, - name)); - if (NULL == attribute_value) + regfun = dlsym (RTLD_DEFAULT, + a->validation_logic); + if (NULL == regfun) { - if (optional) - continue; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Request is missing required attribute `%s'\n", - name); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_GENERIC_PARAMETER_MISSING, - name); - return NULL; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Custom validation function `%s' is not available: %s\n", + a->validation_logic, + dlerror ()); } - if ( (NULL != regexp) && - (! validate_regex (attribute_value, - regexp)) ) + else if (! regfun (attribute_value)) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + cb, cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_REGEX_FAILED, - name); + TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED, + a->name); return NULL; } - - if (NULL != reglog) - { - bool (*regfun)(const char *); - - regfun = dlsym (RTLD_DEFAULT, - reglog); - if (NULL == regfun) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Custom validation function `%s' is not available: %s\n", - reglog, - dlerror ()); - } - else if (! regfun (attribute_value)) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED, - name); - return NULL; - } - } - } /* end for all attributes loop */ - } /* end for all attributes scope */ + } + } /* end for all attributes loop */ /* Transition based on mode */ - { - const char *s_mode = get_state_mode (state); - - if (0 == strcmp (s_mode, - "backup_state")) - { - GNUNET_assert (0 == - json_object_set_new ( - state, - "backup_state", - json_string ( - ANASTASIS_backup_state_to_string_ ( - ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING)))); - return ANASTASIS_REDUX_backup_begin_ (state, - arguments, - cb, - cb_cls); - } - else - { - GNUNET_assert (0 == - json_object_set_new ( - state, - "recovery_state", - json_string ( - ANASTASIS_recovery_state_to_string_ ( - ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING)))); - return ANASTASIS_REDUX_recovery_challenge_begin_ (state, - arguments, - cb, - cb_cls); - } + switch (rs->type) + { + case ANASTASIS_RT_BACKUP: + rs->details.backup.state + = ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING; + return ANASTASIS_REDUX_backup_begin_ (rs, + arguments, + cb, + cb_cls); + case ANASTASIS_RT_RECOVERY: + rs->details.recovery.state + = ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING; + return ANASTASIS_REDUX_recovery_challenge_begin_ (rs, + arguments, + cb, + cb_cls); + case ANASTASIS_RT_ERROR: + break; } + GNUNET_assert (0); + return NULL; } @@ -1481,60 +1553,59 @@ enter_user_attributes (json_t *state, * @param cb_cls callback closure */ static struct ANASTASIS_ReduxAction * -add_provider (json_t *state, +add_provider (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - if (ANASTASIS_add_provider_ (state, + if (ANASTASIS_add_provider_ (rs, arguments, cb, cb_cls)) return NULL; - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } bool -ANASTASIS_add_provider_ (json_t *state, +ANASTASIS_add_provider_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - json_t *tlist; + const char *url; + json_t *params; if (NULL == arguments) { - ANASTASIS_redux_fail_ (cb, + ANASTASIS_REDUX_fail_ (rs, + 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) + json_object_foreach (((json_t *) arguments), url, params) { - tlist = json_object (); - GNUNET_assert (NULL != tlist); - GNUNET_assert (0 == - json_object_set_new (state, - "authentication_providers", - tlist)); - } - { - json_t *params; - const char *url; + const char *detail; - json_object_foreach (((json_t *) arguments), url, params) + if (GNUNET_OK != + ANASTASIS_REDUX_provider_set_ (&rs->common, + url, + params, + &detail)) { - GNUNET_assert (0 == - json_object_set (tlist, - url, - params)); + GNUNET_break_op (0); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, + detail); + return true; /* cb was invoked */ } } return false; /* cb not invoked */ @@ -1542,50 +1613,30 @@ ANASTASIS_add_provider_ (json_t *state, struct ANASTASIS_ReduxAction * -ANASTASIS_back_generic_decrement_ (json_t *state, +ANASTASIS_back_generic_decrement_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls) { - const char *s_mode = get_state_mode (state); - const char *state_string = json_string_value (json_object_get (state, - s_mode)); - (void) arguments; - GNUNET_assert (NULL != state_string); - if (0 == strcmp ("backup_state", - s_mode)) - { - enum ANASTASIS_BackupState state_index; - - state_index = ANASTASIS_backup_state_from_string_ (state_string); - GNUNET_assert (state_index > 0); - state_index = state_index - 1; - - GNUNET_assert (0 == - json_object_set_new ( - state, - s_mode, - json_string ( - ANASTASIS_backup_state_to_string_ (state_index)))); - } - else + switch (rs->type) { - enum ANASTASIS_RecoveryState state_index; - - state_index = ANASTASIS_recovery_state_from_string_ (state_string); - GNUNET_assert (state_index > 0); - state_index = state_index - 1; - GNUNET_assert (0 == - json_object_set_new ( - state, - s_mode, - json_string ( - ANASTASIS_recovery_state_to_string_ (state_index)))); + case ANASTASIS_RT_BACKUP: + GNUNET_assert (rs->details.backup.state > 0); + rs->details.backup.state--; + break; + case ANASTASIS_RT_RECOVERY: + GNUNET_assert (rs->details.recovery.state > 0); + rs->details.recovery.state--; + break; + case ANASTASIS_RT_ERROR: + GNUNET_assert (0); + break; } - cb (cb_cls, - TALER_EC_NONE, - state); + ANASTASIS_REDUX_return_ (rs, + cb, + cb_cls, + TALER_EC_NONE); return NULL; } @@ -1594,14 +1645,18 @@ ANASTASIS_back_generic_decrement_ (json_t *state, * Callback function which is called by the reducer in dependence of * given state and action. * - * @param state the previous state to operate on + * The handler takes ownership of @a rs and must dispose of it, either by + * handing it to #ANASTASIS_REDUX_return_ / #ANASTASIS_REDUX_fail_ or by + * passing it on to another handler that does. + * + * @param[in] rs the previous state to operate on * @param arguments the arguments needed by operation to operate on state * @param cb Callback function which returns the new state * @param cb_cls closure for @a cb * @return handle to cancel async actions, NULL if @a cb was already called */ typedef struct ANASTASIS_ReduxAction * -(*DispatchHandler)(json_t *state, +(*DispatchHandler)(struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -1924,9 +1979,7 @@ ANASTASIS_redux_action (const json_t *state, }, { ANASTASIS_GENERIC_STATE_INVALID, NULL, NULL } }; - bool recovery_mode = false; - const char *s = json_string_value (json_object_get (state, - "backup_state")); + struct ANASTASIS_ReduxState *rs; enum ANASTASIS_GenericState gs; /* If requested, handle action with external reducer, used for testing. */ @@ -1941,63 +1994,62 @@ ANASTASIS_redux_action (const json_t *state, cb_cls); } - if (NULL == s) + /* This is the one place where a state is read. Everything below + operates on the typed representation, and every path back out goes + through ANASTASIS_REDUX_return_() or ANASTASIS_REDUX_fail_(). */ { - s = json_string_value (json_object_get (state, - "recovery_state")); - if (NULL == s) + enum TALER_ErrorCode ec; + const char *detail; + + rs = ANASTASIS_REDUX_state_parse_ (state, + &ec, + &detail); + if (NULL == rs) { GNUNET_break_op (0); - cb (cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - NULL); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + detail); return NULL; } - recovery_mode = true; } - gs = ANASTASIS_generic_state_from_string_ (s); + if (ANASTASIS_RT_ERROR == rs->type) { - json_t *new_state; - struct ANASTASIS_ReduxAction *ret; - - new_state = json_deep_copy (state); - GNUNET_assert (NULL != new_state); - if (gs != ANASTASIS_GENERIC_STATE_INVALID) - { - for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) - { - if ( (gs == dispatchers[i].redux_state) && - (0 == strcmp (action, - dispatchers[i].redux_action)) ) - { - ret = dispatchers[i].fun (new_state, - arguments, - cb, - cb_cls); - json_decref (new_state); - return ret; - } - } - } - if (recovery_mode) - { - ret = ANASTASIS_recovery_action_ (new_state, - action, - arguments, - cb, - cb_cls); - } - else + /* An error state has no step to advance from. */ + GNUNET_break_op (0); + ANASTASIS_REDUX_fail_ (rs, + cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "reducer_type"); + return NULL; + } + gs = get_generic_state (rs); + if (ANASTASIS_GENERIC_STATE_INVALID != gs) + { + for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) { - ret = ANASTASIS_backup_action_ (new_state, - action, - arguments, - cb, - cb_cls); + if ( (gs == dispatchers[i].redux_state) && + (0 == strcmp (action, + dispatchers[i].redux_action)) ) + return dispatchers[i].fun (rs, + arguments, + cb, + cb_cls); } - json_decref (new_state); - return ret; } + if (ANASTASIS_RT_RECOVERY == rs->type) + return ANASTASIS_recovery_action_ (rs, + action, + arguments, + cb, + cb_cls); + return ANASTASIS_backup_action_ (rs, + action, + arguments, + cb, + cb_cls); } @@ -2118,9 +2170,36 @@ ANASTASIS_REDUX_load_continents_ () } +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_lookup_salt_ ( + const struct ANASTASIS_ReduxCommon *common, + const char *provider_url, + struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt) +{ + const struct ANASTASIS_ReduxProvider *p; + + p = ANASTASIS_REDUX_provider_find_ (common, + provider_url); + if (NULL == p) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (ANASTASIS_RPS_DISABLED == p->status) + return GNUNET_NO; + if (! p->have_config) + return GNUNET_NO; + *provider_salt = p->config.provider_salt; + return GNUNET_OK; +} + + /** * Lookup @a provider_salt of @a provider_url in @a state. * + * The public API still speaks JSON here, so this is the typed lookup + * with a parse in front of it. + * * @param state the state to inspect * @param provider_url provider to look into * @param[out] provider_salt value to extract @@ -2132,52 +2211,25 @@ ANASTASIS_reducer_lookup_salt ( 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 () - }; + struct ANASTASIS_ReduxState *rs; + enum GNUNET_GenericReturnValue ret; - 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; + enum TALER_ErrorCode ec; + const char *detail; + + rs = ANASTASIS_REDUX_state_parse_ (state, + &ec, + &detail); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } } - if (0 == strcmp (status, - "disabled")) - return GNUNET_NO; - if (no_salt) - return GNUNET_NO; - return GNUNET_OK; + ret = ANASTASIS_REDUX_lookup_salt_ (&rs->common, + provider_url, + provider_salt); + ANASTASIS_REDUX_state_free_ (rs); + return ret; } diff --git a/src/reducer/anastasis_api_redux.h b/src/reducer/anastasis_api_redux.h @@ -23,12 +23,44 @@ #ifndef ANASTASIS_API_REDUX_H #define ANASTASIS_API_REDUX_H +/** + * The typed state the reducer operates on internally; see + * `anastasis_api_redux_state.h`, which includes this header. + */ +struct ANASTASIS_ReduxState; + +/** + * Fields the backup and recovery variants of a state share. + */ +struct ANASTASIS_ReduxCommon; + +/** + * Signature of the callback used to hand a typed state back to whoever + * started an asynchronous step inside the reducer. The mirror image of + * #ANASTASIS_ActionCallback, which is what the same result eventually + * becomes once it leaves the reducer. + * + * Unlike the callback APIs in `anastasis.h` this one does not declare an + * overridable closure type: its closure is almost always the opaque + * `cb_cls` of the public action the reducer is in the middle of + * executing, which by construction has no type to name here. + * + * @param cls closure + * @param ec status of the step + * @param[in,out] rs the state that was passed in, updated in place; + * ownership is *not* transferred by this call + */ +typedef void +(*ANASTASIS_REDUX_StateCallback)(void *cls, + enum TALER_ErrorCode ec, + struct ANASTASIS_ReduxState *rs); + #define ANASTASIS_GENERIC_STATES(REDUX_STATE) \ - REDUX_STATE (INVALID) \ - REDUX_STATE (CONTINENT_SELECTING) \ - REDUX_STATE (COUNTRY_SELECTING) \ - REDUX_STATE (USER_ATTRIBUTES_COLLECTING) + REDUX_STATE (INVALID) \ + REDUX_STATE (CONTINENT_SELECTING) \ + REDUX_STATE (COUNTRY_SELECTING) \ + REDUX_STATE (USER_ATTRIBUTES_COLLECTING) #define GENERATE_GENERIC_ENUM(ENUM) ANASTASIS_GENERIC_STATE_ ## ENUM, @@ -40,13 +72,13 @@ enum ANASTASIS_GenericState #undef GENERATE_GENERIC_ENUM #define ANASTASIS_BACKUP_STATES(REDUX_STATE) \ - ANASTASIS_GENERIC_STATES (REDUX_STATE) \ - REDUX_STATE (AUTHENTICATIONS_EDITING) \ - REDUX_STATE (POLICIES_REVIEWING) \ - REDUX_STATE (SECRET_EDITING) \ - REDUX_STATE (TRUTHS_PAYING) \ - REDUX_STATE (POLICIES_PAYING) \ - REDUX_STATE (BACKUP_FINISHED) + ANASTASIS_GENERIC_STATES (REDUX_STATE) \ + REDUX_STATE (AUTHENTICATIONS_EDITING) \ + REDUX_STATE (POLICIES_REVIEWING) \ + REDUX_STATE (SECRET_EDITING) \ + REDUX_STATE (TRUTHS_PAYING) \ + REDUX_STATE (POLICIES_PAYING) \ + REDUX_STATE (BACKUP_FINISHED) #define GENERATE_BACKUP_ENUM(ENUM) ANASTASIS_BACKUP_STATE_ ## ENUM, @@ -58,12 +90,12 @@ enum ANASTASIS_BackupState #undef GENERATE_BACKUP_ENUM #define ANASTASIS_RECOVERY_STATES(REDUX_STATE) \ - ANASTASIS_GENERIC_STATES (REDUX_STATE) \ - REDUX_STATE (SECRET_SELECTING) \ - REDUX_STATE (CHALLENGE_SELECTING) \ - REDUX_STATE (CHALLENGE_PAYING) \ - REDUX_STATE (CHALLENGE_SOLVING) \ - REDUX_STATE (RECOVERY_FINISHED) + ANASTASIS_GENERIC_STATES (REDUX_STATE) \ + REDUX_STATE (SECRET_SELECTING) \ + REDUX_STATE (CHALLENGE_SELECTING) \ + REDUX_STATE (CHALLENGE_PAYING) \ + REDUX_STATE (CHALLENGE_SOLVING) \ + REDUX_STATE (RECOVERY_FINISHED) #define GENERATE_RECOVERY_ENUM(ENUM) ANASTASIS_RECOVERY_STATE_ ## ENUM, @@ -112,14 +144,14 @@ ANASTASIS_REDUX_load_continents_ (void); * providers. Upon success, call @a cb with the updated provider * status data. * - * @param[in] state we are in + * @param[in] rs 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, +ANASTASIS_REDUX_poll_providers_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -130,14 +162,14 @@ ANASTASIS_REDUX_poll_providers_ (json_t *state, * 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[in] rs 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, +ANASTASIS_REDUX_sync_providers_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -218,6 +250,24 @@ ANASTASIS_reducer_lookup_salt (const json_t *state, /** + * Lookup @a salt of @a provider_url in @a common. The typed + * counterpart of #ANASTASIS_reducer_lookup_salt(), which stays around + * for the public discovery API. + * + * @param common the state to inspect + * @param provider_url provider to look into + * @param[out] salt value to extract + * @return #GNUNET_OK on success, + * #GNUNET_NO if the provider is disabled or has no /config, + * #GNUNET_SYSERR if the provider is not in @a common at all + */ +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_lookup_salt_ (const struct ANASTASIS_ReduxCommon *common, + const char *provider_url, + struct ANASTASIS_CRYPTO_ProviderSaltP *salt); + + +/** * Function to return a json error response. * * @param cb callback to give error to @@ -237,14 +287,14 @@ ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb, * "add_provider" action. Adds another Anastasis provider * to the list of available providers for storing information. * - * @param state state to operate on + * @param[in] rs state to operate on * @param arguments arguments with a provider URL to add * @param cb callback to call during/after operation * @param cb_cls callback closure * @return true if @a cb was invoked */ bool -ANASTASIS_add_provider_ (json_t *state, +ANASTASIS_add_provider_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -258,7 +308,8 @@ ANASTASIS_add_provider_ (json_t *state, * generating a success or failure outcome asynchronously. * * @param url the provider's base URL to add - * @param[in,out] state the json state to operate on + * @param[in,out] rs the state to update in place; borrowed, + * the caller must keep it alive until it cancels the operation * @param cb callback to call during/after operation * @param cb_cls callback closure * @return handle to cancel asynchronous operation, NULL if @@ -266,23 +317,41 @@ ANASTASIS_add_provider_ (json_t *state, */ struct ANASTASIS_ReduxAction * ANASTASIS_REDUX_add_provider_to_state_ (const char *url, - json_t *state, - ANASTASIS_ActionCallback cb, + struct ANASTASIS_ReduxState *rs, + ANASTASIS_REDUX_StateCallback cb, void *cb_cls); /** + * Add the provider at @a url to @a rs and return the updated state to + * the application. The whole of the "add_provider" action, for the + * state variants that have nothing else to do afterwards. + * + * @param url the provider's base URL to add + * @param[in] rs state to operate on; we take ownership + * @param cb callback to call with the new state + * @param cb_cls closure for @a cb + * @return handle to cancel the asynchronous operation + */ +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_provider_add_ (const char *url, + struct ANASTASIS_ReduxState *rs, + ANASTASIS_ActionCallback cb, + void *cb_cls); + + +/** * A generic DispatchHandler/Callback function which is called for a * "back" action. * - * @param[in,out] state state to operate on + * @param[in] rs state to operate on * @param arguments arguments to use for operation on state * @param cb callback to call during/after operation * @param cb_cls callback closure for @a cb * @return NULL (no asynchronous action) */ struct ANASTASIS_ReduxAction * -ANASTASIS_back_generic_decrement_ (json_t *state, +ANASTASIS_back_generic_decrement_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -303,14 +372,14 @@ ANASTASIS_redux_countries_init_ (void); * by a callback function. * This function can do network access to talk to anastasis service providers. * - * @param[in,out] state input/output state (to be modified) + * @param[in] rs state to operate on * @param action what action to perform * @param arguments data for the @a action * @param cb function to call with the result * @param cb_cls closure for @a cb */ struct ANASTASIS_ReduxAction * -ANASTASIS_recovery_action_ (json_t *state, +ANASTASIS_recovery_action_ (struct ANASTASIS_ReduxState *rs, const char *action, const json_t *arguments, ANASTASIS_ActionCallback cb, @@ -325,14 +394,14 @@ ANASTASIS_recovery_action_ (json_t *state, * * Returns an #ANASTASIS_ReduxAction. * - * @param state state to operate on + * @param[in] rs state to operate on * @param arguments data for the operation * @param cb callback to call during/after operation * @param cb_cls callback closure for @a cb * @return NULL */ struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state, +ANASTASIS_REDUX_recovery_challenge_begin_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -346,13 +415,13 @@ ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state, * * Returns a `struct ANASTASIS_ReduxAction`. * - * @param state state to operate on + * @param[in] rs state to operate on * @param arguments data for the operation * @param cb callback to call during/after operation * @param cb_cls callback closure */ struct ANASTASIS_ReduxAction * -ANASTASIS_REDUX_backup_begin_ (json_t *state, +ANASTASIS_REDUX_backup_begin_ (struct ANASTASIS_ReduxState *rs, const json_t *arguments, ANASTASIS_ActionCallback cb, void *cb_cls); @@ -363,14 +432,14 @@ ANASTASIS_REDUX_backup_begin_ (json_t *state, * callback function. * This function can do network access to talk to anastasis service providers. * - * @param[in,out] state input/output state (to be modified) + * @param[in] rs state to operate on * @param action what action to perform * @param arguments data for the @a action * @param cb function to call with the result * @param cb_cls closure for @a cb */ struct ANASTASIS_ReduxAction * -ANASTASIS_backup_action_ (json_t *state, +ANASTASIS_backup_action_ (struct ANASTASIS_ReduxState *rs, const char *action, const json_t *arguments, ANASTASIS_ActionCallback cb, diff --git a/src/reducer/anastasis_api_redux_free.c b/src/reducer/anastasis_api_redux_free.c @@ -37,6 +37,22 @@ ANASTASIS_REDUX_provider_url_clear_ (struct ANASTASIS_ProviderUrl *pu) void +ANASTASIS_REDUX_provider_config_clear_ (struct ANASTASIS_ReduxProviderConfig * + cfg + ) +{ + for (unsigned int j = 0; j < cfg->methods_len; j++) + GNUNET_free (cfg->methods[j].type); + GNUNET_free (cfg->methods); + GNUNET_free (cfg->business_name); + GNUNET_free (cfg->currency); + memset (cfg, + 0, + sizeof (*cfg)); +} + + +void ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common) { for (unsigned int i = 0; i < common->providers_len; i++) @@ -45,13 +61,7 @@ ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common) ANASTASIS_REDUX_provider_url_clear_ (&p->url); if (p->have_config) - { - for (unsigned int j = 0; j < p->config.methods_len; j++) - GNUNET_free (p->config.methods[j].type); - GNUNET_free (p->config.methods); - GNUNET_free (p->config.business_name); - GNUNET_free (p->config.currency); - } + ANASTASIS_REDUX_provider_config_clear_ (&p->config); } GNUNET_free (common->providers); common->providers = NULL; @@ -59,13 +69,8 @@ ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common) } -/** - * Free the contents of @a common. - * - * @param[in,out] common state to free - */ -static void -free_common (struct ANASTASIS_ReduxCommon *common) +void +ANASTASIS_REDUX_continents_clear_ (struct ANASTASIS_ReduxCommon *common) { for (unsigned int i = 0; i < common->continents_len; i++) { @@ -75,6 +80,14 @@ free_common (struct ANASTASIS_ReduxCommon *common) json_decref (c->name_i18n); } GNUNET_free (common->continents); + common->continents = NULL; + common->continents_len = 0; +} + + +void +ANASTASIS_REDUX_countries_clear_ (struct ANASTASIS_ReduxCommon *common) +{ for (unsigned int i = 0; i < common->countries_len; i++) { struct ANASTASIS_ReduxCountry *c = &common->countries[i]; @@ -88,6 +101,15 @@ free_common (struct ANASTASIS_ReduxCommon *common) json_decref (c->continent_i18n); } GNUNET_free (common->countries); + common->countries = NULL; + common->countries_len = 0; +} + + +void +ANASTASIS_REDUX_required_attributes_clear_ ( + struct ANASTASIS_ReduxCommon *common) +{ for (unsigned int i = 0; i < common->required_attributes_len; i++) { struct ANASTASIS_ReduxAttributeSpec *a = &common->required_attributes[i]; @@ -104,6 +126,22 @@ free_common (struct ANASTASIS_ReduxCommon *common) json_decref (a->label_i18n); } GNUNET_free (common->required_attributes); + common->required_attributes = NULL; + common->required_attributes_len = 0; +} + + +/** + * Free the contents of @a common. + * + * @param[in,out] common state to free + */ +static void +free_common (struct ANASTASIS_ReduxCommon *common) +{ + ANASTASIS_REDUX_continents_clear_ (common); + ANASTASIS_REDUX_countries_clear_ (common); + ANASTASIS_REDUX_required_attributes_clear_ (common); ANASTASIS_REDUX_providers_clear_ (common); for (unsigned int i = 0; i < common->currencies_len; i++) GNUNET_free (common->currencies[i]); @@ -119,42 +157,62 @@ free_common (struct ANASTASIS_ReduxCommon *common) * * @param[in,out] backup state to free */ -static void -free_backup (struct ANASTASIS_ReduxBackup *backup) +void +ANASTASIS_REDUX_policy_clear_ (struct ANASTASIS_ReduxPolicy *p) { - for (unsigned int i = 0; i < backup->authentication_methods_len; i++) + for (unsigned int j = 0; j < p->methods_len; j++) { - struct ANASTASIS_ReduxAuthMethod *am = &backup->authentication_methods[i]; + struct ANASTASIS_ReduxPolicyMethod *pm = &p->methods[j]; - GNUNET_free (am->type); - GNUNET_free (am->instructions); - GNUNET_free (am->challenge); - GNUNET_free (am->mime_type); + ANASTASIS_REDUX_provider_url_clear_ (&pm->provider); + if (NULL != pm->truth) + ANASTASIS_truth_free (pm->truth); } - GNUNET_free (backup->authentication_methods); - for (unsigned int i = 0; i < backup->policies_len; i++) - { - struct ANASTASIS_ReduxPolicy *p = &backup->policies[i]; + GNUNET_free (p->methods); + p->methods = NULL; + p->methods_len = 0; +} - for (unsigned int j = 0; j < p->methods_len; j++) - { - struct ANASTASIS_ReduxPolicyMethod *pm = &p->methods[j]; - ANASTASIS_REDUX_provider_url_clear_ (&pm->provider); - if (NULL != pm->truth) - ANASTASIS_truth_free (pm->truth); - } - GNUNET_free (p->methods); - } +void +ANASTASIS_REDUX_policies_clear_ (struct ANASTASIS_ReduxBackup *backup) +{ + for (unsigned int i = 0; i < backup->policies_len; i++) + ANASTASIS_REDUX_policy_clear_ (&backup->policies[i]); GNUNET_free (backup->policies); + backup->policies = NULL; + backup->policies_len = 0; +} + + +void +ANASTASIS_REDUX_policy_providers_clear_ (struct ANASTASIS_ReduxBackup *backup) +{ for (unsigned int i = 0; i < backup->policy_providers_len; i++) ANASTASIS_REDUX_provider_url_clear_ ( &backup->policy_providers[i].provider_url); GNUNET_free (backup->policy_providers); - GNUNET_free (backup->upload_fees); + backup->policy_providers = NULL; + backup->policy_providers_len = 0; +} + + +void +ANASTASIS_REDUX_payments_clear_ (struct ANASTASIS_ReduxBackup *backup) +{ for (unsigned int i = 0; i < backup->payments_len; i++) GNUNET_free (backup->payments[i]); GNUNET_free (backup->payments); + backup->payments = NULL; + backup->payments_len = 0; + backup->have_payments = false; +} + + +void +ANASTASIS_REDUX_policy_payment_requests_clear_ ( + struct ANASTASIS_ReduxBackup *backup) +{ for (unsigned int i = 0; i < backup->policy_payment_requests_len; i++) { struct ANASTASIS_ReduxPolicyPaymentRequest *ppr @@ -164,6 +222,30 @@ free_backup (struct ANASTASIS_ReduxBackup *backup) ANASTASIS_REDUX_provider_url_clear_ (&ppr->provider); } GNUNET_free (backup->policy_payment_requests); + backup->policy_payment_requests = NULL; + backup->policy_payment_requests_len = 0; + backup->have_policy_payment_requests = false; +} + + +static void +free_backup (struct ANASTASIS_ReduxBackup *backup) +{ + for (unsigned int i = 0; i < backup->authentication_methods_len; i++) + { + struct ANASTASIS_ReduxAuthMethod *am = &backup->authentication_methods[i]; + + GNUNET_free (am->type); + GNUNET_free (am->instructions); + GNUNET_free (am->challenge); + GNUNET_free (am->mime_type); + } + GNUNET_free (backup->authentication_methods); + ANASTASIS_REDUX_policies_clear_ (backup); + ANASTASIS_REDUX_policy_providers_clear_ (backup); + GNUNET_free (backup->upload_fees); + ANASTASIS_REDUX_payments_clear_ (backup); + ANASTASIS_REDUX_policy_payment_requests_clear_ (backup); for (unsigned int i = 0; i < backup->success_details_len; i++) ANASTASIS_REDUX_provider_url_clear_ ( &backup->success_details[i].provider_url); @@ -214,13 +296,8 @@ ANASTASIS_REDUX_feedback_clear_ (struct ANASTASIS_ReduxFeedback *fb) } -/** - * Free the recovery information @a ri. - * - * @param[in] ri recovery information to free, may be NULL - */ -static void -free_recovery_info (struct ANASTASIS_ReduxRecoveryInfo *ri) +void +ANASTASIS_REDUX_recovery_info_free_ (struct ANASTASIS_ReduxRecoveryInfo *ri) { if (NULL == ri) return; @@ -228,6 +305,7 @@ free_recovery_info (struct ANASTASIS_ReduxRecoveryInfo *ri) { GNUNET_free (ri->challenges[i].type); GNUNET_free (ri->challenges[i].instructions); + GNUNET_free (ri->challenges[i].answer); } GNUNET_free (ri->challenges); for (unsigned int i = 0; i < ri->policies_len; i++) @@ -249,7 +327,7 @@ free_recovery (struct ANASTASIS_ReduxRecovery *recovery) { if (NULL != recovery->r) ANASTASIS_recovery_abort (recovery->r); - free_recovery_info (recovery->ri); + ANASTASIS_REDUX_recovery_info_free_ (recovery->ri); for (unsigned int i = 0; i < recovery->challenge_feedback_len; i++) ANASTASIS_REDUX_feedback_clear_ (&recovery->challenge_feedback[i]); GNUNET_free (recovery->challenge_feedback); diff --git a/src/reducer/anastasis_api_redux_parse.c b/src/reducer/anastasis_api_redux_parse.c @@ -1435,6 +1435,7 @@ parse_recovery_info (const json_t *obj, "uuid-display", "type", "instructions", + "answer", "payment_secret", NULL }; @@ -1508,6 +1509,7 @@ parse_recovery_info (const json_t *obj, struct ANASTASIS_ReduxChallengeInfo *ci = &ri->challenges[index]; const char *type; const char *instructions; + const char *answer = NULL; bool no_ps; struct GNUNET_JSON_Specification cspec[] = { GNUNET_JSON_spec_fixed_auto ("uuid", @@ -1517,6 +1519,10 @@ parse_recovery_info (const json_t *obj, GNUNET_JSON_spec_string ("instructions", &instructions), GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("answer", + &answer), + NULL), + GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("payment_secret", &ci->payment_secret), &no_ps), @@ -1540,6 +1546,7 @@ parse_recovery_info (const json_t *obj, } ci->type = GNUNET_strdup (type); ci->instructions = GNUNET_strdup (instructions); + ci->answer = dup_or_null (answer); ci->have_payment_secret = ! no_ps; } ri->policies_len = (unsigned int) json_array_size (policies); @@ -2190,4 +2197,75 @@ ANASTASIS_REDUX_state_parse_ (const json_t *json, } +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_countries_set_ (struct ANASTASIS_ReduxCommon *common, + const json_t *arr, + const char **detail) +{ + ANASTASIS_REDUX_countries_clear_ (common); + if (GNUNET_OK != + parse_countries (arr, + common, + detail)) + { + ANASTASIS_REDUX_countries_clear_ (common); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_required_attributes_set_ (struct ANASTASIS_ReduxCommon *common, + const json_t *arr, + const char **detail) +{ + ANASTASIS_REDUX_required_attributes_clear_ (common); + if (GNUNET_OK != + parse_required_attributes (arr, + common, + detail)) + { + ANASTASIS_REDUX_required_attributes_clear_ (common); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_provider_set_ (struct ANASTASIS_ReduxCommon *common, + const char *url, + const json_t *val, + const char **detail) +{ + struct ANASTASIS_ReduxProvider *p; + + p = ANASTASIS_REDUX_provider_get_ (common, + url); + if (p->have_config) + ANASTASIS_REDUX_provider_config_clear_ (&p->config); + p->have_config = false; + p->status = ANASTASIS_RPS_NOT_CONTACTED; + memset (&p->error, + 0, + sizeof (p->error)); + if (GNUNET_OK != + parse_provider (url, + val, + p, + detail)) + { + /* Leave a well-defined, if uninformative, entry behind: the caller + reports the error, but the state must stay consistent. */ + if (p->have_config) + ANASTASIS_REDUX_provider_config_clear_ (&p->config); + p->have_config = false; + p->status = ANASTASIS_RPS_NOT_CONTACTED; + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /* end of anastasis_api_redux_parse.c */ diff --git a/src/reducer/anastasis_api_redux_serialize.c b/src/reducer/anastasis_api_redux_serialize.c @@ -625,6 +625,10 @@ serialize_recovery_info (const struct ANASTASIS_ReduxRecoveryInfo *ri) GNUNET_JSON_pack_string ("instructions", ci->instructions), GNUNET_JSON_pack_conditional ( + NULL != ci->answer, + GNUNET_JSON_pack_string ("answer", + ci->answer)), + GNUNET_JSON_pack_conditional ( ci->have_payment_secret, GNUNET_JSON_pack_data_auto ("payment_secret", &ci->payment_secret))))); diff --git a/src/reducer/anastasis_api_redux_state.c b/src/reducer/anastasis_api_redux_state.c @@ -144,4 +144,19 @@ ANASTASIS_REDUX_return_ (struct ANASTASIS_ReduxState *rs, } +void +ANASTASIS_REDUX_fail_ (struct ANASTASIS_ReduxState *rs, + ANASTASIS_ActionCallback cb, + void *cb_cls, + enum TALER_ErrorCode ec, + const char *detail) +{ + ANASTASIS_REDUX_state_free_ (rs); + ANASTASIS_redux_fail_ (cb, + cb_cls, + ec, + detail); +} + + /* end of anastasis_api_redux_state.c */ diff --git a/src/reducer/anastasis_api_redux_state.h b/src/reducer/anastasis_api_redux_state.h @@ -744,6 +744,13 @@ struct ANASTASIS_ReduxChallengeInfo char *instructions; /** + * Answer the user gave to a secure question, NULL if none. Kept so + * that the answer survives the round-trip through the application + * when the provider demands payment before accepting it. + */ + char *answer; + + /** * Payment secret remembered from a payment request for this * challenge; only valid if @e have_payment_secret. */ @@ -1332,6 +1339,30 @@ ANASTASIS_REDUX_return_ (struct ANASTASIS_ReduxState *rs, enum TALER_ErrorCode ec); +/** + * Report @a ec to @a cb and free @a rs. The counterpart of + * #ANASTASIS_REDUX_return_ for the failure path: the reducer replies + * with a freshly built `error` state, so whatever @a rs held is + * discarded rather than serialized. + * + * @param[in] rs state to free, may be NULL + * @param cb callback to invoke + * @param cb_cls closure for @a cb + * @param ec error to report + * @param detail human-readable detail, may be NULL + */ +void +ANASTASIS_REDUX_fail_ (struct ANASTASIS_ReduxState *rs, + ANASTASIS_ActionCallback cb, + void *cb_cls, + enum TALER_ErrorCode ec, + const char *detail); + + +/* #ANASTASIS_REDUX_StateCallback, the internal counterpart of + #ANASTASIS_ActionCallback, is declared in `anastasis_api_redux.h`. */ + + /* ************************ Small helpers **************************** */ /** @@ -1358,6 +1389,100 @@ ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common); /** + * Free the contents of @a cfg and zero it. + * + * @param[in,out] cfg provider configuration to clear + */ +void +ANASTASIS_REDUX_provider_config_clear_ ( + struct ANASTASIS_ReduxProviderConfig *cfg); + + +/** + * Drop all continents from @a common, freeing them. + * + * @param[in,out] common state to clear + */ +void +ANASTASIS_REDUX_continents_clear_ (struct ANASTASIS_ReduxCommon *common); + + +/** + * Drop all countries from @a common, freeing them. + * + * @param[in,out] common state to clear + */ +void +ANASTASIS_REDUX_countries_clear_ (struct ANASTASIS_ReduxCommon *common); + + +/** + * Drop all required attributes from @a common, freeing them. + * + * @param[in,out] common state to clear + */ +void +ANASTASIS_REDUX_required_attributes_clear_ ( + struct ANASTASIS_ReduxCommon *common); + + +/* ****************** Importing from resource files ****************** */ + +/* + * The reducer's resource files (`redux.countries.json`, `redux.$CC.json`) + * and the `add_provider` action supply data in exactly the JSON encoding + * the parser already understands. Rather than have a second, subtly + * different reader for them, the relevant parser fragments are exposed + * here. Each replaces whatever the state held before. + */ + +/** + * Replace the countries of @a common with those in @a arr. + * + * @param[in,out] common state to update + * @param arr array of countries in the state's JSON encoding + * @param[out] detail set to the offending field on failure + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_countries_set_ (struct ANASTASIS_ReduxCommon *common, + const json_t *arr, + const char **detail); + + +/** + * Replace the required attributes of @a common with those in @a arr. + * + * @param[in,out] common state to update + * @param arr array of attribute specifications in the state's JSON + * encoding + * @param[out] detail set to the offending field on failure + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_required_attributes_set_ (struct ANASTASIS_ReduxCommon *common, + const json_t *arr, + const char **detail); + + +/** + * Replace the entry for provider @a url in @a common with @a val, + * adding the provider if it was not known yet. + * + * @param[in,out] common state to update + * @param url provider the entry is about + * @param val the entry in the state's JSON encoding + * @param[out] detail set to the offending field on failure + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_REDUX_provider_set_ (struct ANASTASIS_ReduxCommon *common, + const char *url, + const json_t *val, + const char **detail); + + +/** * Find, or create, the provider with the given @a url in @a common. * * @param[in,out] common state to search and possibly grow @@ -1422,6 +1547,63 @@ ANASTASIS_REDUX_challenge_find_ (const struct ANASTASIS_ReduxRecovery *rr, /** + * Free the contents of the policy @a p and zero it. + * + * @param[in,out] p policy to clear + */ +void +ANASTASIS_REDUX_policy_clear_ (struct ANASTASIS_ReduxPolicy *p); + + +/** + * Drop all policies from @a backup, freeing them. + * + * @param[in,out] backup state to clear + */ +void +ANASTASIS_REDUX_policies_clear_ (struct ANASTASIS_ReduxBackup *backup); + + +/** + * Drop all policy providers from @a backup, freeing them. + * + * @param[in,out] backup state to clear + */ +void +ANASTASIS_REDUX_policy_providers_clear_ (struct ANASTASIS_ReduxBackup *backup); + + +/** + * Drop the pending truth payments from @a backup, freeing them, and + * mark the field as absent. + * + * @param[in,out] backup state to clear + */ +void +ANASTASIS_REDUX_payments_clear_ (struct ANASTASIS_ReduxBackup *backup); + + +/** + * Drop the pending policy payments from @a backup, freeing them, and + * mark the field as absent. + * + * @param[in,out] backup state to clear + */ +void +ANASTASIS_REDUX_policy_payment_requests_clear_ ( + struct ANASTASIS_ReduxBackup *backup); + + +/** + * Free the recovery information @a ri. + * + * @param[in] ri recovery information to free, may be NULL + */ +void +ANASTASIS_REDUX_recovery_info_free_ (struct ANASTASIS_ReduxRecoveryInfo *ri); + + +/** * Free the contents of @a pu and set it to NULL. * * @param[in,out] pu provider URL to clear