anastasis

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

anastasis_api_redux.c (68679B)


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