anastasis

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

anastasis_api_redux.c (62262B)


      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.c
     18  * @brief anastasis reducer api
     19  * @author Christian Grothoff
     20  * @author Dominik Meister
     21  * @author Dennis Neufeld
     22  */
     23 #include <platform.h>
     24 #include <jansson.h>
     25 #include "anastasis_redux.h"
     26 #include "anastasis_error_codes.h"
     27 #include <taler/taler_json_lib.h>
     28 #include "anastasis_api_redux.h"
     29 #include "anastasis_api_redux_state.h"
     30 #include "validation.h"
     31 
     32 
     33 /**
     34  * How long do we wait at most for a /config reply from an Anastasis provider.
     35  * 60s is very generous, given the tiny bandwidth required, even for the most
     36  * remote locations.
     37  */
     38 #define CONFIG_GENERIC_TIMEOUT GNUNET_TIME_UNIT_MINUTES
     39 
     40 /**
     41  * How long do we wait in a more "synchronous"
     42  * scenaro for a /config reply from an Anastasis provider.
     43  */
     44 #define CONFIG_FAST_TIMEOUT GNUNET_TIME_UNIT_SECONDS
     45 
     46 
     47 #define GENERATE_STRING(STRING) #STRING,
     48 static const char *generic_strings[] = {
     49   ANASTASIS_GENERIC_STATES (GENERATE_STRING)
     50 };
     51 #undef GENERATE_STRING
     52 
     53 
     54 /**
     55  * #ANASTASIS_REDUX_add_provider_to_state_ waiting for the
     56  * configuration request to complete or fail.
     57  */
     58 struct ConfigReduxWaiting
     59 {
     60   /**
     61    * Kept in a DLL.
     62    */
     63   struct ConfigReduxWaiting *prev;
     64 
     65   /**
     66    * Kept in a DLL.
     67    */
     68   struct ConfigReduxWaiting *next;
     69 
     70   /**
     71    * Associated redux action.
     72    */
     73   struct ANASTASIS_ReduxAction ra;
     74 
     75   /**
     76    * Config request we are waiting for.
     77    */
     78   struct ConfigRequest *cr;
     79 
     80   /**
     81    * State we are processing.  Borrowed, not owned: the operation that
     82    * started us keeps the state alive until it has cancelled us.  See
     83    * #ANASTASIS_REDUX_add_provider_to_state_.
     84    */
     85   struct ANASTASIS_ReduxState *rs;
     86 
     87   /**
     88    * Function to call with updated @e rs.
     89    */
     90   ANASTASIS_REDUX_StateCallback cb;
     91 
     92   /**
     93    * Closure for @e cb.
     94    */
     95   void *cb_cls;
     96 
     97 };
     98 
     99 
    100 /**
    101  * Anastasis authorization method configuration
    102  */
    103 struct AuthorizationMethodConfig
    104 {
    105   /**
    106    * Type of the method, i.e. "question".
    107    */
    108   char *type;
    109 
    110   /**
    111    * Fee charged for accessing key share using this method.
    112    */
    113   struct TALER_Amount usage_fee;
    114 };
    115 
    116 
    117 /**
    118  * State for a "get config" operation.
    119  */
    120 struct ConfigRequest
    121 {
    122 
    123   /**
    124    * Kept in a DLL, given that we may have multiple backends.
    125    */
    126   struct ConfigRequest *next;
    127 
    128   /**
    129    * Kept in a DLL, given that we may have multiple backends.
    130    */
    131   struct ConfigRequest *prev;
    132 
    133   /**
    134    * Head of DLL of REDUX operations waiting for an answer.
    135    */
    136   struct ConfigReduxWaiting *w_head;
    137 
    138   /**
    139    * Tail of DLL of REDUX operations waiting for an answer.
    140    */
    141   struct ConfigReduxWaiting *w_tail;
    142 
    143   /**
    144    * When did we start?
    145    */
    146   struct GNUNET_TIME_Absolute start_time;
    147 
    148   /**
    149    * When do we time out?
    150    */
    151   struct GNUNET_TIME_Absolute timeout_at;
    152 
    153   /**
    154    * How long do we wait before trying again?
    155    */
    156   struct GNUNET_TIME_Relative backoff;
    157 
    158   /**
    159    * Obtained status code.
    160    */
    161   unsigned int http_status;
    162 
    163   /**
    164    * The /config GET operation handle.
    165    */
    166   struct ANASTASIS_ConfigOperation *co;
    167 
    168   /**
    169    * URL of the anastasis backend.
    170    */
    171   char *url;
    172 
    173   /**
    174    * Business name of the anastasis backend.
    175    */
    176   char *business_name;
    177 
    178   /**
    179    * Array of authorization methods supported by the server.
    180    */
    181   struct AuthorizationMethodConfig *methods;
    182 
    183   /**
    184    * Length of the @e methods array.
    185    */
    186   unsigned int methods_length;
    187 
    188   /**
    189    * Maximum size of an upload in megabytes.
    190    */
    191   uint32_t storage_limit_in_megabytes;
    192 
    193   /**
    194    * Annual fee for an account / policy upload.
    195    */
    196   struct TALER_Amount annual_fee;
    197 
    198   /**
    199    * Fee for a truth upload.
    200    */
    201   struct TALER_Amount truth_upload_fee;
    202 
    203   /**
    204    * Maximum legal liability for data loss covered by the
    205    * provider.
    206    */
    207   struct TALER_Amount liability_limit;
    208 
    209   /**
    210    * Provider salt.
    211    */
    212   struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
    213 
    214   /**
    215    * Task to timeout /config requests.
    216    */
    217   struct GNUNET_SCHEDULER_Task *tt;
    218 
    219   /**
    220    * Status of the /config request.
    221    */
    222   enum TALER_ErrorCode ec;
    223 };
    224 
    225 
    226 /**
    227  * Reducer API's CURL context handle.
    228  */
    229 struct GNUNET_CURL_Context *ANASTASIS_REDUX_ctx_;
    230 
    231 /**
    232  * JSON containing country specific identity attributes to ask the user for.
    233  */
    234 static json_t *redux_id_attr;
    235 
    236 /**
    237  * Head of DLL of Anastasis backend configuration requests.
    238  */
    239 static struct ConfigRequest *cr_head;
    240 
    241 /**
    242  * Tail of DLL of Anastasis backend configuration requests.
    243  */
    244 static struct ConfigRequest *cr_tail;
    245 
    246 /**
    247  * JSON containing country specific information.
    248  */
    249 static json_t *redux_countries;
    250 
    251 /**
    252  * List of Anastasis providers.
    253  */
    254 static json_t *provider_list;
    255 
    256 /**
    257  * External reducer binary or NULL
    258  * to use internal reducer.
    259  */
    260 static char *external_reducer_binary;
    261 
    262 
    263 const char *
    264 ANASTASIS_REDUX_probe_external_reducer (void)
    265 {
    266   if (NULL != external_reducer_binary)
    267     return external_reducer_binary;
    268   external_reducer_binary = getenv ("ANASTASIS_EXTERNAL_REDUCER");
    269   if (NULL != external_reducer_binary)
    270     unsetenv ("ANASTASIS_EXTERNAL_REDUCER");
    271 
    272   return external_reducer_binary;
    273 
    274 }
    275 
    276 
    277 /**
    278  * Extract the generic part of the step @a rs is at.
    279  *
    280  * The generic states are the common prefix of the backup and the
    281  * recovery state enumerations (see #ANASTASIS_GENERIC_STATES), so a
    282  * value that is in range for #ANASTASIS_GenericState means the same
    283  * thing in either variant.
    284  *
    285  * @param rs the state to inspect
    286  * @return #ANASTASIS_GENERIC_STATE_INVALID if @a rs is past the generic
    287  *         steps, or is an `error` state
    288  */
    289 static enum ANASTASIS_GenericState
    290 get_generic_state (const struct ANASTASIS_ReduxState *rs)
    291 {
    292   unsigned int s;
    293 
    294   switch (rs->type)
    295   {
    296   case ANASTASIS_RT_BACKUP:
    297     s = (unsigned int) rs->details.backup.state;
    298     break;
    299   case ANASTASIS_RT_RECOVERY:
    300     s = (unsigned int) rs->details.recovery.state;
    301     break;
    302   case ANASTASIS_RT_ERROR:
    303     return ANASTASIS_GENERIC_STATE_INVALID;
    304   }
    305   if (s >= sizeof (generic_strings) / sizeof(*generic_strings))
    306     return ANASTASIS_GENERIC_STATE_INVALID;
    307   return (enum ANASTASIS_GenericState) s;
    308 }
    309 
    310 
    311 enum ANASTASIS_GenericState
    312 ANASTASIS_generic_state_from_string_ (const char *state_string)
    313 {
    314   for (enum ANASTASIS_GenericState i = 0;
    315        i < sizeof (generic_strings) / sizeof(*generic_strings);
    316        i++)
    317     if (0 == strcmp (state_string,
    318                      generic_strings[i]))
    319       return i;
    320   return ANASTASIS_GENERIC_STATE_INVALID;
    321 }
    322 
    323 
    324 const char *
    325 ANASTASIS_generic_state_to_string_ (enum ANASTASIS_GenericState gs)
    326 {
    327   if ( (gs < 0) ||
    328        (gs >= sizeof (generic_strings) / sizeof(*generic_strings)) )
    329   {
    330     GNUNET_break_op (0);
    331     return NULL;
    332   }
    333   return generic_strings[gs];
    334 }
    335 
    336 
    337 void
    338 ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb,
    339                        void *cb_cls,
    340                        enum TALER_ErrorCode ec,
    341                        const char *detail)
    342 {
    343   json_t *estate;
    344 
    345   estate = GNUNET_JSON_PACK (
    346     GNUNET_JSON_pack_allow_null (
    347       GNUNET_JSON_pack_string ("detail",
    348                                detail)),
    349     GNUNET_JSON_pack_string ("reducer_type",
    350                              "error"),
    351     GNUNET_JSON_pack_uint64 ("code",
    352                              ec),
    353     GNUNET_JSON_pack_string ("hint",
    354                              TALER_ErrorCode_get_hint (ec)));
    355   cb (cb_cls,
    356       ec,
    357       estate);
    358   json_decref (estate);
    359 }
    360 
    361 
    362 /**
    363  * Transition @a rs to the generic state @a gs.
    364  *
    365  * @param[in,out] rs state to transition
    366  * @param gs state to transition to
    367  */
    368 static void
    369 redux_transition (struct ANASTASIS_ReduxState *rs,
    370                   enum ANASTASIS_GenericState gs)
    371 {
    372   switch (rs->type)
    373   {
    374   case ANASTASIS_RT_BACKUP:
    375     rs->details.backup.state = (enum ANASTASIS_BackupState) gs;
    376     return;
    377   case ANASTASIS_RT_RECOVERY:
    378     rs->details.recovery.state = (enum ANASTASIS_RecoveryState) gs;
    379     return;
    380   case ANASTASIS_RT_ERROR:
    381     break;
    382   }
    383   GNUNET_assert (0);
    384 }
    385 
    386 
    387 void
    388 ANASTASIS_redux_init (struct GNUNET_CURL_Context *ctx)
    389 {
    390   ANASTASIS_REDUX_ctx_ = ctx;
    391 }
    392 
    393 
    394 /**
    395  * Function to free a `struct ConfigRequest`, an async operation.
    396  *
    397  * @param cr state for a "get config" operation
    398  */
    399 static void
    400 free_config_request (struct ConfigRequest *cr)
    401 {
    402   GNUNET_assert (NULL == cr->w_head);
    403   if (NULL != cr->co)
    404     ANASTASIS_config_cancel (cr->co);
    405   if (NULL != cr->tt)
    406     GNUNET_SCHEDULER_cancel (cr->tt);
    407   GNUNET_free (cr->url);
    408   GNUNET_free (cr->business_name);
    409   for (unsigned int i = 0; i<cr->methods_length; i++)
    410     GNUNET_free (cr->methods[i].type);
    411   GNUNET_free (cr->methods);
    412   GNUNET_free (cr);
    413 }
    414 
    415 
    416 void
    417 ANASTASIS_redux_done ()
    418 {
    419   struct ConfigRequest *cr;
    420 
    421   while (NULL != (cr = cr_head))
    422   {
    423     GNUNET_CONTAINER_DLL_remove (cr_head,
    424                                  cr_tail,
    425                                  cr);
    426     free_config_request (cr);
    427   }
    428   ANASTASIS_REDUX_ctx_ = NULL;
    429   if (NULL != redux_countries)
    430   {
    431     json_decref (redux_countries);
    432     redux_countries = NULL;
    433   }
    434   if (NULL != redux_id_attr)
    435   {
    436     json_decref (redux_id_attr);
    437     redux_id_attr = NULL;
    438   }
    439   if (NULL != provider_list)
    440   {
    441     json_decref (provider_list);
    442     provider_list = NULL;
    443   }
    444 }
    445 
    446 
    447 const json_t *
    448 ANASTASIS_redux_countries_init_ (void)
    449 {
    450   char *dn;
    451   json_error_t error;
    452 
    453   if (NULL != redux_countries)
    454     return redux_countries;
    455 
    456   {
    457     char *path;
    458 
    459     path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    460                                             GNUNET_OS_IPK_DATADIR);
    461     if (NULL == path)
    462     {
    463       GNUNET_break (0);
    464       return NULL;
    465     }
    466     GNUNET_asprintf (&dn,
    467                      "%s/redux.countries.json",
    468                      path);
    469     GNUNET_free (path);
    470   }
    471   redux_countries = json_load_file (dn,
    472                                     JSON_COMPACT,
    473                                     &error);
    474   if (NULL == redux_countries)
    475   {
    476     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    477                 "Failed to parse `%s': %s at %d:%d (%d)\n",
    478                 dn,
    479                 error.text,
    480                 error.line,
    481                 error.column,
    482                 error.position);
    483     GNUNET_free (dn);
    484     return NULL;
    485   }
    486   GNUNET_free (dn);
    487   return redux_countries;
    488 }
    489 
    490 
    491 /**
    492  * Abort waiting for /config reply.
    493  *
    494  * @param cls a `struct ConfigReduxWaiting` handle.
    495  */
    496 static void
    497 abort_provider_config_cb (void *cls)
    498 {
    499   struct ConfigReduxWaiting *w = cls;
    500   struct ConfigRequest *cr = w->cr;
    501 
    502   GNUNET_CONTAINER_DLL_remove (cr->w_head,
    503                                cr->w_tail,
    504                                w);
    505   /* w->rs is borrowed; freeing it is the caller's business. */
    506   GNUNET_free (w);
    507 }
    508 
    509 
    510 /**
    511  * Notify anyone waiting on @a cr that the request is done
    512  * (successful or failed).
    513  *
    514  * @param[in,out] cr request that completed
    515  */
    516 static void
    517 notify_waiting (struct ConfigRequest *cr)
    518 {
    519   struct ConfigReduxWaiting *w;
    520 
    521   while (NULL != (w = cr->w_head))
    522   {
    523     struct ANASTASIS_ReduxProvider *p;
    524 
    525     p = ANASTASIS_REDUX_provider_get_ (&w->rs->common,
    526                                        cr->url);
    527     if (p->have_config)
    528       ANASTASIS_REDUX_provider_config_clear_ (&p->config);
    529     p->have_config = false;
    530     memset (&p->error,
    531             0,
    532             sizeof (p->error));
    533     if (TALER_EC_NONE != cr->ec)
    534     {
    535       p->status = ANASTASIS_RPS_ERROR;
    536       p->error.ec = cr->ec;
    537       p->error.http_status = cr->http_status;
    538     }
    539     else
    540     {
    541       struct ANASTASIS_ReduxProviderConfig *cfg = &p->config;
    542 
    543       p->status = ANASTASIS_RPS_OK;
    544       p->have_config = true;
    545       cfg->methods_len = cr->methods_length;
    546       cfg->methods = GNUNET_new_array (cfg->methods_len,
    547                                        struct ANASTASIS_ReduxMethodSpec);
    548       for (unsigned int i = 0; i<cr->methods_length; i++)
    549       {
    550         cfg->methods[i].type = GNUNET_strdup (cr->methods[i].type);
    551         cfg->methods[i].usage_fee = cr->methods[i].usage_fee;
    552       }
    553       cfg->annual_fee = cr->annual_fee;
    554       cfg->truth_upload_fee = cr->truth_upload_fee;
    555       cfg->liability_limit = cr->liability_limit;
    556       cfg->provider_salt = cr->provider_salt;
    557       cfg->business_name = GNUNET_strdup (cr->business_name);
    558       cfg->storage_limit_in_megabytes = cr->storage_limit_in_megabytes;
    559       cfg->http_status = cr->http_status;
    560     }
    561     w->cb (w->cb_cls,
    562            cr->ec,
    563            w->rs);
    564     abort_provider_config_cb (w);
    565   }
    566 }
    567 
    568 
    569 /**
    570  * Notify anyone waiting on @a cr that the request is done
    571  * (successful or failed).
    572  *
    573  * @param[in,out] cls request that completed
    574  */
    575 static void
    576 notify_waiting_cb (void *cls)
    577 {
    578   struct ConfigRequest *cr = cls;
    579 
    580   cr->tt = NULL;
    581   notify_waiting (cr);
    582 }
    583 
    584 
    585 /**
    586  * Function called when it is time to retry a
    587  * failed /config request.
    588  *
    589  * @param cls the `struct ConfigRequest *` to retry.
    590  */
    591 static void
    592 retry_config (void *cls);
    593 
    594 
    595 /**
    596  * Function called with the results of a #ANASTASIS_get_config().
    597  *
    598  * @param cls closure
    599  * @param acfg anastasis configuration
    600  */
    601 static void
    602 config_cb (void *cls,
    603            const struct ANASTASIS_Config *acfg)
    604 {
    605   struct ConfigRequest *cr = cls;
    606 
    607   cr->co = NULL;
    608   if (NULL != cr->tt)
    609   {
    610     GNUNET_SCHEDULER_cancel (cr->tt);
    611     cr->tt = NULL;
    612   }
    613   cr->http_status = acfg->http_status;
    614   if (MHD_HTTP_OK != acfg->http_status)
    615   {
    616     if (0 == acfg->http_status)
    617       cr->ec = TALER_EC_ANASTASIS_GENERIC_PROVIDER_UNREACHABLE;
    618     else
    619       cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED;
    620   }
    621   if (0 == acfg->details.ok.storage_limit_in_megabytes)
    622   {
    623     cr->http_status = 0;
    624     cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG;
    625   }
    626   else
    627   {
    628     cr->ec = TALER_EC_NONE;
    629     GNUNET_free (cr->business_name);
    630     cr->business_name = GNUNET_strdup (acfg->details.ok.business_name);
    631     for (unsigned int i = 0; i<cr->methods_length; i++)
    632       GNUNET_free (cr->methods[i].type);
    633     GNUNET_free (cr->methods);
    634     cr->methods = GNUNET_new_array (acfg->details.ok.methods_length,
    635                                     struct AuthorizationMethodConfig);
    636     for (unsigned int i = 0; i<acfg->details.ok.methods_length; i++)
    637     {
    638       cr->methods[i].type = GNUNET_strdup (acfg->details.ok.methods[i].type);
    639       cr->methods[i].usage_fee = acfg->details.ok.methods[i].usage_fee;
    640     }
    641     cr->methods_length = acfg->details.ok.methods_length;
    642     cr->storage_limit_in_megabytes =
    643       acfg->details.ok.storage_limit_in_megabytes;
    644     cr->annual_fee = acfg->details.ok.annual_fee;
    645     cr->truth_upload_fee = acfg->details.ok.truth_upload_fee;
    646     cr->liability_limit = acfg->details.ok.liability_limit;
    647     cr->provider_salt = acfg->details.ok.provider_salt;
    648   }
    649   notify_waiting (cr);
    650   if (MHD_HTTP_OK != acfg->http_status)
    651   {
    652     cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff);
    653     GNUNET_assert (NULL == cr->tt);
    654     GNUNET_assert (NULL != cr->url);
    655     cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff,
    656                                            &retry_config,
    657                                            cr);
    658   }
    659 }
    660 
    661 
    662 /**
    663  * Aborts a "get config" after timeout.
    664  *
    665  * @param cls closure for a "get config" request
    666  */
    667 static void
    668 config_request_timeout (void *cls)
    669 {
    670   struct ConfigRequest *cr = cls;
    671 
    672   cr->tt = NULL;
    673   if (NULL != cr->co)
    674   {
    675     ANASTASIS_config_cancel (cr->co);
    676     cr->co = NULL;
    677   }
    678   cr->http_status = 0;
    679   cr->ec = TALER_EC_GENERIC_TIMEOUT;
    680   notify_waiting (cr);
    681   cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff);
    682   GNUNET_assert (NULL == cr->tt);
    683   GNUNET_assert (NULL != cr->url);
    684   cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff,
    685                                          &retry_config,
    686                                          cr);
    687 }
    688 
    689 
    690 static void
    691 retry_config (void *cls)
    692 {
    693   struct ConfigRequest *cr = cls;
    694 
    695   cr->tt = NULL;
    696   if (NULL != cr->co)
    697   {
    698     ANASTASIS_config_cancel (cr->co);
    699     cr->co = NULL;
    700   }
    701   cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT);
    702   GNUNET_assert (NULL == cr->tt);
    703   cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
    704                                     &config_request_timeout,
    705                                     cr);
    706   cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_,
    707                                  cr->url,
    708                                  &config_cb,
    709                                  cr);
    710   GNUNET_break (NULL != cr->co);
    711 }
    712 
    713 
    714 /**
    715  * Schedule job to obtain Anastasis provider configuration at @a url.
    716  *
    717  * @param timeout how long to wait for a reply
    718  * @param url base URL of Anastasis provider
    719  * @return check config handle
    720  */
    721 static struct ConfigRequest *
    722 check_config (struct GNUNET_TIME_Relative timeout,
    723               const char *url)
    724 {
    725   struct ConfigRequest *cr;
    726 
    727   for (cr = cr_head; NULL != cr; cr = cr->next)
    728   {
    729     if (0 != strcmp (url,
    730                      cr->url))
    731       continue;
    732     if (NULL != cr->co)
    733     {
    734       struct GNUNET_TIME_Relative duration;
    735       struct GNUNET_TIME_Relative left;
    736       struct GNUNET_TIME_Relative xleft;
    737 
    738       duration = GNUNET_TIME_absolute_get_duration (cr->start_time);
    739       left = GNUNET_TIME_relative_subtract (timeout,
    740                                             duration);
    741       xleft = GNUNET_TIME_absolute_get_remaining (cr->timeout_at);
    742       if (GNUNET_TIME_relative_cmp (left,
    743                                     <,
    744                                     xleft))
    745       {
    746         /* new timeout is shorter! */
    747         cr->timeout_at = GNUNET_TIME_relative_to_absolute (left);
    748         GNUNET_SCHEDULER_cancel (cr->tt);
    749         cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
    750                                           &config_request_timeout,
    751                                           cr);
    752       }
    753       return cr; /* already on it */
    754     }
    755     break;
    756   }
    757   if (NULL == cr)
    758   {
    759     cr = GNUNET_new (struct ConfigRequest);
    760     cr->start_time = GNUNET_TIME_absolute_get ();
    761     cr->url = GNUNET_strdup (url);
    762     GNUNET_CONTAINER_DLL_insert (cr_head,
    763                                  cr_tail,
    764                                  cr);
    765   }
    766   if (MHD_HTTP_OK == cr->http_status)
    767     return cr;
    768   cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout);
    769   if (NULL != cr->tt)
    770     GNUNET_SCHEDULER_cancel (cr->tt);
    771   cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
    772                                     &config_request_timeout,
    773                                     cr);
    774   cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_,
    775                                  cr->url,
    776                                  &config_cb,
    777                                  cr);
    778   if (NULL == cr->co)
    779   {
    780     GNUNET_break (0);
    781     return NULL;
    782   }
    783   return cr;
    784 }
    785 
    786 
    787 /**
    788  * Begin asynchronous check for provider configurations.
    789  *
    790  * @param cc country code that was selected
    791  * @param[in,out] common state to set the provider list for
    792  * @return #TALER_EC_NONE on success
    793  */
    794 static enum TALER_ErrorCode
    795 begin_provider_config_check (const char *cc,
    796                              struct ANASTASIS_ReduxCommon *common)
    797 {
    798   if (NULL == provider_list)
    799   {
    800     json_error_t error;
    801     char *dn;
    802     char *path;
    803 
    804     path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    805                                             GNUNET_OS_IPK_DATADIR);
    806     if (NULL == path)
    807     {
    808       GNUNET_break (0);
    809       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    810     }
    811     GNUNET_asprintf (&dn,
    812                      "%s/provider-list.json",
    813                      path);
    814     GNUNET_free (path);
    815     provider_list = json_load_file (dn,
    816                                     JSON_COMPACT,
    817                                     &error);
    818     if (NULL == provider_list)
    819     {
    820       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    821                   "Failed to parse `%s': %s at %d:%d (%d)\n",
    822                   dn,
    823                   error.text,
    824                   error.line,
    825                   error.column,
    826                   error.position);
    827       GNUNET_free (dn);
    828       return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED;
    829     }
    830     GNUNET_free (dn);
    831   }
    832 
    833   {
    834     size_t index;
    835     json_t *provider;
    836     const json_t *provider_arr = json_object_get (provider_list,
    837                                                   "anastasis_provider");
    838 
    839     /* The applicable providers depend on the country, so start over
    840        rather than merge into whatever an earlier selection left. */
    841     ANASTASIS_REDUX_providers_clear_ (common);
    842     common->have_providers = true;
    843     json_array_foreach (provider_arr, index, provider)
    844     {
    845       const char *url;
    846       const char *restricted = NULL;
    847       struct GNUNET_JSON_Specification spec[] = {
    848         GNUNET_JSON_spec_string ("url",
    849                                  &url),
    850         GNUNET_JSON_spec_mark_optional (
    851           GNUNET_JSON_spec_string ("restricted",
    852                                    &restricted),
    853           NULL),
    854         GNUNET_JSON_spec_end ()
    855       };
    856 
    857       if (GNUNET_OK !=
    858           GNUNET_JSON_parse (provider,
    859                              spec,
    860                              NULL, NULL))
    861       {
    862         GNUNET_break (0);
    863         ANASTASIS_REDUX_providers_clear_ (common);
    864         return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED;
    865       }
    866       if ( (NULL != restricted) &&
    867            (0 != strcmp (restricted,
    868                          cc)) )
    869       {
    870         /* skip */
    871         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    872                     "Skipping provider restricted to country `%s'\n",
    873                     restricted);
    874         continue;
    875       }
    876       if ( (NULL == restricted) &&
    877            (0 == strcmp (cc,
    878                          "xx")) )
    879       {
    880         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    881                     "Running in demo mode, skipping unrestricted providers\n");
    882         /* demo mode, skipping regular providers */
    883         continue;
    884       }
    885       (void) ANASTASIS_REDUX_provider_get_ (common,
    886                                             url);
    887       check_config (CONFIG_GENERIC_TIMEOUT,
    888                     url);
    889     }
    890   }
    891   return TALER_EC_NONE;
    892 }
    893 
    894 
    895 /**
    896  * Function to validate an input by regular expression ("validation-regex").
    897  *
    898  * @param input text to validate
    899  * @param regexp regular expression to validate
    900  * @return true if validation passed, else false
    901  */
    902 static bool
    903 validate_regex (const char *input,
    904                 const char *regexp)
    905 {
    906   regex_t regex;
    907 
    908   if (0 != regcomp (&regex,
    909                     regexp,
    910                     REG_EXTENDED))
    911   {
    912     GNUNET_break (0);
    913     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    914                 "Failed to compile regular expression `%s'.",
    915                 regexp);
    916     return true;
    917   }
    918   /* check if input has correct form */
    919   if (0 != regexec (&regex,
    920                     input,
    921                     0,
    922                     NULL,
    923                     0))
    924   {
    925     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    926                 "Input `%s' does not match regex `%s'\n",
    927                 input,
    928                 regexp);
    929     regfree (&regex);
    930     return false;
    931   }
    932   regfree (&regex);
    933   return true;
    934 }
    935 
    936 
    937 /**
    938  * Function to load json containing country specific identity
    939  * attributes.  Uses a single-slot cache to avoid loading
    940  * exactly the same attributes twice.
    941  *
    942  * @param country_code country code (e.g. "de")
    943  * @return NULL on error
    944  */
    945 static const json_t *
    946 redux_id_attr_init (const char *country_code)
    947 {
    948   static char redux_id_cc[3];
    949   char *dn;
    950   json_error_t error;
    951 
    952   /* `country_code` originates from untrusted state; it is interpolated into a
    953      file path below, so reject anything that is not exactly two ASCII letters
    954      to prevent path traversal (e.g. "../../..") into arbitrary JSON files. */
    955   {
    956     char c0 = country_code[0];
    957     char c1 = (c0 == '\0') ? '\0' : country_code[1];
    958 
    959     if ( (2 != strlen (country_code)) ||
    960          (! ( ((c0 >= 'A') && (c0 <= 'Z')) || ((c0 >= 'a') && (c0 <= 'z')) )) ||
    961          (! ( ((c1 >= 'A') && (c1 <= 'Z')) || ((c1 >= 'a') && (c1 <= 'z')) )) )
    962     {
    963       GNUNET_break (0);
    964       return NULL;
    965     }
    966   }
    967 
    968   if (0 == strcmp (country_code,
    969                    redux_id_cc))
    970     return redux_id_attr;
    971 
    972   if (NULL != redux_id_attr)
    973   {
    974     json_decref (redux_id_attr);
    975     redux_id_attr = NULL;
    976   }
    977   {
    978     char *path;
    979 
    980     path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    981                                             GNUNET_OS_IPK_DATADIR);
    982     if (NULL == path)
    983     {
    984       GNUNET_break (0);
    985       return NULL;
    986     }
    987     GNUNET_asprintf (&dn,
    988                      "%s/redux.%s.json",
    989                      path,
    990                      country_code);
    991     GNUNET_free (path);
    992   }
    993   redux_id_attr = json_load_file (dn,
    994                                   JSON_COMPACT,
    995                                   &error);
    996   if (NULL == redux_id_attr)
    997   {
    998     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    999                 "Failed to parse `%s': %s at %d:%d (%d)\n",
   1000                 dn,
   1001                 error.text,
   1002                 error.line,
   1003                 error.column,
   1004                 error.position);
   1005     GNUNET_free (dn);
   1006     return NULL;
   1007   }
   1008   GNUNET_free (dn);
   1009   strncpy (redux_id_cc,
   1010            country_code,
   1011            sizeof (redux_id_cc));
   1012   redux_id_cc[2] = '\0';
   1013   return redux_id_attr;
   1014 }
   1015 
   1016 
   1017 /**
   1018  * DispatchHandler/Callback function which is called for a
   1019  * "select_continent" action.
   1020  *
   1021  * @param[in] rs state to operate on
   1022  * @param arguments arguments to use for operation on state
   1023  * @param cb callback to call during/after operation
   1024  * @param cb_cls callback closure
   1025  * @return NULL
   1026  */
   1027 static struct ANASTASIS_ReduxAction *
   1028 select_continent (struct ANASTASIS_ReduxState *rs,
   1029                   const json_t *arguments,
   1030                   ANASTASIS_ActionCallback cb,
   1031                   void *cb_cls)
   1032 {
   1033   const json_t *rc = ANASTASIS_redux_countries_init_ ();
   1034   const json_t *root = json_object_get (rc,
   1035                                         "countries");
   1036   const json_t *continent;
   1037   const char *cname;
   1038   json_t *countries;
   1039 
   1040   if (NULL == root)
   1041   {
   1042     ANASTASIS_REDUX_fail_ (rs,
   1043                            cb,
   1044                            cb_cls,
   1045                            TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED,
   1046                            "'countries' missing");
   1047     return NULL;
   1048   }
   1049   if (NULL == arguments)
   1050   {
   1051     ANASTASIS_REDUX_fail_ (rs,
   1052                            cb,
   1053                            cb_cls,
   1054                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1055                            "arguments missing");
   1056     return NULL;
   1057   }
   1058   continent = json_object_get (arguments,
   1059                                "continent");
   1060   if (NULL == continent)
   1061   {
   1062     ANASTASIS_REDUX_fail_ (rs,
   1063                            cb,
   1064                            cb_cls,
   1065                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1066                            "'continent' missing");
   1067     return NULL;
   1068   }
   1069   cname = json_string_value (continent);
   1070   countries = json_array ();
   1071   GNUNET_assert (NULL != countries);
   1072   {
   1073     size_t index;
   1074     const json_t *country;
   1075 
   1076     json_array_foreach (root, index, country)
   1077     {
   1078       const char *cc = json_string_value (json_object_get (country,
   1079                                                            "continent"));
   1080 
   1081       if ( (NULL != cname) &&
   1082            (NULL != cc) &&
   1083            (0 == strcmp (cname,
   1084                          cc)) )
   1085         GNUNET_assert (0 ==
   1086                        json_array_append (countries,
   1087                                           (json_t *) country));
   1088     }
   1089     if (0 == json_array_size (countries))
   1090     {
   1091       json_decref (countries);
   1092       ANASTASIS_REDUX_fail_ (rs,
   1093                              cb,
   1094                              cb_cls,
   1095                              TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1096                              "'continent' unknown");
   1097       return NULL;
   1098     }
   1099   }
   1100   {
   1101     const char *detail;
   1102 
   1103     if (GNUNET_OK !=
   1104         ANASTASIS_REDUX_countries_set_ (&rs->common,
   1105                                         countries,
   1106                                         &detail))
   1107     {
   1108       GNUNET_break (0);
   1109       json_decref (countries);
   1110       ANASTASIS_REDUX_fail_ (rs,
   1111                              cb,
   1112                              cb_cls,
   1113                              TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED,
   1114                              detail);
   1115       return NULL;
   1116     }
   1117   }
   1118   json_decref (countries);
   1119   redux_transition (rs,
   1120                     ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING);
   1121   GNUNET_free (rs->common.selected_continent);
   1122   rs->common.selected_continent = GNUNET_strdup (cname);
   1123   ANASTASIS_REDUX_return_ (rs,
   1124                            cb,
   1125                            cb_cls,
   1126                            TALER_EC_NONE);
   1127   return NULL;
   1128 }
   1129 
   1130 
   1131 /**
   1132  * DispatchHandler/Callback function which is called for a
   1133  * "select_country" action.
   1134  *
   1135  * @param state state to operate on
   1136  * @param arguments arguments to use for operation on state
   1137  * @param cb callback to call during/after operation
   1138  * @param cb_cls callback closure
   1139  * @return #ANASTASIS_ReduxAction
   1140  */
   1141 static struct ANASTASIS_ReduxAction *
   1142 select_country (struct ANASTASIS_ReduxState *rs,
   1143                 const json_t *arguments,
   1144                 ANASTASIS_ActionCallback cb,
   1145                 void *cb_cls)
   1146 {
   1147   const json_t *required_attrs;
   1148   const char *country_code;
   1149 
   1150   if (NULL == arguments)
   1151   {
   1152     ANASTASIS_REDUX_fail_ (rs,
   1153                            cb,
   1154                            cb_cls,
   1155                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1156                            "arguments missing");
   1157     return NULL;
   1158   }
   1159   if (NULL == json_object_get (arguments,
   1160                                "country_code"))
   1161   {
   1162     ANASTASIS_REDUX_fail_ (rs,
   1163                            cb,
   1164                            cb_cls,
   1165                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1166                            "'country_code' missing");
   1167     return NULL;
   1168   }
   1169   country_code = json_string_value (json_object_get (arguments,
   1170                                                      "country_code"));
   1171 
   1172   {
   1173     bool found = false;
   1174 
   1175     for (unsigned int i = 0; i < rs->common.countries_len; i++)
   1176       if ( (NULL != country_code) &&
   1177            (0 == strcmp (rs->common.countries[i].code,
   1178                          country_code)) )
   1179       {
   1180         found = true;
   1181         break;
   1182       }
   1183     if (! found)
   1184     {
   1185       ANASTASIS_REDUX_fail_ (rs,
   1186                              cb,
   1187                              cb_cls,
   1188                              TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1189                              "specified country not on selected continent");
   1190       return NULL;
   1191     }
   1192   }
   1193 
   1194   /* Begin fetching provider /configs (we likely need them later) */
   1195   {
   1196     enum TALER_ErrorCode ec;
   1197 
   1198     ec = begin_provider_config_check (country_code,
   1199                                       &rs->common);
   1200     if (TALER_EC_NONE != ec)
   1201     {
   1202       GNUNET_break (0);
   1203       ANASTASIS_REDUX_fail_ (rs,
   1204                              cb,
   1205                              cb_cls,
   1206                              ec,
   1207                              NULL);
   1208       return NULL;
   1209     }
   1210   }
   1211 
   1212   {
   1213     const json_t *ria;
   1214 
   1215     ria = redux_id_attr_init (country_code);
   1216     if (NULL == ria)
   1217     {
   1218       GNUNET_break (0);
   1219       ANASTASIS_REDUX_fail_ (rs,
   1220                              cb,
   1221                              cb_cls,
   1222                              TALER_EC_ANASTASIS_REDUCER_RESOURCE_MISSING,
   1223                              country_code);
   1224       return NULL;
   1225     }
   1226     required_attrs = json_object_get (ria,
   1227                                       "required_attributes");
   1228   }
   1229   if (NULL == required_attrs)
   1230   {
   1231     ANASTASIS_REDUX_fail_ (rs,
   1232                            cb,
   1233                            cb_cls,
   1234                            TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED,
   1235                            "'required_attributes' missing");
   1236     return NULL;
   1237   }
   1238   {
   1239     const char *detail;
   1240 
   1241     if (GNUNET_OK !=
   1242         ANASTASIS_REDUX_required_attributes_set_ (&rs->common,
   1243                                                   required_attrs,
   1244                                                   &detail))
   1245     {
   1246       GNUNET_break (0);
   1247       ANASTASIS_REDUX_fail_ (rs,
   1248                              cb,
   1249                              cb_cls,
   1250                              TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED,
   1251                              detail);
   1252       return NULL;
   1253     }
   1254   }
   1255   redux_transition (rs,
   1256                     ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING);
   1257   GNUNET_free (rs->common.selected_country);
   1258   rs->common.selected_country = GNUNET_strdup (country_code);
   1259   ANASTASIS_REDUX_return_ (rs,
   1260                            cb,
   1261                            cb_cls,
   1262                            TALER_EC_NONE);
   1263   return NULL;
   1264 }
   1265 
   1266 
   1267 /**
   1268  * DispatchHandler/Callback function which is called for a
   1269  * "unselect_continent" action.
   1270  *
   1271  * @param state state to operate on
   1272  * @param arguments arguments to use for operation on state
   1273  * @param cb callback to call during/after operation
   1274  * @param cb_cls callback closure
   1275  * @return NULL
   1276  */
   1277 static struct ANASTASIS_ReduxAction *
   1278 unselect_continent (struct ANASTASIS_ReduxState *rs,
   1279                     const json_t *arguments,
   1280                     ANASTASIS_ActionCallback cb,
   1281                     void *cb_cls)
   1282 {
   1283   redux_transition (rs,
   1284                     ANASTASIS_GENERIC_STATE_CONTINENT_SELECTING);
   1285   ANASTASIS_REDUX_return_ (rs,
   1286                            cb,
   1287                            cb_cls,
   1288                            TALER_EC_NONE);
   1289   return NULL;
   1290 }
   1291 
   1292 
   1293 struct ANASTASIS_ReduxAction *
   1294 ANASTASIS_REDUX_add_provider_to_state_ (const char *url,
   1295                                         struct ANASTASIS_ReduxState *rs,
   1296                                         ANASTASIS_REDUX_StateCallback cb,
   1297                                         void *cb_cls)
   1298 {
   1299   struct ConfigRequest *cr;
   1300   struct ConfigReduxWaiting *w;
   1301 
   1302   cr = check_config (CONFIG_FAST_TIMEOUT,
   1303                      url);
   1304   w = GNUNET_new (struct ConfigReduxWaiting);
   1305   w->cr = cr;
   1306   w->rs = rs;
   1307   w->cb = cb;
   1308   w->cb_cls = cb_cls;
   1309   w->ra.cleanup = &abort_provider_config_cb;
   1310   w->ra.cleanup_cls = w;
   1311   GNUNET_CONTAINER_DLL_insert (cr->w_head,
   1312                                cr->w_tail,
   1313                                w);
   1314   if (NULL == cr->co)
   1315   {
   1316     if (NULL != cr->tt)
   1317       GNUNET_SCHEDULER_cancel (cr->tt);
   1318     cr->tt = GNUNET_SCHEDULER_add_now (&notify_waiting_cb,
   1319                                        cr);
   1320   }
   1321   return &w->ra;
   1322 }
   1323 
   1324 
   1325 /**
   1326  * Context for #ANASTASIS_REDUX_provider_add_().
   1327  */
   1328 struct ProviderAddContext
   1329 {
   1330   /**
   1331    * Handle we returned for cancellation of the operation.
   1332    */
   1333   struct ANASTASIS_ReduxAction ra;
   1334 
   1335   /**
   1336    * The /config request we are waiting for, NULL once it completed.
   1337    */
   1338   struct ANASTASIS_ReduxAction *inner;
   1339 
   1340   /**
   1341    * State we own until we hand it to @e cb.
   1342    */
   1343   struct ANASTASIS_ReduxState *rs;
   1344 
   1345   /**
   1346    * Function to call with the new state.
   1347    */
   1348   ANASTASIS_ActionCallback cb;
   1349 
   1350   /**
   1351    * Closure for @e cb.
   1352    */
   1353   void *cb_cls;
   1354 };
   1355 
   1356 
   1357 /**
   1358  * Free @a cls.
   1359  *
   1360  * @param[in] cls a `struct ProviderAddContext *`
   1361  */
   1362 static void
   1363 provider_add_cleanup (void *cls)
   1364 {
   1365   struct ProviderAddContext *pac = cls;
   1366 
   1367   if (NULL != pac->inner)
   1368     pac->inner->cleanup (pac->inner->cleanup_cls);
   1369   ANASTASIS_REDUX_state_free_ (pac->rs);
   1370   GNUNET_free (pac);
   1371 }
   1372 
   1373 
   1374 /**
   1375  * The /config request completed; return the updated state.
   1376  *
   1377  * @param cls a `struct ProviderAddContext *`
   1378  * @param ec status of the request
   1379  * @param[in] rs the updated state
   1380  */
   1381 static void
   1382 provider_add_done (void *cls,
   1383                    enum TALER_ErrorCode ec,
   1384                    struct ANASTASIS_ReduxState *rs)
   1385 {
   1386   struct ProviderAddContext *pac = cls;
   1387   ANASTASIS_ActionCallback cb = pac->cb;
   1388   void *cb_cls = pac->cb_cls;
   1389 
   1390   GNUNET_assert (rs == pac->rs);
   1391   /* our waiter unlinks and frees itself right after this call */
   1392   pac->inner = NULL;
   1393   pac->rs = NULL;
   1394   provider_add_cleanup (pac);
   1395   ANASTASIS_REDUX_return_ (rs,
   1396                            cb,
   1397                            cb_cls,
   1398                            ec);
   1399 }
   1400 
   1401 
   1402 struct ANASTASIS_ReduxAction *
   1403 ANASTASIS_REDUX_provider_add_ (const char *url,
   1404                                struct ANASTASIS_ReduxState *rs,
   1405                                ANASTASIS_ActionCallback cb,
   1406                                void *cb_cls)
   1407 {
   1408   struct ProviderAddContext *pac;
   1409 
   1410   pac = GNUNET_new (struct ProviderAddContext);
   1411   pac->rs = rs;
   1412   pac->cb = cb;
   1413   pac->cb_cls = cb_cls;
   1414   pac->inner = ANASTASIS_REDUX_add_provider_to_state_ (url,
   1415                                                        rs,
   1416                                                        &provider_add_done,
   1417                                                        pac);
   1418   /* the /config request never completes synchronously */
   1419   GNUNET_assert (NULL != pac->inner);
   1420   pac->ra.cleanup = &provider_add_cleanup;
   1421   pac->ra.cleanup_cls = pac;
   1422   return &pac->ra;
   1423 }
   1424 
   1425 
   1426 /**
   1427  * DispatchHandler/Callback function which is called for a
   1428  * "enter_user_attributes" action.
   1429  * Returns an #ANASTASIS_ReduxAction if operation is async.
   1430  *
   1431  * @param state state to operate on
   1432  * @param arguments arguments to use for operation on state
   1433  * @param cb callback to call during/after operation
   1434  * @param cb_cls callback closure
   1435  * @return NULL
   1436  */
   1437 static struct ANASTASIS_ReduxAction *
   1438 enter_user_attributes (struct ANASTASIS_ReduxState *rs,
   1439                        const json_t *arguments,
   1440                        ANASTASIS_ActionCallback cb,
   1441                        void *cb_cls)
   1442 {
   1443   const json_t *attributes;
   1444 
   1445   if (NULL == arguments)
   1446   {
   1447     ANASTASIS_REDUX_fail_ (rs,
   1448                            cb,
   1449                            cb_cls,
   1450                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1451                            "arguments missing");
   1452     return NULL;
   1453   }
   1454   attributes = json_object_get (arguments,
   1455                                 "identity_attributes");
   1456   if (NULL == attributes)
   1457   {
   1458     ANASTASIS_REDUX_fail_ (rs,
   1459                            cb,
   1460                            cb_cls,
   1461                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1462                            "'identity_attributes' missing");
   1463     return NULL;
   1464   }
   1465   json_decref (rs->common.identity_attributes);
   1466   rs->common.identity_attributes = json_incref ((json_t *) attributes);
   1467 
   1468   if (! rs->common.have_required_attributes)
   1469   {
   1470     ANASTASIS_REDUX_fail_ (rs,
   1471                            cb,
   1472                            cb_cls,
   1473                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1474                            "'required_attributes' must be an array");
   1475     return NULL;
   1476   }
   1477   /* Verify required attributes are present and well-formed */
   1478   for (unsigned int i = 0; i < rs->common.required_attributes_len; i++)
   1479   {
   1480     const struct ANASTASIS_ReduxAttributeSpec *a
   1481       = &rs->common.required_attributes[i];
   1482     const char *attribute_value;
   1483 
   1484     attribute_value = json_string_value (json_object_get (attributes,
   1485                                                           a->name));
   1486     if (NULL == attribute_value)
   1487     {
   1488       if (a->optional)
   1489         continue;
   1490       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1491                   "Request is missing required attribute `%s'\n",
   1492                   a->name);
   1493       ANASTASIS_REDUX_fail_ (rs,
   1494                              cb,
   1495                              cb_cls,
   1496                              TALER_EC_GENERIC_PARAMETER_MISSING,
   1497                              a->name);
   1498       return NULL;
   1499     }
   1500     if ( (NULL != a->validation_regex) &&
   1501          (! validate_regex (attribute_value,
   1502                             a->validation_regex)) )
   1503     {
   1504       ANASTASIS_REDUX_fail_ (rs,
   1505                              cb,
   1506                              cb_cls,
   1507                              TALER_EC_ANASTASIS_REDUCER_INPUT_REGEX_FAILED,
   1508                              a->name);
   1509       return NULL;
   1510     }
   1511     if (NULL != a->validation_logic)
   1512     {
   1513       ANASTASIS_ValidationLogic regfun;
   1514 
   1515       regfun = ANASTASIS_REDUX_validation_lookup_ (a->validation_logic);
   1516       if (NULL == regfun)
   1517       {
   1518         /* The name comes from the (untrusted) state, so an unknown one must
   1519            fail the validation instead of skipping it. */
   1520         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1521                     "Validation function `%s' is not known\n",
   1522                     a->validation_logic);
   1523         ANASTASIS_REDUX_fail_ (rs,
   1524                                cb,
   1525                                cb_cls,
   1526                                TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED,
   1527                                a->name);
   1528         return NULL;
   1529       }
   1530       if (! regfun (attribute_value))
   1531       {
   1532         ANASTASIS_REDUX_fail_ (rs,
   1533                                cb,
   1534                                cb_cls,
   1535                                TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED,
   1536                                a->name);
   1537         return NULL;
   1538       }
   1539     }
   1540   } /* end for all attributes loop */
   1541 
   1542   /* Transition based on mode */
   1543   switch (rs->type)
   1544   {
   1545   case ANASTASIS_RT_BACKUP:
   1546     rs->details.backup.state
   1547       = ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING;
   1548     return ANASTASIS_REDUX_backup_begin_ (rs,
   1549                                           arguments,
   1550                                           cb,
   1551                                           cb_cls);
   1552   case ANASTASIS_RT_RECOVERY:
   1553     rs->details.recovery.state
   1554       = ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING;
   1555     return ANASTASIS_REDUX_recovery_challenge_begin_ (rs,
   1556                                                       arguments,
   1557                                                       cb,
   1558                                                       cb_cls);
   1559   case ANASTASIS_RT_ERROR:
   1560     break;
   1561   }
   1562   GNUNET_assert (0);
   1563   return NULL;
   1564 }
   1565 
   1566 
   1567 /**
   1568  * DispatchHandler/Callback function which is called for a
   1569  * "add_provider" action.  Adds another Anastasis provider
   1570  * to the list of available providers for storing information.
   1571  *
   1572  * @param state state to operate on
   1573  * @param arguments arguments with a provider URL to add
   1574  * @param cb callback to call during/after operation
   1575  * @param cb_cls callback closure
   1576  */
   1577 static struct ANASTASIS_ReduxAction *
   1578 add_provider (struct ANASTASIS_ReduxState *rs,
   1579               const json_t *arguments,
   1580               ANASTASIS_ActionCallback cb,
   1581               void *cb_cls)
   1582 {
   1583   if (ANASTASIS_add_provider_ (rs,
   1584                                arguments,
   1585                                cb,
   1586                                cb_cls))
   1587     return NULL;
   1588   ANASTASIS_REDUX_return_ (rs,
   1589                            cb,
   1590                            cb_cls,
   1591                            TALER_EC_NONE);
   1592   return NULL;
   1593 }
   1594 
   1595 
   1596 bool
   1597 ANASTASIS_add_provider_ (struct ANASTASIS_ReduxState *rs,
   1598                          const json_t *arguments,
   1599                          ANASTASIS_ActionCallback cb,
   1600                          void *cb_cls)
   1601 {
   1602   const char *url;
   1603   json_t *params;
   1604 
   1605   if (NULL == arguments)
   1606   {
   1607     ANASTASIS_REDUX_fail_ (rs,
   1608                            cb,
   1609                            cb_cls,
   1610                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1611                            "arguments missing");
   1612     return true; /* cb was invoked */
   1613   }
   1614   json_object_foreach (((json_t *) arguments), url, params)
   1615   {
   1616     const char *detail;
   1617 
   1618     if (GNUNET_OK !=
   1619         ANASTASIS_REDUX_provider_set_ (&rs->common,
   1620                                        url,
   1621                                        params,
   1622                                        &detail))
   1623     {
   1624       GNUNET_break_op (0);
   1625       ANASTASIS_REDUX_fail_ (rs,
   1626                              cb,
   1627                              cb_cls,
   1628                              TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1629                              detail);
   1630       return true; /* cb was invoked */
   1631     }
   1632   }
   1633   return false; /* cb not invoked */
   1634 }
   1635 
   1636 
   1637 struct ANASTASIS_ReduxAction *
   1638 ANASTASIS_back_generic_decrement_ (struct ANASTASIS_ReduxState *rs,
   1639                                    const json_t *arguments,
   1640                                    ANASTASIS_ActionCallback cb,
   1641                                    void *cb_cls)
   1642 {
   1643   (void) arguments;
   1644   switch (rs->type)
   1645   {
   1646   case ANASTASIS_RT_BACKUP:
   1647     GNUNET_assert (rs->details.backup.state > 0);
   1648     rs->details.backup.state--;
   1649     break;
   1650   case ANASTASIS_RT_RECOVERY:
   1651     GNUNET_assert (rs->details.recovery.state > 0);
   1652     rs->details.recovery.state--;
   1653     break;
   1654   case ANASTASIS_RT_ERROR:
   1655     GNUNET_assert (0);
   1656     break;
   1657   }
   1658   ANASTASIS_REDUX_return_ (rs,
   1659                            cb,
   1660                            cb_cls,
   1661                            TALER_EC_NONE);
   1662   return NULL;
   1663 }
   1664 
   1665 
   1666 /**
   1667  * Callback function which is called by the reducer in dependence of
   1668  * given state and action.
   1669  *
   1670  * The handler takes ownership of @a rs and must dispose of it, either by
   1671  * handing it to #ANASTASIS_REDUX_return_ / #ANASTASIS_REDUX_fail_ or by
   1672  * passing it on to another handler that does.
   1673  *
   1674  * @param[in] rs the previous state to operate on
   1675  * @param arguments the arguments needed by operation to operate on state
   1676  * @param cb Callback function which returns the new state
   1677  * @param cb_cls closure for @a cb
   1678  * @return handle to cancel async actions, NULL if @a cb was already called
   1679  */
   1680 typedef struct ANASTASIS_ReduxAction *
   1681 (*DispatchHandler)(struct ANASTASIS_ReduxState *rs,
   1682                    const json_t *arguments,
   1683                    ANASTASIS_ActionCallback cb,
   1684                    void *cb_cls);
   1685 
   1686 
   1687 /**
   1688  * Closure for read operations on the external reducer.
   1689  */
   1690 struct ExternalReducerCls
   1691 {
   1692   struct GNUNET_Buffer read_buffer;
   1693   struct GNUNET_SCHEDULER_Task *read_task;
   1694   struct GNUNET_DISK_PipeHandle *reducer_stdin;
   1695   struct GNUNET_DISK_PipeHandle *reducer_stdout;
   1696   struct GNUNET_Process *reducer_process;
   1697   ANASTASIS_ActionCallback action_cb;
   1698   void *action_cb_cls;
   1699 };
   1700 
   1701 /**
   1702  * Clean up and destroy the external reducer state.
   1703  *
   1704  * @param cls closure, a 'struct ExternalReducerCls *'
   1705  */
   1706 static void
   1707 cleanup_external_reducer (void *cls)
   1708 {
   1709   struct ExternalReducerCls *red_cls = cls;
   1710 
   1711   if (NULL != red_cls->read_task)
   1712   {
   1713     GNUNET_SCHEDULER_cancel (red_cls->read_task);
   1714     red_cls->read_task = NULL;
   1715   }
   1716 
   1717   GNUNET_buffer_clear (&red_cls->read_buffer);
   1718   if (NULL != red_cls->reducer_stdin)
   1719   {
   1720     GNUNET_DISK_pipe_close (red_cls->reducer_stdin);
   1721     red_cls->reducer_stdin = NULL;
   1722   }
   1723   if (NULL != red_cls->reducer_stdout)
   1724   {
   1725     GNUNET_DISK_pipe_close (red_cls->reducer_stdout);
   1726     red_cls->reducer_stdout = NULL;
   1727   }
   1728 
   1729   if (NULL != red_cls->reducer_process)
   1730   {
   1731     enum GNUNET_OS_ProcessStatusType type;
   1732     unsigned long code;
   1733     enum GNUNET_GenericReturnValue pwret;
   1734 
   1735     pwret = GNUNET_process_wait (red_cls->reducer_process,
   1736                                  false,
   1737                                  &type,
   1738                                  &code);
   1739     GNUNET_assert (GNUNET_SYSERR != pwret);
   1740     if (GNUNET_NO == pwret)
   1741     {
   1742       GNUNET_assert (GNUNET_OK ==
   1743                      GNUNET_process_kill (red_cls->reducer_process,
   1744                                           SIGTERM));
   1745       GNUNET_assert (GNUNET_SYSERR !=
   1746                      GNUNET_process_wait (red_cls->reducer_process,
   1747                                           true,
   1748                                           NULL,
   1749                                           NULL));
   1750     }
   1751 
   1752     GNUNET_process_destroy (red_cls->reducer_process);
   1753     red_cls->reducer_process = NULL;
   1754   }
   1755 
   1756   GNUNET_free (red_cls);
   1757 }
   1758 
   1759 
   1760 /**
   1761  * Task called when
   1762  *
   1763  * @param cls closure, a 'struct ExternalReducerCls *'
   1764  */
   1765 static void
   1766 external_reducer_read_cb (void *cls)
   1767 {
   1768   struct ExternalReducerCls *red_cls = cls;
   1769   ssize_t sret;
   1770   char buf[256];
   1771 
   1772   red_cls->read_task = NULL;
   1773 
   1774   sret = GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (
   1775                                   red_cls->reducer_stdout,
   1776                                   GNUNET_DISK_PIPE_END_READ),
   1777                                 buf,
   1778                                 256);
   1779   if (sret < 0)
   1780   {
   1781     GNUNET_break (0);
   1782     red_cls->action_cb (red_cls->action_cb_cls,
   1783                         TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR,
   1784                         NULL);
   1785     cleanup_external_reducer (red_cls);
   1786     return;
   1787   }
   1788   else if (0 == sret)
   1789   {
   1790     char *str = GNUNET_buffer_reap_str (&red_cls->read_buffer);
   1791     json_t *json;
   1792 
   1793     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1794                 "Got external reducer response: '%s'\n",
   1795                 str);
   1796 
   1797     json = json_loads (str, 0, NULL);
   1798 
   1799     if (NULL == json)
   1800     {
   1801       GNUNET_break (0);
   1802       red_cls->action_cb (red_cls->action_cb_cls,
   1803                           TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR,
   1804                           NULL);
   1805       cleanup_external_reducer (red_cls);
   1806       return;
   1807     }
   1808 
   1809     {
   1810       enum TALER_ErrorCode ec;
   1811       ec = json_integer_value (json_object_get (json, "code"));
   1812 
   1813       red_cls->action_cb (red_cls->action_cb_cls,
   1814                           ec,
   1815                           json);
   1816     }
   1817     cleanup_external_reducer (red_cls);
   1818     return;
   1819   }
   1820   else
   1821   {
   1822     GNUNET_buffer_write (&red_cls->read_buffer,
   1823                          buf,
   1824                          sret);
   1825 
   1826     red_cls->read_task = GNUNET_SCHEDULER_add_read_file (
   1827       GNUNET_TIME_UNIT_FOREVER_REL,
   1828       GNUNET_DISK_pipe_handle (
   1829         red_cls->reducer_stdout,
   1830         GNUNET_DISK_PIPE_END_READ),
   1831       external_reducer_read_cb,
   1832       red_cls);
   1833   }
   1834 }
   1835 
   1836 
   1837 /**
   1838  * Handle an action using an external reducer, i.e.
   1839  * by shelling out to another process.
   1840  */
   1841 static struct ANASTASIS_ReduxAction *
   1842 redux_action_external (const char *ext_reducer,
   1843                        const json_t *state,
   1844                        const char *action,
   1845                        const json_t *arguments,
   1846                        ANASTASIS_ActionCallback cb,
   1847                        void *cb_cls)
   1848 {
   1849   char *arg_str;
   1850   char *state_str = json_dumps (state, JSON_COMPACT);
   1851   ssize_t sret;
   1852   struct ExternalReducerCls *red_cls = GNUNET_new (struct ExternalReducerCls);
   1853 
   1854   if (NULL == arguments)
   1855     arg_str = GNUNET_strdup ("{}");
   1856   else
   1857     arg_str = json_dumps (arguments, JSON_COMPACT);
   1858 
   1859   red_cls->action_cb = cb;
   1860   red_cls->action_cb_cls = cb_cls;
   1861 
   1862   GNUNET_assert (NULL != (red_cls->reducer_stdin = GNUNET_DISK_pipe (
   1863                             GNUNET_DISK_PF_NONE)));
   1864   GNUNET_assert (NULL != (red_cls->reducer_stdout = GNUNET_DISK_pipe (
   1865                             GNUNET_DISK_PF_NONE)));
   1866 
   1867   /* By the time we're here, this variable should be unset, because
   1868      otherwise using anastasis-reducer as the external reducer
   1869      will lead to infinite recursion. */
   1870   GNUNET_assert (NULL == getenv ("ANASTASIS_EXTERNAL_REDUCER"));
   1871 
   1872   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1873               "Starting external reducer with action '%s' and argument '%s'\n",
   1874               action,
   1875               arg_str);
   1876 
   1877   red_cls->reducer_process
   1878     = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
   1879   GNUNET_assert (GNUNET_OK ==
   1880                  GNUNET_process_set_options (
   1881                    red_cls->reducer_process,
   1882                    GNUNET_process_option_inherit_rpipe (red_cls->reducer_stdin,
   1883                                                         STDIN_FILENO),
   1884                    GNUNET_process_option_inherit_rpipe (red_cls->reducer_stdout,
   1885                                                         STDOUT_FILENO)));
   1886   if (GNUNET_OK !=
   1887       GNUNET_process_run_command_va (red_cls->reducer_process,
   1888                                      ext_reducer,
   1889                                      ext_reducer,
   1890                                      "-a",
   1891                                      arg_str,
   1892                                      action,
   1893                                      NULL))
   1894   {
   1895     GNUNET_break (0);
   1896     GNUNET_process_destroy (red_cls->reducer_process);
   1897     red_cls->reducer_process = NULL;
   1898     GNUNET_free (arg_str);
   1899     GNUNET_free (state_str);
   1900     cleanup_external_reducer (red_cls);
   1901     return NULL;
   1902   }
   1903   GNUNET_free (arg_str);
   1904 
   1905   /* Close pipe ends we don't use. */
   1906   GNUNET_assert (GNUNET_OK ==
   1907                  GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin,
   1908                                              GNUNET_DISK_PIPE_END_READ));
   1909   GNUNET_assert (GNUNET_OK ==
   1910                  GNUNET_DISK_pipe_close_end (red_cls->reducer_stdout,
   1911                                              GNUNET_DISK_PIPE_END_WRITE));
   1912 
   1913   sret = GNUNET_DISK_file_write_blocking (GNUNET_DISK_pipe_handle (
   1914                                             red_cls->reducer_stdin,
   1915                                             GNUNET_DISK_PIPE_END_WRITE),
   1916                                           state_str,
   1917                                           strlen (state_str));
   1918   GNUNET_free (state_str);
   1919   if (sret <= 0)
   1920   {
   1921     GNUNET_break (0);
   1922     cleanup_external_reducer (red_cls);
   1923     return NULL;
   1924   }
   1925 
   1926   GNUNET_assert (GNUNET_OK ==
   1927                  GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin,
   1928                                              GNUNET_DISK_PIPE_END_WRITE));
   1929 
   1930   red_cls->read_task = GNUNET_SCHEDULER_add_read_file (
   1931     GNUNET_TIME_UNIT_FOREVER_REL,
   1932     GNUNET_DISK_pipe_handle (
   1933       red_cls->reducer_stdout,
   1934       GNUNET_DISK_PIPE_END_READ),
   1935     external_reducer_read_cb,
   1936     red_cls);
   1937 
   1938   {
   1939     struct ANASTASIS_ReduxAction *ra
   1940       = GNUNET_new (struct ANASTASIS_ReduxAction);
   1941     ra->cleanup_cls = red_cls;
   1942     ra->cleanup = cleanup_external_reducer;
   1943     return ra;
   1944   }
   1945 }
   1946 
   1947 
   1948 struct ANASTASIS_ReduxAction *
   1949 ANASTASIS_redux_action (const json_t *state,
   1950                         const char *action,
   1951                         const json_t *arguments,
   1952                         ANASTASIS_ActionCallback cb,
   1953                         void *cb_cls)
   1954 {
   1955   struct Dispatcher
   1956   {
   1957     enum ANASTASIS_GenericState redux_state;
   1958     const char *redux_action;
   1959     DispatchHandler fun;
   1960   } dispatchers[] = {
   1961     {
   1962       ANASTASIS_GENERIC_STATE_CONTINENT_SELECTING,
   1963       "select_continent",
   1964       &select_continent
   1965     },
   1966     /* Deprecated alias for "back" from that state, should be removed eventually. */
   1967     {
   1968       ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
   1969       "unselect_continent",
   1970       &unselect_continent
   1971     },
   1972     {
   1973       ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
   1974       "back",
   1975       &unselect_continent
   1976     },
   1977     {
   1978       ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
   1979       "select_country",
   1980       &select_country
   1981     },
   1982     {
   1983       ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
   1984       "select_continent",
   1985       &select_continent
   1986     },
   1987     {
   1988       ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING,
   1989       "enter_user_attributes",
   1990       &enter_user_attributes
   1991     },
   1992     {
   1993       ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING,
   1994       "add_provider",
   1995       &add_provider
   1996     },
   1997     {
   1998       ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING,
   1999       "back",
   2000       &ANASTASIS_back_generic_decrement_
   2001     },
   2002     { ANASTASIS_GENERIC_STATE_INVALID, NULL, NULL }
   2003   };
   2004   struct ANASTASIS_ReduxState *rs;
   2005   enum ANASTASIS_GenericState gs;
   2006 
   2007   /* If requested, handle action with external reducer, used for testing. */
   2008   {
   2009     const char *ext_reducer = ANASTASIS_REDUX_probe_external_reducer ();
   2010     if (NULL != ext_reducer)
   2011       return redux_action_external (ext_reducer,
   2012                                     state,
   2013                                     action,
   2014                                     arguments,
   2015                                     cb,
   2016                                     cb_cls);
   2017   }
   2018 
   2019   /* This is the one place where a state is read.  Everything below
   2020      operates on the typed representation, and every path back out goes
   2021      through ANASTASIS_REDUX_return_() or ANASTASIS_REDUX_fail_(). */
   2022   {
   2023     enum TALER_ErrorCode ec;
   2024     const char *detail;
   2025 
   2026     rs = ANASTASIS_REDUX_state_parse_ (state,
   2027                                        &ec,
   2028                                        &detail);
   2029     if (NULL == rs)
   2030     {
   2031       GNUNET_break_op (0);
   2032       ANASTASIS_redux_fail_ (cb,
   2033                              cb_cls,
   2034                              ec,
   2035                              detail);
   2036       return NULL;
   2037     }
   2038   }
   2039   if (ANASTASIS_RT_ERROR == rs->type)
   2040   {
   2041     /* An error state has no step to advance from. */
   2042     GNUNET_break_op (0);
   2043     ANASTASIS_REDUX_fail_ (rs,
   2044                            cb,
   2045                            cb_cls,
   2046                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   2047                            "reducer_type");
   2048     return NULL;
   2049   }
   2050   gs = get_generic_state (rs);
   2051   if (ANASTASIS_GENERIC_STATE_INVALID != gs)
   2052   {
   2053     for (unsigned int i = 0; NULL != dispatchers[i].fun; i++)
   2054     {
   2055       if ( (gs == dispatchers[i].redux_state) &&
   2056            (0 == strcmp (action,
   2057                          dispatchers[i].redux_action)) )
   2058         return dispatchers[i].fun (rs,
   2059                                    arguments,
   2060                                    cb,
   2061                                    cb_cls);
   2062     }
   2063   }
   2064   if (ANASTASIS_RT_RECOVERY == rs->type)
   2065     return ANASTASIS_recovery_action_ (rs,
   2066                                        action,
   2067                                        arguments,
   2068                                        cb,
   2069                                        cb_cls);
   2070   return ANASTASIS_backup_action_ (rs,
   2071                                    action,
   2072                                    arguments,
   2073                                    cb,
   2074                                    cb_cls);
   2075 }
   2076 
   2077 
   2078 void
   2079 ANASTASIS_redux_action_cancel (struct ANASTASIS_ReduxAction *ra)
   2080 {
   2081   ra->cleanup (ra->cleanup_cls);
   2082 }
   2083 
   2084 
   2085 json_t *
   2086 ANASTASIS_REDUX_load_continents_ ()
   2087 {
   2088   const json_t *countries;
   2089   json_t *continents;
   2090   const json_t *rc = ANASTASIS_redux_countries_init_ ();
   2091 
   2092   if (NULL == rc)
   2093   {
   2094     GNUNET_break (0);
   2095     return NULL;
   2096   }
   2097   countries = json_object_get (rc,
   2098                                "countries");
   2099   if (NULL == countries)
   2100   {
   2101     GNUNET_break (0);
   2102     return NULL;
   2103   }
   2104   continents = json_array ();
   2105   GNUNET_assert (NULL != continents);
   2106 
   2107   {
   2108     json_t *country;
   2109     size_t index;
   2110 
   2111     json_array_foreach (countries, index, country)
   2112     {
   2113       json_t *ex = NULL;
   2114       const json_t *continent;
   2115 
   2116       continent = json_object_get (country,
   2117                                    "continent");
   2118       if ( (NULL == continent) ||
   2119            (! json_is_string (continent)) )
   2120       {
   2121         GNUNET_break (0);
   2122         continue;
   2123       }
   2124       {
   2125         size_t inner_index;
   2126         json_t *inner_continent;
   2127 
   2128         json_array_foreach (continents, inner_index, inner_continent)
   2129         {
   2130           const json_t *name;
   2131 
   2132           name = json_object_get (inner_continent,
   2133                                   "name");
   2134           if (1 == json_equal (continent,
   2135                                name))
   2136           {
   2137             ex = inner_continent;
   2138             break;
   2139           }
   2140         }
   2141       }
   2142       if (NULL == ex)
   2143       {
   2144         ex = GNUNET_JSON_PACK (
   2145           GNUNET_JSON_pack_string ("name",
   2146                                    json_string_value (continent)));
   2147         GNUNET_assert (0 ==
   2148                        json_array_append_new (continents,
   2149                                               ex));
   2150       }
   2151 
   2152       {
   2153         json_t *i18n_continent;
   2154         json_t *name_ex;
   2155 
   2156         i18n_continent = json_object_get (country,
   2157                                           "continent_i18n");
   2158         name_ex = json_object_get (ex,
   2159                                    "name_i18n");
   2160         if (NULL != i18n_continent)
   2161         {
   2162           const char *lang;
   2163           json_t *trans;
   2164 
   2165           json_object_foreach (i18n_continent, lang, trans)
   2166           {
   2167             if (NULL == name_ex)
   2168             {
   2169               name_ex = json_object ();
   2170               GNUNET_assert (NULL != name_ex);
   2171               GNUNET_assert (0 ==
   2172                              json_object_set_new (ex,
   2173                                                   "name_i18n",
   2174                                                   name_ex));
   2175             }
   2176             if (NULL == json_object_get (name_ex,
   2177                                          lang))
   2178             {
   2179               GNUNET_assert (0 ==
   2180                              json_object_set (name_ex,
   2181                                               lang,
   2182                                               trans));
   2183             }
   2184           }
   2185         }
   2186       }
   2187     }
   2188   }
   2189   return GNUNET_JSON_PACK (
   2190     GNUNET_JSON_pack_array_steal ("continents",
   2191                                   continents));
   2192 }
   2193 
   2194 
   2195 enum GNUNET_GenericReturnValue
   2196 ANASTASIS_REDUX_lookup_salt_ (
   2197   const struct ANASTASIS_ReduxCommon *common,
   2198   const char *provider_url,
   2199   struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt)
   2200 {
   2201   const struct ANASTASIS_ReduxProvider *p;
   2202 
   2203   p = ANASTASIS_REDUX_provider_find_ (common,
   2204                                       provider_url);
   2205   if (NULL == p)
   2206   {
   2207     GNUNET_break (0);
   2208     return GNUNET_SYSERR;
   2209   }
   2210   if (ANASTASIS_RPS_DISABLED == p->status)
   2211     return GNUNET_NO;
   2212   if (! p->have_config)
   2213     return GNUNET_NO;
   2214   *provider_salt = p->config.provider_salt;
   2215   return GNUNET_OK;
   2216 }
   2217 
   2218 
   2219 /**
   2220  * Lookup @a provider_salt of @a provider_url in @a state.
   2221  *
   2222  * The public API still speaks JSON here, so this is the typed lookup
   2223  * with a parse in front of it.
   2224  *
   2225  * @param state the state to inspect
   2226  * @param provider_url provider to look into
   2227  * @param[out] provider_salt value to extract
   2228  * @return #GNUNET_OK on success
   2229  */
   2230 enum GNUNET_GenericReturnValue
   2231 ANASTASIS_reducer_lookup_salt (
   2232   const json_t *state,
   2233   const char *provider_url,
   2234   struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt)
   2235 {
   2236   struct ANASTASIS_ReduxState *rs;
   2237   enum GNUNET_GenericReturnValue ret;
   2238 
   2239   {
   2240     enum TALER_ErrorCode ec;
   2241     const char *detail;
   2242 
   2243     rs = ANASTASIS_REDUX_state_parse_ (state,
   2244                                        &ec,
   2245                                        &detail);
   2246     if (NULL == rs)
   2247     {
   2248       GNUNET_break (0);
   2249       return GNUNET_SYSERR;
   2250     }
   2251   }
   2252   ret = ANASTASIS_REDUX_lookup_salt_ (&rs->common,
   2253                                       provider_url,
   2254                                       provider_salt);
   2255   ANASTASIS_REDUX_state_free_ (rs);
   2256   return ret;
   2257 }