anastasis

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

anastasis_api_redux_parse.c (71375B)


      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     "annual_fees",
    492     "truth_upload_fee",
    493     "truth_upload_fees",
    494     "liability_limit",
    495     "liability_limits",
    496     "business_name",
    497     "storage_limit_in_megabytes",
    498     "provider_salt",
    499     "http_status",
    500     "error_code",
    501     "currency",
    502     "currencies",
    503     NULL
    504   };
    505   const char *status;
    506   struct GNUNET_JSON_Specification sspec[] = {
    507     GNUNET_JSON_spec_string ("status",
    508                              &status),
    509     GNUNET_JSON_spec_end ()
    510   };
    511 
    512   ANASTASIS_REDUX_provider_url_set_ (&p->url,
    513                                      url);
    514   if (GNUNET_OK !=
    515       check_fields (val,
    516                     "authentication_providers",
    517                     allowed,
    518                     detail))
    519     return GNUNET_SYSERR;
    520   if (GNUNET_OK !=
    521       GNUNET_JSON_parse (val,
    522                          sspec,
    523                          NULL, NULL))
    524   {
    525     *detail = set_detail ("authentication_providers",
    526                           "status");
    527     return GNUNET_SYSERR;
    528   }
    529   if (0 == strcmp (status,
    530                    "not-contacted"))
    531   {
    532     p->status = ANASTASIS_RPS_NOT_CONTACTED;
    533     return GNUNET_OK;
    534   }
    535   if (0 == strcmp (status,
    536                    "error"))
    537   {
    538     uint64_t ec = 0;
    539     struct GNUNET_JSON_Specification espec[] = {
    540       GNUNET_JSON_spec_mark_optional (
    541         GNUNET_JSON_spec_uint64 ("error_code",
    542                                  &ec),
    543         NULL),
    544       GNUNET_JSON_spec_mark_optional (
    545         GNUNET_JSON_spec_uint32 ("http_status",
    546                                  &p->error.http_status),
    547         NULL),
    548       GNUNET_JSON_spec_end ()
    549     };
    550 
    551     p->status = ANASTASIS_RPS_ERROR;
    552     if (GNUNET_OK !=
    553         GNUNET_JSON_parse (val,
    554                            espec,
    555                            NULL, NULL))
    556     {
    557       *detail = set_detail ("authentication_providers",
    558                             "error_code");
    559       return GNUNET_SYSERR;
    560     }
    561     p->error.ec = (enum TALER_ErrorCode) ec;
    562     return GNUNET_OK;
    563   }
    564   if (0 == strcmp (status,
    565                    "ok"))
    566     p->status = ANASTASIS_RPS_OK;
    567   else if (0 == strcmp (status,
    568                         "disabled"))
    569     p->status = ANASTASIS_RPS_DISABLED;
    570   else
    571   {
    572     *detail = set_detail ("authentication_providers",
    573                           "status");
    574     return GNUNET_SYSERR;
    575   }
    576 
    577   /* Both "ok" and (usually) "disabled" carry the /config we obtained
    578      earlier; a provider that was disabled before we ever talked to it
    579      does not. */
    580   {
    581     struct ANASTASIS_ReduxProviderConfig *cfg = &p->config;
    582     const char *business_name;
    583     const char *currency = NULL;
    584     const json_t *methods;
    585     const json_t *currencies = NULL;
    586     bool no_config = false;
    587     struct GNUNET_JSON_Specification cspec[] = {
    588       GNUNET_JSON_spec_array_const ("methods",
    589                                     &methods),
    590       TALER_JSON_spec_amount_any ("annual_fee",
    591                                   &cfg->annual_fee),
    592       TALER_JSON_spec_amount_any ("truth_upload_fee",
    593                                   &cfg->truth_upload_fee),
    594       TALER_JSON_spec_amount_any ("liability_limit",
    595                                   &cfg->liability_limit),
    596       /* Optional so that a state serialized by an older reducer still
    597          parses; synthesized from the scalars below when absent. */
    598       GNUNET_JSON_spec_mark_optional (
    599         TALER_JSON_spec_amount_list ("annual_fees",
    600                                      &cfg->annual_fees),
    601         NULL),
    602       GNUNET_JSON_spec_mark_optional (
    603         TALER_JSON_spec_amount_list ("truth_upload_fees",
    604                                      &cfg->truth_upload_fees),
    605         NULL),
    606       GNUNET_JSON_spec_mark_optional (
    607         TALER_JSON_spec_amount_list ("liability_limits",
    608                                      &cfg->liability_limits),
    609         NULL),
    610       GNUNET_JSON_spec_mark_optional (
    611         GNUNET_JSON_spec_array_const ("currencies",
    612                                       &currencies),
    613         NULL),
    614       GNUNET_JSON_spec_string ("business_name",
    615                                &business_name),
    616       GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
    617                                &cfg->storage_limit_in_megabytes),
    618       GNUNET_JSON_spec_fixed_auto ("provider_salt",
    619                                    &cfg->provider_salt),
    620       GNUNET_JSON_spec_uint32 ("http_status",
    621                                &cfg->http_status),
    622       GNUNET_JSON_spec_mark_optional (
    623         GNUNET_JSON_spec_string ("currency",
    624                                  &currency),
    625         NULL),
    626       GNUNET_JSON_spec_end ()
    627     };
    628     size_t index;
    629     json_t *m;
    630 
    631     if (NULL == json_object_get (val,
    632                                  "methods"))
    633       no_config = true;
    634     if (no_config)
    635     {
    636       if (ANASTASIS_RPS_OK == p->status)
    637       {
    638         *detail = set_detail ("authentication_providers",
    639                               "methods");
    640         return GNUNET_SYSERR;
    641       }
    642       return GNUNET_OK;
    643     }
    644     if (GNUNET_OK !=
    645         GNUNET_JSON_parse (val,
    646                            cspec,
    647                            NULL, NULL))
    648     {
    649       *detail = set_detail ("authentication_providers",
    650                             NULL);
    651       return GNUNET_SYSERR;
    652     }
    653     p->have_config = true;
    654     cfg->business_name = GNUNET_strdup (business_name);
    655     cfg->currency = dup_or_null (currency);
    656     if (0 == cfg->annual_fees.tal_len)
    657       GNUNET_array_append (cfg->annual_fees.tal,
    658                            cfg->annual_fees.tal_len,
    659                            cfg->annual_fee);
    660     if (0 == cfg->truth_upload_fees.tal_len)
    661       GNUNET_array_append (cfg->truth_upload_fees.tal,
    662                            cfg->truth_upload_fees.tal_len,
    663                            cfg->truth_upload_fee);
    664     if (0 == cfg->liability_limits.tal_len)
    665       GNUNET_array_append (cfg->liability_limits.tal,
    666                            cfg->liability_limits.tal_len,
    667                            cfg->liability_limit);
    668     if (NULL != currencies)
    669     {
    670       size_t cidx;
    671       json_t *cv;
    672 
    673       json_array_foreach (currencies, cidx, cv)
    674       {
    675         if (! json_is_string (cv))
    676         {
    677           *detail = set_detail ("authentication_providers",
    678                                 "currencies");
    679           return GNUNET_SYSERR;
    680         }
    681         GNUNET_array_append (cfg->currencies,
    682                              cfg->currencies_len,
    683                              GNUNET_strdup (json_string_value (cv)));
    684       }
    685     }
    686     if (0 == cfg->currencies_len)
    687       GNUNET_array_append (cfg->currencies,
    688                            cfg->currencies_len,
    689                            GNUNET_strdup (cfg->annual_fee.currency));
    690     cfg->methods_len = (unsigned int) json_array_size (methods);
    691     cfg->methods = GNUNET_new_array (cfg->methods_len,
    692                                      struct ANASTASIS_ReduxMethodSpec);
    693     json_array_foreach (methods, index, m)
    694     {
    695       static const char *const mallowed[] = {
    696         "type",
    697         "usage_fee",
    698         "usage_fees",
    699         NULL
    700       };
    701       struct ANASTASIS_ReduxMethodSpec *ms = &cfg->methods[index];
    702       const char *type;
    703       struct GNUNET_JSON_Specification mspec[] = {
    704         GNUNET_JSON_spec_string ("type",
    705                                  &type),
    706         TALER_JSON_spec_amount_any ("usage_fee",
    707                                     &ms->usage_fee),
    708         GNUNET_JSON_spec_mark_optional (
    709           TALER_JSON_spec_amount_list ("usage_fees",
    710                                        &ms->usage_fees),
    711           NULL),
    712         GNUNET_JSON_spec_end ()
    713       };
    714 
    715       if (GNUNET_OK !=
    716           check_fields (m,
    717                         "authentication_providers.methods",
    718                         mallowed,
    719                         detail))
    720         return GNUNET_SYSERR;
    721       if (GNUNET_OK !=
    722           GNUNET_JSON_parse (m,
    723                              mspec,
    724                              NULL, NULL))
    725       {
    726         *detail = set_detail ("authentication_providers.methods",
    727                               NULL);
    728         return GNUNET_SYSERR;
    729       }
    730       ms->type = GNUNET_strdup (type);
    731       if (0 == ms->usage_fees.tal_len)
    732         GNUNET_array_append (ms->usage_fees.tal,
    733                              ms->usage_fees.tal_len,
    734                              ms->usage_fee);
    735     }
    736   }
    737   return GNUNET_OK;
    738 }
    739 
    740 
    741 /**
    742  * Parse the `authentication_providers` object.
    743  *
    744  * @param obj the object, may be NULL
    745  * @param[out] common state to fill in
    746  * @param[out] detail set to the offending field on failure
    747  * @return #GNUNET_OK on success
    748  */
    749 static enum GNUNET_GenericReturnValue
    750 parse_providers (const json_t *obj,
    751                  struct ANASTASIS_ReduxCommon *common,
    752                  const char **detail)
    753 {
    754   const char *url;
    755   json_t *val;
    756   unsigned int off = 0;
    757 
    758   if (NULL == obj)
    759     return GNUNET_OK;
    760   if (! json_is_object (obj))
    761   {
    762     *detail = set_detail ("authentication_providers",
    763                           NULL);
    764     return GNUNET_SYSERR;
    765   }
    766   common->have_providers = true;
    767   common->providers_len = (unsigned int) json_object_size (obj);
    768   common->providers = GNUNET_new_array (common->providers_len,
    769                                         struct ANASTASIS_ReduxProvider);
    770   json_object_foreach ((json_t *) obj, url, val)
    771   {
    772     if (GNUNET_OK !=
    773         parse_provider (url,
    774                         val,
    775                         &common->providers[off],
    776                         detail))
    777       return GNUNET_SYSERR;
    778     off++;
    779   }
    780   return GNUNET_OK;
    781 }
    782 
    783 
    784 /**
    785  * Parse the fields shared by the backup and recovery variants.
    786  *
    787  * @param json the state
    788  * @param[out] common state to fill in
    789  * @param[out] detail set to the offending field on failure
    790  * @return #GNUNET_OK on success
    791  */
    792 static enum GNUNET_GenericReturnValue
    793 parse_common (const json_t *json,
    794               struct ANASTASIS_ReduxCommon *common,
    795               const char **detail)
    796 {
    797   const json_t *continents = NULL;
    798   const json_t *countries = NULL;
    799   const json_t *required_attributes = NULL;
    800   const json_t *providers = NULL;
    801   const json_t *identity_attributes = NULL;
    802   const json_t *currencies = NULL;
    803   const char *selected_continent = NULL;
    804   const char *selected_country = NULL;
    805   const char *preferred_currency = NULL;
    806   struct GNUNET_JSON_Specification spec[] = {
    807     GNUNET_JSON_spec_mark_optional (
    808       GNUNET_JSON_spec_array_const ("continents",
    809                                     &continents),
    810       NULL),
    811     GNUNET_JSON_spec_mark_optional (
    812       GNUNET_JSON_spec_array_const ("countries",
    813                                     &countries),
    814       NULL),
    815     GNUNET_JSON_spec_mark_optional (
    816       GNUNET_JSON_spec_array_const ("required_attributes",
    817                                     &required_attributes),
    818       NULL),
    819     GNUNET_JSON_spec_mark_optional (
    820       GNUNET_JSON_spec_object_const ("authentication_providers",
    821                                      &providers),
    822       NULL),
    823     GNUNET_JSON_spec_mark_optional (
    824       GNUNET_JSON_spec_object_const ("identity_attributes",
    825                                      &identity_attributes),
    826       NULL),
    827     GNUNET_JSON_spec_mark_optional (
    828       GNUNET_JSON_spec_array_const ("currencies",
    829                                     &currencies),
    830       NULL),
    831     GNUNET_JSON_spec_mark_optional (
    832       GNUNET_JSON_spec_string ("selected_continent",
    833                                &selected_continent),
    834       NULL),
    835     GNUNET_JSON_spec_mark_optional (
    836       GNUNET_JSON_spec_string ("selected_country",
    837                                &selected_country),
    838       NULL),
    839     GNUNET_JSON_spec_mark_optional (
    840       GNUNET_JSON_spec_string ("preferred_currency",
    841                                &preferred_currency),
    842       NULL),
    843     GNUNET_JSON_spec_end ()
    844   };
    845 
    846   if (GNUNET_OK !=
    847       GNUNET_JSON_parse (json,
    848                          spec,
    849                          NULL, NULL))
    850   {
    851     *detail = set_detail ("state",
    852                           NULL);
    853     return GNUNET_SYSERR;
    854   }
    855   common->selected_continent = dup_or_null (selected_continent);
    856   common->selected_country = dup_or_null (selected_country);
    857   common->preferred_currency = dup_or_null (preferred_currency);
    858   common->identity_attributes = json_incref ((json_t *) identity_attributes);
    859   if (NULL != currencies)
    860   {
    861     size_t index;
    862     json_t *val;
    863 
    864     common->have_currencies = true;
    865     common->currencies_len = (unsigned int) json_array_size (currencies);
    866     common->currencies = GNUNET_new_array (common->currencies_len,
    867                                            char *);
    868     json_array_foreach (currencies, index, val)
    869     {
    870       if (! json_is_string (val))
    871       {
    872         *detail = set_detail ("currencies",
    873                               NULL);
    874         return GNUNET_SYSERR;
    875       }
    876       common->currencies[index] = GNUNET_strdup (json_string_value (val));
    877     }
    878   }
    879   if ( (GNUNET_OK !=
    880         parse_continents (continents,
    881                           common,
    882                           detail)) ||
    883        (GNUNET_OK !=
    884         parse_countries (countries,
    885                          common,
    886                          detail)) ||
    887        (GNUNET_OK !=
    888         parse_required_attributes (required_attributes,
    889                                    common,
    890                                    detail)) ||
    891        (GNUNET_OK !=
    892         parse_providers (providers,
    893                          common,
    894                          detail)) )
    895     return GNUNET_SYSERR;
    896   return GNUNET_OK;
    897 }
    898 
    899 
    900 /**
    901  * Parse the `authentication_methods` array.
    902  *
    903  * @param arr the array, may be NULL
    904  * @param[out] b backup state to fill in
    905  * @param[out] detail set to the offending field on failure
    906  * @return #GNUNET_OK on success
    907  */
    908 static enum GNUNET_GenericReturnValue
    909 parse_auth_methods (const json_t *arr,
    910                     struct ANASTASIS_ReduxBackup *b,
    911                     const char **detail)
    912 {
    913   static const char *const allowed[] = {
    914     "type",
    915     "instructions",
    916     "challenge",
    917     "mime_type",
    918     NULL
    919   };
    920   size_t index;
    921   json_t *val;
    922 
    923   if (NULL == arr)
    924     return GNUNET_OK;
    925   b->have_authentication_methods = true;
    926   b->authentication_methods_len = (unsigned int) json_array_size (arr);
    927   b->authentication_methods
    928     = GNUNET_new_array (b->authentication_methods_len,
    929                         struct ANASTASIS_ReduxAuthMethod);
    930   json_array_foreach (arr, index, val)
    931   {
    932     struct ANASTASIS_ReduxAuthMethod *am = &b->authentication_methods[index];
    933     const char *type;
    934     const char *instructions;
    935     const char *mime_type = NULL;
    936     void *challenge;
    937     size_t challenge_size;
    938     struct GNUNET_JSON_Specification spec[] = {
    939       GNUNET_JSON_spec_string ("type",
    940                                &type),
    941       GNUNET_JSON_spec_string ("instructions",
    942                                &instructions),
    943       GNUNET_JSON_spec_varsize ("challenge",
    944                                 &challenge,
    945                                 &challenge_size),
    946       GNUNET_JSON_spec_mark_optional (
    947         GNUNET_JSON_spec_string ("mime_type",
    948                                  &mime_type),
    949         NULL),
    950       GNUNET_JSON_spec_end ()
    951     };
    952 
    953     if (GNUNET_OK !=
    954         check_fields (val,
    955                       "authentication_methods",
    956                       allowed,
    957                       detail))
    958       return GNUNET_SYSERR;
    959     if (GNUNET_OK !=
    960         GNUNET_JSON_parse (val,
    961                            spec,
    962                            NULL, NULL))
    963     {
    964       *detail = set_detail ("authentication_methods",
    965                             NULL);
    966       return GNUNET_SYSERR;
    967     }
    968     am->type = GNUNET_strdup (type);
    969     am->instructions = GNUNET_strdup (instructions);
    970     am->mime_type = dup_or_null (mime_type);
    971     am->challenge = GNUNET_memdup (challenge,
    972                                    challenge_size);
    973     am->challenge_size = challenge_size;
    974     GNUNET_JSON_parse_free (spec);
    975   }
    976   return GNUNET_OK;
    977 }
    978 
    979 
    980 /**
    981  * Parse the `policies` array.
    982  *
    983  * @param arr the array, may be NULL
    984  * @param[out] b backup state to fill in
    985  * @param[out] detail set to the offending field on failure
    986  * @return #GNUNET_OK on success
    987  */
    988 static enum GNUNET_GenericReturnValue
    989 parse_policies (const json_t *arr,
    990                 struct ANASTASIS_ReduxBackup *b,
    991                 const char **detail)
    992 {
    993   static const char *const pallowed[] = {
    994     "methods",
    995     "recovery_cost",
    996     NULL
    997   };
    998   static const char *const mallowed[] = {
    999     "authentication_method",
   1000     "provider",
   1001     "truth",
   1002     NULL
   1003   };
   1004   size_t index;
   1005   json_t *val;
   1006 
   1007   if (NULL == arr)
   1008     return GNUNET_OK;
   1009   b->have_policies = true;
   1010   b->policies_len = (unsigned int) json_array_size (arr);
   1011   b->policies = GNUNET_new_array (b->policies_len,
   1012                                   struct ANASTASIS_ReduxPolicy);
   1013   json_array_foreach (arr, index, val)
   1014   {
   1015     struct ANASTASIS_ReduxPolicy *p = &b->policies[index];
   1016     const json_t *methods;
   1017     size_t mindex;
   1018     json_t *m;
   1019     bool no_cost;
   1020     struct GNUNET_JSON_Specification spec[] = {
   1021       GNUNET_JSON_spec_array_const ("methods",
   1022                                     &methods),
   1023       GNUNET_JSON_spec_mark_optional (
   1024         TALER_JSON_spec_amount_any ("recovery_cost",
   1025                                     &p->recovery_cost),
   1026         &no_cost),
   1027       GNUNET_JSON_spec_end ()
   1028     };
   1029 
   1030     if (GNUNET_OK !=
   1031         check_fields (val,
   1032                       "policies",
   1033                       pallowed,
   1034                       detail))
   1035       return GNUNET_SYSERR;
   1036     if (GNUNET_OK !=
   1037         GNUNET_JSON_parse (val,
   1038                            spec,
   1039                            NULL, NULL))
   1040     {
   1041       *detail = set_detail ("policies",
   1042                             "methods");
   1043       return GNUNET_SYSERR;
   1044     }
   1045     p->have_recovery_cost = ! no_cost;
   1046     p->methods_len = (unsigned int) json_array_size (methods);
   1047     p->methods = GNUNET_new_array (p->methods_len,
   1048                                    struct ANASTASIS_ReduxPolicyMethod);
   1049     json_array_foreach (methods, mindex, m)
   1050     {
   1051       struct ANASTASIS_ReduxPolicyMethod *pm = &p->methods[mindex];
   1052       const char *provider;
   1053       const json_t *truth = NULL;
   1054       struct GNUNET_JSON_Specification mspec[] = {
   1055         GNUNET_JSON_spec_uint32 ("authentication_method",
   1056                                  &pm->authentication_method.idx),
   1057         GNUNET_JSON_spec_string ("provider",
   1058                                  &provider),
   1059         GNUNET_JSON_spec_mark_optional (
   1060           GNUNET_JSON_spec_object_const ("truth",
   1061                                          &truth),
   1062           NULL),
   1063         GNUNET_JSON_spec_end ()
   1064       };
   1065 
   1066       if (GNUNET_OK !=
   1067           check_fields (m,
   1068                         "policies.methods",
   1069                         mallowed,
   1070                         detail))
   1071         return GNUNET_SYSERR;
   1072       if (GNUNET_OK !=
   1073           GNUNET_JSON_parse (m,
   1074                              mspec,
   1075                              NULL, NULL))
   1076       {
   1077         *detail = set_detail ("policies.methods",
   1078                               NULL);
   1079         return GNUNET_SYSERR;
   1080       }
   1081       ANASTASIS_REDUX_provider_url_set_ (&pm->provider,
   1082                                          provider);
   1083       if (NULL != truth)
   1084       {
   1085         uint32_t us;
   1086         struct GNUNET_JSON_Specification tspec[] = {
   1087           GNUNET_JSON_spec_uint32 ("upload_status",
   1088                                    &us),
   1089           GNUNET_JSON_spec_end ()
   1090         };
   1091 
   1092         if (GNUNET_OK !=
   1093             GNUNET_JSON_parse (truth,
   1094                                tspec,
   1095                                NULL, NULL))
   1096         {
   1097           *detail = set_detail ("policies.methods.truth",
   1098                                 "upload_status");
   1099           return GNUNET_SYSERR;
   1100         }
   1101         pm->upload_status = (enum ANASTASIS_UploadStatus) us;
   1102         pm->truth = ANASTASIS_truth_from_json (truth);
   1103         if (NULL == pm->truth)
   1104         {
   1105           *detail = set_detail ("policies.methods",
   1106                                 "truth");
   1107           return GNUNET_SYSERR;
   1108         }
   1109       }
   1110     }
   1111   }
   1112   return GNUNET_OK;
   1113 }
   1114 
   1115 
   1116 /**
   1117  * Parse the `policy_providers` array.
   1118  *
   1119  * @param arr the array, may be NULL
   1120  * @param[out] b backup state to fill in
   1121  * @param[out] detail set to the offending field on failure
   1122  * @return #GNUNET_OK on success
   1123  */
   1124 static enum GNUNET_GenericReturnValue
   1125 parse_policy_providers (const json_t *arr,
   1126                         struct ANASTASIS_ReduxBackup *b,
   1127                         const char **detail)
   1128 {
   1129   static const char *const allowed[] = {
   1130     "provider_url",
   1131     "payment_secret",
   1132     NULL
   1133   };
   1134   size_t index;
   1135   json_t *val;
   1136 
   1137   if (NULL == arr)
   1138     return GNUNET_OK;
   1139   b->have_policy_providers = true;
   1140   b->policy_providers_len = (unsigned int) json_array_size (arr);
   1141   b->policy_providers = GNUNET_new_array (b->policy_providers_len,
   1142                                           struct
   1143                                           ANASTASIS_ReduxPolicyProvider);
   1144   json_array_foreach (arr, index, val)
   1145   {
   1146     struct ANASTASIS_ReduxPolicyProvider *pp = &b->policy_providers[index];
   1147     const char *url;
   1148     bool no_ps;
   1149     struct GNUNET_JSON_Specification spec[] = {
   1150       GNUNET_JSON_spec_string ("provider_url",
   1151                                &url),
   1152       GNUNET_JSON_spec_mark_optional (
   1153         GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1154                                      &pp->payment_secret),
   1155         &no_ps),
   1156       GNUNET_JSON_spec_end ()
   1157     };
   1158 
   1159     if (GNUNET_OK !=
   1160         check_fields (val,
   1161                       "policy_providers",
   1162                       allowed,
   1163                       detail))
   1164       return GNUNET_SYSERR;
   1165     if (GNUNET_OK !=
   1166         GNUNET_JSON_parse (val,
   1167                            spec,
   1168                            NULL, NULL))
   1169     {
   1170       *detail = set_detail ("policy_providers",
   1171                             NULL);
   1172       return GNUNET_SYSERR;
   1173     }
   1174     ANASTASIS_REDUX_provider_url_set_ (&pp->provider_url,
   1175                                        url);
   1176     pp->have_payment_secret = ! no_ps;
   1177   }
   1178   return GNUNET_OK;
   1179 }
   1180 
   1181 
   1182 /**
   1183  * Parse the `success_details` object.
   1184  *
   1185  * @param obj the object, may be NULL
   1186  * @param[out] b backup state to fill in
   1187  * @param[out] detail set to the offending field on failure
   1188  * @return #GNUNET_OK on success
   1189  */
   1190 static enum GNUNET_GenericReturnValue
   1191 parse_success_details (const json_t *obj,
   1192                        struct ANASTASIS_ReduxBackup *b,
   1193                        const char **detail)
   1194 {
   1195   static const char *const allowed[] = {
   1196     "policy_version",
   1197     "policy_expiration",
   1198     NULL
   1199   };
   1200   const char *url;
   1201   json_t *val;
   1202   unsigned int off = 0;
   1203 
   1204   if (NULL == obj)
   1205     return GNUNET_OK;
   1206   b->have_success_details = true;
   1207   b->success_details_len = (unsigned int) json_object_size (obj);
   1208   b->success_details = GNUNET_new_array (b->success_details_len,
   1209                                          struct ANASTASIS_ReduxSuccessDetail);
   1210   json_object_foreach ((json_t *) obj, url, val)
   1211   {
   1212     struct ANASTASIS_ReduxSuccessDetail *sd = &b->success_details[off];
   1213     struct GNUNET_JSON_Specification spec[] = {
   1214       GNUNET_JSON_spec_uint32 ("policy_version",
   1215                                &sd->policy_version),
   1216       GNUNET_JSON_spec_timestamp ("policy_expiration",
   1217                                   &sd->policy_expiration),
   1218       GNUNET_JSON_spec_end ()
   1219     };
   1220 
   1221     if (GNUNET_OK !=
   1222         check_fields (val,
   1223                       "success_details",
   1224                       allowed,
   1225                       detail))
   1226       return GNUNET_SYSERR;
   1227     if (GNUNET_OK !=
   1228         GNUNET_JSON_parse (val,
   1229                            spec,
   1230                            NULL, NULL))
   1231     {
   1232       *detail = set_detail ("success_details",
   1233                             NULL);
   1234       return GNUNET_SYSERR;
   1235     }
   1236     ANASTASIS_REDUX_provider_url_set_ (&sd->provider_url,
   1237                                        url);
   1238     off++;
   1239   }
   1240   return GNUNET_OK;
   1241 }
   1242 
   1243 
   1244 /**
   1245  * Parse the backup-specific parts of @a json.
   1246  *
   1247  * @param json the state
   1248  * @param[out] b backup state to fill in
   1249  * @param[out] detail set to the offending field on failure
   1250  * @return #GNUNET_OK on success
   1251  */
   1252 static enum GNUNET_GenericReturnValue
   1253 parse_backup (const json_t *json,
   1254               struct ANASTASIS_ReduxBackup *b,
   1255               const char **detail)
   1256 {
   1257   const char *state_str;
   1258   const json_t *auth_methods = NULL;
   1259   const json_t *policies = NULL;
   1260   const json_t *policy_providers = NULL;
   1261   const json_t *upload_fees = NULL;
   1262   const json_t *payments = NULL;
   1263   const json_t *policy_payment_requests = NULL;
   1264   const json_t *success_details = NULL;
   1265   const json_t *core_secret = NULL;
   1266   const json_t *pay_arguments = NULL;
   1267   const char *secret_name = NULL;
   1268   bool no_expiration;
   1269   struct GNUNET_JSON_Specification spec[] = {
   1270     GNUNET_JSON_spec_string ("backup_state",
   1271                              &state_str),
   1272     GNUNET_JSON_spec_mark_optional (
   1273       GNUNET_JSON_spec_array_const ("authentication_methods",
   1274                                     &auth_methods),
   1275       NULL),
   1276     GNUNET_JSON_spec_mark_optional (
   1277       GNUNET_JSON_spec_array_const ("policies",
   1278                                     &policies),
   1279       NULL),
   1280     GNUNET_JSON_spec_mark_optional (
   1281       GNUNET_JSON_spec_array_const ("policy_providers",
   1282                                     &policy_providers),
   1283       NULL),
   1284     GNUNET_JSON_spec_mark_optional (
   1285       GNUNET_JSON_spec_array_const ("upload_fees",
   1286                                     &upload_fees),
   1287       NULL),
   1288     GNUNET_JSON_spec_mark_optional (
   1289       GNUNET_JSON_spec_array_const ("payments",
   1290                                     &payments),
   1291       NULL),
   1292     GNUNET_JSON_spec_mark_optional (
   1293       GNUNET_JSON_spec_array_const ("policy_payment_requests",
   1294                                     &policy_payment_requests),
   1295       NULL),
   1296     GNUNET_JSON_spec_mark_optional (
   1297       GNUNET_JSON_spec_object_const ("success_details",
   1298                                      &success_details),
   1299       NULL),
   1300     GNUNET_JSON_spec_mark_optional (
   1301       GNUNET_JSON_spec_object_const ("core_secret",
   1302                                      &core_secret),
   1303       NULL),
   1304     GNUNET_JSON_spec_mark_optional (
   1305       GNUNET_JSON_spec_object_const ("pay_arguments",
   1306                                      &pay_arguments),
   1307       NULL),
   1308     GNUNET_JSON_spec_mark_optional (
   1309       GNUNET_JSON_spec_string ("secret_name",
   1310                                &secret_name),
   1311       NULL),
   1312     GNUNET_JSON_spec_mark_optional (
   1313       GNUNET_JSON_spec_timestamp ("expiration",
   1314                                   &b->expiration),
   1315       &no_expiration),
   1316     GNUNET_JSON_spec_end ()
   1317   };
   1318 
   1319   if (GNUNET_OK !=
   1320       GNUNET_JSON_parse (json,
   1321                          spec,
   1322                          NULL, NULL))
   1323   {
   1324     *detail = set_detail ("state",
   1325                           "backup_state");
   1326     return GNUNET_SYSERR;
   1327   }
   1328   b->state = ANASTASIS_backup_state_from_string_ (state_str);
   1329   if (ANASTASIS_BACKUP_STATE_INVALID == b->state)
   1330   {
   1331     *detail = set_detail ("state",
   1332                           "backup_state");
   1333     return GNUNET_SYSERR;
   1334   }
   1335   b->have_expiration = ! no_expiration;
   1336   b->secret_name = dup_or_null (secret_name);
   1337   b->core_secret = json_incref ((json_t *) core_secret);
   1338   if ( (GNUNET_OK !=
   1339         parse_auth_methods (auth_methods,
   1340                             b,
   1341                             detail)) ||
   1342        (GNUNET_OK !=
   1343         parse_policies (policies,
   1344                         b,
   1345                         detail)) ||
   1346        (GNUNET_OK !=
   1347         parse_policy_providers (policy_providers,
   1348                                 b,
   1349                                 detail)) ||
   1350        (GNUNET_OK !=
   1351         parse_success_details (success_details,
   1352                                b,
   1353                                detail)) )
   1354     return GNUNET_SYSERR;
   1355   if (NULL != upload_fees)
   1356   {
   1357     static const char *const allowed[] = {
   1358       "fee",
   1359       NULL
   1360     };
   1361     size_t index;
   1362     json_t *val;
   1363 
   1364     b->have_upload_fees = true;
   1365     b->upload_fees_len = (unsigned int) json_array_size (upload_fees);
   1366     b->upload_fees = GNUNET_new_array (b->upload_fees_len,
   1367                                        struct TALER_Amount);
   1368     json_array_foreach (upload_fees, index, val)
   1369     {
   1370       struct GNUNET_JSON_Specification fspec[] = {
   1371         TALER_JSON_spec_amount_any ("fee",
   1372                                     &b->upload_fees[index]),
   1373         GNUNET_JSON_spec_end ()
   1374       };
   1375 
   1376       if ( (GNUNET_OK !=
   1377             check_fields (val,
   1378                           "upload_fees",
   1379                           allowed,
   1380                           detail)) ||
   1381            (GNUNET_OK !=
   1382             GNUNET_JSON_parse (val,
   1383                                fspec,
   1384                                NULL, NULL)) )
   1385       {
   1386         *detail = set_detail ("upload_fees",
   1387                               "fee");
   1388         return GNUNET_SYSERR;
   1389       }
   1390     }
   1391   }
   1392   if (NULL != payments)
   1393   {
   1394     size_t index;
   1395     json_t *val;
   1396 
   1397     b->have_payments = true;
   1398     b->payments_len = (unsigned int) json_array_size (payments);
   1399     b->payments = GNUNET_new_array (b->payments_len,
   1400                                     char *);
   1401     json_array_foreach (payments, index, val)
   1402     {
   1403       if (! json_is_string (val))
   1404       {
   1405         *detail = set_detail ("payments",
   1406                               NULL);
   1407         return GNUNET_SYSERR;
   1408       }
   1409       b->payments[index] = GNUNET_strdup (json_string_value (val));
   1410     }
   1411   }
   1412   if (NULL != policy_payment_requests)
   1413   {
   1414     static const char *const allowed[] = {
   1415       "payto",
   1416       "provider",
   1417       NULL
   1418     };
   1419     size_t index;
   1420     json_t *val;
   1421 
   1422     b->have_policy_payment_requests = true;
   1423     b->policy_payment_requests_len
   1424       = (unsigned int) json_array_size (policy_payment_requests);
   1425     b->policy_payment_requests
   1426       = GNUNET_new_array (b->policy_payment_requests_len,
   1427                           struct ANASTASIS_ReduxPolicyPaymentRequest);
   1428     json_array_foreach (policy_payment_requests, index, val)
   1429     {
   1430       struct ANASTASIS_ReduxPolicyPaymentRequest *ppr
   1431         = &b->policy_payment_requests[index];
   1432       const char *payto;
   1433       const char *provider;
   1434       struct GNUNET_JSON_Specification pspec[] = {
   1435         GNUNET_JSON_spec_string ("payto",
   1436                                  &payto),
   1437         GNUNET_JSON_spec_string ("provider",
   1438                                  &provider),
   1439         GNUNET_JSON_spec_end ()
   1440       };
   1441 
   1442       if ( (GNUNET_OK !=
   1443             check_fields (val,
   1444                           "policy_payment_requests",
   1445                           allowed,
   1446                           detail)) ||
   1447            (GNUNET_OK !=
   1448             GNUNET_JSON_parse (val,
   1449                                pspec,
   1450                                NULL, NULL)) )
   1451       {
   1452         *detail = set_detail ("policy_payment_requests",
   1453                               NULL);
   1454         return GNUNET_SYSERR;
   1455       }
   1456       ppr->payto = GNUNET_strdup (payto);
   1457       ANASTASIS_REDUX_provider_url_set_ (&ppr->provider,
   1458                                          provider);
   1459     }
   1460   }
   1461   if (NULL != pay_arguments)
   1462   {
   1463     static const char *const allowed[] = {
   1464       "timeout",
   1465       NULL
   1466     };
   1467     bool no_timeout;
   1468     struct GNUNET_JSON_Specification pspec[] = {
   1469       GNUNET_JSON_spec_mark_optional (
   1470         GNUNET_JSON_spec_relative_time ("timeout",
   1471                                         &b->pay_arguments.timeout),
   1472         &no_timeout),
   1473       GNUNET_JSON_spec_end ()
   1474     };
   1475 
   1476     if ( (GNUNET_OK !=
   1477           check_fields (pay_arguments,
   1478                         "pay_arguments",
   1479                         allowed,
   1480                         detail)) ||
   1481          (GNUNET_OK !=
   1482           GNUNET_JSON_parse (pay_arguments,
   1483                              pspec,
   1484                              NULL, NULL)) )
   1485     {
   1486       *detail = set_detail ("pay_arguments",
   1487                             "timeout");
   1488       return GNUNET_SYSERR;
   1489     }
   1490     b->have_pay_arguments = true;
   1491     b->pay_arguments.have_timeout = ! no_timeout;
   1492   }
   1493   return GNUNET_OK;
   1494 }
   1495 
   1496 
   1497 /**
   1498  * Parse the `recovery_information` object.
   1499  *
   1500  * @param obj the object
   1501  * @param[out] rr recovery state to fill in
   1502  * @param[out] detail set to the offending field on failure
   1503  * @return #GNUNET_OK on success
   1504  */
   1505 static enum GNUNET_GenericReturnValue
   1506 parse_recovery_info (const json_t *obj,
   1507                      struct ANASTASIS_ReduxRecovery *rr,
   1508                      const char **detail)
   1509 {
   1510   static const char *const allowed[] = {
   1511     "challenges",
   1512     "policies",
   1513     "secret_name",
   1514     "provider_url",
   1515     "version",
   1516     NULL
   1517   };
   1518   static const char *const callowed[] = {
   1519     "uuid",
   1520     "uuid-display",
   1521     "type",
   1522     "instructions",
   1523     "answer",
   1524     "payment_secret",
   1525     NULL
   1526   };
   1527   static const char *const pallowed[] = {
   1528     "uuid",
   1529     NULL
   1530   };
   1531   struct ANASTASIS_ReduxRecoveryInfo *ri;
   1532   const json_t *challenges;
   1533   const json_t *policies;
   1534   const char *secret_name = NULL;
   1535   const char *provider_url;
   1536   size_t index;
   1537   json_t *val;
   1538   struct GNUNET_JSON_Specification spec[] = {
   1539     GNUNET_JSON_spec_array_const ("challenges",
   1540                                   &challenges),
   1541     GNUNET_JSON_spec_array_const ("policies",
   1542                                   &policies),
   1543     GNUNET_JSON_spec_mark_optional (
   1544       GNUNET_JSON_spec_string ("secret_name",
   1545                                &secret_name),
   1546       NULL),
   1547     GNUNET_JSON_spec_string ("provider_url",
   1548                              &provider_url),
   1549     GNUNET_JSON_spec_end ()
   1550   };
   1551 
   1552   ri = GNUNET_new (struct ANASTASIS_ReduxRecoveryInfo);
   1553   rr->ri = ri;
   1554   if (GNUNET_OK !=
   1555       check_fields (obj,
   1556                     "recovery_information",
   1557                     allowed,
   1558                     detail))
   1559     return GNUNET_SYSERR;
   1560   {
   1561     struct GNUNET_JSON_Specification vspec[] = {
   1562       GNUNET_JSON_spec_uint32 ("version",
   1563                                &ri->version),
   1564       GNUNET_JSON_spec_end ()
   1565     };
   1566 
   1567     if (GNUNET_OK !=
   1568         GNUNET_JSON_parse (obj,
   1569                            vspec,
   1570                            NULL, NULL))
   1571     {
   1572       *detail = set_detail ("recovery_information",
   1573                             "version");
   1574       return GNUNET_SYSERR;
   1575     }
   1576   }
   1577   if (GNUNET_OK !=
   1578       GNUNET_JSON_parse (obj,
   1579                          spec,
   1580                          NULL, NULL))
   1581   {
   1582     *detail = set_detail ("recovery_information",
   1583                           NULL);
   1584     return GNUNET_SYSERR;
   1585   }
   1586   ri->secret_name = dup_or_null (secret_name);
   1587   ANASTASIS_REDUX_provider_url_set_ (&ri->provider_url,
   1588                                      provider_url);
   1589   ri->challenges_len = (unsigned int) json_array_size (challenges);
   1590   ri->challenges = GNUNET_new_array (ri->challenges_len,
   1591                                      struct ANASTASIS_ReduxChallengeInfo);
   1592   json_array_foreach (challenges, index, val)
   1593   {
   1594     struct ANASTASIS_ReduxChallengeInfo *ci = &ri->challenges[index];
   1595     const char *type;
   1596     const char *instructions;
   1597     const char *answer = NULL;
   1598     bool no_ps;
   1599     struct GNUNET_JSON_Specification cspec[] = {
   1600       GNUNET_JSON_spec_fixed_auto ("uuid",
   1601                                    &ci->uuid),
   1602       GNUNET_JSON_spec_string ("type",
   1603                                &type),
   1604       GNUNET_JSON_spec_string ("instructions",
   1605                                &instructions),
   1606       GNUNET_JSON_spec_mark_optional (
   1607         GNUNET_JSON_spec_string ("answer",
   1608                                  &answer),
   1609         NULL),
   1610       GNUNET_JSON_spec_mark_optional (
   1611         GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1612                                      &ci->payment_secret),
   1613         &no_ps),
   1614       GNUNET_JSON_spec_end ()
   1615     };
   1616 
   1617     if (GNUNET_OK !=
   1618         check_fields (val,
   1619                       "recovery_information.challenges",
   1620                       callowed,
   1621                       detail))
   1622       return GNUNET_SYSERR;
   1623     if (GNUNET_OK !=
   1624         GNUNET_JSON_parse (val,
   1625                            cspec,
   1626                            NULL, NULL))
   1627     {
   1628       *detail = set_detail ("recovery_information.challenges",
   1629                             NULL);
   1630       return GNUNET_SYSERR;
   1631     }
   1632     ci->type = GNUNET_strdup (type);
   1633     ci->instructions = GNUNET_strdup (instructions);
   1634     ci->answer = dup_or_null (answer);
   1635     ci->have_payment_secret = ! no_ps;
   1636   }
   1637   ri->policies_len = (unsigned int) json_array_size (policies);
   1638   ri->policies = GNUNET_new_array (ri->policies_len,
   1639                                    struct ANASTASIS_ReduxRecoveryPolicy);
   1640   json_array_foreach (policies, index, val)
   1641   {
   1642     struct ANASTASIS_ReduxRecoveryPolicy *p = &ri->policies[index];
   1643     size_t uindex;
   1644     json_t *u;
   1645 
   1646     if (! json_is_array (val))
   1647     {
   1648       *detail = set_detail ("recovery_information",
   1649                             "policies");
   1650       return GNUNET_SYSERR;
   1651     }
   1652     p->uuids_len = (unsigned int) json_array_size (val);
   1653     p->uuids = GNUNET_new_array (p->uuids_len,
   1654                                  struct ANASTASIS_CRYPTO_TruthUUIDP);
   1655     json_array_foreach (val, uindex, u)
   1656     {
   1657       struct GNUNET_JSON_Specification uspec[] = {
   1658         GNUNET_JSON_spec_fixed_auto ("uuid",
   1659                                      &p->uuids[uindex]),
   1660         GNUNET_JSON_spec_end ()
   1661       };
   1662 
   1663       if ( (GNUNET_OK !=
   1664             check_fields (u,
   1665                           "recovery_information.policies",
   1666                           pallowed,
   1667                           detail)) ||
   1668            (GNUNET_OK !=
   1669             GNUNET_JSON_parse (u,
   1670                                uspec,
   1671                                NULL, NULL)) )
   1672       {
   1673         *detail = set_detail ("recovery_information.policies",
   1674                               "uuid");
   1675         return GNUNET_SYSERR;
   1676       }
   1677     }
   1678   }
   1679   return GNUNET_OK;
   1680 }
   1681 
   1682 
   1683 /**
   1684  * Parse one entry of the `challenge_feedback` object.
   1685  *
   1686  * @param val the feedback details
   1687  * @param[out] fb feedback entry to fill in
   1688  * @param[out] detail set to the offending field on failure
   1689  * @return #GNUNET_OK on success
   1690  */
   1691 static enum GNUNET_GenericReturnValue
   1692 parse_feedback_details (const json_t *val,
   1693                         struct ANASTASIS_ReduxFeedback *fb,
   1694                         const char **detail)
   1695 {
   1696   static const char *const allowed[] = {
   1697     "state",
   1698     "display_hint",
   1699     "filename",
   1700     "address_hint",
   1701     "taler_pay_uri",
   1702     "provider",
   1703     "payment_secret",
   1704     "http_status",
   1705     "error_code",
   1706     "target_iban",
   1707     "target_business_name",
   1708     "wire_transfer_subject",
   1709     "challenge_amount",
   1710     "request_limit",
   1711     "request_frequency",
   1712     NULL
   1713   };
   1714   const char *state;
   1715   const char *display_hint = NULL;
   1716   struct GNUNET_JSON_Specification spec[] = {
   1717     GNUNET_JSON_spec_string ("state",
   1718                              &state),
   1719     GNUNET_JSON_spec_mark_optional (
   1720       GNUNET_JSON_spec_string ("display_hint",
   1721                                &display_hint),
   1722       NULL),
   1723     GNUNET_JSON_spec_end ()
   1724   };
   1725 
   1726   if (GNUNET_OK !=
   1727       check_fields (val,
   1728                     "challenge_feedback",
   1729                     allowed,
   1730                     detail))
   1731     return GNUNET_SYSERR;
   1732   if (GNUNET_OK !=
   1733       GNUNET_JSON_parse (val,
   1734                          spec,
   1735                          NULL, NULL))
   1736   {
   1737     *detail = set_detail ("challenge_feedback",
   1738                           "state");
   1739     return GNUNET_SYSERR;
   1740   }
   1741   fb->display_hint = dup_or_null (display_hint);
   1742   if (0 == strcmp (state,
   1743                    "code-in-file"))
   1744   {
   1745     const char *filename;
   1746     struct GNUNET_JSON_Specification dspec[] = {
   1747       GNUNET_JSON_spec_string ("filename",
   1748                                &filename),
   1749       GNUNET_JSON_spec_end ()
   1750     };
   1751 
   1752     fb->status = ANASTASIS_RFS_CODE_IN_FILE;
   1753     if (GNUNET_OK !=
   1754         GNUNET_JSON_parse (val,
   1755                            dspec,
   1756                            NULL, NULL))
   1757     {
   1758       *detail = set_detail ("challenge_feedback",
   1759                             "filename");
   1760       return GNUNET_SYSERR;
   1761     }
   1762     fb->details.code_in_file.filename = GNUNET_strdup (filename);
   1763     return GNUNET_OK;
   1764   }
   1765   if (0 == strcmp (state,
   1766                    "send-to-address"))
   1767   {
   1768     const char *address_hint = NULL;
   1769     struct GNUNET_JSON_Specification dspec[] = {
   1770       GNUNET_JSON_spec_mark_optional (
   1771         GNUNET_JSON_spec_string ("address_hint",
   1772                                  &address_hint),
   1773         NULL),
   1774       GNUNET_JSON_spec_end ()
   1775     };
   1776 
   1777     fb->status = ANASTASIS_RFS_SEND_TO_ADDRESS;
   1778     if (GNUNET_OK !=
   1779         GNUNET_JSON_parse (val,
   1780                            dspec,
   1781                            NULL, NULL))
   1782     {
   1783       *detail = set_detail ("challenge_feedback",
   1784                             "address_hint");
   1785       return GNUNET_SYSERR;
   1786     }
   1787     fb->details.send_to_address.address_hint = dup_or_null (address_hint);
   1788     return GNUNET_OK;
   1789   }
   1790   if (0 == strcmp (state,
   1791                    "taler-payment"))
   1792   {
   1793     const char *uri;
   1794     const char *provider;
   1795     struct GNUNET_JSON_Specification dspec[] = {
   1796       GNUNET_JSON_spec_string ("taler_pay_uri",
   1797                                &uri),
   1798       GNUNET_JSON_spec_string ("provider",
   1799                                &provider),
   1800       GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1801                                    &fb->details.taler_payment.payment_secret),
   1802       GNUNET_JSON_spec_end ()
   1803     };
   1804 
   1805     fb->status = ANASTASIS_RFS_TALER_PAYMENT;
   1806     if (GNUNET_OK !=
   1807         GNUNET_JSON_parse (val,
   1808                            dspec,
   1809                            NULL, NULL))
   1810     {
   1811       *detail = set_detail ("challenge_feedback",
   1812                             "taler_pay_uri");
   1813       return GNUNET_SYSERR;
   1814     }
   1815     fb->details.taler_payment.taler_pay_uri = GNUNET_strdup (uri);
   1816     ANASTASIS_REDUX_provider_url_set_ (&fb->details.taler_payment.provider,
   1817                                        provider);
   1818     return GNUNET_OK;
   1819   }
   1820   if ( (0 == strcmp (state,
   1821                      "server-failure")) ||
   1822        (0 == strcmp (state,
   1823                      "truth-unknown")) )
   1824   {
   1825     uint64_t ec;
   1826     struct GNUNET_JSON_Specification dspec[] = {
   1827       GNUNET_JSON_spec_uint64 ("error_code",
   1828                                &ec),
   1829       GNUNET_JSON_spec_uint32 ("http_status",
   1830                                &fb->details.server_failure.http_status),
   1831       GNUNET_JSON_spec_end ()
   1832     };
   1833 
   1834     fb->status = (0 == strcmp (state,
   1835                                "server-failure"))
   1836       ? ANASTASIS_RFS_SERVER_FAILURE
   1837       : ANASTASIS_RFS_TRUTH_UNKNOWN;
   1838     if (GNUNET_OK !=
   1839         GNUNET_JSON_parse (val,
   1840                            dspec,
   1841                            NULL, NULL))
   1842     {
   1843       *detail = set_detail ("challenge_feedback",
   1844                             "error_code");
   1845       return GNUNET_SYSERR;
   1846     }
   1847     fb->details.server_failure.ec = (enum TALER_ErrorCode) ec;
   1848     return GNUNET_OK;
   1849   }
   1850   if (0 == strcmp (state,
   1851                    "iban-instructions"))
   1852   {
   1853     const char *iban;
   1854     const char *business_name;
   1855     const char *subject;
   1856     struct GNUNET_JSON_Specification dspec[] = {
   1857       GNUNET_JSON_spec_string ("target_iban",
   1858                                &iban),
   1859       GNUNET_JSON_spec_string ("target_business_name",
   1860                                &business_name),
   1861       GNUNET_JSON_spec_string ("wire_transfer_subject",
   1862                                &subject),
   1863       TALER_JSON_spec_amount_any ("challenge_amount",
   1864                                   &fb->details.iban_instructions.amount),
   1865       GNUNET_JSON_spec_end ()
   1866     };
   1867 
   1868     fb->status = ANASTASIS_RFS_IBAN_INSTRUCTIONS;
   1869     if (GNUNET_OK !=
   1870         GNUNET_JSON_parse (val,
   1871                            dspec,
   1872                            NULL, NULL))
   1873     {
   1874       *detail = set_detail ("challenge_feedback",
   1875                             "target_iban");
   1876       return GNUNET_SYSERR;
   1877     }
   1878     fb->details.iban_instructions.target_iban = GNUNET_strdup (iban);
   1879     fb->details.iban_instructions.target_business_name
   1880       = GNUNET_strdup (business_name);
   1881     fb->details.iban_instructions.wire_transfer_subject
   1882       = GNUNET_strdup (subject);
   1883     return GNUNET_OK;
   1884   }
   1885   if (0 == strcmp (state,
   1886                    "solved"))
   1887   {
   1888     fb->status = ANASTASIS_RFS_SOLVED;
   1889     return GNUNET_OK;
   1890   }
   1891   if (0 == strcmp (state,
   1892                    "incorrect-answer"))
   1893   {
   1894     uint64_t ec;
   1895     struct GNUNET_JSON_Specification dspec[] = {
   1896       GNUNET_JSON_spec_uint64 ("error_code",
   1897                                &ec),
   1898       GNUNET_JSON_spec_end ()
   1899     };
   1900 
   1901     fb->status = ANASTASIS_RFS_INCORRECT_ANSWER;
   1902     if (GNUNET_OK !=
   1903         GNUNET_JSON_parse (val,
   1904                            dspec,
   1905                            NULL, NULL))
   1906     {
   1907       *detail = set_detail ("challenge_feedback",
   1908                             "error_code");
   1909       return GNUNET_SYSERR;
   1910     }
   1911     fb->details.incorrect_answer.ec = (enum TALER_ErrorCode) ec;
   1912     return GNUNET_OK;
   1913   }
   1914   if (0 == strcmp (state,
   1915                    "rate-limit-exceeded"))
   1916   {
   1917     uint64_t ec;
   1918     struct GNUNET_JSON_Specification dspec[] = {
   1919       GNUNET_JSON_spec_uint64 ("error_code",
   1920                                &ec),
   1921       GNUNET_JSON_spec_uint64 ("request_limit",
   1922                                &fb->details.rate_limit_exceeded.request_limit),
   1923       GNUNET_JSON_spec_relative_time (
   1924         "request_frequency",
   1925         &fb->details.rate_limit_exceeded.request_frequency),
   1926       GNUNET_JSON_spec_end ()
   1927     };
   1928 
   1929     fb->status = ANASTASIS_RFS_RATE_LIMIT_EXCEEDED;
   1930     if (GNUNET_OK !=
   1931         GNUNET_JSON_parse (val,
   1932                            dspec,
   1933                            NULL, NULL))
   1934     {
   1935       *detail = set_detail ("challenge_feedback",
   1936                             "request_limit");
   1937       return GNUNET_SYSERR;
   1938     }
   1939     fb->details.rate_limit_exceeded.ec = (enum TALER_ErrorCode) ec;
   1940     return GNUNET_OK;
   1941   }
   1942   *detail = set_detail ("challenge_feedback",
   1943                         "state");
   1944   return GNUNET_SYSERR;
   1945 }
   1946 
   1947 
   1948 /**
   1949  * Parse the recovery-specific parts of @a json.
   1950  *
   1951  * @param json the state
   1952  * @param[out] rr recovery state to fill in
   1953  * @param[out] detail set to the offending field on failure
   1954  * @return #GNUNET_OK on success
   1955  */
   1956 static enum GNUNET_GenericReturnValue
   1957 parse_recovery (const json_t *json,
   1958                 struct ANASTASIS_ReduxRecovery *rr,
   1959                 const char **detail)
   1960 {
   1961   const char *state_str;
   1962   const json_t *recovery_information = NULL;
   1963   const json_t *recovery_document = NULL;
   1964   const json_t *challenge_feedback = NULL;
   1965   const json_t *core_secret = NULL;
   1966   bool no_selected;
   1967   struct GNUNET_JSON_Specification spec[] = {
   1968     GNUNET_JSON_spec_string ("recovery_state",
   1969                              &state_str),
   1970     GNUNET_JSON_spec_mark_optional (
   1971       GNUNET_JSON_spec_object_const ("recovery_information",
   1972                                      &recovery_information),
   1973       NULL),
   1974     GNUNET_JSON_spec_mark_optional (
   1975       GNUNET_JSON_spec_object_const ("recovery_document",
   1976                                      &recovery_document),
   1977       NULL),
   1978     GNUNET_JSON_spec_mark_optional (
   1979       GNUNET_JSON_spec_object_const ("challenge_feedback",
   1980                                      &challenge_feedback),
   1981       NULL),
   1982     GNUNET_JSON_spec_mark_optional (
   1983       GNUNET_JSON_spec_object_const ("core_secret",
   1984                                      &core_secret),
   1985       NULL),
   1986     GNUNET_JSON_spec_mark_optional (
   1987       GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid",
   1988                                    &rr->selected_challenge),
   1989       &no_selected),
   1990     GNUNET_JSON_spec_end ()
   1991   };
   1992 
   1993   if (GNUNET_OK !=
   1994       GNUNET_JSON_parse (json,
   1995                          spec,
   1996                          NULL, NULL))
   1997   {
   1998     *detail = set_detail ("state",
   1999                           "recovery_state");
   2000     return GNUNET_SYSERR;
   2001   }
   2002   rr->state = ANASTASIS_recovery_state_from_string_ (state_str);
   2003   if (ANASTASIS_RECOVERY_STATE_INVALID == rr->state)
   2004   {
   2005     *detail = set_detail ("state",
   2006                           "recovery_state");
   2007     return GNUNET_SYSERR;
   2008   }
   2009   rr->have_selected_challenge = ! no_selected;
   2010   rr->core_secret = json_incref ((json_t *) core_secret);
   2011   if (NULL != recovery_information)
   2012   {
   2013     if (GNUNET_OK !=
   2014         parse_recovery_info (recovery_information,
   2015                              rr,
   2016                              detail))
   2017       return GNUNET_SYSERR;
   2018   }
   2019   if (NULL != recovery_document)
   2020   {
   2021     /* Hand the blob straight to src/lib: it owns that schema.  The
   2022        handle comes back inert -- no callbacks, no network activity --
   2023        until a handler calls ANASTASIS_recovery_resume(). */
   2024     rr->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_,
   2025                                             recovery_document);
   2026     if (NULL == rr->r)
   2027     {
   2028       *detail = set_detail ("state",
   2029                             "recovery_document");
   2030       return GNUNET_SYSERR;
   2031     }
   2032   }
   2033   if (NULL != challenge_feedback)
   2034   {
   2035     const char *uuid_str;
   2036     json_t *val;
   2037     unsigned int off = 0;
   2038 
   2039     rr->have_challenge_feedback = true;
   2040     rr->challenge_feedback_len
   2041       = (unsigned int) json_object_size (challenge_feedback);
   2042     rr->challenge_feedback
   2043       = GNUNET_new_array (rr->challenge_feedback_len,
   2044                           struct ANASTASIS_ReduxFeedback);
   2045     json_object_foreach ((json_t *) challenge_feedback, uuid_str, val)
   2046     {
   2047       struct ANASTASIS_ReduxFeedback *fb = &rr->challenge_feedback[off];
   2048 
   2049       if (GNUNET_OK !=
   2050           GNUNET_STRINGS_string_to_data (uuid_str,
   2051                                          strlen (uuid_str),
   2052                                          &fb->uuid,
   2053                                          sizeof (fb->uuid)))
   2054       {
   2055         *detail = set_detail ("challenge_feedback",
   2056                               uuid_str);
   2057         return GNUNET_SYSERR;
   2058       }
   2059       if (GNUNET_OK !=
   2060           parse_feedback_details (val,
   2061                                   fb,
   2062                                   detail))
   2063         return GNUNET_SYSERR;
   2064       off++;
   2065     }
   2066   }
   2067   return GNUNET_OK;
   2068 }
   2069 
   2070 
   2071 /**
   2072  * Parse an `error` state.
   2073  *
   2074  * @param json the state
   2075  * @param[out] e error state to fill in
   2076  * @param[out] detail set to the offending field on failure
   2077  * @return #GNUNET_OK on success
   2078  */
   2079 static enum GNUNET_GenericReturnValue
   2080 parse_error (const json_t *json,
   2081              struct ANASTASIS_ReduxError *e,
   2082              const char **detail)
   2083 {
   2084   static const char *const allowed[] = {
   2085     "reducer_type",
   2086     "code",
   2087     "hint",
   2088     "detail",
   2089     NULL
   2090   };
   2091   uint64_t code;
   2092   const char *hint = NULL;
   2093   const char *dtl = NULL;
   2094   struct GNUNET_JSON_Specification spec[] = {
   2095     GNUNET_JSON_spec_uint64 ("code",
   2096                              &code),
   2097     GNUNET_JSON_spec_mark_optional (
   2098       GNUNET_JSON_spec_string ("hint",
   2099                                &hint),
   2100       NULL),
   2101     GNUNET_JSON_spec_mark_optional (
   2102       GNUNET_JSON_spec_string ("detail",
   2103                                &dtl),
   2104       NULL),
   2105     GNUNET_JSON_spec_end ()
   2106   };
   2107 
   2108   if ( (GNUNET_OK !=
   2109         check_fields (json,
   2110                       "state",
   2111                       allowed,
   2112                       detail)) ||
   2113        (GNUNET_OK !=
   2114         GNUNET_JSON_parse (json,
   2115                            spec,
   2116                            NULL, NULL)) )
   2117   {
   2118     *detail = set_detail ("state",
   2119                           "code");
   2120     return GNUNET_SYSERR;
   2121   }
   2122   e->code = (enum TALER_ErrorCode) code;
   2123   e->hint = dup_or_null (hint);
   2124   e->detail = dup_or_null (dtl);
   2125   return GNUNET_OK;
   2126 }
   2127 
   2128 
   2129 /**
   2130  * Field names permitted at the top level of a backup state.
   2131  */
   2132 static const char *const backup_allowed[] = {
   2133   "reducer_type",
   2134   "backup_state",
   2135   "continents",
   2136   "countries",
   2137   "required_attributes",
   2138   "authentication_providers",
   2139   "identity_attributes",
   2140   "currencies",
   2141   "selected_continent",
   2142   "selected_country",
   2143   "preferred_currency",
   2144   "authentication_methods",
   2145   "policies",
   2146   "policy_providers",
   2147   "upload_fees",
   2148   "payments",
   2149   "policy_payment_requests",
   2150   "success_details",
   2151   "core_secret",
   2152   "secret_name",
   2153   "expiration",
   2154   "pay_arguments",
   2155   NULL
   2156 };
   2157 
   2158 
   2159 /**
   2160  * Field names permitted at the top level of a recovery state.
   2161  */
   2162 static const char *const recovery_allowed[] = {
   2163   "reducer_type",
   2164   "recovery_state",
   2165   "continents",
   2166   "countries",
   2167   "required_attributes",
   2168   "authentication_providers",
   2169   "identity_attributes",
   2170   "currencies",
   2171   "selected_continent",
   2172   "selected_country",
   2173   "preferred_currency",
   2174   "recovery_information",
   2175   "recovery_document",
   2176   "challenge_feedback",
   2177   "selected_challenge_uuid",
   2178   "core_secret",
   2179   NULL
   2180 };
   2181 
   2182 
   2183 struct ANASTASIS_ReduxState *
   2184 ANASTASIS_REDUX_state_parse_ (const json_t *json,
   2185                               enum TALER_ErrorCode *ec,
   2186                               const char **detail)
   2187 {
   2188   struct ANASTASIS_ReduxState *rs;
   2189   const char *type;
   2190 
   2191   *ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID;
   2192   *detail = NULL;
   2193   if (! json_is_object (json))
   2194   {
   2195     *detail = set_detail ("state",
   2196                           NULL);
   2197     return NULL;
   2198   }
   2199   /* The reducer type is not always given explicitly: states written by
   2200      older versions (and the initial states of the CLI tests) are
   2201      identified by which of the two state fields is present. */
   2202   type = json_string_value (json_object_get (json,
   2203                                              "reducer_type"));
   2204   if (NULL == type)
   2205   {
   2206     if (NULL != json_object_get (json,
   2207                                  "backup_state"))
   2208       type = "backup";
   2209     else if (NULL != json_object_get (json,
   2210                                       "recovery_state"))
   2211       type = "recovery";
   2212     else
   2213     {
   2214       *detail = set_detail ("state",
   2215                             "reducer_type");
   2216       return NULL;
   2217     }
   2218   }
   2219   rs = GNUNET_new (struct ANASTASIS_ReduxState);
   2220   if (0 == strcmp (type,
   2221                    "backup"))
   2222   {
   2223     rs->type = ANASTASIS_RT_BACKUP;
   2224     if ( (GNUNET_OK !=
   2225           check_fields (json,
   2226                         "state",
   2227                         backup_allowed,
   2228                         detail)) ||
   2229          (GNUNET_OK !=
   2230           parse_common (json,
   2231                         &rs->common,
   2232                         detail)) ||
   2233          (GNUNET_OK !=
   2234           parse_backup (json,
   2235                         &rs->details.backup,
   2236                         detail)) )
   2237     {
   2238       ANASTASIS_REDUX_state_free_ (rs);
   2239       return NULL;
   2240     }
   2241     return rs;
   2242   }
   2243   if (0 == strcmp (type,
   2244                    "recovery"))
   2245   {
   2246     rs->type = ANASTASIS_RT_RECOVERY;
   2247     if ( (GNUNET_OK !=
   2248           check_fields (json,
   2249                         "state",
   2250                         recovery_allowed,
   2251                         detail)) ||
   2252          (GNUNET_OK !=
   2253           parse_common (json,
   2254                         &rs->common,
   2255                         detail)) ||
   2256          (GNUNET_OK !=
   2257           parse_recovery (json,
   2258                           &rs->details.recovery,
   2259                           detail)) )
   2260     {
   2261       ANASTASIS_REDUX_state_free_ (rs);
   2262       return NULL;
   2263     }
   2264     return rs;
   2265   }
   2266   if (0 == strcmp (type,
   2267                    "error"))
   2268   {
   2269     rs->type = ANASTASIS_RT_ERROR;
   2270     if (GNUNET_OK !=
   2271         parse_error (json,
   2272                      &rs->details.error,
   2273                      detail))
   2274     {
   2275       ANASTASIS_REDUX_state_free_ (rs);
   2276       return NULL;
   2277     }
   2278     return rs;
   2279   }
   2280   *detail = set_detail ("state",
   2281                         "reducer_type");
   2282   ANASTASIS_REDUX_state_free_ (rs);
   2283   return NULL;
   2284 }
   2285 
   2286 
   2287 enum GNUNET_GenericReturnValue
   2288 ANASTASIS_REDUX_countries_set_ (struct ANASTASIS_ReduxCommon *common,
   2289                                 const json_t *arr,
   2290                                 const char **detail)
   2291 {
   2292   ANASTASIS_REDUX_countries_clear_ (common);
   2293   if (GNUNET_OK !=
   2294       parse_countries (arr,
   2295                        common,
   2296                        detail))
   2297   {
   2298     ANASTASIS_REDUX_countries_clear_ (common);
   2299     return GNUNET_SYSERR;
   2300   }
   2301   return GNUNET_OK;
   2302 }
   2303 
   2304 
   2305 enum GNUNET_GenericReturnValue
   2306 ANASTASIS_REDUX_required_attributes_set_ (struct ANASTASIS_ReduxCommon *common,
   2307                                           const json_t *arr,
   2308                                           const char **detail)
   2309 {
   2310   ANASTASIS_REDUX_required_attributes_clear_ (common);
   2311   if (GNUNET_OK !=
   2312       parse_required_attributes (arr,
   2313                                  common,
   2314                                  detail))
   2315   {
   2316     ANASTASIS_REDUX_required_attributes_clear_ (common);
   2317     return GNUNET_SYSERR;
   2318   }
   2319   return GNUNET_OK;
   2320 }
   2321 
   2322 
   2323 enum GNUNET_GenericReturnValue
   2324 ANASTASIS_REDUX_provider_set_ (struct ANASTASIS_ReduxCommon *common,
   2325                                const char *url,
   2326                                const json_t *val,
   2327                                const char **detail)
   2328 {
   2329   struct ANASTASIS_ReduxProvider *p;
   2330 
   2331   p = ANASTASIS_REDUX_provider_get_ (common,
   2332                                      url);
   2333   if (p->have_config)
   2334     ANASTASIS_REDUX_provider_config_clear_ (&p->config);
   2335   p->have_config = false;
   2336   p->status = ANASTASIS_RPS_NOT_CONTACTED;
   2337   memset (&p->error,
   2338           0,
   2339           sizeof (p->error));
   2340   if (GNUNET_OK !=
   2341       parse_provider (url,
   2342                       val,
   2343                       p,
   2344                       detail))
   2345   {
   2346     /* Leave a well-defined, if uninformative, entry behind: the caller
   2347        reports the error, but the state must stay consistent. */
   2348     if (p->have_config)
   2349       ANASTASIS_REDUX_provider_config_clear_ (&p->config);
   2350     p->have_config = false;
   2351     p->status = ANASTASIS_RPS_NOT_CONTACTED;
   2352     return GNUNET_SYSERR;
   2353   }
   2354   return GNUNET_OK;
   2355 }
   2356 
   2357 
   2358 /* end of anastasis_api_redux_parse.c */