anastasis

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

anastasis_api_backup_redux.c (123469B)


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