anastasis

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

anastasis_api_redux_parse.c (68458B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021, 2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file reducer/anastasis_api_redux_parse.c
     18  * @brief parse a reducer state from its JSON encoding
     19  * @author Christian Grothoff
     20  *
     21  * Parsing is strict: every object is checked against the closed set of
     22  * fields declared for it, so a typo or a field from a foreign reducer
     23  * implementation is an error rather than something silently carried along.
     24  *
     25  * On any failure the partially built state is handed to
     26  * ANASTASIS_REDUX_state_free_(), which is safe because every array length is
     27  * assigned before the array is populated.
     28  */
     29 #include "platform.h"
     30 #include "anastasis_redux.h"
     31 #include "anastasis_api_redux.h"
     32 #include "anastasis_api_redux_state.h"
     33 #include "validation.h"
     34 #include <taler/taler_json_lib.h>
     35 
     36 
     37 /**
     38  * Buffer for the error detail returned by
     39  * ANASTASIS_REDUX_state_parse_().  The reducer is single-threaded (it
     40  * runs off the GNUnet scheduler), and the caller copies the detail into
     41  * its error state immediately, so one static buffer is enough.
     42  */
     43 static char parse_detail[256];
     44 
     45 
     46 /**
     47  * Record @a field as the location of a parse error.
     48  *
     49  * @param where name of the enclosing object
     50  * @param field offending field, may be NULL
     51  * @return pointer to the formatted detail
     52  */
     53 static const char *
     54 set_detail (const char *where,
     55             const char *field)
     56 {
     57   if (NULL == field)
     58     GNUNET_snprintf (parse_detail,
     59                      sizeof (parse_detail),
     60                      "%s",
     61                      where);
     62   else
     63     GNUNET_snprintf (parse_detail,
     64                      sizeof (parse_detail),
     65                      "%s.%s",
     66                      where,
     67                      field);
     68   return parse_detail;
     69 }
     70 
     71 
     72 /**
     73  * Check that @a obj is an object and has no members beyond those listed
     74  * in @a allowed.
     75  *
     76  * @param obj object to check
     77  * @param where name of @a obj, for error reporting
     78  * @param allowed NULL-terminated array of permitted field names
     79  * @param[out] detail set to the offending field on failure
     80  * @return #GNUNET_OK if @a obj is acceptable
     81  */
     82 static enum GNUNET_GenericReturnValue
     83 check_fields (const json_t *obj,
     84               const char *where,
     85               const char *const*allowed,
     86               const char **detail)
     87 {
     88   const char *key;
     89   json_t *val;
     90 
     91   if (! json_is_object (obj))
     92   {
     93     *detail = set_detail (where,
     94                           NULL);
     95     return GNUNET_SYSERR;
     96   }
     97   json_object_foreach ((json_t *) obj, key, val)
     98   {
     99     bool found = false;
    100 
    101     for (unsigned int i = 0; NULL != allowed[i]; i++)
    102       if (0 == strcmp (key,
    103                        allowed[i]))
    104       {
    105         found = true;
    106         break;
    107       }
    108     if (! found)
    109     {
    110       *detail = set_detail (where,
    111                             key);
    112       return GNUNET_SYSERR;
    113     }
    114   }
    115   return GNUNET_OK;
    116 }
    117 
    118 
    119 /**
    120  * Duplicate @a s, or return NULL if @a s is NULL.
    121  *
    122  * @param s string to duplicate
    123  * @return copy of @a s
    124  */
    125 static char *
    126 dup_or_null (const char *s)
    127 {
    128   if (NULL == s)
    129     return NULL;
    130   return GNUNET_strdup (s);
    131 }
    132 
    133 
    134 /**
    135  * Take a reference to the i18n map @a obj, or return NULL.
    136  *
    137  * The reducer never looks inside a translation map; it only ever carries
    138  * it from the resource files to the user interface.  So there is nothing
    139  * to type here, and the object is kept verbatim.
    140  *
    141  * @param obj object mapping language tags to strings, may be NULL
    142  * @return new reference to @a obj, or NULL
    143  */
    144 static json_t *
    145 incref_or_null (const json_t *obj)
    146 {
    147   if (NULL == obj)
    148     return NULL;
    149   return json_incref ((json_t *) obj);
    150 }
    151 
    152 
    153 /**
    154  * Parse the `continents` array.
    155  *
    156  * @param arr the array, may be NULL
    157  * @param[out] common state to fill in
    158  * @param[out] detail set to the offending field on failure
    159  * @return #GNUNET_OK on success
    160  */
    161 static enum GNUNET_GenericReturnValue
    162 parse_continents (const json_t *arr,
    163                   struct ANASTASIS_ReduxCommon *common,
    164                   const char **detail)
    165 {
    166   static const char *const allowed[] = {
    167     "name",
    168     "name_i18n",
    169     NULL
    170   };
    171   size_t index;
    172   json_t *val;
    173 
    174   if (NULL == arr)
    175     return GNUNET_OK;
    176   if (! json_is_array (arr))
    177   {
    178     *detail = set_detail ("continents",
    179                           NULL);
    180     return GNUNET_SYSERR;
    181   }
    182   common->have_continents = true;
    183   common->continents_len = (unsigned int) json_array_size (arr);
    184   common->continents = GNUNET_new_array (common->continents_len,
    185                                          struct ANASTASIS_ReduxContinent);
    186   json_array_foreach (arr, index, val)
    187   {
    188     struct ANASTASIS_ReduxContinent *c = &common->continents[index];
    189     const char *name;
    190     const json_t *i18n = NULL;
    191     struct GNUNET_JSON_Specification spec[] = {
    192       GNUNET_JSON_spec_string ("name",
    193                                &name),
    194       GNUNET_JSON_spec_mark_optional (
    195         GNUNET_JSON_spec_object_const ("name_i18n",
    196                                        &i18n),
    197         NULL),
    198       GNUNET_JSON_spec_end ()
    199     };
    200 
    201     if (json_is_string (val))
    202     {
    203       /* hand-written states name continents directly */
    204       c->name = GNUNET_strdup (json_string_value (val));
    205       c->bare = true;
    206       continue;
    207     }
    208     if (GNUNET_OK !=
    209         check_fields (val,
    210                       "continents",
    211                       allowed,
    212                       detail))
    213       return GNUNET_SYSERR;
    214     if (GNUNET_OK !=
    215         GNUNET_JSON_parse (val,
    216                            spec,
    217                            NULL, NULL))
    218     {
    219       *detail = set_detail ("continents",
    220                             "name");
    221       return GNUNET_SYSERR;
    222     }
    223     c->name = GNUNET_strdup (name);
    224     c->name_i18n = incref_or_null (i18n);
    225   }
    226   return GNUNET_OK;
    227 }
    228 
    229 
    230 /**
    231  * Parse the `countries` array.
    232  *
    233  * @param arr the array, may be NULL
    234  * @param[out] common state to fill in
    235  * @param[out] detail set to the offending field on failure
    236  * @return #GNUNET_OK on success
    237  */
    238 static enum GNUNET_GenericReturnValue
    239 parse_countries (const json_t *arr,
    240                  struct ANASTASIS_ReduxCommon *common,
    241                  const char **detail)
    242 {
    243   static const char *const allowed[] = {
    244     "code",
    245     "name",
    246     "continent",
    247     "call_code",
    248     "name_i18n",
    249     "continent_i18n",
    250     "currency",
    251     NULL
    252   };
    253   size_t index;
    254   json_t *val;
    255 
    256   if (NULL == arr)
    257     return GNUNET_OK;
    258   if (! json_is_array (arr))
    259   {
    260     *detail = set_detail ("countries",
    261                           NULL);
    262     return GNUNET_SYSERR;
    263   }
    264   common->have_countries = true;
    265   common->countries_len = (unsigned int) json_array_size (arr);
    266   common->countries = GNUNET_new_array (common->countries_len,
    267                                         struct ANASTASIS_ReduxCountry);
    268   json_array_foreach (arr, index, val)
    269   {
    270     struct ANASTASIS_ReduxCountry *c = &common->countries[index];
    271     const char *code;
    272     const char *name;
    273     const char *continent;
    274     const char *call_code = NULL;
    275     const char *currency = NULL;
    276     const json_t *name_i18n = NULL;
    277     const json_t *continent_i18n = NULL;
    278     struct GNUNET_JSON_Specification spec[] = {
    279       GNUNET_JSON_spec_string ("code",
    280                                &code),
    281       GNUNET_JSON_spec_string ("name",
    282                                &name),
    283       GNUNET_JSON_spec_string ("continent",
    284                                &continent),
    285       GNUNET_JSON_spec_mark_optional (
    286         GNUNET_JSON_spec_string ("call_code",
    287                                  &call_code),
    288         NULL),
    289       GNUNET_JSON_spec_mark_optional (
    290         GNUNET_JSON_spec_object_const ("name_i18n",
    291                                        &name_i18n),
    292         NULL),
    293       GNUNET_JSON_spec_mark_optional (
    294         GNUNET_JSON_spec_object_const ("continent_i18n",
    295                                        &continent_i18n),
    296         NULL),
    297       GNUNET_JSON_spec_mark_optional (
    298         GNUNET_JSON_spec_string ("currency",
    299                                  &currency),
    300         NULL),
    301       GNUNET_JSON_spec_end ()
    302     };
    303 
    304     if (GNUNET_OK !=
    305         check_fields (val,
    306                       "countries",
    307                       allowed,
    308                       detail))
    309       return GNUNET_SYSERR;
    310     if (GNUNET_OK !=
    311         GNUNET_JSON_parse (val,
    312                            spec,
    313                            NULL, NULL))
    314     {
    315       *detail = set_detail ("countries",
    316                             NULL);
    317       return GNUNET_SYSERR;
    318     }
    319     c->code = GNUNET_strdup (code);
    320     c->name = GNUNET_strdup (name);
    321     c->continent = GNUNET_strdup (continent);
    322     c->call_code = dup_or_null (call_code);
    323     c->currency = dup_or_null (currency);
    324     c->name_i18n = incref_or_null (name_i18n);
    325     c->continent_i18n = incref_or_null (continent_i18n);
    326   }
    327   return GNUNET_OK;
    328 }
    329 
    330 
    331 /**
    332  * Parse the `required_attributes` array.
    333  *
    334  * @param arr the array, may be NULL
    335  * @param[out] common state to fill in
    336  * @param[out] detail set to the offending field on failure
    337  * @return #GNUNET_OK on success
    338  */
    339 static enum GNUNET_GenericReturnValue
    340 parse_required_attributes (const json_t *arr,
    341                            struct ANASTASIS_ReduxCommon *common,
    342                            const char **detail)
    343 {
    344   static const char *const allowed[] = {
    345     "type",
    346     "name",
    347     "label",
    348     "widget",
    349     "uuid",
    350     "tooltip",
    351     "label_i18n",
    352     "validation-regex",
    353     "validation-logic",
    354     "autocomplete",
    355     "optional",
    356     NULL
    357   };
    358   size_t index;
    359   json_t *val;
    360 
    361   if (NULL == arr)
    362     return GNUNET_OK;
    363   if (! json_is_array (arr))
    364   {
    365     *detail = set_detail ("required_attributes",
    366                           NULL);
    367     return GNUNET_SYSERR;
    368   }
    369   common->have_required_attributes = true;
    370   common->required_attributes_len = (unsigned int) json_array_size (arr);
    371   common->required_attributes
    372     = GNUNET_new_array (common->required_attributes_len,
    373                         struct ANASTASIS_ReduxAttributeSpec);
    374   json_array_foreach (arr, index, val)
    375   {
    376     struct ANASTASIS_ReduxAttributeSpec *a
    377       = &common->required_attributes[index];
    378     const char *type;
    379     const char *name;
    380     const char *label;
    381     const char *widget;
    382     const char *uuid = NULL;
    383     const char *tooltip = NULL;
    384     const char *regex = NULL;
    385     const char *logic = NULL;
    386     const char *autocomplete = NULL;
    387     const json_t *label_i18n = NULL;
    388     bool no_optional;
    389     struct GNUNET_JSON_Specification spec[] = {
    390       GNUNET_JSON_spec_string ("type",
    391                                &type),
    392       GNUNET_JSON_spec_string ("name",
    393                                &name),
    394       GNUNET_JSON_spec_string ("label",
    395                                &label),
    396       GNUNET_JSON_spec_string ("widget",
    397                                &widget),
    398       GNUNET_JSON_spec_mark_optional (
    399         GNUNET_JSON_spec_string ("uuid",
    400                                  &uuid),
    401         NULL),
    402       GNUNET_JSON_spec_mark_optional (
    403         GNUNET_JSON_spec_string ("tooltip",
    404                                  &tooltip),
    405         NULL),
    406       GNUNET_JSON_spec_mark_optional (
    407         GNUNET_JSON_spec_string ("validation-regex",
    408                                  &regex),
    409         NULL),
    410       GNUNET_JSON_spec_mark_optional (
    411         GNUNET_JSON_spec_string ("validation-logic",
    412                                  &logic),
    413         NULL),
    414       GNUNET_JSON_spec_mark_optional (
    415         GNUNET_JSON_spec_string ("autocomplete",
    416                                  &autocomplete),
    417         NULL),
    418       GNUNET_JSON_spec_mark_optional (
    419         GNUNET_JSON_spec_object_const ("label_i18n",
    420                                        &label_i18n),
    421         NULL),
    422       GNUNET_JSON_spec_mark_optional (
    423         GNUNET_JSON_spec_bool ("optional",
    424                                &a->optional),
    425         &no_optional),
    426       GNUNET_JSON_spec_end ()
    427     };
    428 
    429     if (GNUNET_OK !=
    430         check_fields (val,
    431                       "required_attributes",
    432                       allowed,
    433                       detail))
    434       return GNUNET_SYSERR;
    435     if (GNUNET_OK !=
    436         GNUNET_JSON_parse (val,
    437                            spec,
    438                            NULL, NULL))
    439     {
    440       *detail = set_detail ("required_attributes",
    441                             NULL);
    442       return GNUNET_SYSERR;
    443     }
    444     a->type = GNUNET_strdup (type);
    445     a->name = GNUNET_strdup (name);
    446     a->label = GNUNET_strdup (label);
    447     a->widget = GNUNET_strdup (widget);
    448     a->uuid = dup_or_null (uuid);
    449     a->tooltip = dup_or_null (tooltip);
    450     a->validation_regex = dup_or_null (regex);
    451     if ( (NULL != logic) &&
    452          (NULL == ANASTASIS_REDUX_validation_lookup_ (logic)) )
    453     {
    454       /* reject unknown validation logic already here, so that it cannot even
    455          be round-tripped through a state we hand back to the application */
    456       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    457                   "Validation function `%s' is not known\n",
    458                   logic);
    459       *detail = set_detail ("required_attributes",
    460                             "validation-logic");
    461       return GNUNET_SYSERR;
    462     }
    463     a->validation_logic = dup_or_null (logic);
    464     a->autocomplete = dup_or_null (autocomplete);
    465     a->have_optional = ! no_optional;
    466     a->label_i18n = incref_or_null (label_i18n);
    467   }
    468   return GNUNET_OK;
    469 }
    470 
    471 
    472 /**
    473  * Parse one entry of the `authentication_providers` object.
    474  *
    475  * @param url the provider URL (the key)
    476  * @param val the provider details (the value)
    477  * @param[out] p provider entry to fill in
    478  * @param[out] detail set to the offending field on failure
    479  * @return #GNUNET_OK on success
    480  */
    481 static enum GNUNET_GenericReturnValue
    482 parse_provider (const char *url,
    483                 const json_t *val,
    484                 struct ANASTASIS_ReduxProvider *p,
    485                 const char **detail)
    486 {
    487   static const char *const allowed[] = {
    488     "status",
    489     "methods",
    490     "annual_fee",
    491     "truth_upload_fee",
    492     "liability_limit",
    493     "business_name",
    494     "storage_limit_in_megabytes",
    495     "provider_salt",
    496     "http_status",
    497     "error_code",
    498     "currency",
    499     NULL
    500   };
    501   const char *status;
    502   struct GNUNET_JSON_Specification sspec[] = {
    503     GNUNET_JSON_spec_string ("status",
    504                              &status),
    505     GNUNET_JSON_spec_end ()
    506   };
    507 
    508   ANASTASIS_REDUX_provider_url_set_ (&p->url,
    509                                      url);
    510   if (GNUNET_OK !=
    511       check_fields (val,
    512                     "authentication_providers",
    513                     allowed,
    514                     detail))
    515     return GNUNET_SYSERR;
    516   if (GNUNET_OK !=
    517       GNUNET_JSON_parse (val,
    518                          sspec,
    519                          NULL, NULL))
    520   {
    521     *detail = set_detail ("authentication_providers",
    522                           "status");
    523     return GNUNET_SYSERR;
    524   }
    525   if (0 == strcmp (status,
    526                    "not-contacted"))
    527   {
    528     p->status = ANASTASIS_RPS_NOT_CONTACTED;
    529     return GNUNET_OK;
    530   }
    531   if (0 == strcmp (status,
    532                    "error"))
    533   {
    534     uint64_t ec = 0;
    535     struct GNUNET_JSON_Specification espec[] = {
    536       GNUNET_JSON_spec_mark_optional (
    537         GNUNET_JSON_spec_uint64 ("error_code",
    538                                  &ec),
    539         NULL),
    540       GNUNET_JSON_spec_mark_optional (
    541         GNUNET_JSON_spec_uint32 ("http_status",
    542                                  &p->error.http_status),
    543         NULL),
    544       GNUNET_JSON_spec_end ()
    545     };
    546 
    547     p->status = ANASTASIS_RPS_ERROR;
    548     if (GNUNET_OK !=
    549         GNUNET_JSON_parse (val,
    550                            espec,
    551                            NULL, NULL))
    552     {
    553       *detail = set_detail ("authentication_providers",
    554                             "error_code");
    555       return GNUNET_SYSERR;
    556     }
    557     p->error.ec = (enum TALER_ErrorCode) ec;
    558     return GNUNET_OK;
    559   }
    560   if (0 == strcmp (status,
    561                    "ok"))
    562     p->status = ANASTASIS_RPS_OK;
    563   else if (0 == strcmp (status,
    564                         "disabled"))
    565     p->status = ANASTASIS_RPS_DISABLED;
    566   else
    567   {
    568     *detail = set_detail ("authentication_providers",
    569                           "status");
    570     return GNUNET_SYSERR;
    571   }
    572 
    573   /* Both "ok" and (usually) "disabled" carry the /config we obtained
    574      earlier; a provider that was disabled before we ever talked to it
    575      does not. */
    576   {
    577     struct ANASTASIS_ReduxProviderConfig *cfg = &p->config;
    578     const char *business_name;
    579     const char *currency = NULL;
    580     const json_t *methods;
    581     bool no_config = false;
    582     struct GNUNET_JSON_Specification cspec[] = {
    583       GNUNET_JSON_spec_array_const ("methods",
    584                                     &methods),
    585       TALER_JSON_spec_amount_any ("annual_fee",
    586                                   &cfg->annual_fee),
    587       TALER_JSON_spec_amount_any ("truth_upload_fee",
    588                                   &cfg->truth_upload_fee),
    589       TALER_JSON_spec_amount_any ("liability_limit",
    590                                   &cfg->liability_limit),
    591       GNUNET_JSON_spec_string ("business_name",
    592                                &business_name),
    593       GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
    594                                &cfg->storage_limit_in_megabytes),
    595       GNUNET_JSON_spec_fixed_auto ("provider_salt",
    596                                    &cfg->provider_salt),
    597       GNUNET_JSON_spec_uint32 ("http_status",
    598                                &cfg->http_status),
    599       GNUNET_JSON_spec_mark_optional (
    600         GNUNET_JSON_spec_string ("currency",
    601                                  &currency),
    602         NULL),
    603       GNUNET_JSON_spec_end ()
    604     };
    605     size_t index;
    606     json_t *m;
    607 
    608     if (NULL == json_object_get (val,
    609                                  "methods"))
    610       no_config = true;
    611     if (no_config)
    612     {
    613       if (ANASTASIS_RPS_OK == p->status)
    614       {
    615         *detail = set_detail ("authentication_providers",
    616                               "methods");
    617         return GNUNET_SYSERR;
    618       }
    619       return GNUNET_OK;
    620     }
    621     if (GNUNET_OK !=
    622         GNUNET_JSON_parse (val,
    623                            cspec,
    624                            NULL, NULL))
    625     {
    626       *detail = set_detail ("authentication_providers",
    627                             NULL);
    628       return GNUNET_SYSERR;
    629     }
    630     p->have_config = true;
    631     cfg->business_name = GNUNET_strdup (business_name);
    632     cfg->currency = dup_or_null (currency);
    633     cfg->methods_len = (unsigned int) json_array_size (methods);
    634     cfg->methods = GNUNET_new_array (cfg->methods_len,
    635                                      struct ANASTASIS_ReduxMethodSpec);
    636     json_array_foreach (methods, index, m)
    637     {
    638       static const char *const mallowed[] = {
    639         "type",
    640         "usage_fee",
    641         NULL
    642       };
    643       struct ANASTASIS_ReduxMethodSpec *ms = &cfg->methods[index];
    644       const char *type;
    645       struct GNUNET_JSON_Specification mspec[] = {
    646         GNUNET_JSON_spec_string ("type",
    647                                  &type),
    648         TALER_JSON_spec_amount_any ("usage_fee",
    649                                     &ms->usage_fee),
    650         GNUNET_JSON_spec_end ()
    651       };
    652 
    653       if (GNUNET_OK !=
    654           check_fields (m,
    655                         "authentication_providers.methods",
    656                         mallowed,
    657                         detail))
    658         return GNUNET_SYSERR;
    659       if (GNUNET_OK !=
    660           GNUNET_JSON_parse (m,
    661                              mspec,
    662                              NULL, NULL))
    663       {
    664         *detail = set_detail ("authentication_providers.methods",
    665                               NULL);
    666         return GNUNET_SYSERR;
    667       }
    668       ms->type = GNUNET_strdup (type);
    669     }
    670   }
    671   return GNUNET_OK;
    672 }
    673 
    674 
    675 /**
    676  * Parse the `authentication_providers` object.
    677  *
    678  * @param obj the object, may be NULL
    679  * @param[out] common state to fill in
    680  * @param[out] detail set to the offending field on failure
    681  * @return #GNUNET_OK on success
    682  */
    683 static enum GNUNET_GenericReturnValue
    684 parse_providers (const json_t *obj,
    685                  struct ANASTASIS_ReduxCommon *common,
    686                  const char **detail)
    687 {
    688   const char *url;
    689   json_t *val;
    690   unsigned int off = 0;
    691 
    692   if (NULL == obj)
    693     return GNUNET_OK;
    694   if (! json_is_object (obj))
    695   {
    696     *detail = set_detail ("authentication_providers",
    697                           NULL);
    698     return GNUNET_SYSERR;
    699   }
    700   common->have_providers = true;
    701   common->providers_len = (unsigned int) json_object_size (obj);
    702   common->providers = GNUNET_new_array (common->providers_len,
    703                                         struct ANASTASIS_ReduxProvider);
    704   json_object_foreach ((json_t *) obj, url, val)
    705   {
    706     if (GNUNET_OK !=
    707         parse_provider (url,
    708                         val,
    709                         &common->providers[off],
    710                         detail))
    711       return GNUNET_SYSERR;
    712     off++;
    713   }
    714   return GNUNET_OK;
    715 }
    716 
    717 
    718 /**
    719  * Parse the fields shared by the backup and recovery variants.
    720  *
    721  * @param json the state
    722  * @param[out] common state to fill in
    723  * @param[out] detail set to the offending field on failure
    724  * @return #GNUNET_OK on success
    725  */
    726 static enum GNUNET_GenericReturnValue
    727 parse_common (const json_t *json,
    728               struct ANASTASIS_ReduxCommon *common,
    729               const char **detail)
    730 {
    731   const json_t *continents = NULL;
    732   const json_t *countries = NULL;
    733   const json_t *required_attributes = NULL;
    734   const json_t *providers = NULL;
    735   const json_t *identity_attributes = NULL;
    736   const json_t *currencies = NULL;
    737   const char *selected_continent = NULL;
    738   const char *selected_country = NULL;
    739   struct GNUNET_JSON_Specification spec[] = {
    740     GNUNET_JSON_spec_mark_optional (
    741       GNUNET_JSON_spec_array_const ("continents",
    742                                     &continents),
    743       NULL),
    744     GNUNET_JSON_spec_mark_optional (
    745       GNUNET_JSON_spec_array_const ("countries",
    746                                     &countries),
    747       NULL),
    748     GNUNET_JSON_spec_mark_optional (
    749       GNUNET_JSON_spec_array_const ("required_attributes",
    750                                     &required_attributes),
    751       NULL),
    752     GNUNET_JSON_spec_mark_optional (
    753       GNUNET_JSON_spec_object_const ("authentication_providers",
    754                                      &providers),
    755       NULL),
    756     GNUNET_JSON_spec_mark_optional (
    757       GNUNET_JSON_spec_object_const ("identity_attributes",
    758                                      &identity_attributes),
    759       NULL),
    760     GNUNET_JSON_spec_mark_optional (
    761       GNUNET_JSON_spec_array_const ("currencies",
    762                                     &currencies),
    763       NULL),
    764     GNUNET_JSON_spec_mark_optional (
    765       GNUNET_JSON_spec_string ("selected_continent",
    766                                &selected_continent),
    767       NULL),
    768     GNUNET_JSON_spec_mark_optional (
    769       GNUNET_JSON_spec_string ("selected_country",
    770                                &selected_country),
    771       NULL),
    772     GNUNET_JSON_spec_end ()
    773   };
    774 
    775   if (GNUNET_OK !=
    776       GNUNET_JSON_parse (json,
    777                          spec,
    778                          NULL, NULL))
    779   {
    780     *detail = set_detail ("state",
    781                           NULL);
    782     return GNUNET_SYSERR;
    783   }
    784   common->selected_continent = dup_or_null (selected_continent);
    785   common->selected_country = dup_or_null (selected_country);
    786   common->identity_attributes = json_incref ((json_t *) identity_attributes);
    787   if (NULL != currencies)
    788   {
    789     size_t index;
    790     json_t *val;
    791 
    792     common->have_currencies = true;
    793     common->currencies_len = (unsigned int) json_array_size (currencies);
    794     common->currencies = GNUNET_new_array (common->currencies_len,
    795                                            char *);
    796     json_array_foreach (currencies, index, val)
    797     {
    798       if (! json_is_string (val))
    799       {
    800         *detail = set_detail ("currencies",
    801                               NULL);
    802         return GNUNET_SYSERR;
    803       }
    804       common->currencies[index] = GNUNET_strdup (json_string_value (val));
    805     }
    806   }
    807   if ( (GNUNET_OK !=
    808         parse_continents (continents,
    809                           common,
    810                           detail)) ||
    811        (GNUNET_OK !=
    812         parse_countries (countries,
    813                          common,
    814                          detail)) ||
    815        (GNUNET_OK !=
    816         parse_required_attributes (required_attributes,
    817                                    common,
    818                                    detail)) ||
    819        (GNUNET_OK !=
    820         parse_providers (providers,
    821                          common,
    822                          detail)) )
    823     return GNUNET_SYSERR;
    824   return GNUNET_OK;
    825 }
    826 
    827 
    828 /**
    829  * Parse the `authentication_methods` array.
    830  *
    831  * @param arr the array, may be NULL
    832  * @param[out] b backup state to fill in
    833  * @param[out] detail set to the offending field on failure
    834  * @return #GNUNET_OK on success
    835  */
    836 static enum GNUNET_GenericReturnValue
    837 parse_auth_methods (const json_t *arr,
    838                     struct ANASTASIS_ReduxBackup *b,
    839                     const char **detail)
    840 {
    841   static const char *const allowed[] = {
    842     "type",
    843     "instructions",
    844     "challenge",
    845     "mime_type",
    846     NULL
    847   };
    848   size_t index;
    849   json_t *val;
    850 
    851   if (NULL == arr)
    852     return GNUNET_OK;
    853   b->have_authentication_methods = true;
    854   b->authentication_methods_len = (unsigned int) json_array_size (arr);
    855   b->authentication_methods
    856     = GNUNET_new_array (b->authentication_methods_len,
    857                         struct ANASTASIS_ReduxAuthMethod);
    858   json_array_foreach (arr, index, val)
    859   {
    860     struct ANASTASIS_ReduxAuthMethod *am = &b->authentication_methods[index];
    861     const char *type;
    862     const char *instructions;
    863     const char *mime_type = NULL;
    864     void *challenge;
    865     size_t challenge_size;
    866     struct GNUNET_JSON_Specification spec[] = {
    867       GNUNET_JSON_spec_string ("type",
    868                                &type),
    869       GNUNET_JSON_spec_string ("instructions",
    870                                &instructions),
    871       GNUNET_JSON_spec_varsize ("challenge",
    872                                 &challenge,
    873                                 &challenge_size),
    874       GNUNET_JSON_spec_mark_optional (
    875         GNUNET_JSON_spec_string ("mime_type",
    876                                  &mime_type),
    877         NULL),
    878       GNUNET_JSON_spec_end ()
    879     };
    880 
    881     if (GNUNET_OK !=
    882         check_fields (val,
    883                       "authentication_methods",
    884                       allowed,
    885                       detail))
    886       return GNUNET_SYSERR;
    887     if (GNUNET_OK !=
    888         GNUNET_JSON_parse (val,
    889                            spec,
    890                            NULL, NULL))
    891     {
    892       *detail = set_detail ("authentication_methods",
    893                             NULL);
    894       return GNUNET_SYSERR;
    895     }
    896     am->type = GNUNET_strdup (type);
    897     am->instructions = GNUNET_strdup (instructions);
    898     am->mime_type = dup_or_null (mime_type);
    899     am->challenge = GNUNET_memdup (challenge,
    900                                    challenge_size);
    901     am->challenge_size = challenge_size;
    902     GNUNET_JSON_parse_free (spec);
    903   }
    904   return GNUNET_OK;
    905 }
    906 
    907 
    908 /**
    909  * Parse the `policies` array.
    910  *
    911  * @param arr the array, may be NULL
    912  * @param[out] b backup state to fill in
    913  * @param[out] detail set to the offending field on failure
    914  * @return #GNUNET_OK on success
    915  */
    916 static enum GNUNET_GenericReturnValue
    917 parse_policies (const json_t *arr,
    918                 struct ANASTASIS_ReduxBackup *b,
    919                 const char **detail)
    920 {
    921   static const char *const pallowed[] = {
    922     "methods",
    923     "recovery_cost",
    924     NULL
    925   };
    926   static const char *const mallowed[] = {
    927     "authentication_method",
    928     "provider",
    929     "truth",
    930     NULL
    931   };
    932   size_t index;
    933   json_t *val;
    934 
    935   if (NULL == arr)
    936     return GNUNET_OK;
    937   b->have_policies = true;
    938   b->policies_len = (unsigned int) json_array_size (arr);
    939   b->policies = GNUNET_new_array (b->policies_len,
    940                                   struct ANASTASIS_ReduxPolicy);
    941   json_array_foreach (arr, index, val)
    942   {
    943     struct ANASTASIS_ReduxPolicy *p = &b->policies[index];
    944     const json_t *methods;
    945     size_t mindex;
    946     json_t *m;
    947     bool no_cost;
    948     struct GNUNET_JSON_Specification spec[] = {
    949       GNUNET_JSON_spec_array_const ("methods",
    950                                     &methods),
    951       GNUNET_JSON_spec_mark_optional (
    952         TALER_JSON_spec_amount_any ("recovery_cost",
    953                                     &p->recovery_cost),
    954         &no_cost),
    955       GNUNET_JSON_spec_end ()
    956     };
    957 
    958     if (GNUNET_OK !=
    959         check_fields (val,
    960                       "policies",
    961                       pallowed,
    962                       detail))
    963       return GNUNET_SYSERR;
    964     if (GNUNET_OK !=
    965         GNUNET_JSON_parse (val,
    966                            spec,
    967                            NULL, NULL))
    968     {
    969       *detail = set_detail ("policies",
    970                             "methods");
    971       return GNUNET_SYSERR;
    972     }
    973     p->have_recovery_cost = ! no_cost;
    974     p->methods_len = (unsigned int) json_array_size (methods);
    975     p->methods = GNUNET_new_array (p->methods_len,
    976                                    struct ANASTASIS_ReduxPolicyMethod);
    977     json_array_foreach (methods, mindex, m)
    978     {
    979       struct ANASTASIS_ReduxPolicyMethod *pm = &p->methods[mindex];
    980       const char *provider;
    981       const json_t *truth = NULL;
    982       struct GNUNET_JSON_Specification mspec[] = {
    983         GNUNET_JSON_spec_uint32 ("authentication_method",
    984                                  &pm->authentication_method.idx),
    985         GNUNET_JSON_spec_string ("provider",
    986                                  &provider),
    987         GNUNET_JSON_spec_mark_optional (
    988           GNUNET_JSON_spec_object_const ("truth",
    989                                          &truth),
    990           NULL),
    991         GNUNET_JSON_spec_end ()
    992       };
    993 
    994       if (GNUNET_OK !=
    995           check_fields (m,
    996                         "policies.methods",
    997                         mallowed,
    998                         detail))
    999         return GNUNET_SYSERR;
   1000       if (GNUNET_OK !=
   1001           GNUNET_JSON_parse (m,
   1002                              mspec,
   1003                              NULL, NULL))
   1004       {
   1005         *detail = set_detail ("policies.methods",
   1006                               NULL);
   1007         return GNUNET_SYSERR;
   1008       }
   1009       ANASTASIS_REDUX_provider_url_set_ (&pm->provider,
   1010                                          provider);
   1011       if (NULL != truth)
   1012       {
   1013         uint32_t us;
   1014         struct GNUNET_JSON_Specification tspec[] = {
   1015           GNUNET_JSON_spec_uint32 ("upload_status",
   1016                                    &us),
   1017           GNUNET_JSON_spec_end ()
   1018         };
   1019 
   1020         if (GNUNET_OK !=
   1021             GNUNET_JSON_parse (truth,
   1022                                tspec,
   1023                                NULL, NULL))
   1024         {
   1025           *detail = set_detail ("policies.methods.truth",
   1026                                 "upload_status");
   1027           return GNUNET_SYSERR;
   1028         }
   1029         pm->upload_status = (enum ANASTASIS_UploadStatus) us;
   1030         pm->truth = ANASTASIS_truth_from_json (truth);
   1031         if (NULL == pm->truth)
   1032         {
   1033           *detail = set_detail ("policies.methods",
   1034                                 "truth");
   1035           return GNUNET_SYSERR;
   1036         }
   1037       }
   1038     }
   1039   }
   1040   return GNUNET_OK;
   1041 }
   1042 
   1043 
   1044 /**
   1045  * Parse the `policy_providers` array.
   1046  *
   1047  * @param arr the array, may be NULL
   1048  * @param[out] b backup state to fill in
   1049  * @param[out] detail set to the offending field on failure
   1050  * @return #GNUNET_OK on success
   1051  */
   1052 static enum GNUNET_GenericReturnValue
   1053 parse_policy_providers (const json_t *arr,
   1054                         struct ANASTASIS_ReduxBackup *b,
   1055                         const char **detail)
   1056 {
   1057   static const char *const allowed[] = {
   1058     "provider_url",
   1059     "payment_secret",
   1060     NULL
   1061   };
   1062   size_t index;
   1063   json_t *val;
   1064 
   1065   if (NULL == arr)
   1066     return GNUNET_OK;
   1067   b->have_policy_providers = true;
   1068   b->policy_providers_len = (unsigned int) json_array_size (arr);
   1069   b->policy_providers = GNUNET_new_array (b->policy_providers_len,
   1070                                           struct
   1071                                           ANASTASIS_ReduxPolicyProvider);
   1072   json_array_foreach (arr, index, val)
   1073   {
   1074     struct ANASTASIS_ReduxPolicyProvider *pp = &b->policy_providers[index];
   1075     const char *url;
   1076     bool no_ps;
   1077     struct GNUNET_JSON_Specification spec[] = {
   1078       GNUNET_JSON_spec_string ("provider_url",
   1079                                &url),
   1080       GNUNET_JSON_spec_mark_optional (
   1081         GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1082                                      &pp->payment_secret),
   1083         &no_ps),
   1084       GNUNET_JSON_spec_end ()
   1085     };
   1086 
   1087     if (GNUNET_OK !=
   1088         check_fields (val,
   1089                       "policy_providers",
   1090                       allowed,
   1091                       detail))
   1092       return GNUNET_SYSERR;
   1093     if (GNUNET_OK !=
   1094         GNUNET_JSON_parse (val,
   1095                            spec,
   1096                            NULL, NULL))
   1097     {
   1098       *detail = set_detail ("policy_providers",
   1099                             NULL);
   1100       return GNUNET_SYSERR;
   1101     }
   1102     ANASTASIS_REDUX_provider_url_set_ (&pp->provider_url,
   1103                                        url);
   1104     pp->have_payment_secret = ! no_ps;
   1105   }
   1106   return GNUNET_OK;
   1107 }
   1108 
   1109 
   1110 /**
   1111  * Parse the `success_details` object.
   1112  *
   1113  * @param obj the object, may be NULL
   1114  * @param[out] b backup state to fill in
   1115  * @param[out] detail set to the offending field on failure
   1116  * @return #GNUNET_OK on success
   1117  */
   1118 static enum GNUNET_GenericReturnValue
   1119 parse_success_details (const json_t *obj,
   1120                        struct ANASTASIS_ReduxBackup *b,
   1121                        const char **detail)
   1122 {
   1123   static const char *const allowed[] = {
   1124     "policy_version",
   1125     "policy_expiration",
   1126     NULL
   1127   };
   1128   const char *url;
   1129   json_t *val;
   1130   unsigned int off = 0;
   1131 
   1132   if (NULL == obj)
   1133     return GNUNET_OK;
   1134   b->have_success_details = true;
   1135   b->success_details_len = (unsigned int) json_object_size (obj);
   1136   b->success_details = GNUNET_new_array (b->success_details_len,
   1137                                          struct ANASTASIS_ReduxSuccessDetail);
   1138   json_object_foreach ((json_t *) obj, url, val)
   1139   {
   1140     struct ANASTASIS_ReduxSuccessDetail *sd = &b->success_details[off];
   1141     struct GNUNET_JSON_Specification spec[] = {
   1142       GNUNET_JSON_spec_uint32 ("policy_version",
   1143                                &sd->policy_version),
   1144       GNUNET_JSON_spec_timestamp ("policy_expiration",
   1145                                   &sd->policy_expiration),
   1146       GNUNET_JSON_spec_end ()
   1147     };
   1148 
   1149     if (GNUNET_OK !=
   1150         check_fields (val,
   1151                       "success_details",
   1152                       allowed,
   1153                       detail))
   1154       return GNUNET_SYSERR;
   1155     if (GNUNET_OK !=
   1156         GNUNET_JSON_parse (val,
   1157                            spec,
   1158                            NULL, NULL))
   1159     {
   1160       *detail = set_detail ("success_details",
   1161                             NULL);
   1162       return GNUNET_SYSERR;
   1163     }
   1164     ANASTASIS_REDUX_provider_url_set_ (&sd->provider_url,
   1165                                        url);
   1166     off++;
   1167   }
   1168   return GNUNET_OK;
   1169 }
   1170 
   1171 
   1172 /**
   1173  * Parse the backup-specific parts of @a json.
   1174  *
   1175  * @param json the state
   1176  * @param[out] b backup state to fill in
   1177  * @param[out] detail set to the offending field on failure
   1178  * @return #GNUNET_OK on success
   1179  */
   1180 static enum GNUNET_GenericReturnValue
   1181 parse_backup (const json_t *json,
   1182               struct ANASTASIS_ReduxBackup *b,
   1183               const char **detail)
   1184 {
   1185   const char *state_str;
   1186   const json_t *auth_methods = NULL;
   1187   const json_t *policies = NULL;
   1188   const json_t *policy_providers = NULL;
   1189   const json_t *upload_fees = NULL;
   1190   const json_t *payments = NULL;
   1191   const json_t *policy_payment_requests = NULL;
   1192   const json_t *success_details = NULL;
   1193   const json_t *core_secret = NULL;
   1194   const json_t *pay_arguments = NULL;
   1195   const char *secret_name = NULL;
   1196   bool no_expiration;
   1197   struct GNUNET_JSON_Specification spec[] = {
   1198     GNUNET_JSON_spec_string ("backup_state",
   1199                              &state_str),
   1200     GNUNET_JSON_spec_mark_optional (
   1201       GNUNET_JSON_spec_array_const ("authentication_methods",
   1202                                     &auth_methods),
   1203       NULL),
   1204     GNUNET_JSON_spec_mark_optional (
   1205       GNUNET_JSON_spec_array_const ("policies",
   1206                                     &policies),
   1207       NULL),
   1208     GNUNET_JSON_spec_mark_optional (
   1209       GNUNET_JSON_spec_array_const ("policy_providers",
   1210                                     &policy_providers),
   1211       NULL),
   1212     GNUNET_JSON_spec_mark_optional (
   1213       GNUNET_JSON_spec_array_const ("upload_fees",
   1214                                     &upload_fees),
   1215       NULL),
   1216     GNUNET_JSON_spec_mark_optional (
   1217       GNUNET_JSON_spec_array_const ("payments",
   1218                                     &payments),
   1219       NULL),
   1220     GNUNET_JSON_spec_mark_optional (
   1221       GNUNET_JSON_spec_array_const ("policy_payment_requests",
   1222                                     &policy_payment_requests),
   1223       NULL),
   1224     GNUNET_JSON_spec_mark_optional (
   1225       GNUNET_JSON_spec_object_const ("success_details",
   1226                                      &success_details),
   1227       NULL),
   1228     GNUNET_JSON_spec_mark_optional (
   1229       GNUNET_JSON_spec_object_const ("core_secret",
   1230                                      &core_secret),
   1231       NULL),
   1232     GNUNET_JSON_spec_mark_optional (
   1233       GNUNET_JSON_spec_object_const ("pay_arguments",
   1234                                      &pay_arguments),
   1235       NULL),
   1236     GNUNET_JSON_spec_mark_optional (
   1237       GNUNET_JSON_spec_string ("secret_name",
   1238                                &secret_name),
   1239       NULL),
   1240     GNUNET_JSON_spec_mark_optional (
   1241       GNUNET_JSON_spec_timestamp ("expiration",
   1242                                   &b->expiration),
   1243       &no_expiration),
   1244     GNUNET_JSON_spec_end ()
   1245   };
   1246 
   1247   if (GNUNET_OK !=
   1248       GNUNET_JSON_parse (json,
   1249                          spec,
   1250                          NULL, NULL))
   1251   {
   1252     *detail = set_detail ("state",
   1253                           "backup_state");
   1254     return GNUNET_SYSERR;
   1255   }
   1256   b->state = ANASTASIS_backup_state_from_string_ (state_str);
   1257   if (ANASTASIS_BACKUP_STATE_INVALID == b->state)
   1258   {
   1259     *detail = set_detail ("state",
   1260                           "backup_state");
   1261     return GNUNET_SYSERR;
   1262   }
   1263   b->have_expiration = ! no_expiration;
   1264   b->secret_name = dup_or_null (secret_name);
   1265   b->core_secret = json_incref ((json_t *) core_secret);
   1266   if ( (GNUNET_OK !=
   1267         parse_auth_methods (auth_methods,
   1268                             b,
   1269                             detail)) ||
   1270        (GNUNET_OK !=
   1271         parse_policies (policies,
   1272                         b,
   1273                         detail)) ||
   1274        (GNUNET_OK !=
   1275         parse_policy_providers (policy_providers,
   1276                                 b,
   1277                                 detail)) ||
   1278        (GNUNET_OK !=
   1279         parse_success_details (success_details,
   1280                                b,
   1281                                detail)) )
   1282     return GNUNET_SYSERR;
   1283   if (NULL != upload_fees)
   1284   {
   1285     static const char *const allowed[] = {
   1286       "fee",
   1287       NULL
   1288     };
   1289     size_t index;
   1290     json_t *val;
   1291 
   1292     b->have_upload_fees = true;
   1293     b->upload_fees_len = (unsigned int) json_array_size (upload_fees);
   1294     b->upload_fees = GNUNET_new_array (b->upload_fees_len,
   1295                                        struct TALER_Amount);
   1296     json_array_foreach (upload_fees, index, val)
   1297     {
   1298       struct GNUNET_JSON_Specification fspec[] = {
   1299         TALER_JSON_spec_amount_any ("fee",
   1300                                     &b->upload_fees[index]),
   1301         GNUNET_JSON_spec_end ()
   1302       };
   1303 
   1304       if ( (GNUNET_OK !=
   1305             check_fields (val,
   1306                           "upload_fees",
   1307                           allowed,
   1308                           detail)) ||
   1309            (GNUNET_OK !=
   1310             GNUNET_JSON_parse (val,
   1311                                fspec,
   1312                                NULL, NULL)) )
   1313       {
   1314         *detail = set_detail ("upload_fees",
   1315                               "fee");
   1316         return GNUNET_SYSERR;
   1317       }
   1318     }
   1319   }
   1320   if (NULL != payments)
   1321   {
   1322     size_t index;
   1323     json_t *val;
   1324 
   1325     b->have_payments = true;
   1326     b->payments_len = (unsigned int) json_array_size (payments);
   1327     b->payments = GNUNET_new_array (b->payments_len,
   1328                                     char *);
   1329     json_array_foreach (payments, index, val)
   1330     {
   1331       if (! json_is_string (val))
   1332       {
   1333         *detail = set_detail ("payments",
   1334                               NULL);
   1335         return GNUNET_SYSERR;
   1336       }
   1337       b->payments[index] = GNUNET_strdup (json_string_value (val));
   1338     }
   1339   }
   1340   if (NULL != policy_payment_requests)
   1341   {
   1342     static const char *const allowed[] = {
   1343       "payto",
   1344       "provider",
   1345       NULL
   1346     };
   1347     size_t index;
   1348     json_t *val;
   1349 
   1350     b->have_policy_payment_requests = true;
   1351     b->policy_payment_requests_len
   1352       = (unsigned int) json_array_size (policy_payment_requests);
   1353     b->policy_payment_requests
   1354       = GNUNET_new_array (b->policy_payment_requests_len,
   1355                           struct ANASTASIS_ReduxPolicyPaymentRequest);
   1356     json_array_foreach (policy_payment_requests, index, val)
   1357     {
   1358       struct ANASTASIS_ReduxPolicyPaymentRequest *ppr
   1359         = &b->policy_payment_requests[index];
   1360       const char *payto;
   1361       const char *provider;
   1362       struct GNUNET_JSON_Specification pspec[] = {
   1363         GNUNET_JSON_spec_string ("payto",
   1364                                  &payto),
   1365         GNUNET_JSON_spec_string ("provider",
   1366                                  &provider),
   1367         GNUNET_JSON_spec_end ()
   1368       };
   1369 
   1370       if ( (GNUNET_OK !=
   1371             check_fields (val,
   1372                           "policy_payment_requests",
   1373                           allowed,
   1374                           detail)) ||
   1375            (GNUNET_OK !=
   1376             GNUNET_JSON_parse (val,
   1377                                pspec,
   1378                                NULL, NULL)) )
   1379       {
   1380         *detail = set_detail ("policy_payment_requests",
   1381                               NULL);
   1382         return GNUNET_SYSERR;
   1383       }
   1384       ppr->payto = GNUNET_strdup (payto);
   1385       ANASTASIS_REDUX_provider_url_set_ (&ppr->provider,
   1386                                          provider);
   1387     }
   1388   }
   1389   if (NULL != pay_arguments)
   1390   {
   1391     static const char *const allowed[] = {
   1392       "timeout",
   1393       NULL
   1394     };
   1395     bool no_timeout;
   1396     struct GNUNET_JSON_Specification pspec[] = {
   1397       GNUNET_JSON_spec_mark_optional (
   1398         GNUNET_JSON_spec_relative_time ("timeout",
   1399                                         &b->pay_arguments.timeout),
   1400         &no_timeout),
   1401       GNUNET_JSON_spec_end ()
   1402     };
   1403 
   1404     if ( (GNUNET_OK !=
   1405           check_fields (pay_arguments,
   1406                         "pay_arguments",
   1407                         allowed,
   1408                         detail)) ||
   1409          (GNUNET_OK !=
   1410           GNUNET_JSON_parse (pay_arguments,
   1411                              pspec,
   1412                              NULL, NULL)) )
   1413     {
   1414       *detail = set_detail ("pay_arguments",
   1415                             "timeout");
   1416       return GNUNET_SYSERR;
   1417     }
   1418     b->have_pay_arguments = true;
   1419     b->pay_arguments.have_timeout = ! no_timeout;
   1420   }
   1421   return GNUNET_OK;
   1422 }
   1423 
   1424 
   1425 /**
   1426  * Parse the `recovery_information` object.
   1427  *
   1428  * @param obj the object
   1429  * @param[out] rr recovery state to fill in
   1430  * @param[out] detail set to the offending field on failure
   1431  * @return #GNUNET_OK on success
   1432  */
   1433 static enum GNUNET_GenericReturnValue
   1434 parse_recovery_info (const json_t *obj,
   1435                      struct ANASTASIS_ReduxRecovery *rr,
   1436                      const char **detail)
   1437 {
   1438   static const char *const allowed[] = {
   1439     "challenges",
   1440     "policies",
   1441     "secret_name",
   1442     "provider_url",
   1443     "version",
   1444     NULL
   1445   };
   1446   static const char *const callowed[] = {
   1447     "uuid",
   1448     "uuid-display",
   1449     "type",
   1450     "instructions",
   1451     "answer",
   1452     "payment_secret",
   1453     NULL
   1454   };
   1455   static const char *const pallowed[] = {
   1456     "uuid",
   1457     NULL
   1458   };
   1459   struct ANASTASIS_ReduxRecoveryInfo *ri;
   1460   const json_t *challenges;
   1461   const json_t *policies;
   1462   const char *secret_name = NULL;
   1463   const char *provider_url;
   1464   size_t index;
   1465   json_t *val;
   1466   struct GNUNET_JSON_Specification spec[] = {
   1467     GNUNET_JSON_spec_array_const ("challenges",
   1468                                   &challenges),
   1469     GNUNET_JSON_spec_array_const ("policies",
   1470                                   &policies),
   1471     GNUNET_JSON_spec_mark_optional (
   1472       GNUNET_JSON_spec_string ("secret_name",
   1473                                &secret_name),
   1474       NULL),
   1475     GNUNET_JSON_spec_string ("provider_url",
   1476                              &provider_url),
   1477     GNUNET_JSON_spec_end ()
   1478   };
   1479 
   1480   ri = GNUNET_new (struct ANASTASIS_ReduxRecoveryInfo);
   1481   rr->ri = ri;
   1482   if (GNUNET_OK !=
   1483       check_fields (obj,
   1484                     "recovery_information",
   1485                     allowed,
   1486                     detail))
   1487     return GNUNET_SYSERR;
   1488   {
   1489     struct GNUNET_JSON_Specification vspec[] = {
   1490       GNUNET_JSON_spec_uint32 ("version",
   1491                                &ri->version),
   1492       GNUNET_JSON_spec_end ()
   1493     };
   1494 
   1495     if (GNUNET_OK !=
   1496         GNUNET_JSON_parse (obj,
   1497                            vspec,
   1498                            NULL, NULL))
   1499     {
   1500       *detail = set_detail ("recovery_information",
   1501                             "version");
   1502       return GNUNET_SYSERR;
   1503     }
   1504   }
   1505   if (GNUNET_OK !=
   1506       GNUNET_JSON_parse (obj,
   1507                          spec,
   1508                          NULL, NULL))
   1509   {
   1510     *detail = set_detail ("recovery_information",
   1511                           NULL);
   1512     return GNUNET_SYSERR;
   1513   }
   1514   ri->secret_name = dup_or_null (secret_name);
   1515   ANASTASIS_REDUX_provider_url_set_ (&ri->provider_url,
   1516                                      provider_url);
   1517   ri->challenges_len = (unsigned int) json_array_size (challenges);
   1518   ri->challenges = GNUNET_new_array (ri->challenges_len,
   1519                                      struct ANASTASIS_ReduxChallengeInfo);
   1520   json_array_foreach (challenges, index, val)
   1521   {
   1522     struct ANASTASIS_ReduxChallengeInfo *ci = &ri->challenges[index];
   1523     const char *type;
   1524     const char *instructions;
   1525     const char *answer = NULL;
   1526     bool no_ps;
   1527     struct GNUNET_JSON_Specification cspec[] = {
   1528       GNUNET_JSON_spec_fixed_auto ("uuid",
   1529                                    &ci->uuid),
   1530       GNUNET_JSON_spec_string ("type",
   1531                                &type),
   1532       GNUNET_JSON_spec_string ("instructions",
   1533                                &instructions),
   1534       GNUNET_JSON_spec_mark_optional (
   1535         GNUNET_JSON_spec_string ("answer",
   1536                                  &answer),
   1537         NULL),
   1538       GNUNET_JSON_spec_mark_optional (
   1539         GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1540                                      &ci->payment_secret),
   1541         &no_ps),
   1542       GNUNET_JSON_spec_end ()
   1543     };
   1544 
   1545     if (GNUNET_OK !=
   1546         check_fields (val,
   1547                       "recovery_information.challenges",
   1548                       callowed,
   1549                       detail))
   1550       return GNUNET_SYSERR;
   1551     if (GNUNET_OK !=
   1552         GNUNET_JSON_parse (val,
   1553                            cspec,
   1554                            NULL, NULL))
   1555     {
   1556       *detail = set_detail ("recovery_information.challenges",
   1557                             NULL);
   1558       return GNUNET_SYSERR;
   1559     }
   1560     ci->type = GNUNET_strdup (type);
   1561     ci->instructions = GNUNET_strdup (instructions);
   1562     ci->answer = dup_or_null (answer);
   1563     ci->have_payment_secret = ! no_ps;
   1564   }
   1565   ri->policies_len = (unsigned int) json_array_size (policies);
   1566   ri->policies = GNUNET_new_array (ri->policies_len,
   1567                                    struct ANASTASIS_ReduxRecoveryPolicy);
   1568   json_array_foreach (policies, index, val)
   1569   {
   1570     struct ANASTASIS_ReduxRecoveryPolicy *p = &ri->policies[index];
   1571     size_t uindex;
   1572     json_t *u;
   1573 
   1574     if (! json_is_array (val))
   1575     {
   1576       *detail = set_detail ("recovery_information",
   1577                             "policies");
   1578       return GNUNET_SYSERR;
   1579     }
   1580     p->uuids_len = (unsigned int) json_array_size (val);
   1581     p->uuids = GNUNET_new_array (p->uuids_len,
   1582                                  struct ANASTASIS_CRYPTO_TruthUUIDP);
   1583     json_array_foreach (val, uindex, u)
   1584     {
   1585       struct GNUNET_JSON_Specification uspec[] = {
   1586         GNUNET_JSON_spec_fixed_auto ("uuid",
   1587                                      &p->uuids[uindex]),
   1588         GNUNET_JSON_spec_end ()
   1589       };
   1590 
   1591       if ( (GNUNET_OK !=
   1592             check_fields (u,
   1593                           "recovery_information.policies",
   1594                           pallowed,
   1595                           detail)) ||
   1596            (GNUNET_OK !=
   1597             GNUNET_JSON_parse (u,
   1598                                uspec,
   1599                                NULL, NULL)) )
   1600       {
   1601         *detail = set_detail ("recovery_information.policies",
   1602                               "uuid");
   1603         return GNUNET_SYSERR;
   1604       }
   1605     }
   1606   }
   1607   return GNUNET_OK;
   1608 }
   1609 
   1610 
   1611 /**
   1612  * Parse one entry of the `challenge_feedback` object.
   1613  *
   1614  * @param val the feedback details
   1615  * @param[out] fb feedback entry to fill in
   1616  * @param[out] detail set to the offending field on failure
   1617  * @return #GNUNET_OK on success
   1618  */
   1619 static enum GNUNET_GenericReturnValue
   1620 parse_feedback_details (const json_t *val,
   1621                         struct ANASTASIS_ReduxFeedback *fb,
   1622                         const char **detail)
   1623 {
   1624   static const char *const allowed[] = {
   1625     "state",
   1626     "display_hint",
   1627     "filename",
   1628     "address_hint",
   1629     "taler_pay_uri",
   1630     "provider",
   1631     "payment_secret",
   1632     "http_status",
   1633     "error_code",
   1634     "target_iban",
   1635     "target_business_name",
   1636     "wire_transfer_subject",
   1637     "challenge_amount",
   1638     "request_limit",
   1639     "request_frequency",
   1640     NULL
   1641   };
   1642   const char *state;
   1643   const char *display_hint = NULL;
   1644   struct GNUNET_JSON_Specification spec[] = {
   1645     GNUNET_JSON_spec_string ("state",
   1646                              &state),
   1647     GNUNET_JSON_spec_mark_optional (
   1648       GNUNET_JSON_spec_string ("display_hint",
   1649                                &display_hint),
   1650       NULL),
   1651     GNUNET_JSON_spec_end ()
   1652   };
   1653 
   1654   if (GNUNET_OK !=
   1655       check_fields (val,
   1656                     "challenge_feedback",
   1657                     allowed,
   1658                     detail))
   1659     return GNUNET_SYSERR;
   1660   if (GNUNET_OK !=
   1661       GNUNET_JSON_parse (val,
   1662                          spec,
   1663                          NULL, NULL))
   1664   {
   1665     *detail = set_detail ("challenge_feedback",
   1666                           "state");
   1667     return GNUNET_SYSERR;
   1668   }
   1669   fb->display_hint = dup_or_null (display_hint);
   1670   if (0 == strcmp (state,
   1671                    "code-in-file"))
   1672   {
   1673     const char *filename;
   1674     struct GNUNET_JSON_Specification dspec[] = {
   1675       GNUNET_JSON_spec_string ("filename",
   1676                                &filename),
   1677       GNUNET_JSON_spec_end ()
   1678     };
   1679 
   1680     fb->status = ANASTASIS_RFS_CODE_IN_FILE;
   1681     if (GNUNET_OK !=
   1682         GNUNET_JSON_parse (val,
   1683                            dspec,
   1684                            NULL, NULL))
   1685     {
   1686       *detail = set_detail ("challenge_feedback",
   1687                             "filename");
   1688       return GNUNET_SYSERR;
   1689     }
   1690     fb->details.code_in_file.filename = GNUNET_strdup (filename);
   1691     return GNUNET_OK;
   1692   }
   1693   if (0 == strcmp (state,
   1694                    "send-to-address"))
   1695   {
   1696     const char *address_hint = NULL;
   1697     struct GNUNET_JSON_Specification dspec[] = {
   1698       GNUNET_JSON_spec_mark_optional (
   1699         GNUNET_JSON_spec_string ("address_hint",
   1700                                  &address_hint),
   1701         NULL),
   1702       GNUNET_JSON_spec_end ()
   1703     };
   1704 
   1705     fb->status = ANASTASIS_RFS_SEND_TO_ADDRESS;
   1706     if (GNUNET_OK !=
   1707         GNUNET_JSON_parse (val,
   1708                            dspec,
   1709                            NULL, NULL))
   1710     {
   1711       *detail = set_detail ("challenge_feedback",
   1712                             "address_hint");
   1713       return GNUNET_SYSERR;
   1714     }
   1715     fb->details.send_to_address.address_hint = dup_or_null (address_hint);
   1716     return GNUNET_OK;
   1717   }
   1718   if (0 == strcmp (state,
   1719                    "taler-payment"))
   1720   {
   1721     const char *uri;
   1722     const char *provider;
   1723     struct GNUNET_JSON_Specification dspec[] = {
   1724       GNUNET_JSON_spec_string ("taler_pay_uri",
   1725                                &uri),
   1726       GNUNET_JSON_spec_string ("provider",
   1727                                &provider),
   1728       GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1729                                    &fb->details.taler_payment.payment_secret),
   1730       GNUNET_JSON_spec_end ()
   1731     };
   1732 
   1733     fb->status = ANASTASIS_RFS_TALER_PAYMENT;
   1734     if (GNUNET_OK !=
   1735         GNUNET_JSON_parse (val,
   1736                            dspec,
   1737                            NULL, NULL))
   1738     {
   1739       *detail = set_detail ("challenge_feedback",
   1740                             "taler_pay_uri");
   1741       return GNUNET_SYSERR;
   1742     }
   1743     fb->details.taler_payment.taler_pay_uri = GNUNET_strdup (uri);
   1744     ANASTASIS_REDUX_provider_url_set_ (&fb->details.taler_payment.provider,
   1745                                        provider);
   1746     return GNUNET_OK;
   1747   }
   1748   if ( (0 == strcmp (state,
   1749                      "server-failure")) ||
   1750        (0 == strcmp (state,
   1751                      "truth-unknown")) )
   1752   {
   1753     uint64_t ec;
   1754     struct GNUNET_JSON_Specification dspec[] = {
   1755       GNUNET_JSON_spec_uint64 ("error_code",
   1756                                &ec),
   1757       GNUNET_JSON_spec_uint32 ("http_status",
   1758                                &fb->details.server_failure.http_status),
   1759       GNUNET_JSON_spec_end ()
   1760     };
   1761 
   1762     fb->status = (0 == strcmp (state,
   1763                                "server-failure"))
   1764       ? ANASTASIS_RFS_SERVER_FAILURE
   1765       : ANASTASIS_RFS_TRUTH_UNKNOWN;
   1766     if (GNUNET_OK !=
   1767         GNUNET_JSON_parse (val,
   1768                            dspec,
   1769                            NULL, NULL))
   1770     {
   1771       *detail = set_detail ("challenge_feedback",
   1772                             "error_code");
   1773       return GNUNET_SYSERR;
   1774     }
   1775     fb->details.server_failure.ec = (enum TALER_ErrorCode) ec;
   1776     return GNUNET_OK;
   1777   }
   1778   if (0 == strcmp (state,
   1779                    "iban-instructions"))
   1780   {
   1781     const char *iban;
   1782     const char *business_name;
   1783     const char *subject;
   1784     struct GNUNET_JSON_Specification dspec[] = {
   1785       GNUNET_JSON_spec_string ("target_iban",
   1786                                &iban),
   1787       GNUNET_JSON_spec_string ("target_business_name",
   1788                                &business_name),
   1789       GNUNET_JSON_spec_string ("wire_transfer_subject",
   1790                                &subject),
   1791       TALER_JSON_spec_amount_any ("challenge_amount",
   1792                                   &fb->details.iban_instructions.amount),
   1793       GNUNET_JSON_spec_end ()
   1794     };
   1795 
   1796     fb->status = ANASTASIS_RFS_IBAN_INSTRUCTIONS;
   1797     if (GNUNET_OK !=
   1798         GNUNET_JSON_parse (val,
   1799                            dspec,
   1800                            NULL, NULL))
   1801     {
   1802       *detail = set_detail ("challenge_feedback",
   1803                             "target_iban");
   1804       return GNUNET_SYSERR;
   1805     }
   1806     fb->details.iban_instructions.target_iban = GNUNET_strdup (iban);
   1807     fb->details.iban_instructions.target_business_name
   1808       = GNUNET_strdup (business_name);
   1809     fb->details.iban_instructions.wire_transfer_subject
   1810       = GNUNET_strdup (subject);
   1811     return GNUNET_OK;
   1812   }
   1813   if (0 == strcmp (state,
   1814                    "solved"))
   1815   {
   1816     fb->status = ANASTASIS_RFS_SOLVED;
   1817     return GNUNET_OK;
   1818   }
   1819   if (0 == strcmp (state,
   1820                    "incorrect-answer"))
   1821   {
   1822     uint64_t ec;
   1823     struct GNUNET_JSON_Specification dspec[] = {
   1824       GNUNET_JSON_spec_uint64 ("error_code",
   1825                                &ec),
   1826       GNUNET_JSON_spec_end ()
   1827     };
   1828 
   1829     fb->status = ANASTASIS_RFS_INCORRECT_ANSWER;
   1830     if (GNUNET_OK !=
   1831         GNUNET_JSON_parse (val,
   1832                            dspec,
   1833                            NULL, NULL))
   1834     {
   1835       *detail = set_detail ("challenge_feedback",
   1836                             "error_code");
   1837       return GNUNET_SYSERR;
   1838     }
   1839     fb->details.incorrect_answer.ec = (enum TALER_ErrorCode) ec;
   1840     return GNUNET_OK;
   1841   }
   1842   if (0 == strcmp (state,
   1843                    "rate-limit-exceeded"))
   1844   {
   1845     uint64_t ec;
   1846     struct GNUNET_JSON_Specification dspec[] = {
   1847       GNUNET_JSON_spec_uint64 ("error_code",
   1848                                &ec),
   1849       GNUNET_JSON_spec_uint64 ("request_limit",
   1850                                &fb->details.rate_limit_exceeded.request_limit),
   1851       GNUNET_JSON_spec_relative_time (
   1852         "request_frequency",
   1853         &fb->details.rate_limit_exceeded.request_frequency),
   1854       GNUNET_JSON_spec_end ()
   1855     };
   1856 
   1857     fb->status = ANASTASIS_RFS_RATE_LIMIT_EXCEEDED;
   1858     if (GNUNET_OK !=
   1859         GNUNET_JSON_parse (val,
   1860                            dspec,
   1861                            NULL, NULL))
   1862     {
   1863       *detail = set_detail ("challenge_feedback",
   1864                             "request_limit");
   1865       return GNUNET_SYSERR;
   1866     }
   1867     fb->details.rate_limit_exceeded.ec = (enum TALER_ErrorCode) ec;
   1868     return GNUNET_OK;
   1869   }
   1870   *detail = set_detail ("challenge_feedback",
   1871                         "state");
   1872   return GNUNET_SYSERR;
   1873 }
   1874 
   1875 
   1876 /**
   1877  * Parse the recovery-specific parts of @a json.
   1878  *
   1879  * @param json the state
   1880  * @param[out] rr recovery state to fill in
   1881  * @param[out] detail set to the offending field on failure
   1882  * @return #GNUNET_OK on success
   1883  */
   1884 static enum GNUNET_GenericReturnValue
   1885 parse_recovery (const json_t *json,
   1886                 struct ANASTASIS_ReduxRecovery *rr,
   1887                 const char **detail)
   1888 {
   1889   const char *state_str;
   1890   const json_t *recovery_information = NULL;
   1891   const json_t *recovery_document = NULL;
   1892   const json_t *challenge_feedback = NULL;
   1893   const json_t *core_secret = NULL;
   1894   bool no_selected;
   1895   struct GNUNET_JSON_Specification spec[] = {
   1896     GNUNET_JSON_spec_string ("recovery_state",
   1897                              &state_str),
   1898     GNUNET_JSON_spec_mark_optional (
   1899       GNUNET_JSON_spec_object_const ("recovery_information",
   1900                                      &recovery_information),
   1901       NULL),
   1902     GNUNET_JSON_spec_mark_optional (
   1903       GNUNET_JSON_spec_object_const ("recovery_document",
   1904                                      &recovery_document),
   1905       NULL),
   1906     GNUNET_JSON_spec_mark_optional (
   1907       GNUNET_JSON_spec_object_const ("challenge_feedback",
   1908                                      &challenge_feedback),
   1909       NULL),
   1910     GNUNET_JSON_spec_mark_optional (
   1911       GNUNET_JSON_spec_object_const ("core_secret",
   1912                                      &core_secret),
   1913       NULL),
   1914     GNUNET_JSON_spec_mark_optional (
   1915       GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid",
   1916                                    &rr->selected_challenge),
   1917       &no_selected),
   1918     GNUNET_JSON_spec_end ()
   1919   };
   1920 
   1921   if (GNUNET_OK !=
   1922       GNUNET_JSON_parse (json,
   1923                          spec,
   1924                          NULL, NULL))
   1925   {
   1926     *detail = set_detail ("state",
   1927                           "recovery_state");
   1928     return GNUNET_SYSERR;
   1929   }
   1930   rr->state = ANASTASIS_recovery_state_from_string_ (state_str);
   1931   if (ANASTASIS_RECOVERY_STATE_INVALID == rr->state)
   1932   {
   1933     *detail = set_detail ("state",
   1934                           "recovery_state");
   1935     return GNUNET_SYSERR;
   1936   }
   1937   rr->have_selected_challenge = ! no_selected;
   1938   rr->core_secret = json_incref ((json_t *) core_secret);
   1939   if (NULL != recovery_information)
   1940   {
   1941     if (GNUNET_OK !=
   1942         parse_recovery_info (recovery_information,
   1943                              rr,
   1944                              detail))
   1945       return GNUNET_SYSERR;
   1946   }
   1947   if (NULL != recovery_document)
   1948   {
   1949     /* Hand the blob straight to src/lib: it owns that schema.  The
   1950        handle comes back inert -- no callbacks, no network activity --
   1951        until a handler calls ANASTASIS_recovery_resume(). */
   1952     rr->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_,
   1953                                             recovery_document);
   1954     if (NULL == rr->r)
   1955     {
   1956       *detail = set_detail ("state",
   1957                             "recovery_document");
   1958       return GNUNET_SYSERR;
   1959     }
   1960   }
   1961   if (NULL != challenge_feedback)
   1962   {
   1963     const char *uuid_str;
   1964     json_t *val;
   1965     unsigned int off = 0;
   1966 
   1967     rr->have_challenge_feedback = true;
   1968     rr->challenge_feedback_len
   1969       = (unsigned int) json_object_size (challenge_feedback);
   1970     rr->challenge_feedback
   1971       = GNUNET_new_array (rr->challenge_feedback_len,
   1972                           struct ANASTASIS_ReduxFeedback);
   1973     json_object_foreach ((json_t *) challenge_feedback, uuid_str, val)
   1974     {
   1975       struct ANASTASIS_ReduxFeedback *fb = &rr->challenge_feedback[off];
   1976 
   1977       if (GNUNET_OK !=
   1978           GNUNET_STRINGS_string_to_data (uuid_str,
   1979                                          strlen (uuid_str),
   1980                                          &fb->uuid,
   1981                                          sizeof (fb->uuid)))
   1982       {
   1983         *detail = set_detail ("challenge_feedback",
   1984                               uuid_str);
   1985         return GNUNET_SYSERR;
   1986       }
   1987       if (GNUNET_OK !=
   1988           parse_feedback_details (val,
   1989                                   fb,
   1990                                   detail))
   1991         return GNUNET_SYSERR;
   1992       off++;
   1993     }
   1994   }
   1995   return GNUNET_OK;
   1996 }
   1997 
   1998 
   1999 /**
   2000  * Parse an `error` state.
   2001  *
   2002  * @param json the state
   2003  * @param[out] e error state to fill in
   2004  * @param[out] detail set to the offending field on failure
   2005  * @return #GNUNET_OK on success
   2006  */
   2007 static enum GNUNET_GenericReturnValue
   2008 parse_error (const json_t *json,
   2009              struct ANASTASIS_ReduxError *e,
   2010              const char **detail)
   2011 {
   2012   static const char *const allowed[] = {
   2013     "reducer_type",
   2014     "code",
   2015     "hint",
   2016     "detail",
   2017     NULL
   2018   };
   2019   uint64_t code;
   2020   const char *hint = NULL;
   2021   const char *dtl = NULL;
   2022   struct GNUNET_JSON_Specification spec[] = {
   2023     GNUNET_JSON_spec_uint64 ("code",
   2024                              &code),
   2025     GNUNET_JSON_spec_mark_optional (
   2026       GNUNET_JSON_spec_string ("hint",
   2027                                &hint),
   2028       NULL),
   2029     GNUNET_JSON_spec_mark_optional (
   2030       GNUNET_JSON_spec_string ("detail",
   2031                                &dtl),
   2032       NULL),
   2033     GNUNET_JSON_spec_end ()
   2034   };
   2035 
   2036   if ( (GNUNET_OK !=
   2037         check_fields (json,
   2038                       "state",
   2039                       allowed,
   2040                       detail)) ||
   2041        (GNUNET_OK !=
   2042         GNUNET_JSON_parse (json,
   2043                            spec,
   2044                            NULL, NULL)) )
   2045   {
   2046     *detail = set_detail ("state",
   2047                           "code");
   2048     return GNUNET_SYSERR;
   2049   }
   2050   e->code = (enum TALER_ErrorCode) code;
   2051   e->hint = dup_or_null (hint);
   2052   e->detail = dup_or_null (dtl);
   2053   return GNUNET_OK;
   2054 }
   2055 
   2056 
   2057 /**
   2058  * Field names permitted at the top level of a backup state.
   2059  */
   2060 static const char *const backup_allowed[] = {
   2061   "reducer_type",
   2062   "backup_state",
   2063   "continents",
   2064   "countries",
   2065   "required_attributes",
   2066   "authentication_providers",
   2067   "identity_attributes",
   2068   "currencies",
   2069   "selected_continent",
   2070   "selected_country",
   2071   "authentication_methods",
   2072   "policies",
   2073   "policy_providers",
   2074   "upload_fees",
   2075   "payments",
   2076   "policy_payment_requests",
   2077   "success_details",
   2078   "core_secret",
   2079   "secret_name",
   2080   "expiration",
   2081   "pay_arguments",
   2082   NULL
   2083 };
   2084 
   2085 
   2086 /**
   2087  * Field names permitted at the top level of a recovery state.
   2088  */
   2089 static const char *const recovery_allowed[] = {
   2090   "reducer_type",
   2091   "recovery_state",
   2092   "continents",
   2093   "countries",
   2094   "required_attributes",
   2095   "authentication_providers",
   2096   "identity_attributes",
   2097   "currencies",
   2098   "selected_continent",
   2099   "selected_country",
   2100   "recovery_information",
   2101   "recovery_document",
   2102   "challenge_feedback",
   2103   "selected_challenge_uuid",
   2104   "core_secret",
   2105   NULL
   2106 };
   2107 
   2108 
   2109 struct ANASTASIS_ReduxState *
   2110 ANASTASIS_REDUX_state_parse_ (const json_t *json,
   2111                               enum TALER_ErrorCode *ec,
   2112                               const char **detail)
   2113 {
   2114   struct ANASTASIS_ReduxState *rs;
   2115   const char *type;
   2116 
   2117   *ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID;
   2118   *detail = NULL;
   2119   if (! json_is_object (json))
   2120   {
   2121     *detail = set_detail ("state",
   2122                           NULL);
   2123     return NULL;
   2124   }
   2125   /* The reducer type is not always given explicitly: states written by
   2126      older versions (and the initial states of the CLI tests) are
   2127      identified by which of the two state fields is present. */
   2128   type = json_string_value (json_object_get (json,
   2129                                              "reducer_type"));
   2130   if (NULL == type)
   2131   {
   2132     if (NULL != json_object_get (json,
   2133                                  "backup_state"))
   2134       type = "backup";
   2135     else if (NULL != json_object_get (json,
   2136                                       "recovery_state"))
   2137       type = "recovery";
   2138     else
   2139     {
   2140       *detail = set_detail ("state",
   2141                             "reducer_type");
   2142       return NULL;
   2143     }
   2144   }
   2145   rs = GNUNET_new (struct ANASTASIS_ReduxState);
   2146   if (0 == strcmp (type,
   2147                    "backup"))
   2148   {
   2149     rs->type = ANASTASIS_RT_BACKUP;
   2150     if ( (GNUNET_OK !=
   2151           check_fields (json,
   2152                         "state",
   2153                         backup_allowed,
   2154                         detail)) ||
   2155          (GNUNET_OK !=
   2156           parse_common (json,
   2157                         &rs->common,
   2158                         detail)) ||
   2159          (GNUNET_OK !=
   2160           parse_backup (json,
   2161                         &rs->details.backup,
   2162                         detail)) )
   2163     {
   2164       ANASTASIS_REDUX_state_free_ (rs);
   2165       return NULL;
   2166     }
   2167     return rs;
   2168   }
   2169   if (0 == strcmp (type,
   2170                    "recovery"))
   2171   {
   2172     rs->type = ANASTASIS_RT_RECOVERY;
   2173     if ( (GNUNET_OK !=
   2174           check_fields (json,
   2175                         "state",
   2176                         recovery_allowed,
   2177                         detail)) ||
   2178          (GNUNET_OK !=
   2179           parse_common (json,
   2180                         &rs->common,
   2181                         detail)) ||
   2182          (GNUNET_OK !=
   2183           parse_recovery (json,
   2184                           &rs->details.recovery,
   2185                           detail)) )
   2186     {
   2187       ANASTASIS_REDUX_state_free_ (rs);
   2188       return NULL;
   2189     }
   2190     return rs;
   2191   }
   2192   if (0 == strcmp (type,
   2193                    "error"))
   2194   {
   2195     rs->type = ANASTASIS_RT_ERROR;
   2196     if (GNUNET_OK !=
   2197         parse_error (json,
   2198                      &rs->details.error,
   2199                      detail))
   2200     {
   2201       ANASTASIS_REDUX_state_free_ (rs);
   2202       return NULL;
   2203     }
   2204     return rs;
   2205   }
   2206   *detail = set_detail ("state",
   2207                         "reducer_type");
   2208   ANASTASIS_REDUX_state_free_ (rs);
   2209   return NULL;
   2210 }
   2211 
   2212 
   2213 enum GNUNET_GenericReturnValue
   2214 ANASTASIS_REDUX_countries_set_ (struct ANASTASIS_ReduxCommon *common,
   2215                                 const json_t *arr,
   2216                                 const char **detail)
   2217 {
   2218   ANASTASIS_REDUX_countries_clear_ (common);
   2219   if (GNUNET_OK !=
   2220       parse_countries (arr,
   2221                        common,
   2222                        detail))
   2223   {
   2224     ANASTASIS_REDUX_countries_clear_ (common);
   2225     return GNUNET_SYSERR;
   2226   }
   2227   return GNUNET_OK;
   2228 }
   2229 
   2230 
   2231 enum GNUNET_GenericReturnValue
   2232 ANASTASIS_REDUX_required_attributes_set_ (struct ANASTASIS_ReduxCommon *common,
   2233                                           const json_t *arr,
   2234                                           const char **detail)
   2235 {
   2236   ANASTASIS_REDUX_required_attributes_clear_ (common);
   2237   if (GNUNET_OK !=
   2238       parse_required_attributes (arr,
   2239                                  common,
   2240                                  detail))
   2241   {
   2242     ANASTASIS_REDUX_required_attributes_clear_ (common);
   2243     return GNUNET_SYSERR;
   2244   }
   2245   return GNUNET_OK;
   2246 }
   2247 
   2248 
   2249 enum GNUNET_GenericReturnValue
   2250 ANASTASIS_REDUX_provider_set_ (struct ANASTASIS_ReduxCommon *common,
   2251                                const char *url,
   2252                                const json_t *val,
   2253                                const char **detail)
   2254 {
   2255   struct ANASTASIS_ReduxProvider *p;
   2256 
   2257   p = ANASTASIS_REDUX_provider_get_ (common,
   2258                                      url);
   2259   if (p->have_config)
   2260     ANASTASIS_REDUX_provider_config_clear_ (&p->config);
   2261   p->have_config = false;
   2262   p->status = ANASTASIS_RPS_NOT_CONTACTED;
   2263   memset (&p->error,
   2264           0,
   2265           sizeof (p->error));
   2266   if (GNUNET_OK !=
   2267       parse_provider (url,
   2268                       val,
   2269                       p,
   2270                       detail))
   2271   {
   2272     /* Leave a well-defined, if uninformative, entry behind: the caller
   2273        reports the error, but the state must stay consistent. */
   2274     if (p->have_config)
   2275       ANASTASIS_REDUX_provider_config_clear_ (&p->config);
   2276     p->have_config = false;
   2277     p->status = ANASTASIS_RPS_NOT_CONTACTED;
   2278     return GNUNET_SYSERR;
   2279   }
   2280   return GNUNET_OK;
   2281 }
   2282 
   2283 
   2284 /* end of anastasis_api_redux_parse.c */