anastasis

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

anastasis_api_backup_redux.c (146531B)


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