anastasis

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

anastasis_api_backup_redux.c (127789B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020-2023 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_backup_redux.c
     18  * @brief anastasis reducer backup api
     19  * @author Christian Grothoff
     20  * @author Dominik Meister
     21  * @author Dennis Neufeld
     22  */
     23 
     24 #include "platform.h"
     25 #include "anastasis_redux.h"
     26 #include "anastasis_api_redux.h"
     27 #include "anastasis_api_redux_state.h"
     28 #include <taler/taler_merchant_service.h>
     29 
     30 /**
     31  * How long do Anastasis providers store data if the service
     32  * is free? Must match #ANASTASIS_MAX_YEARS_STORAGE from
     33  * anastasis-httpd.h.
     34  */
     35 #define ANASTASIS_FREE_STORAGE GNUNET_TIME_relative_multiply ( \
     36           GNUNET_TIME_UNIT_YEARS, 5)
     37 
     38 /**
     39  * CPU limiter: do not evaluate more than 16k
     40  * possible policy combinations to find the "best"
     41  * policy.
     42  */
     43 #define MAX_EVALUATIONS (1024 * 16)
     44 
     45 
     46 #define GENERATE_STRING(STRING) #STRING,
     47 static const char *backup_strings[] = {
     48   ANASTASIS_BACKUP_STATES (GENERATE_STRING)
     49 };
     50 #undef GENERATE_STRING
     51 
     52 
     53 /**
     54  * Linked list of costs.
     55  */
     56 struct Costs
     57 {
     58 
     59   /**
     60    * Kept in a LL.
     61    */
     62   struct Costs *next;
     63 
     64   /**
     65    * Cost in one of the currencies.
     66    */
     67   struct TALER_Amount cost;
     68 };
     69 
     70 
     71 /**
     72  * Pick the single amount a provider contributes to an advisory total.
     73  *
     74  * A price list is not a sum: a provider offering both EUR and CHF wants
     75  * one or the other, not both, so adding every entry would tell the user
     76  * they owe twice over.  Each provider therefore contributes exactly one
     77  * amount --- in the preferred currency if it offers one, and otherwise in
     78  * its own primary currency, which is the honest answer that this part of
     79  * the backup will be settled separately.
     80  *
     81  * @param common state holding the user's preferred currency
     82  * @param al price list to pick from
     83  * @return the amount to add, NULL if @a al is empty
     84  */
     85 static const struct TALER_Amount *
     86 preferred_price (const struct ANASTASIS_ReduxCommon *common,
     87                  const struct TALER_AmountList *al)
     88 {
     89   if (NULL != common->preferred_currency)
     90   {
     91     const struct TALER_Amount *a;
     92 
     93     a = TALER_amount_list_find (al,
     94                                 common->preferred_currency);
     95     if (NULL != a)
     96       return a;
     97   }
     98   if (0 == al->tal_len)
     99     return NULL;
    100   return &al->tal[0];
    101 }
    102 
    103 
    104 /**
    105  * Check if two price lists offer the same prices, ignoring order.
    106  *
    107  * @param a first list
    108  * @param b second list
    109  * @return true if they agree on every currency
    110  */
    111 static bool
    112 amount_lists_equal (const struct TALER_AmountList *a,
    113                     const struct TALER_AmountList *b)
    114 {
    115   if (a->tal_len != b->tal_len)
    116     return false;
    117   for (unsigned int i = 0; i<a->tal_len; i++)
    118   {
    119     const struct TALER_Amount *o;
    120 
    121     o = TALER_amount_list_find (b,
    122                                 a->tal[i].currency);
    123     if ( (NULL == o) ||
    124          (0 != TALER_amount_cmp (&a->tal[i],
    125                                  o)) )
    126       return false;
    127   }
    128   return true;
    129 }
    130 
    131 
    132 /**
    133  * Add amount from @a cost to @a my_cost list.
    134  *
    135  * @param[in,out] my_cost pointer to list to modify
    136  * @param cost amount to add
    137  */
    138 static void
    139 add_cost (struct Costs **my_cost,
    140           const struct TALER_Amount *cost)
    141 {
    142   for (struct Costs *pos = *my_cost;
    143        NULL != pos;
    144        pos = pos->next)
    145   {
    146     if (GNUNET_OK !=
    147         TALER_amount_cmp_currency (&pos->cost,
    148                                    cost))
    149       continue;
    150     GNUNET_assert (0 <=
    151                    TALER_amount_add (&pos->cost,
    152                                      &pos->cost,
    153                                      cost));
    154     return;
    155   }
    156   {
    157     struct Costs *nc;
    158 
    159     nc = GNUNET_new (struct Costs);
    160     nc->cost = *cost;
    161     nc->next = *my_cost;
    162     *my_cost = nc;
    163   }
    164 }
    165 
    166 
    167 /**
    168  * Add amount from @a cost to @a my_cost list.
    169  *
    170  * @param[in,out] my_cost pointer to list to modify
    171  * @param cost amount to add
    172  */
    173 static void
    174 add_costs (struct Costs **my_cost,
    175            const struct Costs *costs)
    176 {
    177   for (const struct Costs *pos = costs;
    178        NULL != pos;
    179        pos = pos->next)
    180   {
    181     add_cost (my_cost,
    182               &pos->cost);
    183   }
    184 }
    185 
    186 
    187 enum ANASTASIS_BackupState
    188 ANASTASIS_backup_state_from_string_ (const char *state_string)
    189 {
    190   for (enum ANASTASIS_BackupState i = 0;
    191        i < sizeof (backup_strings) / sizeof(*backup_strings);
    192        i++)
    193     if (0 == strcmp (state_string,
    194                      backup_strings[i]))
    195       return i;
    196   return ANASTASIS_BACKUP_STATE_INVALID;
    197 }
    198 
    199 
    200 const char *
    201 ANASTASIS_backup_state_to_string_ (enum ANASTASIS_BackupState bs)
    202 {
    203   if ( (bs < 0) ||
    204        (bs >= sizeof (backup_strings) / sizeof(*backup_strings)) )
    205   {
    206     GNUNET_break_op (0);
    207     return NULL;
    208   }
    209   return backup_strings[bs];
    210 }
    211 
    212 
    213 /**
    214  * Transition @a rs to @a new_backup_state.
    215  *
    216  * @param[in,out] rs the state to transition
    217  * @param new_backup_state the state to transition to
    218  */
    219 static void
    220 set_state (struct ANASTASIS_ReduxState *rs,
    221            enum ANASTASIS_BackupState new_backup_state)
    222 {
    223   GNUNET_assert (ANASTASIS_RT_BACKUP == rs->type);
    224   rs->details.backup.state = new_backup_state;
    225 }
    226 
    227 
    228 /**
    229  * Returns an initial ANASTASIS backup state (CONTINENT_SELECTING).
    230  *
    231  * @param cfg handle for gnunet configuration
    232  * @return NULL on failure
    233  */
    234 json_t *
    235 ANASTASIS_backup_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
    236 {
    237   json_t *initial_state;
    238   const char *external_reducer = ANASTASIS_REDUX_probe_external_reducer ();
    239 
    240   if (NULL != external_reducer)
    241   {
    242     int pipefd_stdout[2];
    243     pid_t pid = 0;
    244     int status;
    245     FILE *reducer_stdout;
    246 
    247     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    248                 "Using external reducer '%s' for backup start status\n",
    249                 external_reducer);
    250 
    251     GNUNET_assert (0 == pipe (pipefd_stdout));
    252     pid = fork ();
    253     if (pid == 0)
    254     {
    255       GNUNET_assert (0 ==
    256                      close (pipefd_stdout[0]));
    257       GNUNET_assert (STDOUT_FILENO ==
    258                      dup2 (pipefd_stdout[1],
    259                            STDOUT_FILENO));
    260       execlp (external_reducer,
    261               external_reducer,
    262               "-b",
    263               NULL);
    264       GNUNET_assert (0);
    265     }
    266 
    267     GNUNET_assert (0 ==
    268                    close (pipefd_stdout[1]));
    269     reducer_stdout = fdopen (pipefd_stdout[0],
    270                              "r");
    271     GNUNET_assert (NULL != reducer_stdout);
    272     {
    273       json_error_t err;
    274 
    275       initial_state = json_loadf (reducer_stdout,
    276                                   0,
    277                                   &err);
    278 
    279       if (NULL == initial_state)
    280       {
    281         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    282                     "External reducer did not output valid JSON: %s:%d:%d %s\n",
    283                     err.source,
    284                     err.line,
    285                     err.column,
    286                     err.text);
    287         GNUNET_assert (0 == fclose (reducer_stdout));
    288         waitpid (pid, &status, 0);
    289         return NULL;
    290       }
    291     }
    292 
    293     GNUNET_assert (NULL != initial_state);
    294     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    295                 "Waiting for external reducer to terminate.\n");
    296     GNUNET_assert (0 == fclose (reducer_stdout));
    297     reducer_stdout = NULL;
    298     waitpid (pid, &status, 0);
    299 
    300     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    301                 "External reducer finished with exit status '%d'\n",
    302                 status);
    303     return initial_state;
    304   }
    305 
    306   (void) cfg;
    307   initial_state = ANASTASIS_REDUX_load_continents_ ();
    308   if (NULL == initial_state)
    309     return NULL;
    310   GNUNET_assert (
    311     0 ==
    312     json_object_set_new (initial_state,
    313                          "reducer_type",
    314                          json_string ("backup")));
    315   GNUNET_assert (
    316     0 ==
    317     json_object_set_new (
    318       initial_state,
    319       "backup_state",
    320       json_string (
    321         ANASTASIS_backup_state_to_string_ (
    322           ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING))));
    323   return initial_state;
    324 }
    325 
    326 
    327 /**
    328  * Test if @a challenge_size is small enough for the provider's
    329  * @a size_limit_in_mb.
    330  *
    331  * We add 1024 to @a challenge_size here as a "safety margin" as
    332  * the encrypted challenge has some additional headers around it
    333  *
    334  * @param size_limit_in_mb provider's upload limit
    335  * @param challenge_size actual binary size of the challenge
    336  * @return true if this fits
    337  */
    338 static bool
    339 challenge_size_ok (uint32_t size_limit_in_mb,
    340                    size_t challenge_size)
    341 {
    342   return (size_limit_in_mb * 1024LLU * 1024LLU >=
    343           challenge_size + 1024LLU);
    344 }
    345 
    346 
    347 /**
    348  * DispatchHandler/Callback function which is called for a
    349  * "add_authentication" action.
    350  * Returns an #ANASTASIS_ReduxAction if operation is async.
    351  *
    352  * @param state state to operate on
    353  * @param arguments arguments to use for operation on state
    354  * @param cb callback to call during/after operation
    355  * @param cb_cls callback closure
    356  * @return NULL
    357  */
    358 static struct ANASTASIS_ReduxAction *
    359 add_authentication (struct ANASTASIS_ReduxState *rs,
    360                     const json_t *arguments,
    361                     ANASTASIS_ActionCallback cb,
    362                     void *cb_cls)
    363 {
    364   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
    365   const json_t *method;
    366   const char *method_type;
    367   const char *instructions;
    368   const char *mime_type = NULL;
    369   void *challenge;
    370   size_t challenge_size;
    371   struct GNUNET_JSON_Specification spec[] = {
    372     GNUNET_JSON_spec_string ("type",
    373                              &method_type),
    374     GNUNET_JSON_spec_string ("instructions",
    375                              &instructions),
    376     GNUNET_JSON_spec_varsize ("challenge",
    377                               &challenge,
    378                               &challenge_size),
    379     GNUNET_JSON_spec_mark_optional (
    380       GNUNET_JSON_spec_string ("mime_type",
    381                                &mime_type),
    382       NULL),
    383     GNUNET_JSON_spec_end ()
    384   };
    385 
    386   if (! rs->common.have_providers)
    387   {
    388     GNUNET_break (0);
    389     ANASTASIS_REDUX_fail_ (rs,
    390                            cb,
    391                            cb_cls,
    392                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
    393                            "'authentication_providers' missing");
    394     return NULL;
    395   }
    396 
    397   method = json_object_get (arguments,
    398                             "authentication_method");
    399   if (NULL == method)
    400   {
    401     GNUNET_break (0);
    402     ANASTASIS_REDUX_fail_ (rs,
    403                            cb,
    404                            cb_cls,
    405                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
    406                            "'authentication_method' required");
    407     return NULL;
    408   }
    409   if (GNUNET_OK !=
    410       GNUNET_JSON_parse (method,
    411                          spec,
    412                          NULL, NULL))
    413   {
    414     GNUNET_break (0);
    415     json_dumpf ((json_t *) method,
    416                 stderr,
    417                 JSON_INDENT (2));
    418     ANASTASIS_REDUX_fail_ (rs,
    419                            cb,
    420                            cb_cls,
    421                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
    422                            "'authentication_method' content malformed");
    423     return NULL;
    424   }
    425   /* Check we know at least one provider that supports this method */
    426   {
    427     bool found = false;
    428     bool too_big = false;
    429 
    430     for (unsigned int i = 0; i < rs->common.providers_len; i++)
    431     {
    432       const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i];
    433       const struct ANASTASIS_ReduxProviderConfig *cfg = &p->config;
    434 
    435       if (ANASTASIS_RPS_OK != p->status)
    436         continue;
    437       if (! p->have_config)
    438       {
    439         GNUNET_break (0);
    440         continue;
    441       }
    442       if (MHD_HTTP_OK != cfg->http_status)
    443         continue; /* skip providers that are down */
    444       if (0 == cfg->storage_limit_in_megabytes)
    445       {
    446         GNUNET_break (0);
    447         continue;
    448       }
    449       for (unsigned int j = 0; j < cfg->methods_len; j++)
    450         if (0 == strcmp (cfg->methods[j].type,
    451                          method_type))
    452         {
    453           found = true;
    454           break;
    455         }
    456       if (! challenge_size_ok (cfg->storage_limit_in_megabytes,
    457                                challenge_size))
    458       {
    459         /* Challenge data too big for this provider. Try to find another one.
    460            Note: we add 1024 to challenge-size here as a "safety margin" as
    461            the encrypted challenge has some additional headers around it */
    462         too_big = true;
    463         found = false;
    464       }
    465       if (found)
    466         break;
    467     }
    468     if (! found)
    469     {
    470       enum TALER_ErrorCode ec
    471         = too_big
    472           ? TALER_EC_ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG
    473           : TALER_EC_ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED;
    474 
    475       ANASTASIS_REDUX_fail_ (rs,
    476                              cb,
    477                              cb_cls,
    478                              ec,
    479                              method_type);
    480       GNUNET_JSON_parse_free (spec);
    481       return NULL;
    482     }
    483   }
    484 
    485   /* append provided method to our array */
    486   {
    487     struct ANASTASIS_ReduxAuthMethod am = {
    488       .type = GNUNET_strdup (method_type),
    489       .instructions = GNUNET_strdup (instructions),
    490       .mime_type = (NULL == mime_type) ? NULL : GNUNET_strdup (mime_type),
    491       .challenge = GNUNET_memdup (challenge,
    492                                   challenge_size),
    493       .challenge_size = challenge_size
    494     };
    495 
    496     GNUNET_array_append (b->authentication_methods,
    497                          b->authentication_methods_len,
    498                          am);
    499     b->have_authentication_methods = true;
    500   }
    501   GNUNET_JSON_parse_free (spec);
    502   ANASTASIS_REDUX_return_ (rs,
    503                            cb,
    504                            cb_cls,
    505                            TALER_EC_NONE);
    506   return NULL;
    507 }
    508 
    509 
    510 /**
    511  * DispatchHandler/Callback function which is called for a
    512  * "delete_authentication" action.
    513  * Returns an #ANASTASIS_ReduxAction if operation is async.
    514  *
    515  * @param state state to operate on
    516  * @param arguments arguments to use for operation on state
    517  * @param cb callback to call during/after operation
    518  * @param cb_cls callback closure
    519  * @return NULL
    520  */
    521 static struct ANASTASIS_ReduxAction *
    522 del_authentication (struct ANASTASIS_ReduxState *rs,
    523                     const json_t *arguments,
    524                     ANASTASIS_ActionCallback cb,
    525                     void *cb_cls)
    526 {
    527   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
    528   const json_t *idx;
    529   json_int_t index;
    530 
    531   if (! b->have_authentication_methods)
    532   {
    533     ANASTASIS_REDUX_fail_ (rs,
    534                            cb,
    535                            cb_cls,
    536                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
    537                            "'authentication_methods' must be an array");
    538     return NULL;
    539   }
    540   if (NULL == arguments)
    541   {
    542     ANASTASIS_REDUX_fail_ (rs,
    543                            cb,
    544                            cb_cls,
    545                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
    546                            "arguments missing");
    547     return NULL;
    548   }
    549   idx = json_object_get (arguments,
    550                          "authentication_method");
    551   if ( (NULL == idx) ||
    552        (! json_is_integer (idx)) )
    553   {
    554     ANASTASIS_REDUX_fail_ (rs,
    555                            cb,
    556                            cb_cls,
    557                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
    558                            "'authentication_method' must be a number");
    559     return NULL;
    560   }
    561   index = json_integer_value (idx);
    562   if ( (index < 0) ||
    563        (index >= (json_int_t) b->authentication_methods_len) )
    564   {
    565     ANASTASIS_REDUX_fail_ (rs,
    566                            cb,
    567                            cb_cls,
    568                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
    569                            "removal failed");
    570     return NULL;
    571   }
    572   {
    573     struct ANASTASIS_ReduxAuthMethod *am
    574       = &b->authentication_methods[index];
    575 
    576     GNUNET_free (am->type);
    577     GNUNET_free (am->instructions);
    578     GNUNET_free (am->challenge);
    579     GNUNET_free (am->mime_type);
    580     memmove (am,
    581              am + 1,
    582              sizeof (*am) * (b->authentication_methods_len - index - 1));
    583     b->authentication_methods_len--;
    584   }
    585   ANASTASIS_REDUX_return_ (rs,
    586                            cb,
    587                            cb_cls,
    588                            TALER_EC_NONE);
    589   return NULL;
    590 }
    591 
    592 
    593 /* ********************** done_authentication ******************** */
    594 
    595 /**
    596  * Which provider would be used for the given challenge,
    597  * and at what cost?
    598  */
    599 struct PolicyEntry
    600 {
    601   /**
    602    * URL of the provider.
    603    */
    604   const char *provider_name;
    605 
    606   /**
    607    * Recovery fee.
    608    */
    609   struct Costs *usage_fee;
    610 };
    611 
    612 
    613 /**
    614  * Map from challenges to providers.
    615  */
    616 struct PolicyMap
    617 {
    618   /**
    619    * Kept in a DLL.
    620    */
    621   struct PolicyMap *next;
    622 
    623   /**
    624    * Kept in a DLL.
    625    */
    626   struct PolicyMap *prev;
    627 
    628   /**
    629    * Array of proividers selected for each challenge,
    630    * with associated costs.
    631    * Length of the array will be 'req_methods'.
    632    */
    633   struct PolicyEntry *providers;
    634 
    635   /**
    636    * Diversity score for this policy mapping.
    637    */
    638   unsigned int diversity;
    639 
    640 };
    641 
    642 
    643 /**
    644  * Array of challenges for a policy, and DLL with
    645  * possible mappings of challenges to providers.
    646  */
    647 struct Policy
    648 {
    649 
    650   /**
    651    * Kept in DLL of all possible policies.
    652    */
    653   struct Policy *next;
    654 
    655   /**
    656    * Kept in DLL of all possible policies.
    657    */
    658   struct Policy *prev;
    659 
    660   /**
    661    * Head of DLL.
    662    */
    663   struct PolicyMap *pm_head;
    664 
    665   /**
    666    * Tail of DLL.
    667    */
    668   struct PolicyMap *pm_tail;
    669 
    670   /**
    671    * Challenges selected for this policy.
    672    * Length of the array will be 'req_methods'.
    673    */
    674   unsigned int *challenges;
    675 
    676 };
    677 
    678 
    679 /**
    680  * Information for running done_authentication() logic.
    681  */
    682 struct PolicyBuilder
    683 {
    684   /**
    685    * State we are working on; provides the authentication providers
    686    * available overall.
    687    */
    688   const struct ANASTASIS_ReduxCommon *common;
    689 
    690   /**
    691    * Backup state we are working on; provides the authentication
    692    * methods the user entered, and receives the computed policies.
    693    */
    694   struct ANASTASIS_ReduxBackup *backup;
    695 
    696   /**
    697    * Head of DLL of all possible policies.
    698    */
    699   struct Policy *p_head;
    700 
    701   /**
    702    * Tail of DLL of all possible policies.
    703    */
    704   struct Policy *p_tail;
    705 
    706   /**
    707    * Array of length @e req_methods.
    708    */
    709   unsigned int *m_idx;
    710 
    711   /**
    712    * Array of length @e req_methods identifying a set of providers selected
    713    * for each authentication method, while we are trying to compute the
    714    * 'best' allocation of providers to authentication methods.
    715    * Only valid during the go_with() function.
    716    */
    717   const char **best_sel;
    718 
    719   /**
    720    * Error hint to return on failure. Set if @e ec is not #TALER_EC_NONE.
    721    */
    722   const char *hint;
    723 
    724   /**
    725    * Policy we are currently building maps for.
    726    */
    727   struct Policy *current_policy;
    728 
    729   /**
    730    * LL of costs associated with the currently preferred
    731    * policy.
    732    */
    733   struct Costs *best_cost;
    734 
    735   /**
    736    * Array of 'best' policy maps found so far,
    737    * ordered by policy.
    738    */
    739   struct PolicyMap *best_map;
    740 
    741   /**
    742    * Array of the currency policy maps under evaluation
    743    * by find_best_map().
    744    */
    745   struct PolicyMap *curr_map;
    746 
    747   /**
    748    * How many mappings have we evaluated so far?
    749    * Used to limit the computation by aborting after
    750    * #MAX_EVALUATIONS trials.
    751    */
    752   unsigned int evaluations;
    753 
    754   /**
    755    * Overall number of challenges provided by the user.
    756    */
    757   unsigned int num_methods;
    758 
    759   /**
    760    * Number of challenges that must be satisfied to recover the secret.
    761    * Derived from the total number of challenges entered by the user.
    762    */
    763   unsigned int req_methods;
    764 
    765   /**
    766    * Number of different Anastasis providers selected in @e best_sel.
    767    * Only valid during the go_with() function.
    768    */
    769   unsigned int best_diversity;
    770 
    771   /**
    772    * Number of identical challenges duplicated at
    773    * various providers in the best case. Smaller is
    774    * better.
    775    */
    776   unsigned int best_duplicates;
    777 
    778   /**
    779    * Error code to return, #TALER_EC_NONE on success.
    780    */
    781   enum TALER_ErrorCode ec;
    782 
    783 };
    784 
    785 
    786 /**
    787  * Free @a costs LL.
    788  *
    789  * @param[in] costs linked list to free
    790  */
    791 static void
    792 free_costs (struct Costs *costs)
    793 {
    794   while (NULL != costs)
    795   {
    796     struct Costs *next = costs->next;
    797 
    798     GNUNET_free (costs);
    799     costs = next;
    800   }
    801 }
    802 
    803 
    804 /**
    805  * Check if providers @a p1 and @a p2 have equivalent
    806  * methods and cost structures.
    807  *
    808  * @param pb policy builder with list of providers
    809  * @param p1 name of provider to compare
    810  * @param p2 name of provider to compare
    811  * @return true if the providers are fully equivalent
    812  */
    813 static bool
    814 equiv_provider (const struct PolicyBuilder *pb,
    815                 const char *p1,
    816                 const char *p2)
    817 {
    818   const struct ANASTASIS_ReduxProvider *j1;
    819   const struct ANASTASIS_ReduxProvider *j2;
    820   const struct ANASTASIS_ReduxProviderConfig *c1;
    821   const struct ANASTASIS_ReduxProviderConfig *c2;
    822 
    823   j1 = ANASTASIS_REDUX_provider_find_ (pb->common,
    824                                        p1);
    825   j2 = ANASTASIS_REDUX_provider_find_ (pb->common,
    826                                        p2);
    827   if ( (NULL == j1) ||
    828        (NULL == j2) ||
    829        (! j1->have_config) ||
    830        (! j2->have_config) )
    831   {
    832     GNUNET_break (0);
    833     return false;
    834   }
    835   c1 = &j1->config;
    836   c2 = &j2->config;
    837   /* Compare the whole price lists, not just the primary currency: two
    838      providers that agree on EUR but differ on which other currencies
    839      they take are not interchangeable for a user holding one of them. */
    840   if (! amount_lists_equal (&c1->truth_upload_fees,
    841                             &c2->truth_upload_fees))
    842     return false;
    843 
    844   if (c1->methods_len != c2->methods_len)
    845     return false;
    846   for (unsigned int i = 0; i < c1->methods_len; i++)
    847   {
    848     const struct ANASTASIS_ReduxMethodSpec *m1 = &c1->methods[i];
    849     bool matched = false;
    850 
    851     for (unsigned int j = 0; j < c2->methods_len; j++)
    852     {
    853       const struct ANASTASIS_ReduxMethodSpec *m2 = &c2->methods[j];
    854 
    855       if ( (0 == strcmp (m1->type,
    856                          m2->type)) &&
    857            amount_lists_equal (&m1->usage_fees,
    858                                &m2->usage_fees) )
    859       {
    860         matched = true;
    861         break;
    862       }
    863     }
    864     if (! matched)
    865       return false;
    866   }
    867   return true;
    868 }
    869 
    870 
    871 /**
    872  * Evaluate the cost/benefit of the provider selection in @a prov_sel
    873  * and if it is better then the best known one in @a pb, update @a pb.
    874  *
    875  * @param[in,out] pb our operational context
    876  * @param[in,out] prov_sel array of req_methods provider indices to complete
    877  */
    878 static void
    879 eval_provider_selection (struct PolicyBuilder *pb,
    880                          const char *prov_sel[])
    881 {
    882   unsigned int curr_diversity;
    883   struct PolicyEntry policy_ent[pb->req_methods];
    884 
    885   memset (policy_ent,
    886           0,
    887           sizeof (policy_ent));
    888   for (unsigned int i = 0; i < pb->req_methods; i++)
    889   {
    890     const struct ANASTASIS_ReduxAuthMethod *am
    891       = &pb->backup->authentication_methods[pb->m_idx[i]];
    892     const struct ANASTASIS_ReduxProvider *p;
    893     const struct ANASTASIS_ReduxProviderConfig *cfg;
    894     bool found = false;
    895 
    896     policy_ent[i].provider_name = prov_sel[i];
    897     p = ANASTASIS_REDUX_provider_find_ (pb->common,
    898                                         prov_sel[i]);
    899     if ( (NULL == p) ||
    900          (! p->have_config) ||
    901          (MHD_HTTP_OK != p->config.http_status) )
    902     {
    903       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    904                   "Skipping provider %s: no suitable configuration\n",
    905                   prov_sel[i]);
    906       goto cleanup;
    907     }
    908     cfg = &p->config;
    909     for (unsigned int j = 0; j < cfg->methods_len; j++)
    910     {
    911       const struct ANASTASIS_ReduxMethodSpec *ms = &cfg->methods[j];
    912 
    913       if ( (0 == strcmp (ms->type,
    914                          am->type)) &&
    915            (challenge_size_ok (cfg->storage_limit_in_megabytes,
    916                                am->challenge_size) ) )
    917       {
    918         const struct TALER_Amount *uf;
    919         const struct TALER_Amount *tf;
    920 
    921         found = true;
    922         uf = preferred_price (pb->common,
    923                               &ms->usage_fees);
    924         tf = preferred_price (pb->common,
    925                               &cfg->truth_upload_fees);
    926         if (NULL != uf)
    927           add_cost (&policy_ent[i].usage_fee,
    928                     uf);
    929         if (NULL != tf)
    930           add_cost (&policy_ent[i].usage_fee,
    931                     tf);
    932       }
    933     }
    934     if (! found)
    935     {
    936       /* Provider does not OFFER this method, combination not possible.
    937          Cost is basically 'infinite', but we simply then skip this. */
    938       goto cleanup;
    939     }
    940   }
    941 
    942   /* calculate provider diversity by counting number of different
    943      providers selected */
    944   curr_diversity = 0;
    945   for (unsigned int i = 0; i < pb->req_methods; i++)
    946   {
    947     bool found = false;
    948 
    949     for (unsigned int j = 0; j < i; j++)
    950     {
    951       if (prov_sel[i] == prov_sel[j])
    952       {
    953         found = true;
    954         break;
    955       }
    956     }
    957     if (! found)
    958       curr_diversity++;
    959   }
    960 #if DEBUG
    961   fprintf (stderr,
    962            "Diversity: %u (best: %u)\n",
    963            curr_diversity,
    964            pb->best_diversity);
    965 #endif
    966   if (curr_diversity < pb->best_diversity)
    967   {
    968     /* do not allow combinations that are bad
    969        for provider diversity */
    970     goto cleanup;
    971   }
    972   if (curr_diversity > pb->best_diversity)
    973   {
    974     /* drop existing policies, they are all worse */
    975     struct PolicyMap *m;
    976 
    977     while (NULL != (m = pb->current_policy->pm_head))
    978     {
    979       GNUNET_CONTAINER_DLL_remove (pb->current_policy->pm_head,
    980                                    pb->current_policy->pm_tail,
    981                                    m);
    982       for (unsigned int i = 0; i<pb->req_methods; i++)
    983       {
    984         free_costs (m->providers[i].usage_fee);
    985         m->providers[i].usage_fee = NULL;
    986       }
    987       GNUNET_free (m->providers);
    988       GNUNET_free (m);
    989     }
    990     pb->best_diversity = curr_diversity;
    991   }
    992   if (NULL == pb->p_head)
    993   {
    994     /* For the first policy, check for equivalent
    995        policy mapping existing: we
    996      do not want to do spend CPU time investigating
    997      purely equivalent permutations */
    998     for (struct PolicyMap *m = pb->current_policy->pm_head;
    999          NULL != m;
   1000          m = m->next)
   1001     {
   1002       bool equiv = true;
   1003       for (unsigned int i = 0; i<pb->req_methods; i++)
   1004       {
   1005         if (! equiv_provider (pb,
   1006                               m->providers[i].provider_name,
   1007                               policy_ent[i].provider_name))
   1008         {
   1009           equiv = false;
   1010           break;
   1011         }
   1012       }
   1013       if (equiv)
   1014       {
   1015         /* equivalent to known allocation */
   1016         goto cleanup;
   1017       }
   1018     }
   1019   }
   1020 
   1021   /* Add possible mapping to result list */
   1022   {
   1023     struct PolicyMap *m;
   1024 
   1025     m = GNUNET_new (struct PolicyMap);
   1026     m->providers = GNUNET_new_array (pb->req_methods,
   1027                                      struct PolicyEntry);
   1028     memcpy (m->providers,
   1029             policy_ent,
   1030             sizeof (struct PolicyEntry) * pb->req_methods);
   1031     m->diversity = curr_diversity;
   1032     GNUNET_CONTAINER_DLL_insert (pb->current_policy->pm_head,
   1033                                  pb->current_policy->pm_tail,
   1034                                  m);
   1035   }
   1036   return;
   1037 cleanup:
   1038   for (unsigned int i = 0; i<pb->req_methods; i++)
   1039     free_costs (policy_ent[i].usage_fee);
   1040 }
   1041 
   1042 
   1043 /**
   1044  * Recursively compute possible combination(s) of provider candidates
   1045  * in @e prov_sel. The selection is complete up to index @a i.  Calls
   1046  * eval_provider_selection() upon a feasible provider selection for
   1047  * evaluation, resulting in "better" combinations being persisted in
   1048  * @a pb.
   1049  *
   1050  * @param[in,out] pb our operational context
   1051  * @param[in,out] prov_sel array of req_methods provider URLs to complete
   1052  * @param i index up to which @a prov_sel is already initialized
   1053  */
   1054 static void
   1055 provider_candidate (struct PolicyBuilder *pb,
   1056                     const char *prov_sel[],
   1057                     unsigned int i)
   1058 {
   1059   for (unsigned int k = 0; k < pb->common->providers_len; k++)
   1060   {
   1061     const struct ANASTASIS_ReduxProvider *p = &pb->common->providers[k];
   1062 
   1063     if ( (ANASTASIS_RPS_DISABLED == p->status) ||
   1064          (! p->have_config) ||
   1065          (MHD_HTTP_OK != p->config.http_status) )
   1066       continue;
   1067     prov_sel[i] = p->url.url;
   1068     if (i == pb->req_methods - 1)
   1069     {
   1070       eval_provider_selection (pb,
   1071                                prov_sel);
   1072       if (TALER_EC_NONE != pb->ec)
   1073         break;
   1074       continue;
   1075     }
   1076     provider_candidate (pb,
   1077                         prov_sel,
   1078                         i + 1);
   1079   }
   1080 }
   1081 
   1082 
   1083 /**
   1084  * Using the selection of authentication methods from @a pb in
   1085  * "m_idx", compute the best choice of providers.
   1086  *
   1087  * @param[in,out] pb our operational context
   1088  */
   1089 static void
   1090 go_with (struct PolicyBuilder *pb)
   1091 {
   1092   const char *prov_sel[pb->req_methods];
   1093   struct Policy *policy;
   1094 
   1095   /* compute provider selection */
   1096   policy = GNUNET_new (struct Policy);
   1097   policy->challenges = GNUNET_new_array (pb->req_methods,
   1098                                          unsigned int);
   1099   memcpy (policy->challenges,
   1100           pb->m_idx,
   1101           pb->req_methods * sizeof (unsigned int));
   1102   pb->current_policy = policy;
   1103   pb->best_diversity = 0;
   1104   provider_candidate (pb,
   1105                       prov_sel,
   1106                       0);
   1107   GNUNET_CONTAINER_DLL_insert (pb->p_head,
   1108                                pb->p_tail,
   1109                                policy);
   1110   pb->current_policy = NULL;
   1111 }
   1112 
   1113 
   1114 /**
   1115  * Recursively computes all possible subsets of length "req_methods"
   1116  * from an array of length "num_methods", calling "go_with" on each of
   1117  * those subsets (in "m_idx").
   1118  *
   1119  * @param[in,out] pb our operational context
   1120  * @param i offset up to which the "m_idx" has been computed
   1121  */
   1122 static void
   1123 method_candidate (struct PolicyBuilder *pb,
   1124                   unsigned int i)
   1125 {
   1126   unsigned int start;
   1127   unsigned int *m_idx = pb->m_idx;
   1128 
   1129   start = (i > 0) ? m_idx[i - 1] + 1 : 0;
   1130   for (unsigned int j = start; j < pb->num_methods; j++)
   1131   {
   1132     m_idx[i] = j;
   1133     if (i == pb->req_methods - 1)
   1134     {
   1135 #if DEBUG
   1136       fprintf (stderr,
   1137                "Suggesting: ");
   1138       for (unsigned int k = 0; k<pb->req_methods; k++)
   1139       {
   1140         fprintf (stderr,
   1141                  "%u ",
   1142                  m_idx[k]);
   1143       }
   1144       fprintf (stderr, "\n");
   1145 #endif
   1146       go_with (pb);
   1147       continue;
   1148     }
   1149     method_candidate (pb,
   1150                       i + 1);
   1151   }
   1152 }
   1153 
   1154 
   1155 /**
   1156  * Compare two cost lists.
   1157  *
   1158  * @param my cost to compare
   1159  * @param be cost to compare
   1160  * @return 0 if costs are estimated equal,
   1161  *         1 if @a my < @a be
   1162  *        -1 if @a my > @a be
   1163  */
   1164 static int
   1165 compare_costs (const struct Costs *my,
   1166                const struct Costs *be)
   1167 {
   1168   int ranking = 0;
   1169 
   1170   for (const struct Costs *cmp = be;
   1171        NULL != cmp;
   1172        cmp = cmp->next)
   1173   {
   1174     bool found = false;
   1175 
   1176     for (const struct Costs *pos = my;
   1177          NULL != pos;
   1178          pos = pos->next)
   1179     {
   1180       if (GNUNET_OK !=
   1181           TALER_amount_cmp_currency (&cmp->cost,
   1182                                      &pos->cost))
   1183         continue;
   1184       found = true;
   1185     }
   1186     if (! found)
   1187       ranking--;   /* new policy has no cost in this currency */
   1188   }
   1189 
   1190   for (const struct Costs *pos = my;
   1191        NULL != pos;
   1192        pos = pos->next)
   1193   {
   1194     bool found = false;
   1195 
   1196     for (const struct Costs *cmp = be;
   1197          NULL != cmp;
   1198          cmp = cmp->next)
   1199     {
   1200       if (GNUNET_OK !=
   1201           TALER_amount_cmp_currency (&cmp->cost,
   1202                                      &pos->cost))
   1203         continue;
   1204       found = true;
   1205       switch (TALER_amount_cmp (&cmp->cost,
   1206                                 &pos->cost))
   1207       {
   1208       case -1:   /* cmp < pos */
   1209         ranking--;
   1210         break;
   1211       case 0:
   1212         break;
   1213       case 1:   /* cmp > pos */
   1214         ranking++;
   1215         break;
   1216       }
   1217       break;
   1218     }
   1219     if (! found)
   1220       ranking++;   /* old policy has no cost in this currency */
   1221   }
   1222   if (0 == ranking)
   1223     return 0;
   1224   return (0 > ranking) ? -1 : 1;
   1225 }
   1226 
   1227 
   1228 /**
   1229  * Evaluate the combined policy map stack in the ``curr_map`` of @a pb
   1230  * and compare to the current best cost. If we are better, save the
   1231  * stack in the ``best_map``.
   1232  *
   1233  * @param[in,out] pb policy builder we evaluate for
   1234  * @param num_policies length of the ``curr_map`` array
   1235  */
   1236 static void
   1237 evaluate_map (struct PolicyBuilder *pb,
   1238               unsigned int num_policies)
   1239 {
   1240   struct Costs *my_cost = NULL;
   1241   unsigned int i = 0;
   1242   unsigned int duplicates = 0;
   1243   int ccmp;
   1244 
   1245 #if DEBUG
   1246   fprintf (stderr,
   1247            "Checking...\n");
   1248 #endif
   1249   /* calculate cost */
   1250   for (const struct Policy *p = pb->p_head;
   1251        NULL != p;
   1252        p = p->next)
   1253   {
   1254     const struct PolicyMap *pm = &pb->curr_map[i++];
   1255 
   1256 #if DEBUG
   1257     fprintf (stderr,
   1258              "Evaluating %p (%u): ",
   1259              p,
   1260              pm->diversity);
   1261     for (unsigned int k = 0; k<pb->req_methods; k++)
   1262     {
   1263       const struct PolicyEntry *pe = &pm->providers[k];
   1264 
   1265       fprintf (stderr,
   1266                "%u->%s ",
   1267                p->challenges[k],
   1268                pe->provider_name);
   1269     }
   1270     fprintf (stderr, "\n");
   1271 #endif
   1272     for (unsigned int j = 0; j<pb->req_methods; j++)
   1273     {
   1274       const struct PolicyEntry *pe = &pm->providers[j];
   1275       unsigned int cv = p->challenges[j];
   1276       bool found = false;
   1277       unsigned int i2 = 0;
   1278 
   1279       /* check for duplicates */
   1280       for (const struct Policy *p2 = pb->p_head;
   1281            p2 != p;
   1282            p2 = p2->next)
   1283       {
   1284         const struct PolicyMap *pm2 = &pb->curr_map[i2++];
   1285 
   1286         for (unsigned int j2 = 0; j2<pb->req_methods; j2++)
   1287         {
   1288           const struct PolicyEntry *pe2 = &pm2->providers[j2];
   1289           unsigned int cv2 = p2->challenges[j2];
   1290 
   1291           if (cv != cv2)
   1292             continue; /* different challenge */
   1293           if (0 == strcmp (pe->provider_name,
   1294                            pe2->provider_name))
   1295             found = true; /* same challenge&provider! */
   1296           else
   1297             duplicates++; /* penalty for same challenge at two providers */
   1298         }
   1299       }
   1300       if (! found)
   1301       {
   1302         add_costs (&my_cost,
   1303                    pe->usage_fee);
   1304       }
   1305     }
   1306   }
   1307 
   1308   ccmp = -1; /* non-zero if 'best_duplicates' is UINT_MAX */
   1309   if ( (UINT_MAX != pb->best_duplicates) &&
   1310        (0 > (ccmp = compare_costs (my_cost,
   1311                                    pb->best_cost))) )
   1312   {
   1313     /* new method not clearly better, do not use it */
   1314     free_costs (my_cost);
   1315 #if DEBUG
   1316     fprintf (stderr,
   1317              "... useless\n");
   1318 #endif
   1319     return;
   1320   }
   1321   if ( (0 == ccmp) &&
   1322        (duplicates > pb->best_duplicates) )
   1323   {
   1324     /* new method is cost-equal, but looses on duplicates,
   1325        do not use it */
   1326     free_costs (my_cost);
   1327 #if DEBUG
   1328     fprintf (stderr,
   1329              "... useless\n");
   1330 #endif
   1331     return;
   1332   }
   1333   /* new method is better (or first), set as best */
   1334 #if DEBUG
   1335   fprintf (stderr,
   1336            "New best: %u duplicates, %s cost\n",
   1337            duplicates,
   1338            TALER_amount2s (&my_cost->cost));
   1339 #endif
   1340   free_costs (pb->best_cost);
   1341   pb->best_cost = my_cost;
   1342   pb->best_duplicates = duplicates;
   1343   memcpy (pb->best_map,
   1344           pb->curr_map,
   1345           sizeof (struct PolicyMap) * num_policies);
   1346 }
   1347 
   1348 
   1349 /**
   1350  * Try all policy maps for @a pos and evaluate the
   1351  * resulting total cost, saving the best result in
   1352  * @a pb.
   1353  *
   1354  * @param[in,out] pb policy builder context
   1355  * @param pos policy we are currently looking at maps for
   1356  * @param off index of @a pos for the policy map
   1357  */
   1358 static void
   1359 find_best_map (struct PolicyBuilder *pb,
   1360                struct Policy *pos,
   1361                unsigned int off)
   1362 {
   1363   if (NULL == pos)
   1364   {
   1365     evaluate_map (pb,
   1366                   off);
   1367     pb->evaluations++;
   1368     return;
   1369   }
   1370   for (struct PolicyMap *pm = pos->pm_head;
   1371        NULL != pm;
   1372        pm = pm->next)
   1373   {
   1374     pb->curr_map[off] = *pm;
   1375     find_best_map (pb,
   1376                    pos->next,
   1377                    off + 1);
   1378     if (pb->evaluations >= MAX_EVALUATIONS)
   1379       break;
   1380   }
   1381 }
   1382 
   1383 
   1384 /**
   1385  * Select cheapest policy combinations and store them in the ``policies``
   1386  * of the backup state @a pb builds.
   1387  *
   1388  * @param[in,out] pb policy builder with our state
   1389  */
   1390 static void
   1391 select_policies (struct PolicyBuilder *pb)
   1392 {
   1393   unsigned int cnt = 0;
   1394 
   1395   for (struct Policy *p = pb->p_head;
   1396        NULL != p;
   1397        p = p->next)
   1398     cnt++;
   1399   {
   1400     struct PolicyMap best[cnt];
   1401     struct PolicyMap curr[cnt];
   1402     unsigned int off;
   1403 
   1404     pb->best_map = best;
   1405     pb->curr_map = curr;
   1406     pb->best_duplicates = UINT_MAX; /* worst */
   1407     find_best_map (pb,
   1408                    pb->p_head,
   1409                    0);
   1410     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1411                 "Assessed %u/%u policies\n",
   1412                 pb->evaluations,
   1413                 (unsigned int) MAX_EVALUATIONS);
   1414     off = 0;
   1415     for (struct Policy *p = pb->p_head;
   1416          NULL != p;
   1417          p = p->next)
   1418     {
   1419       struct PolicyMap *pm = &best[off++];
   1420       struct ANASTASIS_ReduxPolicy pol = {
   1421         .methods_len = pb->req_methods,
   1422         .methods = GNUNET_new_array (pb->req_methods,
   1423                                      struct ANASTASIS_ReduxPolicyMethod)
   1424       };
   1425 
   1426 #if DEBUG
   1427       fprintf (stderr,
   1428                "Best map (%u): ",
   1429                pm->diversity);
   1430       for (unsigned int k = 0; k<pb->req_methods; k++)
   1431       {
   1432         fprintf (stderr,
   1433                  "%u->%s ",
   1434                  p->challenges[k],
   1435                  pm->providers[k].provider_name);
   1436       }
   1437       fprintf (stderr, "\n");
   1438 #endif
   1439       /* Convert "best" selection into the 'policies' array */
   1440       for (unsigned int i = 0; i < pb->req_methods; i++)
   1441       {
   1442         struct ANASTASIS_ReduxPolicyMethod *pm2 = &pol.methods[i];
   1443 
   1444         pm2->authentication_method.idx = p->challenges[i];
   1445         ANASTASIS_REDUX_provider_url_set_ (&pm2->provider,
   1446                                            pm->providers[i].provider_name);
   1447       }
   1448       GNUNET_array_append (pb->backup->policies,
   1449                            pb->backup->policies_len,
   1450                            pol);
   1451       pb->backup->have_policies = true;
   1452     }
   1453   }
   1454 }
   1455 
   1456 
   1457 /**
   1458  * Clean up @a pb, in particular the policies DLL.
   1459  *
   1460  * @param[in] pb builder to clean up
   1461  */
   1462 static void
   1463 clean_pb (struct PolicyBuilder *pb)
   1464 {
   1465   struct Policy *p;
   1466 
   1467   while (NULL != (p = pb->p_head))
   1468   {
   1469     struct PolicyMap *pm;
   1470 
   1471     while (NULL != (pm = p->pm_head))
   1472     {
   1473       GNUNET_CONTAINER_DLL_remove (p->pm_head,
   1474                                    p->pm_tail,
   1475                                    pm);
   1476       for (unsigned int i = 0; i<pb->req_methods; i++)
   1477         free_costs (pm->providers[i].usage_fee);
   1478       GNUNET_free (pm->providers);
   1479       GNUNET_free (pm);
   1480     }
   1481     GNUNET_CONTAINER_DLL_remove (pb->p_head,
   1482                                  pb->p_tail,
   1483                                  p);
   1484     GNUNET_free (p->challenges);
   1485     GNUNET_free (p);
   1486   }
   1487   free_costs (pb->best_cost);
   1488 }
   1489 
   1490 
   1491 /**
   1492  * DispatchHandler/Callback function which is called for a
   1493  * "done_authentication" action.  Automaticially computes policies
   1494  * based on available Anastasis providers and challenges provided by
   1495  * the user.
   1496  *
   1497  * @param state state to operate on
   1498  * @param arguments arguments to use for operation on state
   1499  * @param cb callback to call during/after operation
   1500  * @param cb_cls callback closure
   1501  * @return NULL
   1502  */
   1503 static struct ANASTASIS_ReduxAction *
   1504 done_authentication (struct ANASTASIS_ReduxState *rs,
   1505                      const json_t *arguments,
   1506                      ANASTASIS_ActionCallback cb,
   1507                      void *cb_cls)
   1508 {
   1509   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   1510   struct PolicyBuilder pb = {
   1511     .ec = TALER_EC_NONE,
   1512     .common = &rs->common,
   1513     .backup = b
   1514   };
   1515   const json_t *providers;
   1516 
   1517   if (! rs->common.have_providers)
   1518   {
   1519     ANASTASIS_REDUX_fail_ (rs,
   1520                            cb,
   1521                            cb_cls,
   1522                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1523                            "'authentication_providers' must be provided");
   1524     return NULL;
   1525   }
   1526   if (! b->have_authentication_methods)
   1527   {
   1528     ANASTASIS_REDUX_fail_ (rs,
   1529                            cb,
   1530                            cb_cls,
   1531                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1532                            "'authentication_methods' must be provided");
   1533     return NULL;
   1534   }
   1535   pb.num_methods = b->authentication_methods_len;
   1536   switch (pb.num_methods)
   1537   {
   1538   case 0:
   1539     ANASTASIS_REDUX_fail_ (rs,
   1540                            cb,
   1541                            cb_cls,
   1542                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1543                            "'authentication_methods' must not be empty");
   1544     return NULL;
   1545   case 1:
   1546     ANASTASIS_REDUX_fail_ (rs,
   1547                            cb,
   1548                            cb_cls,
   1549                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1550                            "Two factor authentication (2-FA) is required");
   1551     return NULL;
   1552   case 2:
   1553     pb.req_methods = pb.num_methods;
   1554     break;
   1555   case 3:
   1556   case 4:
   1557     pb.req_methods = pb.num_methods - 1;
   1558     break;
   1559   case 5:
   1560   case 6:
   1561     pb.req_methods = pb.num_methods - 2;
   1562     break;
   1563   case 7:
   1564     pb.req_methods = pb.num_methods - 3;
   1565     break;
   1566   default:
   1567     /* cap at 4 for auto-generation, algorithm
   1568        to compute mapping gets too expensive
   1569        otherwise. */
   1570     pb.req_methods = 4;
   1571     break;
   1572   }
   1573   {
   1574     unsigned int m_idx[pb.req_methods];
   1575 
   1576     /* select req_methods from num_methods. */
   1577     pb.m_idx = m_idx;
   1578     method_candidate (&pb,
   1579                       0);
   1580   }
   1581   /* the computed policies replace whatever was there before */
   1582   ANASTASIS_REDUX_policies_clear_ (b);
   1583   b->have_policies = true;
   1584   select_policies (&pb);
   1585   clean_pb (&pb);
   1586   if (TALER_EC_NONE != pb.ec)
   1587   {
   1588     ANASTASIS_REDUX_fail_ (rs,
   1589                            cb,
   1590                            cb_cls,
   1591                            pb.ec,
   1592                            pb.hint);
   1593     return NULL;
   1594   }
   1595   ANASTASIS_REDUX_policy_providers_clear_ (b);
   1596   b->have_policy_providers = true;
   1597   providers = json_object_get (arguments,
   1598                                "providers");
   1599   if (NULL == providers)
   1600   {
   1601     /* Setup a providers array from all working providers */
   1602     for (unsigned int i = 0; i < rs->common.providers_len; i++)
   1603     {
   1604       const char *url = rs->common.providers[i].url.url;
   1605       struct ANASTASIS_ReduxPolicyProvider pp = { 0 };
   1606       struct ANASTASIS_CRYPTO_ProviderSaltP salt;
   1607 
   1608       if (GNUNET_OK !=
   1609           ANASTASIS_REDUX_lookup_salt_ (&rs->common,
   1610                                         url,
   1611                                         &salt))
   1612         continue; /* skip providers that are down */
   1613       ANASTASIS_REDUX_provider_url_set_ (&pp.provider_url,
   1614                                          url);
   1615       GNUNET_array_append (b->policy_providers,
   1616                            b->policy_providers_len,
   1617                            pp);
   1618     }
   1619   }
   1620   else
   1621   {
   1622     /* Setup a providers array from the requested providers */
   1623     size_t off;
   1624     json_t *url;
   1625 
   1626     json_array_foreach (providers, off, url)
   1627     {
   1628       struct ANASTASIS_ReduxPolicyProvider pp = { 0 };
   1629       struct ANASTASIS_CRYPTO_ProviderSaltP salt;
   1630       const char *url_str;
   1631 
   1632       url_str = json_string_value (url);
   1633       if ( (NULL == url_str) ||
   1634            (GNUNET_OK !=
   1635             ANASTASIS_REDUX_lookup_salt_ (&rs->common,
   1636                                           url_str,
   1637                                           &salt)) )
   1638       {
   1639         GNUNET_break (0);
   1640         ANASTASIS_REDUX_fail_ (rs,
   1641                                cb,
   1642                                cb_cls,
   1643                                TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1644                                "unworkable provider requested");
   1645         return NULL;
   1646       }
   1647       ANASTASIS_REDUX_provider_url_set_ (&pp.provider_url,
   1648                                          url_str);
   1649       GNUNET_array_append (b->policy_providers,
   1650                            b->policy_providers_len,
   1651                            pp);
   1652     }
   1653   }
   1654   if (0 == b->policy_providers_len)
   1655   {
   1656     ANASTASIS_REDUX_fail_ (rs,
   1657                            cb,
   1658                            cb_cls,
   1659                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1660                            "no workable providers in state");
   1661     return NULL;
   1662   }
   1663   set_state (rs,
   1664              ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING);
   1665   ANASTASIS_REDUX_return_ (rs,
   1666                            cb,
   1667                            cb_cls,
   1668                            TALER_EC_NONE);
   1669   return NULL;
   1670 }
   1671 
   1672 
   1673 /* ******************** add_provider ******************* */
   1674 
   1675 
   1676 /**
   1677  * DispatchHandler/Callback function which is called for a
   1678  * "add_provider" action.  Adds another Anastasis provider
   1679  * to the list of available providers for storing information.
   1680  *
   1681  * @param state state to operate on
   1682  * @param arguments arguments with a provider URL to add
   1683  * @param cb callback to call during/after operation
   1684  * @param cb_cls callback closure
   1685  */
   1686 static struct ANASTASIS_ReduxAction *
   1687 add_provider (struct ANASTASIS_ReduxState *rs,
   1688               const json_t *arguments,
   1689               ANASTASIS_ActionCallback cb,
   1690               void *cb_cls)
   1691 {
   1692   if (ANASTASIS_add_provider_ (rs,
   1693                                arguments,
   1694                                cb,
   1695                                cb_cls))
   1696     return NULL;
   1697   return ANASTASIS_REDUX_backup_begin_ (rs,
   1698                                         NULL,
   1699                                         cb,
   1700                                         cb_cls);
   1701 }
   1702 
   1703 
   1704 /* ******************** add_policy ******************* */
   1705 
   1706 
   1707 /**
   1708  * DispatchHandler/Callback function which is called for a
   1709  * "add_policy" action.
   1710  *
   1711  * @param state state to operate on
   1712  * @param arguments arguments to use for operation on state
   1713  * @param cb callback to call during/after operation
   1714  * @param cb_cls callback closure
   1715  * @return NULL
   1716  */
   1717 static struct ANASTASIS_ReduxAction *
   1718 add_policy (struct ANASTASIS_ReduxState *rs,
   1719             const json_t *arguments,
   1720             ANASTASIS_ActionCallback cb,
   1721             void *cb_cls)
   1722 {
   1723   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   1724   const json_t *arg_array;
   1725   struct ANASTASIS_ReduxPolicy pol = { 0 };
   1726 
   1727   if (NULL == arguments)
   1728   {
   1729     GNUNET_break (0);
   1730     ANASTASIS_REDUX_fail_ (rs,
   1731                            cb,
   1732                            cb_cls,
   1733                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1734                            "arguments missing");
   1735     return NULL;
   1736   }
   1737   arg_array = json_object_get (arguments,
   1738                                "policy");
   1739   if (! json_is_array (arg_array))
   1740   {
   1741     GNUNET_break (0);
   1742     ANASTASIS_REDUX_fail_ (rs,
   1743                            cb,
   1744                            cb_cls,
   1745                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1746                            "'policy' not an array");
   1747     return NULL;
   1748   }
   1749   if (! b->have_policies)
   1750   {
   1751     GNUNET_break (0);
   1752     ANASTASIS_REDUX_fail_ (rs,
   1753                            cb,
   1754                            cb_cls,
   1755                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1756                            "'policies' not an array");
   1757     return NULL;
   1758   }
   1759   if (! rs->common.have_providers)
   1760   {
   1761     GNUNET_break (0);
   1762     ANASTASIS_REDUX_fail_ (rs,
   1763                            cb,
   1764                            cb_cls,
   1765                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1766                            "'auth_providers' not an object");
   1767     return NULL;
   1768   }
   1769   if (! b->have_authentication_methods)
   1770   {
   1771     GNUNET_break (0);
   1772     ANASTASIS_REDUX_fail_ (rs,
   1773                            cb,
   1774                            cb_cls,
   1775                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1776                            "'auth_methods' not an array");
   1777     return NULL;
   1778   }
   1779 
   1780   /* Add all methods from 'arg_array' to the new policy */
   1781   {
   1782     size_t aindex;
   1783     json_t *method;
   1784 
   1785     json_array_foreach (arg_array, aindex, method)
   1786     {
   1787       const char *provider_url;
   1788       uint32_t method_idx;
   1789       const char *method_type;
   1790       const struct ANASTASIS_ReduxProvider *p;
   1791       const struct ANASTASIS_ReduxProviderConfig *cfg;
   1792       struct GNUNET_JSON_Specification ispec[] = {
   1793         GNUNET_JSON_spec_string ("provider",
   1794                                  &provider_url),
   1795         GNUNET_JSON_spec_uint32 ("authentication_method",
   1796                                  &method_idx),
   1797         GNUNET_JSON_spec_end ()
   1798       };
   1799 
   1800       if (GNUNET_OK !=
   1801           GNUNET_JSON_parse (method,
   1802                              ispec,
   1803                              NULL, NULL))
   1804       {
   1805         GNUNET_break (0);
   1806         ANASTASIS_REDUX_policy_clear_ (&pol);
   1807         ANASTASIS_REDUX_fail_ (rs,
   1808                                cb,
   1809                                cb_cls,
   1810                                TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1811                                "'method' details malformed");
   1812         return NULL;
   1813       }
   1814 
   1815       p = ANASTASIS_REDUX_provider_find_ (&rs->common,
   1816                                           provider_url);
   1817       if (NULL == p)
   1818       {
   1819         GNUNET_break (0);
   1820         ANASTASIS_REDUX_policy_clear_ (&pol);
   1821         ANASTASIS_REDUX_fail_ (rs,
   1822                                cb,
   1823                                cb_cls,
   1824                                TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1825                                "provider URL unknown");
   1826         return NULL;
   1827       }
   1828       if ( (! p->have_config) ||
   1829            (ANASTASIS_RPS_OK != p->status) ||
   1830            (MHD_HTTP_OK != p->config.http_status) )
   1831         continue; /* skip provider, disabled or down */
   1832       cfg = &p->config;
   1833 
   1834       if (method_idx >= b->authentication_methods_len)
   1835       {
   1836         GNUNET_break (0);
   1837         ANASTASIS_REDUX_policy_clear_ (&pol);
   1838         ANASTASIS_REDUX_fail_ (rs,
   1839                                cb,
   1840                                cb_cls,
   1841                                TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1842                                "authentication method unknown");
   1843         return NULL;
   1844       }
   1845       method_type = b->authentication_methods[method_idx].type;
   1846 
   1847       {
   1848         bool found = false;
   1849 
   1850         for (unsigned int j = 0; j < cfg->methods_len; j++)
   1851           if (0 == strcmp (cfg->methods[j].type,
   1852                            method_type))
   1853           {
   1854             found = true;
   1855             break;
   1856           }
   1857         if (! found)
   1858         {
   1859           GNUNET_break (0);
   1860           ANASTASIS_REDUX_policy_clear_ (&pol);
   1861           ANASTASIS_REDUX_fail_ (
   1862             rs,
   1863             cb,
   1864             cb_cls,
   1865             TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1866             "selected provider does not support authentication method");
   1867           return NULL;
   1868         }
   1869       }
   1870       {
   1871         struct ANASTASIS_ReduxPolicyMethod pm = {
   1872           .authentication_method.idx = method_idx
   1873         };
   1874 
   1875         ANASTASIS_REDUX_provider_url_set_ (&pm.provider,
   1876                                            provider_url);
   1877         GNUNET_array_append (pol.methods,
   1878                              pol.methods_len,
   1879                              pm);
   1880       }
   1881     } /* end of json_array_foreach (arg_array, mindex, method) */
   1882   }
   1883 
   1884   /* add new policy to array of existing policies */
   1885   {
   1886     const json_t *idx;
   1887     unsigned int at;
   1888 
   1889     idx = json_object_get (arguments,
   1890                            "policy_index");
   1891     if ( (NULL == idx) ||
   1892          (! json_is_integer (idx)) )
   1893       at = b->policies_len;
   1894     else
   1895       at = GNUNET_MIN ((unsigned int) json_integer_value (idx),
   1896                        b->policies_len);
   1897     GNUNET_array_append (b->policies,
   1898                          b->policies_len,
   1899                          pol);
   1900     if (at != b->policies_len - 1)
   1901     {
   1902       /* shift the tail one to the right and drop the new entry in */
   1903       memmove (&b->policies[at + 1],
   1904                &b->policies[at],
   1905                sizeof (struct ANASTASIS_ReduxPolicy)
   1906                * (b->policies_len - at - 1));
   1907       b->policies[at] = pol;
   1908     }
   1909   }
   1910 
   1911   ANASTASIS_REDUX_return_ (rs,
   1912                            cb,
   1913                            cb_cls,
   1914                            TALER_EC_NONE);
   1915   return NULL;
   1916 }
   1917 
   1918 
   1919 /* ******************** update_policy ******************* */
   1920 
   1921 
   1922 /**
   1923  * DispatchHandler/Callback function which is called for a
   1924  * "update_policy" action.
   1925  *
   1926  * @param state state to operate on
   1927  * @param arguments arguments to use for operation on state
   1928  * @param cb callback to call during/after operation
   1929  * @param cb_cls callback closure
   1930  * @return NULL
   1931  */
   1932 static struct ANASTASIS_ReduxAction *
   1933 update_policy (struct ANASTASIS_ReduxState *rs,
   1934                const json_t *arguments,
   1935                ANASTASIS_ActionCallback cb,
   1936                void *cb_cls)
   1937 {
   1938   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   1939   const json_t *idx;
   1940   json_int_t index;
   1941 
   1942   if (NULL == arguments)
   1943   {
   1944     GNUNET_break (0);
   1945     ANASTASIS_REDUX_fail_ (rs,
   1946                            cb,
   1947                            cb_cls,
   1948                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1949                            "arguments missing");
   1950     return NULL;
   1951   }
   1952   idx = json_object_get (arguments,
   1953                          "policy_index");
   1954   if (! json_is_integer (idx))
   1955   {
   1956     GNUNET_break (0);
   1957     ANASTASIS_REDUX_fail_ (rs,
   1958                            cb,
   1959                            cb_cls,
   1960                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   1961                            "'policy_index' must be an integer");
   1962     return NULL;
   1963   }
   1964   index = json_integer_value (idx);
   1965   if (! b->have_policies)
   1966   {
   1967     GNUNET_break (0);
   1968     ANASTASIS_REDUX_fail_ (rs,
   1969                            cb,
   1970                            cb_cls,
   1971                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   1972                            "'policies' must be an array");
   1973     return NULL;
   1974   }
   1975   if ( (index < 0) ||
   1976        (index >= (json_int_t) b->policies_len) )
   1977   {
   1978     GNUNET_break (0);
   1979     ANASTASIS_REDUX_fail_ (rs,
   1980                            cb,
   1981                            cb_cls,
   1982                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   1983                            "removal failed");
   1984     return NULL;
   1985   }
   1986   ANASTASIS_REDUX_policy_clear_ (&b->policies[index]);
   1987   memmove (&b->policies[index],
   1988            &b->policies[index + 1],
   1989            sizeof (struct ANASTASIS_ReduxPolicy)
   1990            * (b->policies_len - index - 1));
   1991   b->policies_len--;
   1992   return add_policy (rs,
   1993                      arguments,
   1994                      cb,
   1995                      cb_cls);
   1996 }
   1997 
   1998 
   1999 /* ******************** del_policy ******************* */
   2000 
   2001 
   2002 /**
   2003  * DispatchHandler/Callback function which is called for a
   2004  * "delete_policy" action.
   2005  *
   2006  * @param state state to operate on
   2007  * @param arguments arguments to use for operation on state
   2008  * @param cb callback to call during/after operation
   2009  * @param cb_cls callback closure
   2010  * @return NULL
   2011  */
   2012 static struct ANASTASIS_ReduxAction *
   2013 del_policy (struct ANASTASIS_ReduxState *rs,
   2014             const json_t *arguments,
   2015             ANASTASIS_ActionCallback cb,
   2016             void *cb_cls)
   2017 {
   2018   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   2019   const json_t *idx;
   2020   json_int_t index;
   2021 
   2022   if (NULL == arguments)
   2023   {
   2024     ANASTASIS_REDUX_fail_ (rs,
   2025                            cb,
   2026                            cb_cls,
   2027                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2028                            "arguments missing");
   2029     return NULL;
   2030   }
   2031   idx = json_object_get (arguments,
   2032                          "policy_index");
   2033   if (! json_is_integer (idx))
   2034   {
   2035     ANASTASIS_REDUX_fail_ (rs,
   2036                            cb,
   2037                            cb_cls,
   2038                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2039                            "'policy_index' must be an integer");
   2040     return NULL;
   2041   }
   2042   index = json_integer_value (idx);
   2043   if (! b->have_policies)
   2044   {
   2045     ANASTASIS_REDUX_fail_ (rs,
   2046                            cb,
   2047                            cb_cls,
   2048                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   2049                            "'policies' must be an array");
   2050     return NULL;
   2051   }
   2052   if ( (index < 0) ||
   2053        (index >= (json_int_t) b->policies_len) )
   2054   {
   2055     ANASTASIS_REDUX_fail_ (rs,
   2056                            cb,
   2057                            cb_cls,
   2058                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   2059                            "removal failed");
   2060     return NULL;
   2061   }
   2062   ANASTASIS_REDUX_policy_clear_ (&b->policies[index]);
   2063   memmove (&b->policies[index],
   2064            &b->policies[index + 1],
   2065            sizeof (struct ANASTASIS_ReduxPolicy)
   2066            * (b->policies_len - index - 1));
   2067   b->policies_len--;
   2068   ANASTASIS_REDUX_return_ (rs,
   2069                            cb,
   2070                            cb_cls,
   2071                            TALER_EC_NONE);
   2072   return NULL;
   2073 }
   2074 
   2075 
   2076 /* ******************** del_challenge ******************* */
   2077 
   2078 
   2079 /**
   2080  * DispatchHandler/Callback function which is called for a
   2081  * "delete_challenge" action.
   2082  *
   2083  * @param state state to operate on
   2084  * @param arguments arguments to use for operation on state
   2085  * @param cb callback to call during/after operation
   2086  * @param cb_cls callback closure
   2087  * @return NULL
   2088  */
   2089 static struct ANASTASIS_ReduxAction *
   2090 del_challenge (struct ANASTASIS_ReduxState *rs,
   2091                const json_t *arguments,
   2092                ANASTASIS_ActionCallback cb,
   2093                void *cb_cls)
   2094 {
   2095   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   2096   const json_t *pidx;
   2097   const json_t *cidx;
   2098   json_int_t index;
   2099   struct ANASTASIS_ReduxPolicy *policy;
   2100 
   2101   if (NULL == arguments)
   2102   {
   2103     ANASTASIS_REDUX_fail_ (rs,
   2104                            cb,
   2105                            cb_cls,
   2106                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2107                            "arguments missing");
   2108     return NULL;
   2109   }
   2110   pidx = json_object_get (arguments,
   2111                           "policy_index");
   2112   cidx = json_object_get (arguments,
   2113                           "challenge_index");
   2114   if (! json_is_integer (pidx))
   2115   {
   2116     ANASTASIS_REDUX_fail_ (rs,
   2117                            cb,
   2118                            cb_cls,
   2119                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2120                            "'policy_index' must be an integer");
   2121     return NULL;
   2122   }
   2123   if (! json_is_integer (cidx))
   2124   {
   2125     ANASTASIS_REDUX_fail_ (rs,
   2126                            cb,
   2127                            cb_cls,
   2128                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2129                            "'challenge_index' must be an integer");
   2130     return NULL;
   2131   }
   2132   index = json_integer_value (pidx);
   2133   if (! b->have_policies)
   2134   {
   2135     ANASTASIS_REDUX_fail_ (rs,
   2136                            cb,
   2137                            cb_cls,
   2138                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   2139                            "'policies' must be an array");
   2140     return NULL;
   2141   }
   2142   if ( (index < 0) ||
   2143        (index >= (json_int_t) b->policies_len) )
   2144   {
   2145     ANASTASIS_REDUX_fail_ (rs,
   2146                            cb,
   2147                            cb_cls,
   2148                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   2149                            "'policy_index' out of range");
   2150     return NULL;
   2151   }
   2152   policy = &b->policies[index];
   2153   index = json_integer_value (cidx);
   2154   if ( (index < 0) ||
   2155        (index >= (json_int_t) policy->methods_len) )
   2156   {
   2157     ANASTASIS_REDUX_fail_ (rs,
   2158                            cb,
   2159                            cb_cls,
   2160                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   2161                            "removal failed");
   2162     return NULL;
   2163   }
   2164   {
   2165     struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[index];
   2166 
   2167     ANASTASIS_REDUX_provider_url_clear_ (&pm->provider);
   2168     if (NULL != pm->truth)
   2169       ANASTASIS_truth_free (pm->truth);
   2170     memmove (pm,
   2171              pm + 1,
   2172              sizeof (*pm) * (policy->methods_len - index - 1));
   2173     policy->methods_len--;
   2174   }
   2175   ANASTASIS_REDUX_return_ (rs,
   2176                            cb,
   2177                            cb_cls,
   2178                            TALER_EC_NONE);
   2179   return NULL;
   2180 }
   2181 
   2182 
   2183 /* ********************** done_policy_review ***************** */
   2184 
   2185 
   2186 /**
   2187  * Calculate how many years of service we need
   2188  * from the desired @a expiration time,
   2189  * rounding up.
   2190  *
   2191  * @param expiration desired expiration time
   2192  * @return number of years of service to pay for
   2193 */
   2194 static unsigned int
   2195 expiration_to_years (struct GNUNET_TIME_Timestamp expiration)
   2196 {
   2197   struct GNUNET_TIME_Relative rem;
   2198   unsigned int years;
   2199 
   2200   rem = GNUNET_TIME_absolute_get_remaining (expiration.abs_time);
   2201   years = rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us;
   2202   if (0 != rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)
   2203     years++;
   2204   return years;
   2205 }
   2206 
   2207 
   2208 /**
   2209  * Update @a state such that the earliest expiration for
   2210  * any truth or policy is @a expiration. Recalculate
   2211  * the ``upload_fees`` array with the associated costs.
   2212  *
   2213  * @param[in,out] state our state to update
   2214  * @param expiration new expiration to enforce
   2215  * @return #GNUNET_OK on success,
   2216  *         #GNUNET_SYSERR if the state is invalid
   2217  */
   2218 static enum GNUNET_GenericReturnValue
   2219 update_expiration_cost (struct ANASTASIS_ReduxState *rs,
   2220                         struct GNUNET_TIME_Timestamp expiration)
   2221 {
   2222   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   2223   struct Costs *costs = NULL;
   2224   unsigned int years;
   2225   bool is_free = true;
   2226 
   2227   if (! rs->common.have_providers)
   2228   {
   2229     GNUNET_break (0);
   2230     return GNUNET_SYSERR;
   2231   }
   2232 
   2233   years = expiration_to_years (expiration);
   2234 
   2235   /* Publish the currencies in which the *whole* backup can be settled
   2236      with one choice of currency, i.e. the intersection over the
   2237      providers that will actually be used.  An empty intersection is not
   2238      an error: it means the user will pay more than one order in more
   2239      than one currency, which is exactly what `upload_fees' then shows. */
   2240   {
   2241     bool first = true;
   2242 
   2243     for (unsigned int i = 0; i < rs->common.currencies_len; i++)
   2244       GNUNET_free (rs->common.currencies[i]);
   2245     GNUNET_array_grow (rs->common.currencies,
   2246                        rs->common.currencies_len,
   2247                        0);
   2248     rs->common.have_currencies = true;
   2249     for (unsigned int i = 0; i < rs->common.providers_len; i++)
   2250     {
   2251       const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i];
   2252 
   2253       if ( (ANASTASIS_RPS_OK != p->status) ||
   2254            (! p->have_config) ||
   2255            (MHD_HTTP_OK != p->config.http_status) )
   2256         continue;
   2257       if (first)
   2258       {
   2259         for (unsigned int j = 0; j < p->config.currencies_len; j++)
   2260           GNUNET_array_append (rs->common.currencies,
   2261                                rs->common.currencies_len,
   2262                                GNUNET_strdup (p->config.currencies[j]));
   2263         first = false;
   2264         continue;
   2265       }
   2266       for (unsigned int j = 0; j < rs->common.currencies_len;)
   2267       {
   2268         bool found = false;
   2269 
   2270         for (unsigned int k = 0; k < p->config.currencies_len; k++)
   2271           if (0 == strcasecmp (rs->common.currencies[j],
   2272                                p->config.currencies[k]))
   2273             found = true;
   2274         if (found)
   2275         {
   2276           j++;
   2277           continue;
   2278         }
   2279         GNUNET_free (rs->common.currencies[j]);
   2280         rs->common.currencies[j]
   2281           = rs->common.currencies[rs->common.currencies_len - 1];
   2282         GNUNET_array_grow (rs->common.currencies,
   2283                            rs->common.currencies_len,
   2284                            rs->common.currencies_len - 1);
   2285       }
   2286     }
   2287   }
   2288 
   2289   /* go over all providers and add up cost */
   2290   for (unsigned int i = 0; i < rs->common.providers_len; i++)
   2291   {
   2292     const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i];
   2293     const struct TALER_Amount *af;
   2294     struct TALER_Amount fee;
   2295 
   2296     if ( (ANASTASIS_RPS_OK != p->status) ||
   2297          (! p->have_config) ||
   2298          (MHD_HTTP_OK != p->config.http_status) )
   2299       continue; /* skip providers that are down or disabled */
   2300     af = preferred_price (&rs->common,
   2301                           &p->config.annual_fees);
   2302     if (NULL == af)
   2303       continue; /* provider is free */
   2304     if (0 >
   2305         TALER_amount_multiply (&fee,
   2306                                af,
   2307                                years))
   2308     {
   2309       GNUNET_break (0);
   2310       free_costs (costs);
   2311       return GNUNET_SYSERR;
   2312     }
   2313     add_cost (&costs,
   2314               &fee);
   2315   }
   2316 
   2317   /* go over all truths and add up cost */
   2318   {
   2319     unsigned int off = 0;
   2320     unsigned int len = 0;
   2321     struct AlreadySeen
   2322     {
   2323       unsigned int method;
   2324       const char *provider_url;
   2325     } *seen = NULL;
   2326 
   2327     for (unsigned int pidx = 0; pidx < b->policies_len; pidx++)
   2328     {
   2329       const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pidx];
   2330 
   2331       for (unsigned int midx = 0; midx < policy->methods_len; midx++)
   2332       {
   2333         const struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[midx];
   2334         const char *provider_url = pm->provider.url;
   2335         unsigned int method_idx = pm->authentication_method.idx;
   2336         const struct ANASTASIS_ReduxProvider *p;
   2337 
   2338         /* check if we have seen this one before */
   2339         {
   2340           bool found = false;
   2341 
   2342           for (unsigned int i = 0; i<off; i++)
   2343             if ( (seen[i].method == method_idx) &&
   2344                  (0 == strcmp (seen[i].provider_url,
   2345                                provider_url)) )
   2346               found = true;
   2347           if (found)
   2348             continue; /* skip */
   2349         }
   2350         if (off == len)
   2351         {
   2352           GNUNET_array_grow (seen,
   2353                              len,
   2354                              4 + len * 2);
   2355         }
   2356         seen[off].method = method_idx;
   2357         seen[off].provider_url = provider_url;
   2358         off++;
   2359         p = ANASTASIS_REDUX_provider_find_ (&rs->common,
   2360                                             provider_url);
   2361         if ( (NULL == p) ||
   2362              (! p->have_config) ||
   2363              (ANASTASIS_RPS_OK != p->status) ||
   2364              (MHD_HTTP_OK != p->config.http_status) )
   2365         {
   2366           GNUNET_break (0);
   2367           GNUNET_array_grow (seen,
   2368                              len,
   2369                              0);
   2370           free_costs (costs);
   2371           return GNUNET_SYSERR;
   2372         }
   2373         {
   2374           const struct TALER_Amount *tf;
   2375           struct TALER_Amount fee;
   2376 
   2377           tf = preferred_price (&rs->common,
   2378                                 &p->config.truth_upload_fees);
   2379           if (NULL == tf)
   2380             continue; /* provider stores truths for free */
   2381           if (0 >
   2382               TALER_amount_multiply (&fee,
   2383                                      tf,
   2384                                      years))
   2385           {
   2386             GNUNET_break (0);
   2387             GNUNET_array_grow (seen,
   2388                                len,
   2389                                0);
   2390             free_costs (costs);
   2391             return GNUNET_SYSERR;
   2392           }
   2393           add_cost (&costs,
   2394                     &fee);
   2395         }
   2396       }
   2397     }
   2398     GNUNET_array_grow (seen,
   2399                        len,
   2400                        0);
   2401   }
   2402 
   2403   /* convert 'costs' into state */
   2404   GNUNET_free (b->upload_fees);
   2405   b->upload_fees_len = 0;
   2406   b->have_upload_fees = true;
   2407   while (NULL != costs)
   2408   {
   2409     struct Costs *nxt = costs->next;
   2410 
   2411     if (! TALER_amount_is_zero (&costs->cost))
   2412     {
   2413       GNUNET_array_append (b->upload_fees,
   2414                            b->upload_fees_len,
   2415                            costs->cost);
   2416       is_free = false;
   2417     }
   2418     GNUNET_free (costs);
   2419     costs = nxt;
   2420   }
   2421 
   2422   if (is_free)
   2423     expiration = GNUNET_TIME_relative_to_timestamp (ANASTASIS_FREE_STORAGE);
   2424   b->expiration = expiration;
   2425   b->have_expiration = true;
   2426   return GNUNET_OK;
   2427 }
   2428 
   2429 
   2430 /**
   2431  * DispatchHandler/Callback function which is called for a
   2432  * "done_policy_review" action.
   2433  *
   2434  * @param state state to operate on
   2435  * @param arguments arguments to use for operation on state
   2436  * @param cb callback to call during/after operation
   2437  * @param cb_cls callback closure
   2438  * @return NULL
   2439  */
   2440 static struct ANASTASIS_ReduxAction *
   2441 done_policy_review (struct ANASTASIS_ReduxState *rs,
   2442                     const json_t *arguments,
   2443                     ANASTASIS_ActionCallback cb,
   2444                     void *cb_cls)
   2445 {
   2446   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   2447   struct GNUNET_TIME_Timestamp exp;
   2448 
   2449   if (0 == b->policies_len)
   2450   {
   2451     ANASTASIS_REDUX_fail_ (rs,
   2452                            cb,
   2453                            cb_cls,
   2454                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   2455                            "no policies specified");
   2456     return NULL;
   2457   }
   2458   exp = b->have_expiration
   2459         ? b->expiration
   2460         : GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS);
   2461   if (GNUNET_TIME_absolute_is_zero (exp.abs_time))
   2462     exp = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS);
   2463   if (GNUNET_OK !=
   2464       update_expiration_cost (rs,
   2465                               exp))
   2466   {
   2467     ANASTASIS_REDUX_fail_ (rs,
   2468                            cb,
   2469                            cb_cls,
   2470                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   2471                            "could not calculate expiration cost");
   2472     return NULL;
   2473   }
   2474   set_state (rs,
   2475              ANASTASIS_BACKUP_STATE_SECRET_EDITING);
   2476   ANASTASIS_REDUX_return_ (rs,
   2477                            cb,
   2478                            cb_cls,
   2479                            TALER_EC_NONE);
   2480   return NULL;
   2481 }
   2482 
   2483 
   2484 /**
   2485  * Information we keep for an upload() operation.
   2486  */
   2487 struct UploadContext;
   2488 
   2489 
   2490 /**
   2491  * Maps a TruthUpload to a policy and recovery method where this
   2492  * truth is used.
   2493  */
   2494 struct PolicyMethodReference
   2495 {
   2496   /**
   2497    * Offset into the "policies" array.
   2498    */
   2499   unsigned int policy_index;
   2500 
   2501   /**
   2502    * Offset into the "methods" array (of the policy selected
   2503    * by @e policy_index).
   2504    */
   2505   unsigned int method_index;
   2506 
   2507 };
   2508 
   2509 
   2510 /**
   2511  * Entry we keep per truth upload.
   2512  */
   2513 struct TruthUpload
   2514 {
   2515 
   2516   /**
   2517    * Kept in a DLL.
   2518    */
   2519   struct TruthUpload *next;
   2520 
   2521   /**
   2522    * Kept in a DLL.
   2523    */
   2524   struct TruthUpload *prev;
   2525 
   2526   /**
   2527    * Handle to the actual upload operation.
   2528    */
   2529   struct ANASTASIS_TruthUpload *tu;
   2530 
   2531   /**
   2532    * Upload context this operation is part of.
   2533    */
   2534   struct UploadContext *uc;
   2535 
   2536   /**
   2537    * Truth resulting from the upload, if any.
   2538    */
   2539   struct ANASTASIS_Truth *t;
   2540 
   2541   /**
   2542    * A taler://pay/-URI with a request to pay the annual fee for
   2543    * the service.  Set if payment is required.
   2544    */
   2545   char *payment_request;
   2546 
   2547   /**
   2548    * Which policies and methods does this truth affect?
   2549    */
   2550   struct PolicyMethodReference *policies;
   2551 
   2552   /**
   2553    * Where are we uploading to?
   2554    */
   2555   char *provider_url;
   2556 
   2557   /**
   2558    * Which challenge object are we uploading?
   2559    */
   2560   uint32_t am_idx;
   2561 
   2562   /**
   2563    * Length of the @e policies array.
   2564    */
   2565   unsigned int policies_length;
   2566 
   2567   /**
   2568    * Status of the upload.
   2569    */
   2570   enum ANASTASIS_UploadStatus us;
   2571 
   2572   /**
   2573    * Taler error code of the upload.
   2574    */
   2575   enum TALER_ErrorCode ec;
   2576 
   2577 };
   2578 
   2579 
   2580 /**
   2581  * Information we keep for an upload() operation.
   2582  */
   2583 struct UploadContext
   2584 {
   2585   /**
   2586    * Recovery action returned to caller for aborting the operation.
   2587    */
   2588   struct ANASTASIS_ReduxAction ra;
   2589 
   2590   /**
   2591    * Function to call upon completion.
   2592    */
   2593   ANASTASIS_ActionCallback cb;
   2594 
   2595   /**
   2596    * Closure for @e cb.
   2597    */
   2598   void *cb_cls;
   2599 
   2600   /**
   2601    * Our state; we own it.
   2602    */
   2603   struct ANASTASIS_ReduxState *rs;
   2604 
   2605   /**
   2606    * Master secret sharing operation, NULL if not yet running.
   2607    */
   2608   struct ANASTASIS_SecretShare *ss;
   2609 
   2610   /**
   2611    * Head of DLL of truth uploads.
   2612    */
   2613   struct TruthUpload *tues_head;
   2614 
   2615   /**
   2616    * Tail of DLL of truth uploads.
   2617    */
   2618   struct TruthUpload *tues_tail;
   2619 
   2620   /**
   2621    * Timeout to use for the operation, from the arguments.
   2622    */
   2623   struct GNUNET_TIME_Relative timeout;
   2624 
   2625   /**
   2626    * For how many years should we pay?
   2627    */
   2628   unsigned int years;
   2629 
   2630 };
   2631 
   2632 
   2633 /**
   2634  * Function called when the #upload transition is being aborted.
   2635  *
   2636  * @param cls a `struct UploadContext`
   2637  */
   2638 static void
   2639 upload_cancel_cb (void *cls)
   2640 {
   2641   struct UploadContext *uc = cls;
   2642   struct TruthUpload *tue;
   2643 
   2644   while (NULL != (tue = uc->tues_head))
   2645   {
   2646     GNUNET_CONTAINER_DLL_remove (uc->tues_head,
   2647                                  uc->tues_tail,
   2648                                  tue);
   2649     if (NULL != tue->tu)
   2650     {
   2651       ANASTASIS_truth_upload_cancel (tue->tu);
   2652       tue->tu = NULL;
   2653     }
   2654     if (NULL != tue->t)
   2655     {
   2656       ANASTASIS_truth_free (tue->t);
   2657       tue->t = NULL;
   2658     }
   2659     GNUNET_free (tue->provider_url);
   2660     GNUNET_free (tue->payment_request);
   2661     GNUNET_free (tue->policies);
   2662     GNUNET_free (tue);
   2663   }
   2664   if (NULL != uc->ss)
   2665   {
   2666     ANASTASIS_secret_share_cancel (uc->ss);
   2667     uc->ss = NULL;
   2668   }
   2669   ANASTASIS_REDUX_state_free_ (uc->rs);
   2670   GNUNET_free (uc);
   2671 }
   2672 
   2673 
   2674 /**
   2675  * Return the state of @a uc to the application and dispose of @a uc.
   2676  *
   2677  * @param[in] uc context to finish
   2678  * @param ec error code to report alongside the state
   2679  */
   2680 static void
   2681 uc_return (struct UploadContext *uc,
   2682            enum TALER_ErrorCode ec)
   2683 {
   2684   struct ANASTASIS_ReduxState *rs = uc->rs;
   2685   ANASTASIS_ActionCallback cb = uc->cb;
   2686   void *cb_cls = uc->cb_cls;
   2687 
   2688   uc->rs = NULL;
   2689   upload_cancel_cb (uc);
   2690   ANASTASIS_REDUX_return_ (rs,
   2691                            cb,
   2692                            cb_cls,
   2693                            ec);
   2694 }
   2695 
   2696 
   2697 /**
   2698  * Report an error to the application and dispose of @a uc.
   2699  *
   2700  * @param[in] uc context to finish
   2701  * @param ec error to report
   2702  * @param detail human-readable detail, may be NULL
   2703  */
   2704 static void
   2705 uc_fail (struct UploadContext *uc,
   2706          enum TALER_ErrorCode ec,
   2707          const char *detail)
   2708 {
   2709   ANASTASIS_ActionCallback cb = uc->cb;
   2710   void *cb_cls = uc->cb_cls;
   2711 
   2712   upload_cancel_cb (uc);
   2713   ANASTASIS_redux_fail_ (cb,
   2714                          cb_cls,
   2715                          ec,
   2716                          detail);
   2717 }
   2718 
   2719 
   2720 /**
   2721  * Duplicate the truth @a t.  Truths are shared between the policies
   2722  * that use them, but each `struct ANASTASIS_Truth` has a single owner,
   2723  * so the state gets its own copy.
   2724  *
   2725  * @param t truth to copy
   2726  * @return a copy of @a t
   2727  */
   2728 static struct ANASTASIS_Truth *
   2729 truth_dup (const struct ANASTASIS_Truth *t)
   2730 {
   2731   json_t *jt = ANASTASIS_truth_to_json (t);
   2732   struct ANASTASIS_Truth *r;
   2733 
   2734   GNUNET_assert (NULL != jt);
   2735   r = ANASTASIS_truth_from_json (jt);
   2736   GNUNET_assert (NULL != r);
   2737   json_decref (jt);
   2738   return r;
   2739 }
   2740 
   2741 
   2742 /**
   2743  * Take all of the ongoing truth uploads and store them in the @a uc
   2744  * state, so that they survive a round-trip through the application.
   2745  *
   2746  * @param[in,out] uc context to take truth uploads from and to update state of
   2747  */
   2748 static void
   2749 serialize_truth (struct UploadContext *uc)
   2750 {
   2751   struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup;
   2752 
   2753   for (struct TruthUpload *tue = uc->tues_head;
   2754        NULL != tue;
   2755        tue = tue->next)
   2756   {
   2757     if (NULL == tue->t)
   2758       continue;
   2759     for (unsigned int i = 0; i<tue->policies_length; i++)
   2760     {
   2761       const struct PolicyMethodReference *pmr = &tue->policies[i];
   2762       struct ANASTASIS_ReduxPolicy *policy;
   2763       struct ANASTASIS_ReduxPolicyMethod *pm;
   2764 
   2765       GNUNET_assert (pmr->policy_index < b->policies_len);
   2766       policy = &b->policies[pmr->policy_index];
   2767       GNUNET_assert (pmr->method_index < policy->methods_len);
   2768       pm = &policy->methods[pmr->method_index];
   2769       if (NULL != pm->truth)
   2770         ANASTASIS_truth_free (pm->truth);
   2771       pm->truth = truth_dup (tue->t);
   2772       pm->upload_status = tue->us;
   2773     }
   2774   }
   2775 }
   2776 
   2777 
   2778 /**
   2779  * Test if the given @a provider_url is used by any of the
   2780  * authentication methods and thus the provider should be
   2781  * considered mandatory for storing the policy.
   2782  *
   2783  * @param state state to inspect
   2784  * @param provider_url provider to test
   2785  * @return false if the provider can be removed from policy
   2786  *   upload considerations without causing a problem
   2787  */
   2788 static bool
   2789 provider_required (const struct ANASTASIS_ReduxState *rs,
   2790                    const char *provider_url)
   2791 {
   2792   const struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   2793 
   2794   for (unsigned int pidx = 0; pidx < b->policies_len; pidx++)
   2795   {
   2796     const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pidx];
   2797 
   2798     for (unsigned int midx = 0; midx < policy->methods_len; midx++)
   2799       if (0 == strcmp (policy->methods[midx].provider.url,
   2800                        provider_url))
   2801         return true;
   2802   }
   2803   return false;
   2804 }
   2805 
   2806 
   2807 /**
   2808  * All truth uploads are done, begin with uploading the policy.
   2809  *
   2810  * @param[in,out] uc context for the operation
   2811  */
   2812 static void
   2813 share_secret (struct UploadContext *uc);
   2814 
   2815 
   2816 /**
   2817  * Function called with the results of a #ANASTASIS_secret_share().
   2818  *
   2819  * @param cls closure with a `struct UploadContext *`
   2820  * @param sr share result
   2821  */
   2822 static void
   2823 secret_share_result_cb (void *cls,
   2824                         const struct ANASTASIS_ShareResult *sr)
   2825 {
   2826   struct UploadContext *uc = cls;
   2827   struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup;
   2828 
   2829   uc->ss = NULL;
   2830   switch (sr->ss)
   2831   {
   2832   case ANASTASIS_SHARE_STATUS_SUCCESS:
   2833     /* Just to be safe, delete the "core_secret" so that it is not
   2834        accidentally preserved anywhere */
   2835     json_decref (b->core_secret);
   2836     b->core_secret = NULL;
   2837     for (unsigned int i = 0; i < b->success_details_len; i++)
   2838       ANASTASIS_REDUX_provider_url_clear_ (
   2839         &b->success_details[i].provider_url);
   2840     GNUNET_free (b->success_details);
   2841     b->success_details_len = 0;
   2842     b->have_success_details = true;
   2843     for (unsigned int i = 0; i<sr->details.success.num_providers; i++)
   2844     {
   2845       const struct ANASTASIS_ProviderSuccessStatus *pssi
   2846         = &sr->details.success.pss[i];
   2847       struct ANASTASIS_ReduxSuccessDetail d = {
   2848         .policy_version = pssi->policy_version,
   2849         .policy_expiration = pssi->policy_expiration
   2850       };
   2851 
   2852       ANASTASIS_REDUX_provider_url_set_ (&d.provider_url,
   2853                                          pssi->provider_url);
   2854       GNUNET_array_append (b->success_details,
   2855                            b->success_details_len,
   2856                            d);
   2857     }
   2858     set_state (uc->rs,
   2859                ANASTASIS_BACKUP_STATE_BACKUP_FINISHED);
   2860     uc_return (uc,
   2861                TALER_EC_NONE);
   2862     return;
   2863   case ANASTASIS_SHARE_STATUS_PAYMENT_REQUIRED:
   2864     set_state (uc->rs,
   2865                ANASTASIS_BACKUP_STATE_POLICIES_PAYING);
   2866     serialize_truth (uc);
   2867     ANASTASIS_REDUX_policy_payment_requests_clear_ (b);
   2868     b->have_policy_payment_requests = true;
   2869     for (unsigned int i = 0; i<
   2870          sr->details.payment_required.payment_requests_length; i++)
   2871     {
   2872       const struct ANASTASIS_SharePaymentRequest *spr;
   2873       struct ANASTASIS_ReduxPolicyPaymentRequest ppr = { 0 };
   2874 
   2875       spr = &sr->details.payment_required.payment_requests[i];
   2876       ppr.payto = GNUNET_strdup (spr->payment_request_url);
   2877       ANASTASIS_REDUX_provider_url_set_ (&ppr.provider,
   2878                                          spr->provider_url);
   2879       GNUNET_array_append (b->policy_payment_requests,
   2880                            b->policy_payment_requests_len,
   2881                            ppr);
   2882       for (unsigned int off = 0; off < b->policy_providers_len; off++)
   2883       {
   2884         struct ANASTASIS_ReduxPolicyProvider *pp = &b->policy_providers[off];
   2885 
   2886         if (0 == strcmp (pp->provider_url.url,
   2887                          spr->provider_url))
   2888         {
   2889           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2890                       "Remembering payment secret for provider `%s'\n",
   2891                       spr->provider_url);
   2892           pp->payment_secret = spr->payment_secret;
   2893           pp->have_payment_secret = true;
   2894         }
   2895       }
   2896     }
   2897     uc_return (uc,
   2898                TALER_EC_NONE);
   2899     return;
   2900   case ANASTASIS_SHARE_STATUS_PROVIDER_FAILED:
   2901     {
   2902       json_t *details;
   2903       ANASTASIS_ActionCallback cb;
   2904       void *cb_cls;
   2905 
   2906       if (! provider_required (uc->rs,
   2907                                sr->details.provider_failure.provider_url))
   2908       {
   2909         /* try again without that provider */
   2910         struct ANASTASIS_ReduxProvider *p;
   2911 
   2912         p = ANASTASIS_REDUX_provider_find_ (
   2913           &uc->rs->common,
   2914           sr->details.provider_failure.provider_url);
   2915         if (NULL == p)
   2916           GNUNET_break (0);
   2917         else
   2918           p->status = ANASTASIS_RPS_DISABLED;
   2919         for (unsigned int idx = 0; idx < b->policy_providers_len; idx++)
   2920         {
   2921           struct ANASTASIS_ReduxPolicyProvider *pp
   2922             = &b->policy_providers[idx];
   2923 
   2924           if (0 == strcmp (sr->details.provider_failure.provider_url,
   2925                            pp->provider_url.url))
   2926           {
   2927             ANASTASIS_REDUX_provider_url_clear_ (&pp->provider_url);
   2928             memmove (pp,
   2929                      pp + 1,
   2930                      sizeof (*pp) * (b->policy_providers_len - idx - 1));
   2931             b->policy_providers_len--;
   2932             break;
   2933           }
   2934         }
   2935         share_secret (uc);
   2936         return;
   2937       }
   2938       details = GNUNET_JSON_PACK (
   2939         GNUNET_JSON_pack_uint64 ("http_status",
   2940                                  sr->details.provider_failure.http_status),
   2941         GNUNET_JSON_pack_uint64 ("code",
   2942                                  sr->details.provider_failure.ec),
   2943         GNUNET_JSON_pack_string ("hint",
   2944                                  TALER_ErrorCode_get_hint (
   2945                                    sr->details.provider_failure.ec)),
   2946         GNUNET_JSON_pack_string ("provider_url",
   2947                                  sr->details.provider_failure.provider_url));
   2948       cb = uc->cb;
   2949       cb_cls = uc->cb_cls;
   2950       upload_cancel_cb (uc);
   2951       cb (cb_cls,
   2952           TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED,
   2953           details);
   2954       json_decref (details);
   2955     }
   2956     return;
   2957   default:
   2958     GNUNET_break (0);
   2959     uc_fail (uc,
   2960              TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   2961              "unexpected share result");
   2962     return;
   2963   }
   2964 }
   2965 
   2966 
   2967 /**
   2968  * All truth uploads are done, begin with uploading the policy.
   2969  *
   2970  * @param[in,out] uc context for the operation
   2971  */
   2972 static void
   2973 share_secret (struct UploadContext *uc)
   2974 {
   2975   struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup;
   2976   unsigned int policies_len = b->policies_len;
   2977   unsigned int pds_len = b->policy_providers_len;
   2978   struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
   2979 
   2980   if ( (NULL == uc->rs->common.identity_attributes) ||
   2981        (NULL == b->core_secret) ||
   2982        (! b->have_policies) )
   2983   {
   2984     uc_fail (uc,
   2985              TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   2986              "State parsing failed when preparing to share secret");
   2987     return;
   2988   }
   2989   if (b->have_pay_arguments &&
   2990       b->pay_arguments.have_timeout)
   2991     timeout = b->pay_arguments.timeout;
   2992   if (0 == policies_len)
   2993   {
   2994     uc_fail (uc,
   2995              TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   2996              "State parsing failed checks when preparing to share secret");
   2997     return;
   2998   }
   2999   if (0 == pds_len)
   3000   {
   3001     uc_fail (uc,
   3002              TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3003              "no workable providers in state");
   3004     return;
   3005   }
   3006 
   3007   {
   3008     struct ANASTASIS_Policy *vpolicies[policies_len];
   3009     const struct ANASTASIS_Policy *policies[policies_len];
   3010     struct ANASTASIS_ProviderDetails pds[pds_len];
   3011 
   3012     /* initialize policies/vpolicies arrays */
   3013     memset (pds,
   3014             0,
   3015             sizeof (pds));
   3016     for (unsigned int i = 0; i<policies_len; i++)
   3017     {
   3018       const struct ANASTASIS_ReduxPolicy *policy = &b->policies[i];
   3019       unsigned int methods_len = policy->methods_len;
   3020 
   3021       if (0 == methods_len)
   3022       {
   3023         GNUNET_break (0);
   3024         for (unsigned int k = 0; k<i; k++)
   3025           ANASTASIS_policy_destroy (vpolicies[k]);
   3026         uc_fail (uc,
   3027                  TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3028                  "'methods' must be an array of sane length");
   3029         return;
   3030       }
   3031       {
   3032         struct ANASTASIS_Policy *p;
   3033         struct ANASTASIS_Truth *truths[methods_len];
   3034         const struct ANASTASIS_Truth *ctruths[methods_len];
   3035 
   3036         for (unsigned int j = 0; j<methods_len; j++)
   3037         {
   3038           const struct ANASTASIS_ReduxPolicyMethod *pm = &policy->methods[j];
   3039 
   3040           if (NULL != pm->truth)
   3041           {
   3042             /* Truth we already have in the state */
   3043             truths[j] = truth_dup (pm->truth);
   3044           }
   3045           else
   3046           {
   3047             bool found = false;
   3048             /* Maybe we never stored the truth; find it in our DLL */
   3049             for (struct TruthUpload *tue = uc->tues_head;
   3050                  NULL != tue;
   3051                  tue = tue->next)
   3052             {
   3053               GNUNET_break (NULL != tue->t);
   3054               if ( (tue->am_idx == pm->authentication_method.idx) &&
   3055                    (0 == strcmp (pm->provider.url,
   3056                                  tue->provider_url)) )
   3057               {
   3058                 truths[j] = truth_dup (tue->t);
   3059                 found = true;
   3060                 break;
   3061               }
   3062             }
   3063             if (! found)
   3064             {
   3065               GNUNET_break (0);
   3066               for (unsigned int k = 0; k<j; k++)
   3067                 ANASTASIS_truth_free (truths[k]);
   3068               for (unsigned int k = 0; k<i; k++)
   3069                 ANASTASIS_policy_destroy (vpolicies[k]);
   3070               uc_fail (uc,
   3071                        TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3072                        "'truth' failed to decode");
   3073               return;
   3074             }
   3075           }
   3076           ctruths[j] = truths[j];
   3077         }
   3078         p = ANASTASIS_policy_create (ctruths,
   3079                                      methods_len);
   3080         vpolicies[i] = p;
   3081         policies[i] = p;
   3082         for (unsigned int k = 0; k<methods_len; k++)
   3083           ANASTASIS_truth_free (truths[k]);
   3084       }
   3085     }
   3086 
   3087     /* initialize 'pds' array */
   3088     for (unsigned int i = 0; i<pds_len; i++)
   3089     {
   3090       const struct ANASTASIS_ReduxPolicyProvider *pp
   3091         = &b->policy_providers[i];
   3092 
   3093       pds[i].provider_url = pp->provider_url.url;
   3094       if (pp->have_payment_secret)
   3095         pds[i].payment_secret = pp->payment_secret;
   3096       if (GNUNET_OK !=
   3097           ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common,
   3098                                         pds[i].provider_url,
   3099                                         &pds[i].provider_salt))
   3100       {
   3101         GNUNET_break (0);
   3102         for (unsigned int p = 0; p<policies_len; p++)
   3103           ANASTASIS_policy_destroy (vpolicies[p]);
   3104         uc_fail (uc,
   3105                  TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3106                  "'providers' entry malformed");
   3107         return;
   3108       }
   3109     }
   3110 
   3111     {
   3112       char *secret;
   3113       size_t secret_size;
   3114 
   3115       secret = json_dumps (b->core_secret,
   3116                            JSON_COMPACT | JSON_SORT_KEYS);
   3117       GNUNET_assert (NULL != secret);
   3118       secret_size = strlen (secret);
   3119       uc->ss = ANASTASIS_secret_share (ANASTASIS_REDUX_ctx_,
   3120                                        uc->rs->common.identity_attributes,
   3121                                        pds,
   3122                                        pds_len,
   3123                                        policies,
   3124                                        policies_len,
   3125                                        uc->years,
   3126                                        timeout,
   3127                                        &secret_share_result_cb,
   3128                                        uc,
   3129                                        b->secret_name,
   3130                                        secret,
   3131                                        secret_size);
   3132       GNUNET_free (secret);
   3133     }
   3134     for (unsigned int i = 0; i<policies_len; i++)
   3135       ANASTASIS_policy_destroy (vpolicies[i]);
   3136   }
   3137   if (NULL == uc->ss)
   3138   {
   3139     GNUNET_break (0);
   3140     uc_fail (uc,
   3141              TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   3142              "Failed to begin secret sharing");
   3143     return;
   3144   }
   3145 }
   3146 
   3147 
   3148 /**
   3149  * Some truth uploads require payment, serialize state and
   3150  * request payment to be executed by the application.
   3151  *
   3152  * @param[in,out] uc context for the operation
   3153  */
   3154 static void
   3155 request_truth_payment (struct UploadContext *uc)
   3156 {
   3157   struct ANASTASIS_ReduxBackup *b = &uc->rs->details.backup;
   3158 
   3159   serialize_truth (uc);
   3160   ANASTASIS_REDUX_payments_clear_ (b);
   3161   b->have_payments = true;
   3162   for (struct TruthUpload *tue = uc->tues_head;
   3163        NULL != tue;
   3164        tue = tue->next)
   3165   {
   3166     char *pr;
   3167 
   3168     if (NULL == tue->payment_request)
   3169       continue;
   3170     pr = GNUNET_strdup (tue->payment_request);
   3171     GNUNET_array_append (b->payments,
   3172                          b->payments_len,
   3173                          pr);
   3174   }
   3175   set_state (uc->rs,
   3176              ANASTASIS_BACKUP_STATE_TRUTHS_PAYING);
   3177   uc_return (uc,
   3178              TALER_EC_NONE);
   3179 }
   3180 
   3181 
   3182 /**
   3183  * We may be finished with all (active) asynchronous operations.
   3184  * Check if any are pending and continue accordingly.
   3185  *
   3186  * @param[in,out] uc context for the operation
   3187  */
   3188 static void
   3189 check_upload_finished (struct UploadContext *uc)
   3190 {
   3191   bool pay = false;
   3192   bool active = false;
   3193 
   3194   for (struct TruthUpload *tue = uc->tues_head;
   3195        NULL != tue;
   3196        tue = tue->next)
   3197   {
   3198     if (TALER_EC_NONE != tue->ec)
   3199     {
   3200       ANASTASIS_ActionCallback cb = uc->cb;
   3201       void *cb_cls = uc->cb_cls;
   3202       enum TALER_ErrorCode ec = tue->ec;
   3203 
   3204       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3205                   "Truth upload failed with error %d\n",
   3206                   (int) ec);
   3207       upload_cancel_cb (uc);
   3208       cb (cb_cls,
   3209           ec,
   3210           NULL);
   3211       return;
   3212     }
   3213     if (NULL != tue->tu)
   3214       active = true;
   3215     if (NULL != tue->payment_request)
   3216       pay = true;
   3217   }
   3218   if (active)
   3219     return;
   3220   if (pay)
   3221   {
   3222     request_truth_payment (uc);
   3223     return;
   3224   }
   3225   share_secret (uc);
   3226 }
   3227 
   3228 
   3229 /**
   3230  * Upload result information.  The resulting truth object can be used
   3231  * to create policies.  If payment is required, the @a taler_pay_url
   3232  * is returned and the operation must be retried after payment.
   3233  * Callee MUST free @a t using ANASTASIS_truth_free().
   3234  *
   3235  * @param cls closure with a `struct TruthUpload`
   3236  * @param t truth object to create policies, NULL on failure
   3237  * @param ud upload details
   3238  */
   3239 static void
   3240 truth_upload_cb (void *cls,
   3241                  struct ANASTASIS_Truth *t,
   3242                  const struct ANASTASIS_UploadDetails *ud)
   3243 {
   3244   struct TruthUpload *tue = cls;
   3245 
   3246   tue->tu = NULL;
   3247   tue->t = t;
   3248   tue->ec = ud->ec;
   3249   tue->us = ud->us;
   3250   if (ANASTASIS_US_PAYMENT_REQUIRED == ud->us)
   3251   {
   3252     tue->payment_request = GNUNET_strdup (
   3253       ud->details.payment.payment_request);
   3254   }
   3255   check_upload_finished (tue->uc);
   3256 }
   3257 
   3258 
   3259 /**
   3260  * Check if we still need to create a new truth object for the truth
   3261  * identified by @a provider_url and @a am_idx. If so, create it from
   3262  * @a truth for policy reference @a pmr. If such a truth object
   3263  * already exists, append @a pmr to its list of reasons.
   3264  *
   3265  * @param[in,out] uc our upload context
   3266  * @param pmr policy method combination that requires the truth
   3267  * @param provider_url the URL of the Anastasis provider to upload
   3268  *                     the truth to, used to check for existing entries
   3269  * @param am_idx index of the authentication method, used to check for existing entries
   3270  * @param[in] truth object representing already uploaded truth, reference captured!
   3271  * @param[in,out] async_truth pointer to counter with the number of ongoing uploads,
   3272  *                updated
   3273  * @param auth_method object with the challenge details, to generate the truth
   3274  * @return #GNUNET_SYSERR error requiring abort,
   3275  *         #GNUNET_OK on success
   3276  */
   3277 static int
   3278 add_truth_object (struct UploadContext *uc,
   3279                   const struct PolicyMethodReference *pmr,
   3280                   const char *provider_url,
   3281                   uint32_t am_idx,
   3282                   const struct ANASTASIS_ReduxPolicyMethod *pm,
   3283                   unsigned int *async_truth,
   3284                   const struct ANASTASIS_ReduxAuthMethod *auth_method)
   3285 {
   3286   /* check if we are already uploading this truth */
   3287   struct TruthUpload *tue;
   3288   bool must_upload;
   3289 
   3290   for (tue = uc->tues_head;
   3291        NULL != tue;
   3292        tue = tue->next)
   3293   {
   3294     if ( (0 == strcmp (tue->provider_url,
   3295                        provider_url)) &&
   3296          (am_idx == tue->am_idx) )
   3297     {
   3298       GNUNET_array_append (tue->policies,
   3299                            tue->policies_length,
   3300                            *pmr);
   3301       break;
   3302     }
   3303   }
   3304 
   3305   if (NULL == tue)
   3306   {
   3307     /* Create new entry */
   3308     tue = GNUNET_new (struct TruthUpload);
   3309 
   3310     GNUNET_CONTAINER_DLL_insert (uc->tues_head,
   3311                                  uc->tues_tail,
   3312                                  tue);
   3313     tue->uc = uc;
   3314     tue->policies = GNUNET_new (struct PolicyMethodReference);
   3315     *tue->policies = *pmr;
   3316     tue->provider_url = GNUNET_strdup (provider_url);
   3317     tue->am_idx = am_idx;
   3318     tue->policies_length = 1;
   3319   }
   3320 
   3321   must_upload = (ANASTASIS_US_SUCCESS != pm->upload_status);
   3322 
   3323   if (NULL == tue->t)
   3324     tue->t = truth_dup (pm->truth);
   3325 
   3326   if ( (NULL != tue->tu) &&
   3327        (! must_upload) )
   3328   {
   3329     ANASTASIS_truth_upload_cancel (tue->tu);
   3330     (*async_truth)--;
   3331     tue->tu = NULL;
   3332     return GNUNET_OK;
   3333   }
   3334 
   3335   if ( (NULL == tue->tu) &&
   3336        (must_upload) )
   3337   {
   3338     struct ANASTASIS_CRYPTO_ProviderSaltP salt;
   3339     struct ANASTASIS_CRYPTO_UserIdentifierP id;
   3340 
   3341     if (GNUNET_OK !=
   3342         ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common,
   3343                                       provider_url,
   3344                                       &salt))
   3345     {
   3346       GNUNET_break (0);
   3347       return GNUNET_SYSERR;
   3348     }
   3349     if (NULL == uc->rs->common.identity_attributes)
   3350     {
   3351       GNUNET_break (0);
   3352       return GNUNET_SYSERR;
   3353     }
   3354     ANASTASIS_CRYPTO_user_identifier_derive (
   3355       uc->rs->common.identity_attributes,
   3356       &salt,
   3357       &id);
   3358     tue->tu = ANASTASIS_truth_upload3 (ANASTASIS_REDUX_ctx_,
   3359                                        &id,
   3360                                        tue->t,
   3361                                        auth_method->challenge,
   3362                                        auth_method->challenge_size,
   3363                                        uc->years,
   3364                                        uc->timeout,
   3365                                        &truth_upload_cb,
   3366                                        tue);
   3367     tue->t = NULL;
   3368     (*async_truth)++;
   3369   }
   3370 
   3371   if ( (NULL != tue->tu) &&
   3372        (NULL != tue->t) )
   3373   {
   3374     /* no point in having both */
   3375     ANASTASIS_truth_free (tue->t);
   3376     tue->t = NULL;
   3377   }
   3378   return GNUNET_OK;
   3379 }
   3380 
   3381 
   3382 /**
   3383  * Check if we still need to upload the truth identified by
   3384  * @a provider_url and @a am_idx. If so, upload it for
   3385  * policy reference @a pmr. If the upload is already queued,
   3386  * append @a pmr to its list of reasons.
   3387  *
   3388  * @param[in,out] uc our upload context
   3389  * @param pmr policy method combination that requires the truth
   3390  * @param provider_url the URL of the Anastasis provider to upload
   3391  *                     the truth to, used to check for existing entries
   3392  * @param am_idx index of the authentication method, used to check for existing entries
   3393  * @param auth_method object with the challenge details, to generate the truth
   3394  * @return #GNUNET_SYSERR on error requiring abort, in which case @a uc
   3395  *           has already been disposed of,
   3396  *         #GNUNET_NO if no new truth upload was generated (@a pmr was appended)
   3397  *         #GNUNET_OK if a new truth upload was initiated
   3398  */
   3399 static int
   3400 check_truth_upload (struct UploadContext *uc,
   3401                     const struct PolicyMethodReference *pmr,
   3402                     const char *provider_url,
   3403                     uint32_t am_idx,
   3404                     const struct ANASTASIS_ReduxAuthMethod *auth_method)
   3405 {
   3406   const json_t *user_id = uc->rs->common.identity_attributes;
   3407   struct TruthUpload *tue;
   3408 
   3409   if (NULL == user_id)
   3410   {
   3411     GNUNET_break (0);
   3412     upload_cancel_cb (uc);
   3413     return GNUNET_SYSERR;
   3414   }
   3415 
   3416   /* check if we are already uploading this truth */
   3417   for (tue = uc->tues_head;
   3418        NULL != tue;
   3419        tue = tue->next)
   3420   {
   3421     if ( (0 == strcmp (tue->provider_url,
   3422                        provider_url)) &&
   3423          (am_idx == tue->am_idx) )
   3424     {
   3425       GNUNET_array_append (tue->policies,
   3426                            tue->policies_length,
   3427                            *pmr);
   3428       return GNUNET_NO;
   3429     }
   3430   }
   3431 
   3432   /* need new upload */
   3433   tue = GNUNET_new (struct TruthUpload);
   3434   {
   3435     struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
   3436     struct ANASTASIS_CRYPTO_UserIdentifierP id;
   3437 
   3438     GNUNET_CONTAINER_DLL_insert (uc->tues_head,
   3439                                  uc->tues_tail,
   3440                                  tue);
   3441     tue->uc = uc;
   3442     tue->policies = GNUNET_new (struct PolicyMethodReference);
   3443     *tue->policies = *pmr;
   3444     tue->provider_url = GNUNET_strdup (provider_url);
   3445     tue->am_idx = am_idx;
   3446     tue->policies_length = 1;
   3447     if (GNUNET_OK !=
   3448         ANASTASIS_REDUX_lookup_salt_ (&uc->rs->common,
   3449                                       provider_url,
   3450                                       &provider_salt))
   3451     {
   3452       GNUNET_break (0);
   3453       upload_cancel_cb (uc);
   3454       return GNUNET_SYSERR;
   3455     }
   3456     ANASTASIS_CRYPTO_user_identifier_derive (user_id,
   3457                                              &provider_salt,
   3458                                              &id);
   3459     tue->tu = ANASTASIS_truth_upload (ANASTASIS_REDUX_ctx_,
   3460                                       &id,
   3461                                       provider_url,
   3462                                       auth_method->type,
   3463                                       auth_method->instructions,
   3464                                       auth_method->mime_type,
   3465                                       &provider_salt,
   3466                                       auth_method->challenge,
   3467                                       auth_method->challenge_size,
   3468                                       uc->years,
   3469                                       uc->timeout,
   3470                                       &truth_upload_cb,
   3471                                       tue);
   3472     if (NULL == tue->tu)
   3473     {
   3474       GNUNET_break (0);
   3475       upload_cancel_cb (uc);
   3476       return GNUNET_SYSERR;
   3477     }
   3478     return GNUNET_OK;
   3479   }
   3480 }
   3481 
   3482 
   3483 /**
   3484  * Function to upload truths and recovery document policies.
   3485  * Ultimately transitions to failed state (allowing user to go back
   3486  * and change providers/policies), or payment, or finished.
   3487  *
   3488  * @param state state to operate on
   3489  * @param cb callback (#ANASTASIS_ActionCallback) to call after upload
   3490  * @param cb_cls callback closure
   3491  */
   3492 static struct ANASTASIS_ReduxAction *
   3493 upload (struct ANASTASIS_ReduxState *rs,
   3494         ANASTASIS_ActionCallback cb,
   3495         void *cb_cls)
   3496 {
   3497   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   3498   struct UploadContext *uc;
   3499 
   3500   if (! b->have_expiration)
   3501   {
   3502     ANASTASIS_REDUX_fail_ (rs,
   3503                            cb,
   3504                            cb_cls,
   3505                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3506                            "'expiration' missing");
   3507     return NULL;
   3508   }
   3509   if (0 == b->authentication_methods_len)
   3510   {
   3511     ANASTASIS_REDUX_fail_ (rs,
   3512                            cb,
   3513                            cb_cls,
   3514                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3515                            "'authentication_methods' must be non-empty array");
   3516     return NULL;
   3517   }
   3518   if (0 == b->policies_len)
   3519   {
   3520     ANASTASIS_REDUX_fail_ (rs,
   3521                            cb,
   3522                            cb_cls,
   3523                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3524                            "'policies' must be non-empty array");
   3525     return NULL;
   3526   }
   3527 
   3528   uc = GNUNET_new (struct UploadContext);
   3529   uc->ra.cleanup = &upload_cancel_cb;
   3530   uc->ra.cleanup_cls = uc;
   3531   uc->cb = cb;
   3532   uc->cb_cls = cb_cls;
   3533   uc->rs = rs;
   3534   uc->years = expiration_to_years (b->expiration);
   3535   if (b->have_pay_arguments &&
   3536       b->pay_arguments.have_timeout)
   3537     uc->timeout = b->pay_arguments.timeout;
   3538 
   3539   {
   3540     unsigned int async_truth = 0;
   3541 
   3542     for (unsigned int pindex = 0; pindex < b->policies_len; pindex++)
   3543     {
   3544       const struct ANASTASIS_ReduxPolicy *policy = &b->policies[pindex];
   3545 
   3546       if (0 == policy->methods_len)
   3547       {
   3548         uc_fail (uc,
   3549                  TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3550                  "'policies' must be non-empty array");
   3551         return NULL;
   3552       }
   3553       for (unsigned int mindex = 0; mindex < policy->methods_len; mindex++)
   3554       {
   3555         const struct ANASTASIS_ReduxPolicyMethod *pm
   3556           = &policy->methods[mindex];
   3557         unsigned int am_idx = pm->authentication_method.idx;
   3558         struct PolicyMethodReference pmr = {
   3559           .policy_index = pindex,
   3560           .method_index = mindex
   3561         };
   3562         const struct ANASTASIS_ReduxAuthMethod *amj;
   3563         int ret;
   3564 
   3565         if (am_idx >= b->authentication_methods_len)
   3566         {
   3567           uc_fail (
   3568             uc,
   3569             TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3570             "'authentication_method' refers to invalid authorization index malformed");
   3571           return NULL;
   3572         }
   3573         amj = &b->authentication_methods[am_idx];
   3574         if (NULL == pm->truth)
   3575         {
   3576           ret = check_truth_upload (uc,
   3577                                     &pmr,
   3578                                     pm->provider.url,
   3579                                     am_idx,
   3580                                     amj);
   3581           if (GNUNET_SYSERR == ret)
   3582           {
   3583             /* check_truth_upload() already disposed of @a uc */
   3584             ANASTASIS_redux_fail_ (cb,
   3585                                    cb_cls,
   3586                                    TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3587                                    NULL);
   3588             return NULL;
   3589           }
   3590           if (GNUNET_OK == ret)
   3591             async_truth++;
   3592         }
   3593         else
   3594         {
   3595           ret = add_truth_object (uc,
   3596                                   &pmr,
   3597                                   pm->provider.url,
   3598                                   am_idx,
   3599                                   pm,
   3600                                   &async_truth,
   3601                                   amj);
   3602           if (GNUNET_SYSERR == ret)
   3603           {
   3604             uc_fail (uc,
   3605                      TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3606                      NULL);
   3607             return NULL;
   3608           }
   3609         }
   3610       } /* end for all methods of policy */
   3611     } /* end for all policies */
   3612     if (async_truth > 0)
   3613       return &uc->ra;
   3614   }
   3615   share_secret (uc);
   3616   if (NULL == uc->ss)
   3617     return NULL;
   3618   return &uc->ra;
   3619 }
   3620 
   3621 
   3622 /**
   3623  * Test if the core secret @a secret_size is small enough to be stored
   3624  * at all providers, which have a minimum upload limit of @a min_limit_in_mb.
   3625  *
   3626  * For now, we do not precisely calculate the size of the recovery document,
   3627  * and simply assume that the instructions (i.e. security questions) are all
   3628  * relatively small (aka sane), and that the number of authentication methods
   3629  * and recovery policies is similarly small so that all of this meta data
   3630  * fits in 512 kb (which is VERY big).
   3631  *
   3632  * Even with the minimum permitted upload limit of 1 MB (which is likely,
   3633  * given that there is hardly a reason for providers to offer more), this
   3634  * leaves 512 kb for the @a secret_size, which should be plenty (given
   3635  * that this is supposed to be for a master key, and not the actual data).
   3636  *
   3637  * @param rs our state, could be used in the future to calculate the
   3638  *        size of the recovery document without the core secret
   3639  * @param secret_size size of the core secret
   3640  * @param min_limit_in_mb minimum upload size of all providers
   3641  */
   3642 static bool
   3643 core_secret_fits (const struct ANASTASIS_ReduxState *rs,
   3644                   size_t secret_size,
   3645                   uint32_t min_limit_in_mb)
   3646 {
   3647   (void) rs;
   3648   return (min_limit_in_mb * 1024LL * 1024LL >
   3649           512LLU * 1024LLU + secret_size);
   3650 }
   3651 
   3652 
   3653 /**
   3654  * Check if the upload size limit is satisfied.
   3655  *
   3656  * @param rs our state
   3657  * @param jsecret the uploaded secret
   3658  * @return #GNUNET_OK if @a secret_size works for all providers,
   3659  *     #GNUNET_NO if the @a secret_size is too big,
   3660  *     #GNUNET_SYSERR if a provider has a limit of 0
   3661  */
   3662 static enum GNUNET_GenericReturnValue
   3663 check_upload_size_limit (const struct ANASTASIS_ReduxState *rs,
   3664                          const json_t *jsecret)
   3665 {
   3666   uint32_t min_limit = UINT32_MAX;
   3667   size_t secret_size;
   3668 
   3669   {
   3670     char *secret;
   3671 
   3672     secret = json_dumps (jsecret,
   3673                          JSON_COMPACT | JSON_SORT_KEYS);
   3674     GNUNET_assert (NULL != secret);
   3675     secret_size = strlen (secret);
   3676     GNUNET_free (secret);
   3677   }
   3678 
   3679   /* We calculate the minimum upload limit of all possible providers;
   3680      this is under the (simplified) assumption that we store the
   3681      recovery document at all providers; this may be changed later,
   3682      see #6760. */
   3683   for (unsigned int i = 0; i < rs->common.providers_len; i++)
   3684   {
   3685     const struct ANASTASIS_ReduxProvider *p = &rs->common.providers[i];
   3686 
   3687     if ( (ANASTASIS_RPS_OK != p->status) ||
   3688          (! p->have_config) ||
   3689          (MHD_HTTP_OK != p->config.http_status) )
   3690       continue;
   3691     if (0 == p->config.storage_limit_in_megabytes)
   3692       return GNUNET_SYSERR;
   3693     min_limit = GNUNET_MIN (min_limit,
   3694                             p->config.storage_limit_in_megabytes);
   3695   }
   3696   if (! core_secret_fits (rs,
   3697                           secret_size,
   3698                           min_limit))
   3699     return GNUNET_NO;
   3700   return GNUNET_OK;
   3701 }
   3702 
   3703 
   3704 /**
   3705  * DispatchHandler/Callback function which is called for a
   3706  * "enter_secret" action.
   3707  *
   3708  * @param state state to operate on
   3709  * @param arguments arguments to use for operation on state
   3710  * @param cb callback to call during/after operation
   3711  * @param cb_cls callback closure
   3712  * @return NULL
   3713  */
   3714 static struct ANASTASIS_ReduxAction *
   3715 enter_secret (struct ANASTASIS_ReduxState *rs,
   3716               const json_t *arguments,
   3717               ANASTASIS_ActionCallback cb,
   3718               void *cb_cls)
   3719 {
   3720   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   3721   const json_t *jsecret;
   3722   struct GNUNET_TIME_Timestamp expiration
   3723     = GNUNET_TIME_UNIT_ZERO_TS;
   3724   struct GNUNET_JSON_Specification spec[] = {
   3725     GNUNET_JSON_spec_object_const ("secret",
   3726                                    &jsecret),
   3727     GNUNET_JSON_spec_mark_optional (
   3728       GNUNET_JSON_spec_timestamp ("expiration",
   3729                                   &expiration),
   3730       NULL),
   3731     GNUNET_JSON_spec_end ()
   3732   };
   3733 
   3734   if (NULL == arguments)
   3735   {
   3736     ANASTASIS_REDUX_fail_ (rs,
   3737                            cb,
   3738                            cb_cls,
   3739                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3740                            "arguments missing");
   3741     return NULL;
   3742   }
   3743   if (GNUNET_OK !=
   3744       GNUNET_JSON_parse (arguments,
   3745                          spec,
   3746                          NULL, NULL))
   3747   {
   3748     ANASTASIS_REDUX_fail_ (rs,
   3749                            cb,
   3750                            cb_cls,
   3751                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3752                            "'secret' argument required");
   3753     return NULL;
   3754   }
   3755 
   3756   /* check upload size limit */
   3757   switch (check_upload_size_limit (rs,
   3758                                    jsecret))
   3759   {
   3760   case GNUNET_SYSERR:
   3761     ANASTASIS_REDUX_fail_ (rs,
   3762                            cb,
   3763                            cb_cls,
   3764                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3765                            "provider has an upload limit of 0");
   3766     return NULL;
   3767   case GNUNET_NO:
   3768     ANASTASIS_REDUX_fail_ (rs,
   3769                            cb,
   3770                            cb_cls,
   3771                            TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG,
   3772                            NULL);
   3773     return NULL;
   3774   default:
   3775     break;
   3776   }
   3777   if (! GNUNET_TIME_absolute_is_zero (expiration.abs_time))
   3778   {
   3779     if (GNUNET_OK !=
   3780         update_expiration_cost (rs,
   3781                                 expiration))
   3782     {
   3783       ANASTASIS_REDUX_fail_ (rs,
   3784                              cb,
   3785                              cb_cls,
   3786                              TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   3787                              "could not calculate expiration cost");
   3788       return NULL;
   3789     }
   3790   }
   3791   json_decref (b->core_secret);
   3792   b->core_secret = json_incref ((json_t *) jsecret);
   3793   ANASTASIS_REDUX_return_ (rs,
   3794                            cb,
   3795                            cb_cls,
   3796                            TALER_EC_NONE);
   3797   return NULL;
   3798 }
   3799 
   3800 
   3801 /**
   3802  * DispatchHandler/Callback function which is called for a
   3803  * "clear_secret" action.
   3804  *
   3805  * @param state state to operate on
   3806  * @param arguments arguments to use for operation on state
   3807  * @param cb callback to call during/after operation
   3808  * @param cb_cls callback closure
   3809  * @return NULL
   3810  */
   3811 static struct ANASTASIS_ReduxAction *
   3812 clear_secret (struct ANASTASIS_ReduxState *rs,
   3813               const json_t *arguments,
   3814               ANASTASIS_ActionCallback cb,
   3815               void *cb_cls)
   3816 {
   3817   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   3818 
   3819   if (NULL == b->core_secret)
   3820   {
   3821     ANASTASIS_REDUX_fail_ (rs,
   3822                            cb,
   3823                            cb_cls,
   3824                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3825                            "'core_secret' not set");
   3826     return NULL;
   3827   }
   3828   json_decref (b->core_secret);
   3829   b->core_secret = NULL;
   3830   ANASTASIS_REDUX_return_ (rs,
   3831                            cb,
   3832                            cb_cls,
   3833                            TALER_EC_NONE);
   3834   return NULL;
   3835 }
   3836 
   3837 
   3838 /**
   3839  * DispatchHandler/Callback function which is called for an
   3840  * "enter_secret_name" action.
   3841  *
   3842  * @param state state to operate on
   3843  * @param arguments arguments to use for operation on state
   3844  * @param cb callback to call during/after operation
   3845  * @param cb_cls callback closure
   3846  * @return NULL
   3847  */
   3848 static struct ANASTASIS_ReduxAction *
   3849 enter_secret_name (struct ANASTASIS_ReduxState *rs,
   3850                    const json_t *arguments,
   3851                    ANASTASIS_ActionCallback cb,
   3852                    void *cb_cls)
   3853 {
   3854   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   3855   const char *secret_name = NULL;
   3856   struct GNUNET_JSON_Specification spec[] = {
   3857     GNUNET_JSON_spec_string ("name",
   3858                              &secret_name),
   3859     GNUNET_JSON_spec_end ()
   3860   };
   3861 
   3862   if (NULL == arguments)
   3863   {
   3864     ANASTASIS_REDUX_fail_ (rs,
   3865                            cb,
   3866                            cb_cls,
   3867                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3868                            "arguments missing");
   3869     return NULL;
   3870   }
   3871   if (GNUNET_OK !=
   3872       GNUNET_JSON_parse (arguments,
   3873                          spec,
   3874                          NULL, NULL))
   3875   {
   3876     ANASTASIS_REDUX_fail_ (rs,
   3877                            cb,
   3878                            cb_cls,
   3879                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3880                            "'name' argument required");
   3881     return NULL;
   3882   }
   3883   GNUNET_free (b->secret_name);
   3884   b->secret_name = GNUNET_strdup (secret_name);
   3885   ANASTASIS_REDUX_return_ (rs,
   3886                            cb,
   3887                            cb_cls,
   3888                            TALER_EC_NONE);
   3889   return NULL;
   3890 }
   3891 
   3892 
   3893 /**
   3894  * DispatchHandler/Callback function which is called for the
   3895  * "update_expiration" action in the "secret editing" state.
   3896  * Updates how long we are to store the truth and policies
   3897  * and computes the new cost.
   3898  *
   3899  * @param state state to operate on
   3900  * @param arguments arguments to use for operation on state
   3901  * @param cb callback to call during/after operation
   3902  * @param cb_cls callback closure
   3903  * @return NULL (synchronous operation)
   3904  */
   3905 static struct ANASTASIS_ReduxAction *
   3906 update_expiration (struct ANASTASIS_ReduxState *rs,
   3907                    const json_t *arguments,
   3908                    ANASTASIS_ActionCallback cb,
   3909                    void *cb_cls)
   3910 {
   3911   struct GNUNET_TIME_Timestamp expiration;
   3912   struct GNUNET_JSON_Specification spec[] = {
   3913     GNUNET_JSON_spec_timestamp ("expiration",
   3914                                 &expiration),
   3915     GNUNET_JSON_spec_end ()
   3916   };
   3917 
   3918   if (NULL == arguments)
   3919   {
   3920     GNUNET_break (0);
   3921     ANASTASIS_REDUX_fail_ (rs,
   3922                            cb,
   3923                            cb_cls,
   3924                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3925                            "arguments missing");
   3926     return NULL;
   3927   }
   3928   if (GNUNET_OK !=
   3929       GNUNET_JSON_parse (arguments,
   3930                          spec,
   3931                          NULL, NULL))
   3932   {
   3933     GNUNET_break (0);
   3934     ANASTASIS_REDUX_fail_ (rs,
   3935                            cb,
   3936                            cb_cls,
   3937                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3938                            "'expiration' argument required");
   3939     return NULL;
   3940   }
   3941   if (GNUNET_OK !=
   3942       update_expiration_cost (rs,
   3943                               expiration))
   3944   {
   3945     GNUNET_break (0);
   3946     ANASTASIS_REDUX_fail_ (rs,
   3947                            cb,
   3948                            cb_cls,
   3949                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
   3950                            "could not calculate expiration cost");
   3951     return NULL;
   3952   }
   3953   ANASTASIS_REDUX_return_ (rs,
   3954                            cb,
   3955                            cb_cls,
   3956                            TALER_EC_NONE);
   3957   return NULL;
   3958 }
   3959 
   3960 
   3961 /**
   3962  * DispatchHandler/Callback function which is called for the
   3963  * "next" action in the "secret editing" state.
   3964  * Returns an #ANASTASIS_ReduxAction as operation is async.
   3965  *
   3966  * @param state state to operate on
   3967  * @param arguments arguments to use for operation on state
   3968  * @param cb callback to call during/after operation
   3969  * @param cb_cls callback closure
   3970  */
   3971 static struct ANASTASIS_ReduxAction *
   3972 finish_secret (struct ANASTASIS_ReduxState *rs,
   3973                const json_t *arguments,
   3974                ANASTASIS_ActionCallback cb,
   3975                void *cb_cls)
   3976 {
   3977   const struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   3978 
   3979   if (NULL == b->core_secret)
   3980   {
   3981     ANASTASIS_REDUX_fail_ (rs,
   3982                            cb,
   3983                            cb_cls,
   3984                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   3985                            "State parsing failed: 'core_secret' is missing");
   3986     return NULL;
   3987   }
   3988 
   3989   /* check upload size limit */
   3990   switch (check_upload_size_limit (rs,
   3991                                    b->core_secret))
   3992   {
   3993   case GNUNET_SYSERR:
   3994     ANASTASIS_REDUX_fail_ (rs,
   3995                            cb,
   3996                            cb_cls,
   3997                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   3998                            "provider has an upload limit of 0");
   3999     return NULL;
   4000   case GNUNET_NO:
   4001     ANASTASIS_REDUX_fail_ (rs,
   4002                            cb,
   4003                            cb_cls,
   4004                            TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG,
   4005                            NULL);
   4006     return NULL;
   4007   default:
   4008     break;
   4009   }
   4010   return upload (rs,
   4011                  cb,
   4012                  cb_cls);
   4013 }
   4014 
   4015 
   4016 /**
   4017  * Remember the arguments of a "pay" action in @a rs, so that they are
   4018  * still around when the upload is retried after the payment.
   4019  *
   4020  * @param[in,out] rs state to update
   4021  * @param arguments arguments of the action, may be NULL
   4022  * @param cb callback to report a failure to
   4023  * @param cb_cls closure for @a cb
   4024  * @return false if @a arguments were malformed, in which case @a cb has
   4025  *         been invoked and @a rs freed
   4026  */
   4027 static bool
   4028 set_pay_arguments (struct ANASTASIS_ReduxState *rs,
   4029                    const json_t *arguments,
   4030                    ANASTASIS_ActionCallback cb,
   4031                    void *cb_cls)
   4032 {
   4033   struct ANASTASIS_ReduxBackup *b = &rs->details.backup;
   4034   bool no_timeout;
   4035   struct GNUNET_JSON_Specification spec[] = {
   4036     GNUNET_JSON_spec_mark_optional (
   4037       GNUNET_JSON_spec_relative_time ("timeout",
   4038                                       &b->pay_arguments.timeout),
   4039       &no_timeout),
   4040     GNUNET_JSON_spec_end ()
   4041   };
   4042 
   4043   if (NULL == arguments)
   4044     return true;
   4045   if (GNUNET_OK !=
   4046       GNUNET_JSON_parse (arguments,
   4047                          spec,
   4048                          NULL, NULL))
   4049   {
   4050     json_dumpf ((json_t *) arguments,
   4051                 stderr,
   4052                 JSON_INDENT (2));
   4053     GNUNET_break (0);
   4054     ANASTASIS_REDUX_fail_ (rs,
   4055                            cb,
   4056                            cb_cls,
   4057                            TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
   4058                            "'timeout' must be valid delay");
   4059     return false;
   4060   }
   4061   b->have_pay_arguments = true;
   4062   b->pay_arguments.have_timeout = ! no_timeout;
   4063   return true;
   4064 }
   4065 
   4066 
   4067 /**
   4068  * DispatchHandler/Callback function which is called for a
   4069  * "pay" action.
   4070  * Returns an #ANASTASIS_ReduxAction as operation is async.
   4071  *
   4072  * @param[in] rs state to operate on
   4073  * @param arguments arguments to use for operation on state
   4074  * @param cb callback to call during/after operation
   4075  * @param cb_cls callback closure
   4076  */
   4077 static struct ANASTASIS_ReduxAction *
   4078 pay_truths_backup (struct ANASTASIS_ReduxState *rs,
   4079                    const json_t *arguments,
   4080                    ANASTASIS_ActionCallback cb,
   4081                    void *cb_cls)
   4082 {
   4083   /* Clear 'payments' if it exists */
   4084   ANASTASIS_REDUX_payments_clear_ (&rs->details.backup);
   4085   if (! set_pay_arguments (rs,
   4086                            arguments,
   4087                            cb,
   4088                            cb_cls))
   4089     return NULL;
   4090   return upload (rs,
   4091                  cb,
   4092                  cb_cls);
   4093 }
   4094 
   4095 
   4096 /**
   4097  * DispatchHandler/Callback function which is called for a
   4098  * "pay" action.
   4099  * Returns an #ANASTASIS_ReduxAction as operation is async.
   4100  *
   4101  * @param state state to operate on
   4102  * @param arguments arguments to use for operation on state
   4103  * @param cb callback to call during/after operation
   4104  * @param cb_cls callback closure
   4105  */
   4106 static struct ANASTASIS_ReduxAction *
   4107 pay_policies_backup (struct ANASTASIS_ReduxState *rs,
   4108                      const json_t *arguments,
   4109                      ANASTASIS_ActionCallback cb,
   4110                      void *cb_cls)
   4111 {
   4112   /* Clear 'policy_payment_requests' if it exists */
   4113   ANASTASIS_REDUX_policy_payment_requests_clear_ (&rs->details.backup);
   4114   if (! set_pay_arguments (rs,
   4115                            arguments,
   4116                            cb,
   4117                            cb_cls))
   4118     return NULL;
   4119   return upload (rs,
   4120                  cb,
   4121                  cb_cls);
   4122 }
   4123 
   4124 
   4125 /**
   4126  * DispatchHandler/Callback function which is called for a
   4127  * "back" action if state is "FINISHED".
   4128  *
   4129  * @param state state to operate on
   4130  * @param arguments arguments to use for operation on state
   4131  * @param cb callback to call during/after operation
   4132  * @param cb_cls callback closure
   4133  * @return NULL
   4134  */
   4135 static struct ANASTASIS_ReduxAction *
   4136 back_finished (struct ANASTASIS_ReduxState *rs,
   4137                const json_t *arguments,
   4138                ANASTASIS_ActionCallback cb,
   4139                void *cb_cls)
   4140 {
   4141   set_state (rs,
   4142              ANASTASIS_BACKUP_STATE_SECRET_EDITING);
   4143   ANASTASIS_REDUX_return_ (rs,
   4144                            cb,
   4145                            cb_cls,
   4146                            TALER_EC_NONE);
   4147   return NULL;
   4148 }
   4149 
   4150 
   4151 /**
   4152  * Signature of callback function that implements a state transition.
   4153  *
   4154  *  @param[in] rs current state
   4155  *  @param arguments arguments for the state transition
   4156  *  @param cb function to call when done
   4157  *  @param cb_cls closure for @a cb
   4158  */
   4159 typedef struct ANASTASIS_ReduxAction *
   4160 (*DispatchHandler)(struct ANASTASIS_ReduxState *rs,
   4161                    const json_t *arguments,
   4162                    ANASTASIS_ActionCallback cb,
   4163                    void *cb_cls);
   4164 
   4165 
   4166 struct ANASTASIS_ReduxAction *
   4167 ANASTASIS_backup_action_ (struct ANASTASIS_ReduxState *rs,
   4168                           const char *action,
   4169                           const json_t *arguments,
   4170                           ANASTASIS_ActionCallback cb,
   4171                           void *cb_cls)
   4172 {
   4173   struct Dispatcher
   4174   {
   4175     enum ANASTASIS_BackupState backup_state;
   4176     const char *backup_action;
   4177     DispatchHandler fun;
   4178   } dispatchers[] = {
   4179     {
   4180       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4181       "add_authentication",
   4182       &add_authentication
   4183     },
   4184     {
   4185       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4186       "delete_authentication",
   4187       &del_authentication
   4188     },
   4189     {
   4190       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4191       "next",
   4192       &done_authentication
   4193     },
   4194     {
   4195       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4196       "add_provider",
   4197       &add_provider
   4198     },
   4199     {
   4200       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4201       "poll_providers",
   4202       &ANASTASIS_REDUX_poll_providers_
   4203     },
   4204     {
   4205       ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
   4206       "back",
   4207       &ANASTASIS_back_generic_decrement_
   4208     },
   4209     {
   4210       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4211       "add_policy",
   4212       &add_policy
   4213     },
   4214     {
   4215       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4216       "update_policy",
   4217       &update_policy
   4218     },
   4219     {
   4220       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4221       "delete_policy",
   4222       &del_policy
   4223     },
   4224     {
   4225       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4226       "delete_challenge",
   4227       &del_challenge
   4228     },
   4229     {
   4230       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4231       "next",
   4232       &done_policy_review
   4233     },
   4234     {
   4235       ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING,
   4236       "back",
   4237       &ANASTASIS_back_generic_decrement_
   4238     },
   4239     {
   4240       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4241       "enter_secret",
   4242       &enter_secret
   4243     },
   4244     {
   4245       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4246       "clear_secret",
   4247       &clear_secret
   4248     },
   4249     {
   4250       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4251       "enter_secret_name",
   4252       &enter_secret_name
   4253     },
   4254     {
   4255       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4256       "back",
   4257       &ANASTASIS_back_generic_decrement_
   4258     },
   4259     {
   4260       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4261       "update_expiration",
   4262       &update_expiration
   4263     },
   4264     {
   4265       ANASTASIS_BACKUP_STATE_SECRET_EDITING,
   4266       "next",
   4267       &finish_secret
   4268     },
   4269     {
   4270       ANASTASIS_BACKUP_STATE_TRUTHS_PAYING,
   4271       "pay",
   4272       &pay_truths_backup
   4273     },
   4274     {
   4275       ANASTASIS_BACKUP_STATE_POLICIES_PAYING,
   4276       "pay",
   4277       &pay_policies_backup
   4278     },
   4279     {
   4280       ANASTASIS_BACKUP_STATE_BACKUP_FINISHED,
   4281       "back",
   4282       &back_finished
   4283     },
   4284     { ANASTASIS_BACKUP_STATE_INVALID, NULL, NULL }
   4285   };
   4286   enum ANASTASIS_BackupState bs = rs->details.backup.state;
   4287 
   4288   GNUNET_assert (ANASTASIS_RT_BACKUP == rs->type);
   4289   for (unsigned int i = 0; NULL != dispatchers[i].fun; i++)
   4290   {
   4291     if ( (bs == dispatchers[i].backup_state) &&
   4292          (0 == strcmp (action,
   4293                        dispatchers[i].backup_action)) )
   4294     {
   4295       return dispatchers[i].fun (rs,
   4296                                  arguments,
   4297                                  cb,
   4298                                  cb_cls);
   4299     }
   4300   }
   4301   ANASTASIS_REDUX_fail_ (rs,
   4302                          cb,
   4303                          cb_cls,
   4304                          TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
   4305                          action);
   4306   return NULL;
   4307 }
   4308 
   4309 
   4310 /**
   4311  * State for a #ANASTASIS_REDUX_backup_begin_() operation.
   4312  */
   4313 struct BackupStartState;
   4314 
   4315 
   4316 /**
   4317  * Entry in the list of all known applicable Anastasis providers.
   4318  * Used to wait for it to complete downloading /config.
   4319  */
   4320 struct BackupStartStateProviderEntry
   4321 {
   4322   /**
   4323    * Kept in a DLL.
   4324    */
   4325   struct BackupStartStateProviderEntry *next;
   4326 
   4327   /**
   4328    * Kept in a DLL.
   4329    */
   4330   struct BackupStartStateProviderEntry *prev;
   4331 
   4332   /**
   4333    * Main operation this entry is part of.
   4334    */
   4335   struct BackupStartState *bss;
   4336 
   4337   /**
   4338    * Ongoing reducer action to obtain /config, NULL if completed.
   4339    */
   4340   struct ANASTASIS_ReduxAction *ra;
   4341 
   4342   /**
   4343    * Final result of the operation (once completed).
   4344    */
   4345   enum TALER_ErrorCode ec;
   4346 };
   4347 
   4348 
   4349 struct BackupStartState
   4350 {
   4351   /**
   4352    * Head of list of provider /config operations we are doing.
   4353    */
   4354   struct BackupStartStateProviderEntry *pe_head;
   4355 
   4356   /**
   4357    * Tail of list of provider /config operations we are doing.
   4358    */
   4359   struct BackupStartStateProviderEntry *pe_tail;
   4360 
   4361   /**
   4362    * State we are updating; we own it.  All our entries update it in
   4363    * place, so there is nothing to merge once they are done.
   4364    */
   4365   struct ANASTASIS_ReduxState *rs;
   4366 
   4367   /**
   4368    * Function to call when we are done.
   4369    */
   4370   ANASTASIS_ActionCallback cb;
   4371 
   4372   /**
   4373    * Closure for @e cb.
   4374    */
   4375   void *cb_cls;
   4376 
   4377   /**
   4378    * Redux action we returned to our controller.
   4379    */
   4380   struct ANASTASIS_ReduxAction ra;
   4381 
   4382   /**
   4383    * Number of provider /config operations in @e ba_head that
   4384    * are still awaiting completion.
   4385    */
   4386   unsigned int pending;
   4387 };
   4388 
   4389 
   4390 /**
   4391  * The backup start operation is being aborted, terminate.
   4392  *
   4393  * @param cls a `struct BackupStartState *`
   4394  */
   4395 static void
   4396 abort_backup_begin_cb (void *cls)
   4397 {
   4398   struct BackupStartState *bss = cls;
   4399   struct BackupStartStateProviderEntry *pe;
   4400 
   4401   while (NULL != (pe = bss->pe_head))
   4402   {
   4403     GNUNET_CONTAINER_DLL_remove (bss->pe_head,
   4404                                  bss->pe_tail,
   4405                                  pe);
   4406     if (NULL != pe->ra)
   4407       pe->ra->cleanup (pe->ra->cleanup_cls);
   4408     GNUNET_free (pe);
   4409   }
   4410   ANASTASIS_REDUX_state_free_ (bss->rs);
   4411   GNUNET_free (bss);
   4412 }
   4413 
   4414 
   4415 /**
   4416  * We finished downloading /config from all providers, trigger the
   4417  * continuation and free our state.
   4418  *
   4419  * @param[in] bss main state to return
   4420  */
   4421 static void
   4422 providers_complete (struct BackupStartState *bss)
   4423 {
   4424   struct ANASTASIS_ReduxState *rs = bss->rs;
   4425   ANASTASIS_ActionCallback cb = bss->cb;
   4426   void *cb_cls = bss->cb_cls;
   4427 
   4428   bss->rs = NULL;
   4429   abort_backup_begin_cb (bss);
   4430   ANASTASIS_REDUX_return_ (rs,
   4431                            cb,
   4432                            cb_cls,
   4433                            TALER_EC_NONE);
   4434 }
   4435 
   4436 
   4437 /**
   4438  * Function called when the complete information about a provider
   4439  * was added to the state.
   4440  *
   4441  * @param cls a `struct BackupStartStateProviderEntry`
   4442  * @param error error code
   4443  * @param rs the state, updated in place
   4444  */
   4445 static void
   4446 provider_added_cb (void *cls,
   4447                    enum TALER_ErrorCode error,
   4448                    struct ANASTASIS_ReduxState *rs)
   4449 {
   4450   struct BackupStartStateProviderEntry *pe = cls;
   4451   struct BackupStartState *bss = pe->bss;
   4452 
   4453   GNUNET_assert (rs == bss->rs);
   4454   /* our waiter unlinks and frees itself right after this call */
   4455   pe->ra = NULL;
   4456   GNUNET_CONTAINER_DLL_remove (bss->pe_head,
   4457                                bss->pe_tail,
   4458                                pe);
   4459   pe->ec = error;
   4460   GNUNET_free (pe);
   4461   bss->pending--;
   4462   if (0 == bss->pending)
   4463     providers_complete (bss);
   4464 }
   4465 
   4466 
   4467 struct ANASTASIS_ReduxAction *
   4468 ANASTASIS_REDUX_backup_begin_ (struct ANASTASIS_ReduxState *rs,
   4469                                const json_t *arguments,
   4470                                ANASTASIS_ActionCallback cb,
   4471                                void *cb_cls)
   4472 {
   4473   struct BackupStartState *bss;
   4474 
   4475   if (! rs->common.have_providers)
   4476   {
   4477     GNUNET_break (0);
   4478     ANASTASIS_REDUX_fail_ (rs,
   4479                            cb,
   4480                            cb_cls,
   4481                            TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
   4482                            "'authentication_providers' missing");
   4483     return NULL;
   4484   }
   4485   bss = GNUNET_new (struct BackupStartState);
   4486   bss->rs = rs;
   4487   bss->cb = cb;
   4488   bss->cb_cls = cb_cls;
   4489   bss->ra.cleanup_cls = bss;
   4490   bss->ra.cleanup = &abort_backup_begin_cb;
   4491   bss->pending = 1; /* decremented after initialization loop */
   4492 
   4493   {
   4494     /* The /config replies add providers to the state, so take a
   4495        snapshot of the URLs to query before starting any of them. */
   4496     unsigned int len = rs->common.providers_len;
   4497     char *urls[GNUNET_NZL (len)];
   4498 
   4499     for (unsigned int i = 0; i < len; i++)
   4500       urls[i] = (ANASTASIS_RPS_DISABLED == rs->common.providers[i].status)
   4501                 ? NULL
   4502                 : GNUNET_strdup (rs->common.providers[i].url.url);
   4503     for (unsigned int i = 0; i < len; i++)
   4504     {
   4505       struct BackupStartStateProviderEntry *pe;
   4506 
   4507       if (NULL == urls[i])
   4508         continue;
   4509       pe = GNUNET_new (struct BackupStartStateProviderEntry);
   4510       pe->bss = bss;
   4511       GNUNET_CONTAINER_DLL_insert (bss->pe_head,
   4512                                    bss->pe_tail,
   4513                                    pe);
   4514       bss->pending++;
   4515       pe->ra = ANASTASIS_REDUX_add_provider_to_state_ (urls[i],
   4516                                                        rs,
   4517                                                        &provider_added_cb,
   4518                                                        pe);
   4519       GNUNET_assert (NULL != pe->ra);
   4520     }
   4521     for (unsigned int i = 0; i < len; i++)
   4522       GNUNET_free (urls[i]);
   4523   }
   4524   bss->pending--;
   4525   if (0 == bss->pending)
   4526   {
   4527     providers_complete (bss);
   4528     return NULL;
   4529   }
   4530   return &bss->ra;
   4531 }