anastasis-gtk

Demonstrator GUI for Anastasis
Log | Files | Refs | README | LICENSE

anastasis-gtk_action.c (105290B)


      1 /*
      2      This file is part of anastasis-gtk.
      3      Copyright (C) 2020, 2021, 2022 Anastasis SARL
      4 
      5      Anastasis is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      Anastasis is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with Anastasis; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 /**
     21  * @file src/anastasis/anastasis-gtk_action.c
     22  * @brief Handle redux action results
     23  * @author Christian Grothoff
     24  * @author Dennis Neufeld
     25  */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <strings.h>
     28 #include "anastasis_gtk_util.h"
     29 #include "anastasis-gtk_action.h"
     30 #include "anastasis-gtk_attributes.h"
     31 #include "anastasis-gtk_dispatch.h"
     32 #include "anastasis-gtk_helper.h"
     33 #include "anastasis-gtk_handle-identity-changed.h"
     34 #include "anastasis-gtk_progress.h"
     35 #include "anastasis-gtk_rows.h"
     36 #include <jansson.h>
     37 #include <qrencode.h>
     38 #include <gdk-pixbuf/gdk-pixbuf.h>
     39 
     40 
     41 /**
     42  * After how long does our long-poller time out?
     43  */
     44 #define LP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
     45 
     46 /**
     47  * Are we currently processing an action?
     48  */
     49 bool AG_in_action;
     50 
     51 /**
     52  * Are we currently editing the secret?
     53  */
     54 bool AG_in_secret_editing;
     55 
     56 /**
     57  * Are we currently editing the secret name?
     58  */
     59 bool AG_in_secret_name_editing;
     60 
     61 
     62 #define DEBUG 0
     63 
     64 
     65 /**
     66  * Add space on both sides of @a child along the orientation of the box it is
     67  * about to be appended to.  This is what the `padding` argument of GTK3's
     68  * gtk_box_pack_start() used to do; GTK4 boxes only know margins.
     69  *
     70  * @param orientation orientation of the box @a child goes into
     71  * @param child widget to pad
     72  * @param padding number of pixels to add on either side
     73  */
     74 static void
     75 pad_along (GtkOrientation orientation,
     76            GtkWidget *child,
     77            int padding)
     78 {
     79   if (GTK_ORIENTATION_HORIZONTAL == orientation)
     80   {
     81     gtk_widget_set_margin_start (child,
     82                                  padding);
     83     gtk_widget_set_margin_end (child,
     84                                padding);
     85   }
     86   else
     87   {
     88     gtk_widget_set_margin_top (child,
     89                                padding);
     90     gtk_widget_set_margin_bottom (child,
     91                                   padding);
     92   }
     93 }
     94 
     95 
     96 /**
     97  * Lookup provider name by provider URL in our state.
     98  *
     99  * @param provider_url URL to lookup name for
    100  * @return provider name, or if unknown, the @a provider_url
    101  */
    102 static const char *
    103 lookup_provider_name (const char *provider_url)
    104 {
    105   json_t *providers;
    106   json_t *provider;
    107   const char *name = NULL;
    108   struct GNUNET_JSON_Specification spec[] = {
    109     GNUNET_JSON_spec_mark_optional (
    110       GNUNET_JSON_spec_string ("business_name",
    111                                &name),
    112       NULL),
    113     GNUNET_JSON_spec_end ()
    114   };
    115 
    116   providers = json_object_get (AG_redux_state,
    117                                "authentication_providers");
    118   provider = json_object_get (providers,
    119                               provider_url);
    120   if (NULL == provider)
    121     return provider_url;
    122   if (GNUNET_OK !=
    123       GNUNET_JSON_parse (provider,
    124                          spec,
    125                          NULL, NULL))
    126   {
    127     GNUNET_break (0);
    128     json_dumpf (provider,
    129                 stderr,
    130                 JSON_INDENT (2));
    131     return provider_url;
    132   }
    133   return name;
    134 }
    135 
    136 
    137 /**
    138  * Set country selection next-button sensitivity based on
    139  * whether a country is currently selected.
    140  */
    141 static void
    142 set_country_selection_next (void)
    143 {
    144   if (NULL != AG_selected_row ("anastasis_gtk_country_selection"))
    145     AG_enable_next ();
    146   else
    147     AG_insensitive ("anastasis_gtk_main_window_forward_button");
    148 }
    149 
    150 
    151 /**
    152  * Prepare window for selection of the continent.
    153  */
    154 static void
    155 action_continent_selecting (void)
    156 {
    157   AG_hide_all_frames ();
    158   AG_list_clear ("country_liststore");
    159   {
    160     json_t *continents;
    161 
    162     AG_list_clear ("continent_liststore");
    163     continents = json_object_get (AG_redux_state,
    164                                   "continents");
    165     if (NULL != continents)
    166     {
    167       json_t *continent;
    168       size_t index;
    169 
    170       json_array_foreach (continents,
    171                           index,
    172                           continent)
    173       {
    174         const char *name;
    175         struct GNUNET_JSON_Specification spec[] = {
    176           GNUNET_JSON_spec_string ("name",
    177                                    &name),
    178           GNUNET_JSON_spec_end ()
    179         };
    180 
    181         if (GNUNET_OK !=
    182             GNUNET_JSON_parse (continent,
    183                                spec,
    184                                NULL, NULL))
    185         {
    186           GNUNET_break (0);
    187           GNUNET_JSON_parse_free (spec);
    188           continue;
    189         }
    190 
    191         AG_list_add ("continent_liststore",
    192                      GNUNET_JSON_PACK (
    193                        GNUNET_JSON_pack_string ("name",
    194                                                 name),
    195                        GNUNET_JSON_pack_string ("name_i18n",
    196                                                 dgettext ("anastasis",
    197                                                           name))));
    198         GNUNET_JSON_parse_free (spec);
    199       }
    200     }
    201   }
    202 
    203   AG_sensitive ("anastasis_gtk_main_window_prev_button");
    204   AG_insensitive ("anastasis_gtk_main_window_forward_button");
    205   AG_show ("anastasis_gtk_progress_vbox");
    206   AG_progress_update ();
    207   if (NULL != json_object_get (AG_redux_state,
    208                                "backup_state"))
    209   {
    210     AG_show ("anastasis_gtk_backup_progress_scrolled_window");
    211     AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
    212   }
    213   else
    214   {
    215     AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
    216     AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
    217   }
    218   AG_show ("anastasis_gtk_main_window_prev_button");
    219   set_country_selection_next ();
    220   AG_show ("anastasis_gtk_main_control_vbox");
    221   AG_show ("anastasis_gtk_continent_frame");
    222 }
    223 
    224 
    225 /**
    226  * Prepare window for selection of the country.
    227  */
    228 static void
    229 action_country_selecting (void)
    230 {
    231   json_t *countries;
    232   const char *selected_country;
    233 
    234   AG_hide_all_frames ();
    235   countries = json_object_get (AG_redux_state,
    236                                "countries");
    237   selected_country
    238     = json_string_value (json_object_get (AG_redux_state,
    239                                           "selected_country"));
    240   AG_list_clear ("country_liststore");
    241   {
    242     json_t *country;
    243     size_t index;
    244     guint position = 0;
    245 
    246     json_array_foreach (countries, index, country)
    247     {
    248       const char *code;
    249       const char *name;
    250       struct GNUNET_JSON_Specification spec[] = {
    251         GNUNET_JSON_spec_string ("code",
    252                                  &code),
    253         GNUNET_JSON_spec_string ("name",
    254                                  &name),
    255         GNUNET_JSON_spec_end ()
    256       };
    257 
    258       if (GNUNET_OK !=
    259           GNUNET_JSON_parse (country,
    260                              spec,
    261                              NULL, NULL))
    262       {
    263         GNUNET_break (0);
    264         GNUNET_JSON_parse_free (spec);
    265         continue;
    266       }
    267 
    268       AG_list_add ("country_liststore",
    269                    GNUNET_JSON_PACK (
    270                      GNUNET_JSON_pack_string ("name",
    271                                               dgettext ("anastasis",
    272                                                         name)),
    273                      GNUNET_JSON_pack_string ("code",
    274                                               code)));
    275       if ( (NULL != selected_country) &&
    276            (NULL != code) &&
    277            (0 == strcmp (code,
    278                          selected_country)) )
    279         AG_select_row ("anastasis_gtk_country_selection",
    280                        position);
    281       position++;
    282       GNUNET_JSON_parse_free (spec);
    283     }
    284   }
    285 
    286   AG_sensitive ("anastasis_gtk_main_window_prev_button");
    287   AG_insensitive ("anastasis_gtk_main_window_forward_button");
    288   AG_show ("anastasis_gtk_main_control_vbox");
    289   AG_show ("anastasis_gtk_progress_vbox");
    290   AG_progress_update ();
    291   if (NULL != json_object_get (AG_redux_state,
    292                                "backup_state"))
    293   {
    294     AG_show ("anastasis_gtk_backup_progress_scrolled_window");
    295     AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
    296   }
    297   else
    298   {
    299     AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
    300     AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
    301   }
    302   AG_show ("anastasis_gtk_main_window_prev_button");
    303 
    304   set_country_selection_next ();
    305 
    306   AG_show ("anastasis_gtk_continent_frame");
    307 }
    308 
    309 
    310 /**
    311  * Create widget for "string" type user attributes.
    312  *
    313  * @param details not used
    314  * @return widget to be used for string entry
    315  */
    316 static GtkWidget *
    317 ctor_entry (const json_t *details)
    318 {
    319   (void) details;
    320   return gtk_entry_new ();
    321 }
    322 
    323 
    324 /**
    325  * Create widget for "date" type user attributes.
    326  *
    327  * @param details not used
    328  * @return widget to be used for date entry
    329  */
    330 static GtkWidget *
    331 ctor_date (const json_t *details)
    332 {
    333   (void) details;
    334   return gtk_calendar_new ();
    335 }
    336 
    337 
    338 /**
    339  * Create widget of @a type under @a uuid with @a label and @a tooltip
    340  * for the identity attribute editing dialog.  Stores all created widgets
    341  * in the #AG_entry_attributes and ensures that we never create the same
    342  * widget (by @a uuid) twice.
    343  *
    344  * @param uh hash of unique ID of the widget, only create one per UUID
    345  * @param type type of the widget to create
    346  * @param label label to use
    347  * @param tooltip tooltip to use
    348  * @param id_attr potential additional inputs for the widget creation
    349  * @return created widget
    350  */
    351 static GtkWidget *
    352 create_attribute_widget (const struct GNUNET_HashCode *uh,
    353                          const char *type,
    354                          const char *label,
    355                          const char *tooltip,
    356                          const json_t *id_attr)
    357 {
    358   static struct
    359   {
    360     const char *type;
    361     GtkWidget *(*ctor)(const json_t *details);
    362   } type_map [] = {
    363     { .type = "string",
    364       .ctor = &ctor_entry },
    365     { .type = "date",
    366       .ctor = &ctor_date },
    367     { .type = NULL,
    368       .ctor = NULL }
    369   };
    370   GtkWidget *w;
    371 
    372   w = GNUNET_CONTAINER_multihashmap_get (AG_entry_attributes,
    373                                          uh);
    374   if (NULL != w)
    375   {
    376     GtkWidget *p;
    377 
    378     gtk_widget_set_visible (w,
    379                             TRUE);
    380     p = gtk_widget_get_parent (w);
    381     gtk_widget_set_visible (p,
    382                             TRUE);
    383     p = gtk_widget_get_parent (p);
    384     gtk_widget_set_visible (p,
    385                             TRUE);
    386     return w;
    387   }
    388   for (unsigned int i = 0; NULL != type_map[i].type; i++)
    389   {
    390     GtkBox *box;
    391     GtkBox *vbox;
    392 
    393     if (0 != strcmp (type_map[i].type,
    394                      type))
    395       continue;
    396     w = type_map[i].ctor (id_attr);
    397     GNUNET_assert (NULL != w);
    398     box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
    399                                 5 /* spacing in pixels */));
    400     vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL,
    401                                  5 /* spacing in pixels */));
    402     {
    403       GtkWidget *glabel;
    404 
    405       glabel = gtk_label_new (label);
    406       pad_along (GTK_ORIENTATION_HORIZONTAL,
    407                  glabel,
    408                  5);
    409       gtk_box_append (box,
    410                       glabel);
    411     }
    412     GNUNET_assert (0 <
    413                    g_signal_connect (w,
    414                                      "changed",
    415                                      G_CALLBACK (&AG_identity_changed),
    416                                      NULL));
    417     gtk_widget_set_tooltip_text (w,
    418                                  tooltip);
    419     pad_along (GTK_ORIENTATION_HORIZONTAL,
    420                w,
    421                5);
    422     gtk_box_append (box,
    423                     w);
    424     pad_along (GTK_ORIENTATION_VERTICAL,
    425                GTK_WIDGET (box),
    426                5);
    427     gtk_box_append (vbox,
    428                     GTK_WIDGET (box));
    429     {
    430       GtkWidget *private_widget;
    431       GtkBuilder *builder;
    432       GtkWindow *window;
    433 
    434       builder =
    435         ANASTASIS_GTK_get_new_builder (
    436           ANASTASIS_GTK_project_data (),
    437           "this_stays_private.ui",
    438           NULL);
    439       GNUNET_break (NULL != builder);
    440       /* load frame */
    441       window = GTK_WINDOW (gtk_builder_get_object (builder,
    442                                                    "private_dummy_window"));
    443       GNUNET_break (NULL != window);
    444       private_widget = gtk_window_get_child (window);
    445       GNUNET_break (NULL != private_widget);
    446       g_object_ref (private_widget);
    447       gtk_window_set_child (window,
    448                             NULL);
    449       gtk_window_destroy (window);
    450       g_object_unref (G_OBJECT (builder));
    451       pad_along (GTK_ORIENTATION_VERTICAL,
    452                  private_widget,
    453                  5);
    454       gtk_box_append (vbox,
    455                       private_widget);
    456       g_object_unref (private_widget);
    457     }
    458     GNUNET_assert (GNUNET_OK ==
    459                    GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes,
    460                                                       uh,
    461                                                       w,
    462                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    463     {
    464       GtkBox *pbox;
    465 
    466       pbox = GTK_BOX (GCG_get_main_window_object (
    467                         "anastasis_gtk_identity_vbox"));
    468       pad_along (GTK_ORIENTATION_VERTICAL,
    469                  GTK_WIDGET (vbox),
    470                  5);
    471       gtk_box_append (pbox,
    472                       GTK_WIDGET (vbox));
    473     }
    474     return w;
    475   }
    476   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    477               "FATAL: required attribute type `%s' not supported\n",
    478               type);
    479   GNUNET_assert (0);
    480   return NULL;
    481 }
    482 
    483 
    484 /**
    485  * Update GtkLabel @a name, setting text to @a value.
    486  *
    487  * @param name name of the widget to update
    488  * @param value value to set
    489  */
    490 static void
    491 update_label (const char *name,
    492               const char *value)
    493 {
    494   GtkLabel *label;
    495 
    496   label = GTK_LABEL (GCG_get_main_window_object (name));
    497   if (NULL == label)
    498     return;
    499   if (NULL == value)
    500   {
    501     gtk_widget_set_visible (GTK_WIDGET (label),
    502                             FALSE);
    503   }
    504   else
    505   {
    506     gtk_label_set_text (label,
    507                         value);
    508     gtk_widget_set_visible (GTK_WIDGET (label),
    509                             TRUE);
    510   }
    511 }
    512 
    513 
    514 /**
    515  * We are entering the tab where the user has to enter their personal
    516  * attributes.  Show widgets applicable to the selected country, and
    517  * dynamically generate widgets if necessary.  Furthermore, restore
    518  * widget contents from our state (if stored attribute values are
    519  * present in our state).
    520  */
    521 static void
    522 action_user_attributes_collecting (void)
    523 {
    524   const json_t *id_attributes;
    525 
    526   AG_hide_all_frames ();
    527   id_attributes = json_object_get (AG_redux_state,
    528                                    "required_attributes");
    529   GNUNET_assert (NULL != id_attributes);
    530   AG_hide_children ("anastasis_gtk_identity_vbox");
    531   {
    532     size_t index;
    533     json_t *id_attr;
    534 
    535     json_array_foreach (id_attributes, index, id_attr)
    536     {
    537       const char *widget_name = NULL;
    538       const char *attr_tooltip = NULL;
    539       const char *attr_label = NULL;
    540       const char *attr_type;
    541       const char *attr_uuid;
    542       const char *attr_name;
    543       bool optional = false;
    544       struct GNUNET_JSON_Specification spec[] = {
    545         GNUNET_JSON_spec_mark_optional (
    546           GNUNET_JSON_spec_string ("widget",
    547                                    &widget_name),
    548           NULL),
    549         GNUNET_JSON_spec_mark_optional (
    550           GNUNET_JSON_spec_bool ("optional",
    551                                  &optional),
    552           NULL),
    553         GNUNET_JSON_spec_mark_optional (
    554           GNUNET_JSON_spec_string ("tooltip",
    555                                    &attr_tooltip),
    556           NULL),
    557         GNUNET_JSON_spec_string ("type",
    558                                  &attr_type),
    559         GNUNET_JSON_spec_string ("uuid",
    560                                  &attr_uuid),
    561         GNUNET_JSON_spec_string ("name",
    562                                  &attr_name),
    563         GNUNET_JSON_spec_mark_optional (
    564           GNUNET_JSON_spec_string ("label",
    565                                    &attr_label),
    566           NULL),
    567         GNUNET_JSON_spec_end ()
    568       };
    569       struct GNUNET_HashCode uh;
    570       GtkWidget *w = NULL;
    571       char *l = NULL;
    572 
    573       GNUNET_assert (GNUNET_OK ==
    574                      GNUNET_JSON_parse (id_attr,
    575                                         spec,
    576                                         NULL, NULL));
    577       GNUNET_CRYPTO_hash (attr_uuid,
    578                           strlen (attr_uuid),
    579                           &uh);
    580       if (NULL != widget_name)
    581       {
    582         char *data_name;
    583 
    584         data_name = AG_expand_name (widget_name,
    585                                     attr_type);
    586         w = GTK_WIDGET (GCG_get_main_window_object (data_name));
    587         if (NULL == w)
    588         {
    589           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    590                       "Widget `%s' not found, will try to create dynamic replacement\n",
    591                       data_name);
    592         }
    593         GNUNET_free (data_name);
    594       }
    595       if (optional)
    596         GNUNET_asprintf (&l,
    597                          _ ("%s (optional)"),
    598                          dgettext ("anastasis",
    599                                    attr_label));
    600       else
    601         l = GNUNET_strdup (dgettext ("anastasis",
    602                                      attr_label));
    603       if ( (NULL != widget_name) &&
    604            (NULL != w) )
    605       {
    606         char *box_widget;
    607         GObject *box;
    608 
    609         GNUNET_asprintf (&box_widget,
    610                          "%s_box",
    611                          widget_name);
    612         box = GCG_get_main_window_object (box_widget);
    613         if (NULL == box)
    614         {
    615           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    616                       "Widget `%s' not found, cannot show entry element. BAD.\n",
    617                       box_widget);
    618         }
    619         else
    620         {
    621           AG_show (box_widget);
    622           AG_show_children (box_widget);
    623         }
    624         GNUNET_free (box_widget);
    625       }
    626       if ( (NULL != w) &&
    627            (! GNUNET_CONTAINER_multihashmap_contains (AG_entry_attributes,
    628                                                       &uh)) )
    629       {
    630         GNUNET_assert (GNUNET_OK ==
    631                        GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes,
    632                                                           &uh,
    633                                                           w,
    634                                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    635       }
    636       if (NULL == w)
    637         w = create_attribute_widget (&uh,
    638                                      attr_type,
    639                                      l,
    640                                      attr_tooltip,
    641                                      id_attr);
    642       GNUNET_free (l);
    643       if (NULL != w)
    644       {
    645         json_t *ia;
    646         json_t *val;
    647 
    648         ia = json_object_get (AG_redux_state,
    649                               "identity_attributes");
    650         val = json_object_get (ia,
    651                                attr_name);
    652         if ( (NULL != val) &&
    653              (! json_is_null (val)) )
    654           AG_import_attribute_data (w,
    655                                     attr_type,
    656                                     val);
    657         gtk_widget_set_visible (w,
    658                                 TRUE);
    659       }
    660       GNUNET_JSON_parse_free (spec);
    661     }
    662   }
    663 
    664   AG_sensitive ("anastasis_gtk_main_window_prev_button");
    665   AG_identity_changed ();
    666   AG_show ("anastasis_gtk_progress_vbox");
    667   AG_progress_update ();
    668   if (NULL != json_object_get (AG_redux_state,
    669                                "backup_state"))
    670   {
    671     AG_show ("anastasis_gtk_backup_progress_scrolled_window");
    672     AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
    673   }
    674   else
    675   {
    676     AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
    677     AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
    678   }
    679   AG_show ("anastasis_gtk_main_window_save_as_button");
    680   AG_show ("anastasis_gtk_main_control_vbox");
    681   AG_show ("anastasis_gtk_main_window_prev_button");
    682   AG_identity_changed ();
    683   AG_show ("anastasis_gtk_identity_frame");
    684   AG_focus ("anastasis_gtk_ia_full_name_entry");
    685 }
    686 
    687 
    688 static void
    689 activate_by_method (json_t *methods)
    690 {
    691   size_t index;
    692   const json_t *method;
    693 
    694   json_array_foreach (methods,
    695                       index,
    696                       method)
    697   {
    698     const char *type;
    699     struct GNUNET_JSON_Specification spec[] = {
    700       GNUNET_JSON_spec_string ("type",
    701                                &type),
    702       GNUNET_JSON_spec_end ()
    703     };
    704 
    705     if (GNUNET_OK !=
    706         GNUNET_JSON_parse (method,
    707                            spec,
    708                            NULL, NULL))
    709     {
    710       GNUNET_break (0);
    711       json_dumpf (method,
    712                   stderr,
    713                   JSON_INDENT (2));
    714       continue;
    715     }
    716 
    717     {
    718       char btn[64];
    719 
    720       GNUNET_snprintf (btn,
    721                        sizeof (btn),
    722                        "anastasis_gtk_btn_add_auth_%s",
    723                        type);
    724       AG_sensitive (btn);
    725     }
    726   }
    727 }
    728 
    729 
    730 /**
    731  * Function called with the results of #ANASTASIS_redux_action on "poll_providers".
    732  *
    733  * @param cls NULL
    734  * @param error_code Error code
    735  * @param response new state as result or config information of a provider
    736  */
    737 static void
    738 long_poll_providers_action_cb (void *cls,
    739                                enum TALER_ErrorCode error_code,
    740                                json_t *response);
    741 
    742 
    743 /**
    744  * Schedules the specified action.
    745  *
    746  * @param cls NULL
    747  */
    748 static void
    749 long_poll_providers_task (void *cls)
    750 {
    751   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS];
    752   json_t *tspec;
    753 
    754   (void) cls;
    755   la->task = NULL;
    756   if (GNUNET_TIME_absolute_is_future (la->next_time))
    757   {
    758     la->task = GNUNET_SCHEDULER_add_at (la->next_time,
    759                                         &long_poll_providers_task,
    760                                         NULL);
    761     return;
    762   }
    763   la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT);
    764   tspec = GNUNET_JSON_PACK (
    765     GNUNET_JSON_pack_time_rel ("timeout",
    766                                LP_TIMEOUT));
    767   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    768               "Starting long polling task for provider configurations\n");
    769   la->ra = ANASTASIS_redux_action (AG_redux_state,
    770                                    "poll_providers",
    771                                    tspec,
    772                                    &long_poll_providers_action_cb,
    773                                    NULL);
    774   json_decref (tspec);
    775 }
    776 
    777 
    778 /**
    779  * Check if all providers we care about are ready,
    780  * and if not try to long poll them.
    781  *
    782  * @return false if we have no providers at all
    783  */
    784 static bool
    785 sync_providers (void)
    786 {
    787   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS];
    788   json_t *ap;
    789   const char *url;
    790   json_t *obj;
    791   bool ready = false;
    792   bool poll = false;
    793 
    794   ap = json_object_get (AG_redux_state,
    795                         "authentication_providers");
    796   json_object_foreach (ap, url, obj)
    797   {
    798     struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
    799     enum GNUNET_GenericReturnValue ret;
    800 
    801     ret = ANASTASIS_reducer_lookup_salt (AG_redux_state,
    802                                          url,
    803                                          &provider_salt);
    804     switch (ret)
    805     {
    806     case GNUNET_OK:
    807       ready = true;
    808       break;
    809     case GNUNET_NO:
    810       poll = true;
    811       break;
    812     case GNUNET_SYSERR:
    813       GNUNET_break (0);
    814       break;
    815     }
    816   }
    817   if (poll)
    818   {
    819     la->next_time = GNUNET_TIME_UNIT_ZERO_ABS;
    820     la->task = GNUNET_SCHEDULER_add_now (&long_poll_providers_task,
    821                                          NULL);
    822   }
    823   return ready || poll;
    824 }
    825 
    826 
    827 /**
    828  * Allow the user to configure authorization methods from
    829  * the set of methods offered by known providers.
    830  */
    831 static void
    832 action_authentications_editing (void)
    833 {
    834   json_t *aps;
    835   bool have_auth;
    836 
    837   AG_hide_all_frames ();
    838   AG_insensitive_children ("anastasis_gtk_auth_button_grid");
    839   (void) sync_providers ();
    840   aps = json_object_get (AG_redux_state,
    841                          "authentication_providers");
    842   {
    843     const json_t *ap;
    844     const char *provider_url;
    845 
    846     json_object_foreach (aps,
    847                          provider_url,
    848                          ap)
    849     {
    850       uint32_t ec = 0;
    851       uint32_t hc = 0;
    852       const char *status;
    853       json_t *methods = NULL;
    854       struct GNUNET_JSON_Specification spec[] = {
    855         GNUNET_JSON_spec_string ("status",
    856                                  &status),
    857         GNUNET_JSON_spec_mark_optional (
    858           GNUNET_JSON_spec_uint32 ("error_code",
    859                                    &ec),
    860           NULL),
    861         GNUNET_JSON_spec_mark_optional (
    862           GNUNET_JSON_spec_json ("methods",
    863                                  &methods),
    864           NULL),
    865         GNUNET_JSON_spec_mark_optional (
    866           GNUNET_JSON_spec_uint32 ("http_status",
    867                                    &hc),
    868           NULL),
    869         GNUNET_JSON_spec_end ()
    870       };
    871 
    872       if (GNUNET_OK !=
    873           GNUNET_JSON_parse (ap,
    874                              spec,
    875                              NULL, NULL))
    876       {
    877         GNUNET_break (0);
    878         continue;
    879       }
    880       if (0 == strcmp (status,
    881                        "disabled"))
    882         continue;
    883       switch (hc)
    884       {
    885       case MHD_HTTP_OK:
    886         if (NULL == methods)
    887         {
    888           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    889                       "Provider `%s' has no authentication methods?\n",
    890                       provider_url);
    891           break;
    892         }
    893         activate_by_method (methods);
    894         break;
    895       default:
    896         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    897                     "Status of provider `%s' is %u/%u\n",
    898                     provider_url,
    899                     (unsigned int) ec,
    900                     (unsigned int) hc);
    901         break;
    902       }
    903       GNUNET_JSON_parse_free (spec);
    904     }
    905   }
    906 
    907   have_auth = false;
    908   {
    909     json_t *ams;
    910     size_t index;
    911     json_t *am;
    912 
    913     AG_list_clear ("authentication_methods_liststore");
    914     ams = json_object_get (AG_redux_state,
    915                            "authentication_methods");
    916     json_array_foreach (ams,
    917                         index,
    918                         am)
    919     {
    920       const char *type;
    921       const char *instructions;
    922       struct GNUNET_JSON_Specification spec[] = {
    923         GNUNET_JSON_spec_string ("type",
    924                                  &type),
    925         GNUNET_JSON_spec_string ("instructions",
    926                                  &instructions),
    927         GNUNET_JSON_spec_end ()
    928       };
    929 
    930       GNUNET_assert (GNUNET_OK ==
    931                      GNUNET_JSON_parse (am,
    932                                         spec,
    933                                         NULL, NULL));
    934       AG_list_add ("authentication_methods_liststore",
    935                    GNUNET_JSON_PACK (
    936                      GNUNET_JSON_pack_string ("type",
    937                                               type),
    938                      GNUNET_JSON_pack_string ("visualization",
    939                                               instructions),
    940                      GNUNET_JSON_pack_uint64 ("index",
    941                                               index)));
    942       have_auth = true;
    943     }
    944   }
    945 
    946   AG_sensitive ("anastasis_gtk_main_window_prev_button");
    947   if (have_auth)
    948     AG_enable_next ();
    949   else
    950     AG_insensitive ("anastasis_gtk_main_window_forward_button");
    951   AG_show ("anastasis_gtk_progress_vbox");
    952   AG_progress_update ();
    953   AG_show ("anastasis_gtk_backup_progress_scrolled_window");
    954   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
    955   AG_show ("anastasis_gtk_main_control_vbox");
    956   AG_show ("anastasis_gtk_main_window_prev_button");
    957   AG_enable_next ();
    958   AG_show ("anastasis_gtk_b_authentication_frame");
    959 }
    960 
    961 
    962 /**
    963  * Lookup @a method_cost of authentication method @a type at @a provider in our
    964  * #AG_redux_state.
    965  *
    966  * @param provider URL of provider
    967  * @param type authentication method to look for
    968  * @param[out] method_cost cost to return
    969  * @return #GNUNET_OK on success, #GNUNET_NO if
    970  *       the provider is down, #GNUNET_SYSERR on other failures
    971  */
    972 static enum GNUNET_GenericReturnValue
    973 lookup_recovery_cost (const char *provider,
    974                       const char *type,
    975                       struct TALER_Amount *method_cost)
    976 {
    977   json_t *aps;
    978   json_t *ap;
    979   json_t *methods;
    980   size_t index;
    981   json_t *method;
    982 
    983   memset (method_cost,
    984           0,
    985           sizeof (struct TALER_Amount));
    986   aps = json_object_get (AG_redux_state,
    987                          "authentication_providers");
    988   GNUNET_assert (NULL != aps);
    989   ap = json_object_get (aps,
    990                         provider);
    991   if (NULL == ap)
    992   {
    993     GNUNET_break (0);
    994     json_dumpf (aps,
    995                 stderr,
    996                 JSON_INDENT (2));
    997     return GNUNET_SYSERR;
    998   }
    999   methods = json_object_get (ap,
   1000                              "methods");
   1001   if (NULL == methods)
   1002     return GNUNET_NO; /* provider down */
   1003   json_array_foreach (methods, index, method)
   1004   {
   1005     struct TALER_Amount fee;
   1006     const char *mtype;
   1007     struct GNUNET_JSON_Specification spec[] = {
   1008       TALER_JSON_spec_amount_any ("usage_fee",
   1009                                   &fee),
   1010       GNUNET_JSON_spec_string ("type",
   1011                                &mtype),
   1012       GNUNET_JSON_spec_end ()
   1013     };
   1014 
   1015     if (GNUNET_OK !=
   1016         GNUNET_JSON_parse (method,
   1017                            spec,
   1018                            NULL, NULL))
   1019     {
   1020       GNUNET_break (0);
   1021       json_dumpf (method,
   1022                   stderr,
   1023                   JSON_INDENT (2));
   1024       continue;
   1025     }
   1026     if (0 == strcmp (mtype,
   1027                      type))
   1028     {
   1029       *method_cost = fee;
   1030       return GNUNET_OK;
   1031     }
   1032   }
   1033   GNUNET_break (0);
   1034   json_dumpf (methods,
   1035               stderr,
   1036               JSON_INDENT (2));
   1037   return GNUNET_SYSERR;
   1038 }
   1039 
   1040 
   1041 static void
   1042 action_policies_reviewing (void)
   1043 {
   1044   json_t *policies;
   1045   size_t pindex;
   1046   json_t *policy;
   1047   GListStore *ts;
   1048 
   1049   AG_hide_all_frames ();
   1050   ts = AG_list ("policy_review_treestore");
   1051   if (NULL == ts)
   1052     return;
   1053   g_list_store_remove_all (ts);
   1054   policies = json_object_get (AG_redux_state,
   1055                               "policies");
   1056   GNUNET_assert (NULL != policies);
   1057   json_array_foreach (policies, pindex, policy)
   1058   {
   1059     AGRow *prow;
   1060     json_t *methods;
   1061     struct GNUNET_JSON_Specification pspec[] = {
   1062       GNUNET_JSON_spec_json ("methods",
   1063                              &methods),
   1064       GNUNET_JSON_spec_end ()
   1065     };
   1066     size_t mindex;
   1067     json_t *method;
   1068     char *summary;
   1069 
   1070     if (GNUNET_OK !=
   1071         GNUNET_JSON_parse (policy,
   1072                            pspec,
   1073                            NULL, NULL))
   1074     {
   1075       GNUNET_break (0);
   1076       continue;
   1077     }
   1078     /* the row is only added to the list once its challenges are known,
   1079        as that is when the list learns that it can be expanded */
   1080     prow = AG_row_new (GNUNET_JSON_PACK (
   1081                          GNUNET_JSON_pack_uint64 ("policy_index",
   1082                                                   pindex),
   1083                          GNUNET_JSON_pack_bool ("is_challenge",
   1084                                                 false),
   1085                          GNUNET_JSON_pack_string ("expiration",
   1086                                                   "N/A")));
   1087     summary = NULL;
   1088     json_array_foreach (methods, mindex, method)
   1089     {
   1090       uint32_t imethod;
   1091       const char *provider_url;
   1092       struct GNUNET_JSON_Specification mspec[] = {
   1093         GNUNET_JSON_spec_string ("provider",
   1094                                  &provider_url),
   1095         GNUNET_JSON_spec_uint32 ("authentication_method",
   1096                                  &imethod),
   1097         GNUNET_JSON_spec_end ()
   1098       };
   1099       json_t *jmethods;
   1100       json_t *jmethod;
   1101 
   1102       if (GNUNET_OK !=
   1103           GNUNET_JSON_parse (method,
   1104                              mspec,
   1105                              NULL, NULL))
   1106       {
   1107         json_dumpf (method,
   1108                     stderr,
   1109                     JSON_INDENT (2));
   1110         GNUNET_break (0);
   1111         continue;
   1112       }
   1113       jmethods = json_object_get (AG_redux_state,
   1114                                   "authentication_methods");
   1115       jmethod = json_array_get (jmethods,
   1116                                 imethod);
   1117       {
   1118         const char *instructions;
   1119         const char *type;
   1120         struct GNUNET_JSON_Specification tspec[] = {
   1121           GNUNET_JSON_spec_string ("instructions",
   1122                                    &instructions),
   1123           GNUNET_JSON_spec_string ("type",
   1124                                    &type),
   1125           GNUNET_JSON_spec_end ()
   1126         };
   1127         struct TALER_Amount method_cost;
   1128         const char *provider_name;
   1129 
   1130         if (GNUNET_OK !=
   1131             GNUNET_JSON_parse (jmethod,
   1132                                tspec,
   1133                                NULL, NULL))
   1134         {
   1135           GNUNET_break (0);
   1136           continue;
   1137         }
   1138         if (GNUNET_OK !=
   1139             lookup_recovery_cost (provider_url,
   1140                                   type,
   1141                                   &method_cost))
   1142         {
   1143           GNUNET_break (0);
   1144           continue;
   1145         }
   1146         provider_name = lookup_provider_name (provider_url);
   1147         AG_list_append (AG_row_children (prow),
   1148                         GNUNET_JSON_PACK (
   1149                           GNUNET_JSON_pack_string ("policy_name",
   1150                                                    instructions),
   1151                           GNUNET_JSON_pack_string ("method_type",
   1152                                                    type),
   1153                           GNUNET_JSON_pack_string ("cost",
   1154                                                    TALER_amount2s (
   1155                                                      &method_cost)),
   1156                           GNUNET_JSON_pack_string ("provider_url",
   1157                                                    provider_url),
   1158                           GNUNET_JSON_pack_string ("expiration",
   1159                                                    "N/A"),
   1160                           GNUNET_JSON_pack_uint64 ("policy_index",
   1161                                                    pindex),
   1162                           GNUNET_JSON_pack_bool ("is_challenge",
   1163                                                  true),
   1164                           GNUNET_JSON_pack_uint64 ("method_index",
   1165                                                    mindex),
   1166                           GNUNET_JSON_pack_string ("provider_name",
   1167                                                    provider_name)));
   1168         if (NULL == summary)
   1169         {
   1170           summary = GNUNET_strdup (type);
   1171         }
   1172         else
   1173         {
   1174           char *tmp;
   1175 
   1176           GNUNET_asprintf (&tmp,
   1177                            "%s + %s",
   1178                            summary,
   1179                            type);
   1180           GNUNET_free (summary);
   1181           summary = tmp;
   1182         }
   1183       }
   1184       GNUNET_JSON_parse_free (mspec);
   1185     }
   1186     if (NULL != summary)
   1187     {
   1188       AG_row_set (prow,
   1189                   "policy_name",
   1190                   json_string (summary));
   1191       GNUNET_free (summary);
   1192     }
   1193     AG_list_add_row (ts,
   1194                      prow);
   1195     GNUNET_JSON_parse_free (pspec);
   1196   }
   1197   AG_sensitive ("anastasis_gtk_main_window_prev_button");
   1198   AG_show ("anastasis_gtk_progress_vbox");
   1199   AG_progress_update ();
   1200   AG_show ("anastasis_gtk_backup_progress_scrolled_window");
   1201   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
   1202   AG_show ("anastasis_gtk_b_policy_frame");
   1203   AG_show ("anastasis_gtk_main_control_vbox");
   1204   AG_show ("anastasis_gtk_main_window_prev_button");
   1205   AG_enable_next ();
   1206 }
   1207 
   1208 
   1209 /**
   1210  * Update GtkEntry @a name, setting text to @a value.
   1211  *
   1212  * @param name name of the widget to update
   1213  * @param value value to set
   1214  */
   1215 static void
   1216 update_entry (const char *name,
   1217               const char *value)
   1218 {
   1219   GtkEntry *entry;
   1220   const char *old;
   1221 
   1222   if (NULL == value)
   1223     value = "";
   1224   entry = GTK_ENTRY (GCG_get_main_window_object (name));
   1225   if (NULL == entry)
   1226   {
   1227     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1228                 "`%s' is not a GtkEntry!\n",
   1229                 name);
   1230     return;
   1231   }
   1232   old = gtk_editable_get_text (GTK_EDITABLE (entry));
   1233   if (NULL == old)
   1234     old = "";
   1235   if (0 != strcmp (old,
   1236                    value))
   1237     gtk_editable_set_text (GTK_EDITABLE (entry),
   1238                            value);
   1239 }
   1240 
   1241 
   1242 /**
   1243  * Function called when we begin editing the secret.
   1244  */
   1245 static void
   1246 action_secret_editing (void)
   1247 {
   1248   struct GNUNET_TIME_Timestamp exp_time;
   1249   struct GNUNET_JSON_Specification spec[] = {
   1250     GNUNET_JSON_spec_timestamp ("expiration",
   1251                                 &exp_time),
   1252     GNUNET_JSON_spec_end ()
   1253   };
   1254   struct tm tv;
   1255   bool is_free = false;
   1256 
   1257   AG_hide_all_frames ();
   1258   if (GNUNET_OK !=
   1259       GNUNET_JSON_parse (AG_redux_state,
   1260                          spec,
   1261                          NULL, NULL))
   1262   {
   1263     GNUNET_break (0);
   1264     AG_error ("State did not parse correctly: lacks expiration");
   1265     return;
   1266   }
   1267 
   1268   {
   1269     time_t t;
   1270 
   1271     t = exp_time.abs_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
   1272     GNUNET_assert (NULL !=
   1273                    localtime_r (&t,
   1274                                 &tv));
   1275   }
   1276 
   1277   {
   1278     json_t *fees;
   1279 
   1280     fees = json_object_get (AG_redux_state,
   1281                             "upload_fees");
   1282     if (0 == json_array_size (fees))
   1283     {
   1284       update_label ("backup_fee_value_label",
   1285                     _ ("gratis"));
   1286       is_free = true;
   1287     }
   1288     else
   1289     {
   1290       char *val = GNUNET_strdup ("");
   1291       size_t pos;
   1292       json_t *fee;
   1293       struct TALER_Amount a;
   1294 
   1295       json_array_foreach (fees, pos, fee)
   1296       {
   1297         struct GNUNET_JSON_Specification ispec[] = {
   1298           TALER_JSON_spec_amount_any ("fee",
   1299                                       &a),
   1300           GNUNET_JSON_spec_end ()
   1301         };
   1302         char *tmp;
   1303 
   1304         if (GNUNET_OK !=
   1305             GNUNET_JSON_parse (fee,
   1306                                ispec,
   1307                                NULL, NULL))
   1308         {
   1309           GNUNET_break (0);
   1310           json_dumpf (fees,
   1311                       stderr,
   1312                       JSON_INDENT (2));
   1313           continue;
   1314         }
   1315 
   1316         GNUNET_asprintf (&tmp,
   1317                          "%s%s%llu.%u %s",
   1318                          val,
   1319                          strlen (val) > 0 ? "\n" : "",
   1320                          (unsigned long long) a.value,
   1321                          (unsigned int) a.fraction,
   1322                          a.currency);
   1323         GNUNET_free (val);
   1324         val = tmp;
   1325       }
   1326       update_label ("backup_fee_value_label",
   1327                     val);
   1328       GNUNET_free (val);
   1329     }
   1330   }
   1331   {
   1332     char estr[128];
   1333 
   1334     if (is_free)
   1335       GNUNET_assert (sizeof (estr) >
   1336                      strftime (estr,
   1337                                sizeof (estr),
   1338                                "%d %B %Y",
   1339                                &tv));
   1340     else
   1341       GNUNET_assert (sizeof (estr) >
   1342                      strftime (estr,
   1343                                sizeof (estr),
   1344                                "%d %B",
   1345                                &tv));
   1346     update_label ("expiration_date_without_year_label",
   1347                   estr);
   1348   }
   1349 
   1350   {
   1351     GtkSpinButton *sb;
   1352     unsigned int this_year;
   1353     unsigned int exp_year;
   1354 
   1355     sb = GTK_SPIN_BUTTON (GCG_get_main_window_object (
   1356                             "expiration_year_spin_button"));
   1357     if (is_free)
   1358       gtk_widget_set_visible (GTK_WIDGET (sb),
   1359                               FALSE);
   1360     else
   1361       gtk_widget_set_visible (GTK_WIDGET (sb),
   1362                               TRUE);
   1363     this_year = GNUNET_TIME_get_current_year ();
   1364     /* We allow at most 5 years into the future */
   1365     gtk_spin_button_set_range (sb,
   1366                                this_year + 1,
   1367                                this_year + 6);
   1368     exp_year = tv.tm_year + 1900;
   1369     gtk_spin_button_set_value (sb,
   1370                                (double) exp_year);
   1371   }
   1372 
   1373   AG_insensitive ("anastasis_gtk_main_window_forward_button");
   1374   AG_sensitive ("anastasis_gtk_enter_secret_open_button");
   1375   AG_sensitive ("anastasis_gtk_enter_secret_textview");
   1376   AG_hide ("anastasis_gtk_secret_clear_file_button");
   1377   AG_hide ("anastasis_gtk_secret_clear_text_button");
   1378   AG_hide ("anastasis_gtk_secret_file_name_hbox");
   1379   AG_show ("anastasis_gtk_secret_file_chooser_hbox");
   1380   {
   1381     const char *name = "";
   1382     const json_t *jsecret = NULL;
   1383     const char *filename = NULL;
   1384     struct GNUNET_JSON_Specification ispec[] = {
   1385       GNUNET_JSON_spec_mark_optional (
   1386         GNUNET_JSON_spec_object_const ("core_secret",
   1387                                        &jsecret),
   1388         NULL),
   1389       GNUNET_JSON_spec_mark_optional (
   1390         GNUNET_JSON_spec_string ("secret_name",
   1391                                  &name),
   1392         NULL),
   1393       GNUNET_JSON_spec_end ()
   1394     };
   1395 
   1396     if (GNUNET_OK !=
   1397         GNUNET_JSON_parse (AG_redux_state,
   1398                            ispec,
   1399                            NULL, NULL))
   1400     {
   1401       GNUNET_break (0);
   1402       json_dumpf (AG_redux_state,
   1403                   stderr,
   1404                   JSON_INDENT (2));
   1405       AG_error ("State did not parse correctly: invalid secret data");
   1406       return;
   1407     }
   1408     if (! AG_in_secret_name_editing)
   1409       update_entry ("anastasis_gtk_secret_name_entry",
   1410                     name);
   1411     if (NULL != jsecret)
   1412     {
   1413       const char *mime = NULL;
   1414       const char *text = NULL;
   1415       struct GNUNET_JSON_Specification sspec[] = {
   1416         GNUNET_JSON_spec_mark_optional (
   1417           GNUNET_JSON_spec_string ("text",
   1418                                    &text),
   1419           NULL),
   1420         GNUNET_JSON_spec_mark_optional (
   1421           GNUNET_JSON_spec_string ("mime",
   1422                                    &mime),
   1423           NULL),
   1424         GNUNET_JSON_spec_mark_optional (
   1425           GNUNET_JSON_spec_string ("filename",
   1426                                    &filename),
   1427           NULL),
   1428         GNUNET_JSON_spec_end ()
   1429       };
   1430 
   1431       if (GNUNET_OK !=
   1432           GNUNET_JSON_parse (jsecret,
   1433                              sspec,
   1434                              NULL, NULL))
   1435       {
   1436         GNUNET_break (0);
   1437         json_dumpf (AG_redux_state,
   1438                     stderr,
   1439                     JSON_INDENT (2));
   1440         AG_error ("State did not parse correctly: invalid secret data");
   1441         return;
   1442       }
   1443       if ( (NULL != text) &&
   1444            (0 == strlen (text)) )
   1445         text = NULL;
   1446       if (! AG_in_secret_editing)
   1447       {
   1448         GtkTextBuffer *tb = GTK_TEXT_BUFFER (GCG_get_main_window_object (
   1449                                                "anastasis_gtk_enter_secret_textbuffer"));
   1450         const char *old;
   1451         GtkTextIter start;
   1452         GtkTextIter end;
   1453 
   1454         gtk_text_buffer_get_start_iter (tb,
   1455                                         &start);
   1456         gtk_text_buffer_get_end_iter (tb,
   1457                                       &end);
   1458         old = gtk_text_buffer_get_text (tb,
   1459                                         &start,
   1460                                         &end,
   1461                                         true);
   1462         if (0 != strcmp (old,
   1463                          NULL != text
   1464        ? text
   1465        : ""))
   1466           gtk_text_buffer_set_text (tb,
   1467                                     NULL != text
   1468             ? text
   1469             : "",
   1470                                     -1);
   1471       }
   1472       update_label ("anastasis_gtk_secret_file_name_label",
   1473                     filename);
   1474       if ( (NULL != text) ||
   1475            (NULL != filename) )
   1476       {
   1477         AG_enable_next ();
   1478       }
   1479       if (NULL != text)
   1480       {
   1481         AG_insensitive ("anastasis_gtk_enter_secret_open_button");
   1482         AG_show ("anastasis_gtk_secret_clear_text_button");
   1483       }
   1484       if (NULL != filename)
   1485       {
   1486         AG_insensitive ("anastasis_gtk_enter_secret_textview");
   1487         AG_show ("anastasis_gtk_secret_clear_file_button");
   1488         AG_show ("anastasis_gtk_secret_file_name_hbox");
   1489         AG_hide ("anastasis_gtk_secret_file_chooser_hbox");
   1490       }
   1491       GNUNET_JSON_parse_free (sspec);
   1492     }
   1493     else
   1494     {
   1495       /* secret is NULL */
   1496       GtkTextBuffer *tb = GTK_TEXT_BUFFER (GCG_get_main_window_object (
   1497                                              "anastasis_gtk_enter_secret_textbuffer"));
   1498 
   1499       gtk_text_buffer_set_text (tb,
   1500                                 "",
   1501                                 -1);
   1502     }
   1503     if ( (NULL == name) ||
   1504          (0 == strlen (name) ) )
   1505       AG_focus ("anastasis_gtk_secret_name_entry");
   1506     else if (NULL == filename)
   1507       AG_focus ("anastasis_gtk_enter_secret_textview");
   1508   }
   1509   AG_sensitive ("anastasis_gtk_main_window_prev_button");
   1510   AG_show ("anastasis_gtk_progress_vbox");
   1511   AG_progress_update ();
   1512   AG_show ("anastasis_gtk_backup_progress_scrolled_window");
   1513   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
   1514   AG_show ("anastasis_gtk_enter_secret_frame");
   1515   AG_show ("anastasis_gtk_main_control_vbox");
   1516   AG_show ("anastasis_gtk_main_window_prev_button");
   1517   AG_enable_next ();
   1518 }
   1519 
   1520 
   1521 /**
   1522  * Add a row with the QR code of the payment request @a payto to the list of
   1523  * unpaid payment requests.
   1524  *
   1525  * @param payto the "taler://pay" URI to encode
   1526  * @param provider the provider that wants to be paid
   1527  */
   1528 static void
   1529 add_qrcode (const char *payto,
   1530             const char *provider)
   1531 {
   1532   GtkWidget *w;
   1533   GdkPixbuf *pb;
   1534   GListStore *list;
   1535   AGRow *row;
   1536 
   1537   w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
   1538   if (NULL == w)
   1539   {
   1540     GNUNET_break (0);
   1541     return;
   1542   }
   1543   pb = AG_setup_qrcode (w,
   1544                         payto,
   1545                         strlen (payto));
   1546   if (NULL == pb)
   1547   {
   1548     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1549                 _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
   1550                 payto);
   1551     return;
   1552   }
   1553   list = AG_list ("unpaid_qrcodes_liststore");
   1554   if (NULL == list)
   1555   {
   1556     g_object_unref (pb);
   1557     return;
   1558   }
   1559   row = AG_row_new (GNUNET_JSON_PACK (
   1560                       GNUNET_JSON_pack_string ("url",
   1561                                                payto),
   1562                       GNUNET_JSON_pack_string ("provider",
   1563                                                provider)));
   1564   /* the row must know its image before it is added to the list, as the
   1565      list renders it right away */
   1566   AG_row_set_image (row,
   1567                     GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pb)));
   1568   g_object_unref (pb);
   1569   AG_list_add_row (list,
   1570                    row);
   1571 }
   1572 
   1573 
   1574 static void
   1575 action_truths_paying (void)
   1576 {
   1577   json_t *pprs;
   1578   size_t index;
   1579   json_t *pt;
   1580 
   1581   AG_hide_all_frames ();
   1582   AG_list_clear ("unpaid_qrcodes_liststore");
   1583   pprs = json_object_get (AG_redux_state,
   1584                           "payments");
   1585   json_array_foreach (pprs, index, pt)
   1586   {
   1587     const char *payto = json_string_value (pt);
   1588 
   1589     if (NULL == payto)
   1590     {
   1591       GNUNET_break (0);
   1592       continue;
   1593     }
   1594     add_qrcode (payto,
   1595                 "");
   1596   }
   1597 
   1598   {
   1599     json_t *args;
   1600     struct GNUNET_TIME_Relative timeout;
   1601 
   1602     timeout = GNUNET_TIME_UNIT_MINUTES;
   1603     GNUNET_assert (NULL == AG_ra);
   1604     args = GNUNET_JSON_PACK (
   1605       GNUNET_JSON_pack_time_rel ("timeout",
   1606                                  timeout));
   1607     AG_ra = ANASTASIS_redux_action (AG_redux_state,
   1608                                     "pay",
   1609                                     args,
   1610                                     &AG_action_cb,
   1611                                     NULL);
   1612     json_decref (args);
   1613   }
   1614   AG_show ("anastasis_gtk_pay_frame");
   1615   AG_show ("anastasis_gtk_main_control_vbox");
   1616   AG_show ("anastasis_gtk_progress_vbox");
   1617   AG_progress_update ();
   1618   AG_show ("anastasis_gtk_backup_progress_scrolled_window");
   1619   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
   1620   AG_hide ("anastasis_gtk_main_window_prev_button");
   1621   AG_hide ("anastasis_gtk_main_window_forward_button");
   1622 }
   1623 
   1624 
   1625 static void
   1626 action_policies_paying (void)
   1627 {
   1628   json_t *pprs;
   1629   size_t index;
   1630   json_t *ppr;
   1631 
   1632   AG_hide_all_frames ();
   1633   AG_list_clear ("unpaid_qrcodes_liststore");
   1634   pprs = json_object_get (AG_redux_state,
   1635                           "policy_payment_requests");
   1636   json_array_foreach (pprs, index, ppr)
   1637   {
   1638     const char *provider;
   1639     const char *payto;
   1640     struct GNUNET_JSON_Specification spec[] = {
   1641       GNUNET_JSON_spec_string ("provider",
   1642                                &provider),
   1643       GNUNET_JSON_spec_string ("payto",
   1644                                &payto),
   1645       GNUNET_JSON_spec_end ()
   1646     };
   1647 
   1648     if (GNUNET_OK !=
   1649         GNUNET_JSON_parse (ppr,
   1650                            spec,
   1651                            NULL, NULL))
   1652     {
   1653       GNUNET_break (0);
   1654       continue;
   1655     }
   1656     add_qrcode (payto,
   1657                 provider);
   1658   }
   1659   {
   1660     json_t *args;
   1661     struct GNUNET_TIME_Relative timeout;
   1662 
   1663     timeout = GNUNET_TIME_UNIT_MINUTES;
   1664     GNUNET_assert (NULL == AG_ra);
   1665     args = GNUNET_JSON_PACK (
   1666       GNUNET_JSON_pack_time_rel ("timeout",
   1667                                  timeout));
   1668     AG_ra = ANASTASIS_redux_action (AG_redux_state,
   1669                                     "pay",
   1670                                     args,
   1671                                     &AG_action_cb,
   1672                                     NULL);
   1673     json_decref (args);
   1674   }
   1675   AG_show ("anastasis_gtk_pay_frame");
   1676   AG_show ("anastasis_gtk_main_control_vbox");
   1677   AG_show ("anastasis_gtk_progress_vbox");
   1678   AG_progress_update ();
   1679   AG_show ("anastasis_gtk_backup_progress_scrolled_window");
   1680   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
   1681   AG_hide ("anastasis_gtk_main_window_prev_button");
   1682   AG_hide ("anastasis_gtk_main_window_forward_button");
   1683 }
   1684 
   1685 
   1686 /**
   1687  * The backup has finished, show the providers, policy version and
   1688  * expiration dates.
   1689  */
   1690 static void
   1691 action_backup_finished (void)
   1692 {
   1693   json_t *det;
   1694   json_t *se;
   1695   const char *url;
   1696   struct GNUNET_TIME_Timestamp mexp;
   1697 
   1698   AG_hide_all_frames ();
   1699   det = json_object_get (AG_redux_state,
   1700                          "success_details");
   1701   AG_list_clear ("backup_provider_liststore");
   1702   mexp = GNUNET_TIME_UNIT_FOREVER_TS;
   1703   json_object_foreach (det, url, se)
   1704   {
   1705     struct GNUNET_TIME_Timestamp pexp;
   1706     uint64_t version;
   1707     struct GNUNET_JSON_Specification spec[] = {
   1708       GNUNET_JSON_spec_uint64 ("policy_version",
   1709                                &version),
   1710       GNUNET_JSON_spec_timestamp ("policy_expiration",
   1711                                   &pexp),
   1712       GNUNET_JSON_spec_end ()
   1713     };
   1714 
   1715     if (GNUNET_OK !=
   1716         GNUNET_JSON_parse (se,
   1717                            spec,
   1718                            NULL, NULL))
   1719     {
   1720       GNUNET_break_op (0);
   1721       AG_error ("State did not parse correctly");
   1722       return;
   1723     }
   1724     mexp = GNUNET_TIME_timestamp_min (mexp,
   1725                                       pexp);
   1726     AG_list_add ("backup_provider_liststore",
   1727                  GNUNET_JSON_PACK (
   1728                    GNUNET_JSON_pack_string ("provider_url",
   1729                                             url),
   1730                    GNUNET_JSON_pack_uint64 ("version",
   1731                                             version),
   1732                    GNUNET_JSON_pack_string ("expiration",
   1733                                             GNUNET_TIME_timestamp2s (pexp)),
   1734                    GNUNET_JSON_pack_bool ("success",
   1735                                           true),
   1736                    GNUNET_JSON_pack_string ("provider_name",
   1737                                             lookup_provider_name (url))));
   1738   }
   1739   {
   1740     struct tm tv;
   1741     char estr[128];
   1742     time_t t;
   1743     struct GNUNET_TIME_Absolute sexp;
   1744 
   1745     /* be more conservative in what we show */
   1746     sexp = GNUNET_TIME_absolute_subtract (mexp.abs_time,
   1747                                           GNUNET_TIME_UNIT_DAYS);
   1748     t = sexp.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
   1749     GNUNET_assert (NULL !=
   1750                    localtime_r (&t,
   1751                                 &tv));
   1752     GNUNET_assert (sizeof (estr) >
   1753                    strftime (estr,
   1754                              sizeof (estr),
   1755                              "%d %B %Y",
   1756                              &tv));
   1757     update_label ("backup_expiration_date_label",
   1758                   GNUNET_TIME_absolute2s (sexp));
   1759   }
   1760   AG_hide ("anastasis_gtk_progress_vbox");
   1761   AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
   1762   AG_show ("anastasis_gtk_completed_frame");
   1763   AG_show ("anastasis_gtk_backup_complete_box");
   1764   AG_hide ("anastasis_gtk_success_recovery_box");
   1765   AG_show ("anastasis_gtk_success_backup_label");
   1766   AG_hide ("anastasis_gtk_success_recovery_box");
   1767   AG_show ("anastasis_gtk_main_control_vbox");
   1768   AG_hide ("anastasis_gtk_main_window_save_as_button");
   1769   AG_show ("anastasis_gtk_restart_button");
   1770   AG_show ("anastasis_gtk_main_window_quit_button");
   1771   AG_hide ("anastasis_gtk_main_window_prev_button");
   1772   AG_hide ("anastasis_gtk_main_window_forward_button");
   1773 }
   1774 
   1775 
   1776 static const json_t *
   1777 find_challenge_by_uuid (const char *uuid)
   1778 {
   1779   json_t *rd;
   1780   json_t *cs;
   1781   size_t index;
   1782   json_t *c;
   1783 
   1784   rd = json_object_get (AG_redux_state,
   1785                         "recovery_document");
   1786   cs = json_object_get (rd,
   1787                         "challenges");
   1788   json_array_foreach (cs, index, c)
   1789   {
   1790     const char *u;
   1791 
   1792     u = json_string_value (json_object_get (c,
   1793                                             "uuid"));
   1794     if (NULL == u)
   1795     {
   1796       GNUNET_break (0);
   1797       continue;
   1798     }
   1799     if (0 == strcmp (u,
   1800                      uuid))
   1801       return c;
   1802   }
   1803   return NULL;
   1804 }
   1805 
   1806 
   1807 /**
   1808  * Update the list store with the challenge feedback.
   1809  *
   1810  * @param uuid the UUID to render feedback for
   1811  * @param builder the builder to use to grab display widgets
   1812  */
   1813 static void
   1814 show_challenge_feedback (const char *uuid,
   1815                          GtkBuilder *builder)
   1816 {
   1817   json_t *cf;
   1818   const json_t *f;
   1819   const char *state;
   1820   const char *hint = NULL;
   1821   const char *taler_pay_uri = NULL;
   1822   uint32_t ec = TALER_EC_NONE;
   1823   struct GNUNET_JSON_Specification spec[] = {
   1824     GNUNET_JSON_spec_string ("state",
   1825                              &state),
   1826     GNUNET_JSON_spec_mark_optional (
   1827       GNUNET_JSON_spec_string ("taler_pay_uri",
   1828                                &taler_pay_uri),
   1829       NULL),
   1830     GNUNET_JSON_spec_mark_optional (
   1831       GNUNET_JSON_spec_string ("display_hint",
   1832                                &hint),
   1833       NULL),
   1834     GNUNET_JSON_spec_mark_optional (
   1835       GNUNET_JSON_spec_uint32 ("error_code",
   1836                                &ec),
   1837       NULL),
   1838     GNUNET_JSON_spec_end ()
   1839   };
   1840 
   1841   cf = json_object_get (AG_redux_state,
   1842                         "challenge_feedback");
   1843   f = json_object_get (cf, uuid);
   1844   if (NULL == f)
   1845     return; /* no feedback */
   1846   if (GNUNET_OK !=
   1847       GNUNET_JSON_parse (f,
   1848                          spec,
   1849                          NULL, NULL))
   1850   {
   1851     GNUNET_break (0);
   1852     json_dumpf (f,
   1853                 stderr,
   1854                 JSON_INDENT (2));
   1855     return;
   1856   }
   1857   if (0)
   1858   {
   1859     /* FIXME: do we want to show the payment request here at all? */
   1860     if (NULL != taler_pay_uri)
   1861     {
   1862       GdkPixbuf *qr;
   1863       GtkWidget *w = NULL;
   1864 
   1865       qr = AG_setup_qrcode (w,
   1866                             taler_pay_uri,
   1867                             strlen (taler_pay_uri));
   1868       g_object_unref (qr);
   1869     }
   1870   }
   1871   if (TALER_EC_NONE != ec)
   1872   {
   1873     const char *emsg;
   1874     GtkLabel *l;
   1875     char *msg;
   1876 
   1877     emsg = TALER_ErrorCode_get_hint (ec);
   1878     GNUNET_asprintf (&msg,
   1879                      "#%u: %s",
   1880                      (unsigned int) ec,
   1881                      emsg);
   1882     l = GTK_LABEL (gtk_builder_get_object (builder,
   1883                                            "error_label"));
   1884     gtk_label_set_text (l,
   1885                         msg);
   1886     GNUNET_free (msg);
   1887     gtk_widget_set_visible (GTK_WIDGET (l),
   1888                             TRUE);
   1889   }
   1890   if (NULL != hint)
   1891   {
   1892     GtkLabel *l;
   1893 
   1894     l = GTK_LABEL (gtk_builder_get_object (builder,
   1895                                            "hint_label"));
   1896     gtk_label_set_text (l,
   1897                         hint);
   1898     gtk_widget_set_visible (GTK_WIDGET (l),
   1899                             TRUE);
   1900   }
   1901   GNUNET_JSON_parse_free (spec);
   1902 }
   1903 
   1904 
   1905 /**
   1906  * Function called on each discovered recovery policy. Called
   1907  * with all arguments NULL if we have received all policies that
   1908  * we could possibly receive for the current operation.
   1909  *
   1910  * The client can then start a new policy discovery process, using the
   1911  * smallest (also most recent) @a version received per @a provider_url
   1912  * in the cursor to resume.  Note that in this case, the application
   1913  * logic is responsible for de-duplication using @a hcpd, or it may show
   1914  * policies again if they are at different providers under versions not
   1915  * queried up to the cursor.
   1916  *
   1917  * @param cls closure with the list of secrets to update.
   1918  * @param hcpd hash of the compressed policy document (unique per policy)
   1919  * @param provider_url which provider claims to have this policy
   1920  * @param version version of the policy at this provider
   1921  * @param attribute_mask combination of optional identity attributes
   1922  *           present in the state that was used to locate this version
   1923  * @param server_time when did the provider receive the upload
   1924  * @param secret_name name the user assigned to the backup
   1925  * @param providers json array of providers offering this policy
   1926  */
   1927 static void
   1928 expand_policy_list (void *cls,
   1929                     const struct GNUNET_HashCode *hcpd,
   1930                     const char *provider_url,
   1931                     uint32_t version,
   1932                     json_int_t attribute_mask,
   1933                     struct GNUNET_TIME_Timestamp server_time,
   1934                     const char *secret_name,
   1935                     const json_t *providers)
   1936 {
   1937   GListStore *ls = cls;
   1938   bool have_first;
   1939 
   1940   have_first = (0 != g_list_model_get_n_items (G_LIST_MODEL (ls)));
   1941   AG_list_append (
   1942     ls,
   1943     GNUNET_JSON_PACK (
   1944       GNUNET_JSON_pack_string ("provider_url",
   1945                                provider_url),
   1946       GNUNET_JSON_pack_uint64 ("version",
   1947                                version),
   1948       GNUNET_JSON_pack_uint64 ("attribute_mask",
   1949                                attribute_mask),
   1950       GNUNET_JSON_pack_string ("secret_name",
   1951                                secret_name),
   1952       GNUNET_JSON_pack_string ("date_string",
   1953                                GNUNET_TIME_timestamp2s (server_time)),
   1954       GNUNET_JSON_pack_uint64 ("date",
   1955                                server_time.abs_time.abs_value_us),
   1956       GNUNET_JSON_pack_array_incref ("providers",
   1957                                      (json_t *) providers)));
   1958   if (! have_first)
   1959     AG_select_row ("anastasis_gtk_secret_selection_treeselection",
   1960                    0);
   1961 }
   1962 
   1963 
   1964 /**
   1965  * Function called with the results of #ANASTASIS_redux_action on "poll".
   1966  *
   1967  * @param cls NULL
   1968  * @param error_code Error code
   1969  * @param response new state as result or config information of a provider
   1970  */
   1971 static void
   1972 long_poll_challenges_cb (void *cls,
   1973                          enum TALER_ErrorCode error_code,
   1974                          json_t *response);
   1975 
   1976 
   1977 /**
   1978  * Schedules the "poll" challenges action.
   1979  *
   1980  * @param cls NULL
   1981  */
   1982 static void
   1983 long_poll_challenges_task (void *cls)
   1984 {
   1985   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES];
   1986   json_t *tspec;
   1987 
   1988   (void) cls;
   1989   la->task = NULL;
   1990   if (GNUNET_TIME_absolute_is_future (la->next_time))
   1991   {
   1992     la->task = GNUNET_SCHEDULER_add_at (la->next_time,
   1993                                         &long_poll_challenges_task,
   1994                                         NULL);
   1995     return;
   1996   }
   1997   la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT);
   1998   tspec = GNUNET_JSON_PACK (
   1999     GNUNET_JSON_pack_time_rel ("timeout",
   2000                                LP_TIMEOUT));
   2001   la->ra = ANASTASIS_redux_action (AG_redux_state,
   2002                                    "poll",
   2003                                    tspec,
   2004                                    &long_poll_challenges_cb,
   2005                                    NULL);
   2006   json_decref (tspec);
   2007 }
   2008 
   2009 
   2010 static void
   2011 long_poll_challenges_cb (void *cls,
   2012                          enum TALER_ErrorCode error_code,
   2013                          json_t *response)
   2014 {
   2015   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES];
   2016 
   2017   (void) cls;
   2018   la->ra = NULL;
   2019   switch (error_code)
   2020   {
   2021   case TALER_EC_NONE:
   2022     /* continued below */
   2023     break;
   2024   default:
   2025     AG_error (_ ("poll failed: %s (#%u)"),
   2026               TALER_ErrorCode_get_hint (error_code),
   2027               (unsigned int) error_code);
   2028     /* simply try again */
   2029     la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task,
   2030                                          NULL);
   2031     return;
   2032   }
   2033   AG_action_cb (NULL,
   2034                 TALER_EC_NONE,
   2035                 response);
   2036 }
   2037 
   2038 
   2039 /**
   2040  * Function called with the results of #ANASTASIS_redux_action on "sync_providers".
   2041  *
   2042  * @param cls NULL
   2043  * @param error_code Error code
   2044  * @param response new state as result or config information of a provider
   2045  */
   2046 static void
   2047 long_poll_sync_action_cb (void *cls,
   2048                           enum TALER_ErrorCode error_code,
   2049                           json_t *response);
   2050 
   2051 
   2052 /**
   2053  * Schedules the "sync_providers" action.
   2054  *
   2055  * @param cls NULL
   2056  */
   2057 static void
   2058 long_poll_sync_task (void *cls)
   2059 {
   2060   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS];
   2061   json_t *tspec;
   2062 
   2063   (void) cls;
   2064   la->task = NULL;
   2065   if (GNUNET_TIME_absolute_is_future (la->next_time))
   2066   {
   2067     la->task = GNUNET_SCHEDULER_add_at (la->next_time,
   2068                                         &long_poll_sync_task,
   2069                                         NULL);
   2070     return;
   2071   }
   2072   la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT);
   2073   tspec = GNUNET_JSON_PACK (
   2074     GNUNET_JSON_pack_time_rel ("timeout",
   2075                                LP_TIMEOUT));
   2076   la->ra = ANASTASIS_redux_action (AG_redux_state,
   2077                                    "sync_providers",
   2078                                    tspec,
   2079                                    &long_poll_sync_action_cb,
   2080                                    NULL);
   2081   json_decref (tspec);
   2082 }
   2083 
   2084 
   2085 static void
   2086 long_poll_sync_action_cb (void *cls,
   2087                           enum TALER_ErrorCode error_code,
   2088                           json_t *response)
   2089 {
   2090   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS];
   2091 
   2092   (void) cls;
   2093   la->ra = NULL;
   2094   switch (error_code)
   2095   {
   2096   case TALER_EC_NONE:
   2097     /* continued below */
   2098     break;
   2099   case TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID:
   2100     /* we are in sync, nothing left to do */
   2101     return;
   2102   default:
   2103     AG_error (_ ("Synchronizing with providers failed: %s (#%u)"),
   2104               TALER_ErrorCode_get_hint (error_code),
   2105               (unsigned int) error_code);
   2106     /* simply try again */
   2107     la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task,
   2108                                          NULL);
   2109     return;
   2110   }
   2111   AG_action_cb (NULL,
   2112                 TALER_EC_NONE,
   2113                 response);
   2114 }
   2115 
   2116 
   2117 /**
   2118  * Start policy discovery process. Triggers download(s)
   2119  * of the various provider configurations and (once we
   2120  * have any) starts the policy discovery process.
   2121  */
   2122 static void
   2123 begin_discovery (void);
   2124 
   2125 
   2126 static void
   2127 long_poll_providers_action_cb (void *cls,
   2128                                enum TALER_ErrorCode error_code,
   2129                                json_t *response)
   2130 {
   2131   struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS];
   2132   json_t *ap;
   2133   const char *url;
   2134   json_t *obj;
   2135 
   2136   la->ra = NULL;
   2137   switch (error_code)
   2138   {
   2139   case TALER_EC_NONE:
   2140     /* continued below */
   2141     break;
   2142   default:
   2143     AG_error (_ ("Polling for providers failed: %s (#%u)"),
   2144               TALER_ErrorCode_get_hint (error_code),
   2145               (unsigned int) error_code);
   2146     /* simply try again */
   2147     la->task = GNUNET_SCHEDULER_add_now (&long_poll_providers_task,
   2148                                          NULL);
   2149     return;
   2150   }
   2151   ap = json_object_get (response,
   2152                         "authentication_providers");
   2153   GNUNET_assert (NULL != ap);
   2154   if (NULL == AG_pd)
   2155   {
   2156     json_t *ns;
   2157 
   2158     /* Simply merge the state */
   2159     ap = json_object_get (response,
   2160                           "authentication_providers");
   2161     ns = json_incref (AG_redux_state);
   2162     GNUNET_assert (0 ==
   2163                    json_object_set (ns,
   2164                                     "authentication_providers",
   2165                                     ap));
   2166     AG_action_cb (NULL,
   2167                   TALER_EC_NONE,
   2168                   ns);
   2169     json_decref (ns);
   2170     return;
   2171   }
   2172   /* Find out which provider is new, merge that one! */
   2173   json_object_foreach (ap, url, obj)
   2174   {
   2175     struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
   2176 
   2177     if (GNUNET_OK ==
   2178         ANASTASIS_reducer_lookup_salt (AG_redux_state,
   2179                                        url,
   2180                                        &provider_salt))
   2181       continue;
   2182     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2183                 "Expanding policy discovery to recently discovered live provider `%s'\n",
   2184                 url);
   2185     ANASTASIS_policy_discovery_more (AG_pd,
   2186                                      url,
   2187                                      obj);
   2188   }
   2189   /* update our state to the new state, but without
   2190      going through the usual action loop */
   2191   json_decref (AG_redux_state);
   2192   AG_redux_state = json_incref (response);
   2193   GNUNET_assert (NULL != AG_redux_state);
   2194 }
   2195 
   2196 
   2197 static void
   2198 begin_discovery (void)
   2199 {
   2200   GListStore *ls;
   2201   bool have_providers;
   2202 
   2203   ls = AG_list ("secret_selection_liststore");
   2204   GNUNET_assert (NULL != ls);
   2205   if (NULL == AG_pd)
   2206     g_list_store_remove_all (ls);
   2207   have_providers = sync_providers ();
   2208   if (NULL == AG_pd)
   2209     AG_pd = ANASTASIS_policy_discovery_start (AG_redux_state,
   2210                                               NULL,
   2211                                               &expand_policy_list,
   2212                                               ls);
   2213   if (! have_providers)
   2214   {
   2215     AG_error (_ ("No available providers! Try to add one!"));
   2216   }
   2217 }
   2218 
   2219 
   2220 /**
   2221  * Function called when it is time for the user to select a secret
   2222  * from the list of secrets.  Builds the respective tree model.
   2223  */
   2224 static void
   2225 action_secret_selecting (void)
   2226 {
   2227   AG_hide ("anastasis_gtk_start_frame");
   2228   AG_stop_long_action ();
   2229   if (AG_have_error)
   2230     AG_show ("anastasis_gtk_error_label");
   2231   AG_hide ("anastasis_gtk_challenge_frame");
   2232   AG_hide ("anastasis_gtk_identity_frame");
   2233   if (NULL == AG_pd)
   2234     begin_discovery ();
   2235   AG_show ("anastasis_gtk_progress_vbox");
   2236   AG_progress_update ();
   2237   AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
   2238   AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
   2239   AG_show ("anastasis_gtk_main_control_vbox");
   2240   AG_show ("anastasis_gtk_main_window_save_as_button");
   2241   AG_show ("anastasis_gtk_select_secret_frame");
   2242   AG_show ("anastasis_gtk_main_window_prev_button");
   2243   AG_hide ("anastasis_gtk_main_window_quit_button");
   2244   AG_insensitive ("anastasis_gtk_main_window_forward_button");
   2245 }
   2246 
   2247 
   2248 /**
   2249  * Callback invoked if the a "challenge"-button is clicked.
   2250  *
   2251  * @param object
   2252  * @param user_data points to the UUID of the challenge
   2253  */
   2254 static void
   2255 challenge_button_clicked_cb (GObject *object,
   2256                              gpointer user_data)
   2257 {
   2258   const char *uuid = user_data;
   2259   json_t *args;
   2260 
   2261   (void) object;
   2262   args = GNUNET_JSON_PACK (
   2263     GNUNET_JSON_pack_string ("uuid",
   2264                              uuid));
   2265   AG_freeze ();
   2266   AG_ra = ANASTASIS_redux_action (AG_redux_state,
   2267                                   "select_challenge",
   2268                                   args,
   2269                                   &AG_action_cb,
   2270                                   NULL);
   2271   json_decref (args);
   2272 }
   2273 
   2274 
   2275 /**
   2276  * Generate widget about a @a challenge and add it to the
   2277  * @a challenge_box.
   2278  *
   2279  * @param challenge_box box to expand
   2280  * @param rd our recovery document (to use)
   2281  * @param challenge the challenge to add
   2282  * @return #GNUNET_OK on success, #GNUNET_NO
   2283  *    if the provider is down, #GNUNET_SYSERR on harder
   2284  *    internal failures
   2285  */
   2286 static enum GNUNET_GenericReturnValue
   2287 add_challenge (GtkBox *challenge_box,
   2288                json_t *rd,
   2289                json_t *uchallenge)
   2290 {
   2291   GtkBuilder *builder;
   2292   GtkWindow *window;
   2293   GtkWidget *hbox;
   2294   GtkLabel *label;
   2295   GtkButton *button;
   2296   const char *instructions;
   2297   const char *provider;
   2298   const char *type;
   2299   const char *uuid;
   2300   struct TALER_Amount cost;
   2301   bool async = false;
   2302   bool solved = false;
   2303   struct GNUNET_JSON_Specification uspec[] = {
   2304     GNUNET_JSON_spec_string ("uuid",
   2305                              &uuid),
   2306     GNUNET_JSON_spec_end ()
   2307   };
   2308   const char *iuuid;
   2309   struct GNUNET_JSON_Specification spec[] = {
   2310     GNUNET_JSON_spec_string ("instructions",
   2311                              &instructions),
   2312     GNUNET_JSON_spec_string ("type",
   2313                              &type),
   2314     GNUNET_JSON_spec_string ("url",
   2315                              &provider),
   2316     GNUNET_JSON_spec_string ("uuid",
   2317                              &iuuid),
   2318     GNUNET_JSON_spec_mark_optional (
   2319       GNUNET_JSON_spec_bool ("async",
   2320                              &async),
   2321       NULL),
   2322     GNUNET_JSON_spec_mark_optional (
   2323       GNUNET_JSON_spec_bool ("solved",
   2324                              &solved),
   2325       NULL),
   2326     GNUNET_JSON_spec_end ()
   2327   };
   2328   bool ok = true;
   2329   enum GNUNET_GenericReturnValue ret;
   2330 
   2331   if (GNUNET_OK !=
   2332       GNUNET_JSON_parse (uchallenge,
   2333                          uspec,
   2334                          NULL, NULL))
   2335   {
   2336     GNUNET_break (0);
   2337     return GNUNET_SYSERR;
   2338   }
   2339   {
   2340     json_t *challenges;
   2341     size_t index;
   2342     json_t *challenge;
   2343 
   2344     challenges = json_object_get (rd,
   2345                                   "challenges");
   2346     /* FIXME: change data structure to have 'uuid'
   2347        as the index into the 'challenges' object, instead of this
   2348        'challenges' being an array */
   2349     json_array_foreach (challenges, index, challenge)
   2350     {
   2351       if (GNUNET_OK !=
   2352           GNUNET_JSON_parse (challenge,
   2353                              spec,
   2354                              NULL, NULL))
   2355       {
   2356         GNUNET_break (0);
   2357         return GNUNET_SYSERR;
   2358       }
   2359       if (0 == strcmp (uuid,
   2360                        iuuid))
   2361         break;
   2362     }
   2363   }
   2364   ret = lookup_recovery_cost (provider,
   2365                               type,
   2366                               &cost);
   2367   if (GNUNET_SYSERR == ret)
   2368   {
   2369     GNUNET_break (0);
   2370     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2371                 "Could not find cost of %s at %s\n",
   2372                 type,
   2373                 provider);
   2374     json_dumpf (AG_redux_state,
   2375                 stderr,
   2376                 JSON_INDENT (2));
   2377     return GNUNET_SYSERR;
   2378   }
   2379   if (GNUNET_NO == ret)
   2380     ok = false;
   2381 
   2382   builder = ANASTASIS_GTK_get_new_builder (
   2383     ANASTASIS_GTK_project_data (),
   2384     "anastasis_gtk_challenge_template.ui",
   2385     NULL);
   2386   window = GTK_WINDOW (gtk_builder_get_object (builder,
   2387                                                "challenge_template_window"));
   2388   hbox = GTK_WIDGET (gtk_builder_get_object (builder,
   2389                                              "challenge_hbox"));
   2390   label = GTK_LABEL (gtk_builder_get_object (builder,
   2391                                              "challenge_label"));
   2392   button = GTK_BUTTON (gtk_builder_get_object (builder,
   2393                                                "solve_challenge_button"));
   2394   if (! ok)
   2395     gtk_widget_set_sensitive (GTK_WIDGET (button),
   2396                               false);
   2397   if (solved)
   2398   {
   2399     GtkWidget *solved_img;
   2400 
   2401     solved_img = GTK_WIDGET (gtk_builder_get_object (builder,
   2402                                                      "challenge_solved_image"));
   2403     gtk_widget_set_visible (solved_img,
   2404                             TRUE);
   2405     gtk_widget_set_visible (GTK_WIDGET (button),
   2406                             FALSE);
   2407   }
   2408   else
   2409   {
   2410     g_signal_connect (button,
   2411                       "clicked",
   2412                       G_CALLBACK (challenge_button_clicked_cb),
   2413                       (void *) uuid);
   2414     if (ok)
   2415       gtk_widget_set_tooltip_text (GTK_WIDGET (button),
   2416                                    TALER_amount2s (&cost));
   2417     else
   2418       gtk_widget_set_tooltip_text (GTK_WIDGET (button),
   2419                                    _ ("unknown"));
   2420   }
   2421   {
   2422     GtkWidget *icon;
   2423     char *lab;
   2424 
   2425     GNUNET_asprintf (&lab,
   2426                      "challenge_category_%s_image",
   2427                      type);
   2428     icon = GTK_WIDGET (gtk_builder_get_object (builder,
   2429                                                lab));
   2430     GNUNET_free (lab);
   2431     if (NULL != icon)
   2432       gtk_widget_set_visible (icon,
   2433                               TRUE);
   2434   }
   2435   show_challenge_feedback (uuid,
   2436                            builder);
   2437   if (ok)
   2438     gtk_label_set_text (label,
   2439                         instructions);
   2440   else
   2441     gtk_label_set_text (label,
   2442                         _ ("provider down"));
   2443   g_object_ref (hbox);
   2444   gtk_window_set_child (window,
   2445                         NULL);
   2446   g_object_unref (builder);
   2447   pad_along (GTK_ORIENTATION_VERTICAL,
   2448              hbox,
   2449              15);
   2450   gtk_box_append (challenge_box,
   2451                   hbox);
   2452   g_object_unref (hbox);
   2453   if (! ok)
   2454     return GNUNET_NO;
   2455   return GNUNET_OK;
   2456 }
   2457 
   2458 
   2459 /**
   2460  * Generate widget about a @a policy and add it to the
   2461  * @a policy_box.
   2462  *
   2463  * @param policy_box box to expand
   2464  * @param rd our recovery document (to use)
   2465  * @param pindex policy index (for the label)
   2466  * @param policy policy to add
   2467  * @return true if we should long poll "sync_providers"
   2468  */
   2469 static bool
   2470 add_policy (GtkBox *policy_box,
   2471             json_t *rd,
   2472             size_t pindex,
   2473             json_t *policy)
   2474 {
   2475   GtkBuilder *builder;
   2476   GtkWindow *window;
   2477   GtkWidget *widget;
   2478   GtkLabel *label;
   2479   GtkBox *vbox;
   2480   char *txt;
   2481   json_t *challenges;
   2482   bool ok;
   2483 
   2484   challenges = json_object_get (policy,
   2485                                 "challenges");
   2486   if (NULL == challenges)
   2487   {
   2488     GNUNET_break_op (0);
   2489     AG_error ("Policy did not parse correctly");
   2490     return false;
   2491   }
   2492   builder = ANASTASIS_GTK_get_new_builder (
   2493     ANASTASIS_GTK_project_data (),
   2494     "anastasis_gtk_policy_template.ui",
   2495     NULL);
   2496   window = GTK_WINDOW (gtk_builder_get_object (builder,
   2497                                                "policy_template_window"));
   2498   widget = GTK_WIDGET (gtk_builder_get_object (builder,
   2499                                                "policy_frame"));
   2500   label = GTK_LABEL (gtk_builder_get_object (builder,
   2501                                              "policy_label"));
   2502   vbox = GTK_BOX (gtk_builder_get_object (builder,
   2503                                           "policy_vbox"));
   2504   ok = true;
   2505   {
   2506     size_t index;
   2507     json_t *challenge;
   2508 
   2509     json_array_foreach (challenges, index, challenge)
   2510     {
   2511       enum GNUNET_GenericReturnValue ret;
   2512 
   2513       ret = add_challenge (vbox,
   2514                            rd,
   2515                            challenge);
   2516       if (GNUNET_SYSERR == ret)
   2517       {
   2518         GNUNET_break (0);
   2519         g_object_unref (builder);
   2520         GNUNET_asprintf (&txt,
   2521                          "Failed to process challenge #%u of policy #%u",
   2522                          (unsigned int) (1 + index),
   2523                          (unsigned int) (1 + pindex));
   2524         AG_error ("%s",
   2525                   txt);
   2526         GNUNET_free (txt);
   2527         return false;
   2528       }
   2529       if (GNUNET_NO == ret)
   2530       {
   2531         gtk_widget_set_sensitive (GTK_WIDGET (vbox),
   2532                                   false);
   2533         ok = false;
   2534       }
   2535     }
   2536   }
   2537   if (ok)
   2538   {
   2539     GNUNET_asprintf (&txt,
   2540                      _ ("Policy #%u"),
   2541                      (unsigned int) (1 + pindex));
   2542   }
   2543   else
   2544   {
   2545     GNUNET_asprintf (&txt,
   2546                      _ ("Policy #%u (unavailable, provider down)"),
   2547                      (unsigned int) (1 + pindex));
   2548   }
   2549   gtk_label_set_text (label,
   2550                       txt);
   2551   GNUNET_free (txt);
   2552   g_object_ref (widget);
   2553   gtk_window_set_child (window,
   2554                         NULL);
   2555   g_object_unref (builder);
   2556   pad_along (GTK_ORIENTATION_VERTICAL,
   2557              widget,
   2558              15);
   2559   gtk_box_append (policy_box,
   2560                   widget);
   2561   g_object_unref (widget);
   2562   return ! ok;
   2563 }
   2564 
   2565 
   2566 /**
   2567  * The user must select the next challenge to solve
   2568  * during the recovery process.
   2569  */
   2570 static void
   2571 action_challenge_selecting (void)
   2572 {
   2573   json_t *rd;
   2574   json_t *policies;
   2575   GtkBox *policy_box;
   2576   bool sp = false;
   2577 
   2578   AG_hide_all_frames ();
   2579   AG_stop_long_action ();
   2580   rd = json_object_get (AG_redux_state,
   2581                         "recovery_document");
   2582   policies = json_object_get (rd,
   2583                               "decryption_policies");
   2584   GNUNET_assert (NULL != policies);
   2585   policy_box = GTK_BOX (GCG_get_main_window_object (
   2586                           "anastasis_gtk_policy_vbox"));
   2587   /* remove all existing entries from the box */
   2588   {
   2589     GtkWidget *child;
   2590 
   2591     while (NULL != (child = gtk_widget_get_first_child (
   2592                       GTK_WIDGET (policy_box))))
   2593       gtk_box_remove (policy_box,
   2594                       child);
   2595   }
   2596   {
   2597     size_t pindex;
   2598     json_t *policy;
   2599 
   2600     json_array_foreach (policies, pindex, policy)
   2601     {
   2602       sp |= add_policy (policy_box,
   2603                         rd,
   2604                         pindex,
   2605                         policy);
   2606     }
   2607   }
   2608   if (sp)
   2609   {
   2610     struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS];
   2611 
   2612     la->next_time = GNUNET_TIME_UNIT_ZERO_ABS;
   2613     GNUNET_assert (NULL == la->task);
   2614     la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task,
   2615                                          NULL);
   2616   }
   2617 
   2618   {
   2619     struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES];
   2620     json_t *challenges;
   2621     size_t index;
   2622     json_t *challenge;
   2623 
   2624     challenges = json_object_get (rd,
   2625                                   "challenges");
   2626     GNUNET_assert (NULL != challenges);
   2627     json_array_foreach (challenges, index, challenge)
   2628     {
   2629       bool async = false;
   2630       struct GNUNET_JSON_Specification spec[] = {
   2631         GNUNET_JSON_spec_mark_optional (
   2632           GNUNET_JSON_spec_bool ("async",
   2633                                  &async),
   2634           NULL),
   2635         GNUNET_JSON_spec_end ()
   2636       };
   2637       const json_t *ks;
   2638 
   2639       ks = json_object_get (challenge,
   2640                             "key_share");
   2641       if ( (NULL != ks) &&
   2642            (! json_is_null (ks)) )
   2643         continue;   /* already solved */
   2644       if (GNUNET_OK !=
   2645           GNUNET_JSON_parse (challenge,
   2646                              spec,
   2647                              NULL, NULL))
   2648       {
   2649         GNUNET_break (0);
   2650         json_dumpf (challenge,
   2651                     stderr,
   2652                     JSON_INDENT (2));
   2653         continue;
   2654       }
   2655       if (async &&
   2656           (NULL == la->task) )
   2657       {
   2658         la->next_time = GNUNET_TIME_UNIT_ZERO_ABS;
   2659         la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task,
   2660                                              NULL);
   2661       }
   2662     }
   2663   }
   2664 
   2665   AG_sensitive ("anastasis_gtk_review_policy_treeview");
   2666   AG_show ("anastasis_gtk_progress_vbox");
   2667   AG_progress_update ();
   2668   AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
   2669   AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
   2670   AG_show ("anastasis_gtk_main_control_vbox");
   2671   AG_show ("anastasis_gtk_main_window_save_as_button");
   2672   AG_show ("anastasis_gtk_challenge_frame");
   2673   AG_show ("anastasis_gtk_main_window_prev_button");
   2674   AG_hide ("anastasis_gtk_main_window_quit_button");
   2675   AG_hide ("anastasis_gtk_main_window_forward_button");
   2676 }
   2677 
   2678 
   2679 /**
   2680  * An Anastasis provider requires payment for a challenge.
   2681  * Give opportunity to the user to pay.
   2682  */
   2683 static void
   2684 action_challenge_paying (void)
   2685 {
   2686   json_t *pprs;
   2687   json_t *ppr;
   2688   const char *uuid;
   2689   bool found = false;
   2690   const char *ps;
   2691 
   2692   AG_hide_all_frames ();
   2693   AG_list_clear ("unpaid_qrcodes_liststore");
   2694   pprs = json_object_get (AG_redux_state,
   2695                           "challenge_feedback");
   2696   json_object_foreach (pprs, uuid, ppr)
   2697   {
   2698     const char *state;
   2699     const char *payto = NULL;
   2700     const char *provider = NULL;
   2701     struct GNUNET_JSON_Specification spec[] = {
   2702       GNUNET_JSON_spec_string ("taler_pay_uri",
   2703                                &payto),
   2704       GNUNET_JSON_spec_mark_optional (
   2705         GNUNET_JSON_spec_string ("provider",
   2706                                  &provider),
   2707         NULL),
   2708       GNUNET_JSON_spec_string ("payment_secret",
   2709                                &ps),
   2710       GNUNET_JSON_spec_end ()
   2711     };
   2712 
   2713     /* The state must be tested before the payment details are parsed: the
   2714        other feedback states have no payment secret, and the reducer calls
   2715        this one "taler-payment" (it was "payment" before Anastasis v0.3.0). */
   2716     state = json_string_value (json_object_get (ppr,
   2717                                                 "state"));
   2718     if ( (NULL == state) ||
   2719          (0 != strcmp (state,
   2720                        "taler-payment")) )
   2721       continue; /* this challenge does not want to be paid for */
   2722     if (GNUNET_OK !=
   2723         GNUNET_JSON_parse (ppr,
   2724                            spec,
   2725                            NULL, NULL))
   2726     {
   2727       GNUNET_break (0);
   2728       json_dumpf (ppr,
   2729                   stderr,
   2730                   JSON_INDENT (2));
   2731       continue;
   2732     }
   2733     found = true;
   2734     add_qrcode (payto,
   2735                 (NULL != provider) ? provider : "");
   2736     break;
   2737   }
   2738 
   2739   if (found)
   2740   {
   2741     json_t *args;
   2742     struct GNUNET_TIME_Relative timeout;
   2743 
   2744     timeout = GNUNET_TIME_UNIT_MINUTES;
   2745     GNUNET_assert (NULL == AG_ra);
   2746     args = GNUNET_JSON_PACK (
   2747       GNUNET_JSON_pack_time_rel ("timeout",
   2748                                  timeout),
   2749       GNUNET_JSON_pack_string ("payment_secret",
   2750                                ps));
   2751     AG_ra = ANASTASIS_redux_action (AG_redux_state,
   2752                                     "pay",
   2753                                     args,
   2754                                     &AG_action_cb,
   2755                                     NULL);
   2756     json_decref (args);
   2757   }
   2758   else
   2759   {
   2760     AG_error ("ERROR: Internal error: should pay, but do not know what");
   2761   }
   2762   AG_show ("anastasis_gtk_progress_vbox");
   2763   AG_progress_update ();
   2764   AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
   2765   AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
   2766   AG_show ("anastasis_gtk_pay_frame");
   2767   AG_show ("anastasis_gtk_main_control_vbox");
   2768   AG_sensitive ("anastasis_gtk_main_window_prev_button");
   2769   AG_show ("anastasis_gtk_main_window_prev_button");
   2770   AG_hide ("anastasis_gtk_main_window_forward_button");
   2771 }
   2772 
   2773 
   2774 /**
   2775  * Render challenge feedback for challenge @a uuid_str in a dialog of
   2776  * @a builder in the label under @a target_widget.
   2777  *
   2778  * Useful in case the operation previously failed at the
   2779  * server and we have some useful information to return
   2780  * to the user.
   2781  *
   2782  * FIXME: only do it AFTER the first attempt of the
   2783  * user to enter a code, and/or change what the
   2784  * server returns so we do NOT render a confusing
   2785  * error message on first use!
   2786  *
   2787  * @param builder a builder to get widgets from
   2788  * @param target_widget the widget to update
   2789  * @param uuid_str the UUID to render feedback for
   2790  */
   2791 static void
   2792 render_feedback (GtkBuilder *builder,
   2793                  const char *target_widget,
   2794                  const char *uuid_str)
   2795 {
   2796   json_t *cf;
   2797   json_t *cs;
   2798   const char *state;
   2799   const char *redirect_url = NULL;
   2800   const char *hint = NULL;
   2801   json_t *details = NULL;
   2802   const char *taler_pay_uri = NULL;
   2803   uint32_t ec = TALER_EC_NONE;
   2804   uint32_t http_status = 0;
   2805   bool hide = false;
   2806   struct GNUNET_JSON_Specification spec[] = {
   2807     GNUNET_JSON_spec_string ("state",
   2808                              &state),
   2809     GNUNET_JSON_spec_mark_optional (
   2810       GNUNET_JSON_spec_string ("taler_pay_uri",
   2811                                &taler_pay_uri),
   2812       NULL),
   2813     GNUNET_JSON_spec_mark_optional (
   2814       GNUNET_JSON_spec_json ("details",
   2815                              &details),
   2816       NULL),
   2817     GNUNET_JSON_spec_mark_optional (
   2818       GNUNET_JSON_spec_string ("redirect_url",
   2819                                &redirect_url),
   2820       NULL),
   2821     GNUNET_JSON_spec_mark_optional (
   2822       GNUNET_JSON_spec_string ("hint",
   2823                                &hint),
   2824       NULL),
   2825     GNUNET_JSON_spec_mark_optional (
   2826       GNUNET_JSON_spec_uint32 ("http_status",
   2827                                &http_status),
   2828       NULL),
   2829     GNUNET_JSON_spec_mark_optional (
   2830       GNUNET_JSON_spec_uint32 ("error_code",
   2831                                &ec),
   2832       NULL),
   2833     GNUNET_JSON_spec_end ()
   2834   };
   2835   GtkLabel *elabel;
   2836   char *msg;
   2837 
   2838   cf = json_object_get (AG_redux_state,
   2839                         "challenge_feedback");
   2840   cs = json_object_get (cf,
   2841                         uuid_str);
   2842   if (NULL == cs)
   2843     return;
   2844 
   2845   elabel = GTK_LABEL (gtk_builder_get_object (builder,
   2846                                               target_widget));
   2847   if (NULL == elabel)
   2848   {
   2849     GNUNET_break (0);
   2850     return;
   2851   }
   2852   if (GNUNET_OK !=
   2853       GNUNET_JSON_parse (cs,
   2854                          spec,
   2855                          NULL, NULL))
   2856   {
   2857     GNUNET_break (0);
   2858     gtk_label_set_text (elabel,
   2859                         _ ("INTERNAL ERROR: could not parse state"));
   2860     gtk_widget_set_visible (GTK_WIDGET (elabel),
   2861                             TRUE);
   2862     return;
   2863   }
   2864   if ( (0 == strcmp (state,
   2865                      "hint")) &&
   2866        (NULL != hint) )
   2867   {
   2868     GNUNET_asprintf (&msg,
   2869                      _ ("Hint (#%u): %s"),
   2870                      (unsigned int) http_status,
   2871                      dgettext ("taler-exchange",
   2872                                hint));
   2873   }
   2874   else if ( (0 == strcmp (state,
   2875                           "details")) &&
   2876             (NULL != details) )
   2877   {
   2878     uint32_t code;
   2879     const char *dhint = NULL;
   2880     const char *detail = NULL;
   2881     struct GNUNET_JSON_Specification ispec[] = {
   2882       GNUNET_JSON_spec_uint32 ("code",
   2883                                &code),
   2884       GNUNET_JSON_spec_mark_optional (
   2885         GNUNET_JSON_spec_string ("hint",
   2886                                  &dhint),
   2887         NULL),
   2888       GNUNET_JSON_spec_mark_optional (
   2889         GNUNET_JSON_spec_string ("detail",
   2890                                  &detail),
   2891         NULL),
   2892       GNUNET_JSON_spec_end ()
   2893     };
   2894 
   2895     if (GNUNET_OK !=
   2896         GNUNET_JSON_parse (details,
   2897                            ispec,
   2898                            NULL, NULL))
   2899     {
   2900       GNUNET_break (0);
   2901       json_dumpf (details,
   2902                   stderr,
   2903                   JSON_INDENT (2));
   2904       msg = GNUNET_strdup (
   2905         _ ("ERROR: failed to parse server JSON instructions"));
   2906     }
   2907     else
   2908     {
   2909       const char *ihint;
   2910       const char *type = "Error";
   2911 
   2912       switch (code)
   2913       {
   2914       case TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED:
   2915         hide = true;
   2916         type = "Hint";
   2917         break;
   2918       default:
   2919         break;
   2920       }
   2921       ihint = TALER_ErrorCode_get_hint (code);
   2922       if ( (NULL != hint) &&
   2923            ( (NULL == ihint) ||
   2924              ('<' == ihint[0])) )
   2925         ihint = dhint;           /* use server hint */
   2926       ihint = dgettext ("taler-exchange",
   2927                         ihint);
   2928       if (NULL == detail)
   2929       {
   2930         if (NULL == ihint)
   2931           GNUNET_asprintf (&msg,
   2932                            "%s #%u",
   2933                            type,
   2934                            (unsigned int) code);
   2935         else
   2936           GNUNET_asprintf (&msg,
   2937                            "%s #%u: %s",
   2938                            type,
   2939                            (unsigned int) code,
   2940                            ihint);
   2941       }
   2942       else
   2943       {
   2944         if (NULL == ihint)
   2945           GNUNET_asprintf (&msg,
   2946                            "%s #%u (%s)",
   2947                            type,
   2948                            (unsigned int) code,
   2949                            detail);
   2950         else
   2951           GNUNET_asprintf (&msg,
   2952                            "%s #%u: %s (%s)",
   2953                            type,
   2954                            (unsigned int) code,
   2955                            ihint,
   2956                            detail);
   2957       }
   2958     }
   2959   }
   2960   else
   2961   {
   2962     GNUNET_asprintf (&msg,
   2963                      "ERROR: state `%s` with HTTP Status %u",
   2964                      state,
   2965                      (unsigned int) http_status);
   2966   }
   2967   gtk_label_set_text (elabel,
   2968                       msg);
   2969   GNUNET_free (msg);
   2970   if (hide)
   2971     gtk_widget_set_visible (GTK_WIDGET (elabel),
   2972                             FALSE);
   2973   else
   2974     gtk_widget_set_visible (GTK_WIDGET (elabel),
   2975                             TRUE);
   2976   GNUNET_JSON_parse_free (spec);
   2977 }
   2978 
   2979 
   2980 /**
   2981  * Open dialog to allow user to answer security question.
   2982  *
   2983  * @param details details about the challenge
   2984  * @return the dialog object, or NULL on error
   2985  */
   2986 static GtkDialog *
   2987 diag_question (const json_t *details)
   2988 {
   2989   GtkBuilder *builder;
   2990   GtkDialog *ad;
   2991 
   2992   builder = ANASTASIS_GTK_get_new_builder (
   2993     ANASTASIS_GTK_project_data (),
   2994     "anastasis_gtk_challenge_question.ui",
   2995     NULL);
   2996   if (NULL == builder)
   2997   {
   2998     GNUNET_break (0);
   2999     return NULL;
   3000   }
   3001   ad = GTK_DIALOG (gtk_builder_get_object (builder,
   3002                                            "anastasis_gtk_c_question_dialog"));
   3003   {
   3004     GtkLabel *label;
   3005     const char *instructions;
   3006 
   3007     label = GTK_LABEL (gtk_builder_get_object (builder,
   3008                                                "security_question_label"));
   3009     instructions = json_string_value (json_object_get (details,
   3010                                                        "instructions"));
   3011     gtk_label_set_text (label,
   3012                         instructions);
   3013   }
   3014   {
   3015     const char *uuid_str;
   3016 
   3017     uuid_str = json_string_value (json_object_get (details,
   3018                                                    "uuid-display"));
   3019     render_feedback (builder,
   3020                      "anastasis_gtk_c_question_error_label",
   3021                      uuid_str);
   3022   }
   3023   return ad;
   3024 }
   3025 
   3026 
   3027 /**
   3028  * Create a dialog for the user to enter a PIN code.
   3029  *
   3030  * @param details details about the dialog to render
   3031  * @return dialog object
   3032  */
   3033 static GtkDialog *
   3034 diag_code (const json_t *details)
   3035 {
   3036   GtkBuilder *builder;
   3037   const char *instructions;
   3038   const char *uuid_str;
   3039   struct GNUNET_JSON_Specification spec[] = {
   3040     GNUNET_JSON_spec_string ("instructions",
   3041                              &instructions),
   3042     GNUNET_JSON_spec_string ("uuid-display",
   3043                              &uuid_str),
   3044     GNUNET_JSON_spec_end ()
   3045   };
   3046 
   3047   if (GNUNET_OK !=
   3048       GNUNET_JSON_parse (details,
   3049                          spec,
   3050                          NULL, NULL))
   3051   {
   3052     GNUNET_break (0);
   3053     json_dumpf (details,
   3054                 stderr,
   3055                 JSON_INDENT (2));
   3056     return NULL;
   3057   }
   3058 
   3059   builder = ANASTASIS_GTK_get_new_builder (
   3060     ANASTASIS_GTK_project_data (),
   3061     "anastasis_gtk_challenge_code.ui",
   3062     NULL);
   3063   if (NULL == builder)
   3064   {
   3065     GNUNET_break (0);
   3066     return NULL;
   3067   }
   3068   {
   3069     GtkLabel *label;
   3070 
   3071     label = GTK_LABEL (gtk_builder_get_object (builder,
   3072                                                "challenge_instructions_label"));
   3073     gtk_label_set_text (label,
   3074                         instructions);
   3075     label = GTK_LABEL (gtk_builder_get_object (builder,
   3076                                                "anastasis_gtk_c_challenge_label"));
   3077     gtk_label_set_text (label,
   3078                         uuid_str);
   3079     render_feedback (builder,
   3080                      "anastasis_gtk_c_code_error_label",
   3081                      uuid_str);
   3082   }
   3083   {
   3084     GtkDialog *ad;
   3085 
   3086     ad = GTK_DIALOG (gtk_builder_get_object (builder,
   3087                                              "anastasis_gtk_c_code_dialog"));
   3088     return ad;
   3089   }
   3090 }
   3091 
   3092 
   3093 /**
   3094  * Create a dialog for the user to enter an TOTP code.
   3095  *
   3096  * @param details details about the dialog to render
   3097  * @return dialog object
   3098  */
   3099 static GtkDialog *
   3100 diag_totp (const json_t *details)
   3101 {
   3102   GtkBuilder *builder;
   3103   const char *instructions;
   3104   const char *uuid_str;
   3105   struct GNUNET_JSON_Specification spec[] = {
   3106     GNUNET_JSON_spec_string ("instructions",
   3107                              &instructions),
   3108     GNUNET_JSON_spec_string ("uuid-display",
   3109                              &uuid_str),
   3110     GNUNET_JSON_spec_end ()
   3111   };
   3112 
   3113   if (GNUNET_OK !=
   3114       GNUNET_JSON_parse (details,
   3115                          spec,
   3116                          NULL, NULL))
   3117   {
   3118     GNUNET_break (0);
   3119     json_dumpf (details,
   3120                 stderr,
   3121                 JSON_INDENT (2));
   3122     return NULL;
   3123   }
   3124 
   3125   builder = ANASTASIS_GTK_get_new_builder (
   3126     ANASTASIS_GTK_project_data (),
   3127     "anastasis_gtk_challenge_totp.ui",
   3128     NULL);
   3129   if (NULL == builder)
   3130   {
   3131     GNUNET_break (0);
   3132     return NULL;
   3133   }
   3134   {
   3135     GtkLabel *label;
   3136 
   3137     label = GTK_LABEL (gtk_builder_get_object (builder,
   3138                                                "challenge_instructions_label"));
   3139     gtk_label_set_text (label,
   3140                         instructions);
   3141   }
   3142   render_feedback (builder,
   3143                    "anastasis_gtk_c_totp_error_label",
   3144                    uuid_str);
   3145   {
   3146     GtkDialog *ad;
   3147 
   3148     ad = GTK_DIALOG (gtk_builder_get_object (builder,
   3149                                              "anastasis_gtk_c_totp_dialog"));
   3150     return ad;
   3151   }
   3152 }
   3153 
   3154 
   3155 /**
   3156  * Create a dialog for the user to make an IBAN transfer.
   3157  *
   3158  * @param details details about the dialog to render
   3159  * @return dialog object
   3160  */
   3161 static GtkDialog *
   3162 diag_iban (const json_t *details)
   3163 {
   3164   GtkBuilder *builder;
   3165   struct TALER_Amount amount;
   3166   const char *credit_iban;
   3167   const char *business;
   3168   const char *subject;
   3169   const char *uuid_str;
   3170   const char *debit_iban_hint;
   3171   struct GNUNET_JSON_Specification spec[] = {
   3172     GNUNET_JSON_spec_string ("uuid",
   3173                              &uuid_str),
   3174     GNUNET_JSON_spec_string ("instructions",
   3175                              &debit_iban_hint),
   3176     GNUNET_JSON_spec_end ()
   3177   };
   3178 
   3179   if (GNUNET_OK !=
   3180       GNUNET_JSON_parse (details,
   3181                          spec,
   3182                          NULL, NULL))
   3183   {
   3184     GNUNET_break (0);
   3185     json_dumpf (details,
   3186                 stderr,
   3187                 JSON_INDENT (2));
   3188     return NULL;
   3189   }
   3190   {
   3191     json_t *cf = json_object_get (AG_redux_state,
   3192                                   "challenge_feedback");
   3193     json_t *ci = json_object_get (cf,
   3194                                   uuid_str);
   3195     json_t *cd = json_object_get (ci,
   3196                                   "details");
   3197     struct GNUNET_JSON_Specification ispec[] = {
   3198       TALER_JSON_spec_amount_any ("challenge_amount",
   3199                                   &amount),
   3200       GNUNET_JSON_spec_string ("credit_iban",
   3201                                &credit_iban),
   3202       GNUNET_JSON_spec_string ("business_name",
   3203                                &business),
   3204       GNUNET_JSON_spec_string ("wire_transfer_subject",
   3205                                &subject),
   3206       GNUNET_JSON_spec_end ()
   3207     };
   3208 
   3209     if ( (NULL == cd) ||
   3210          (GNUNET_OK !=
   3211           GNUNET_JSON_parse (cd,
   3212                              ispec,
   3213                              NULL, NULL)) )
   3214     {
   3215       GNUNET_break (0);
   3216       json_dumpf (AG_redux_state,
   3217                   stderr,
   3218                   JSON_INDENT (2));
   3219       return NULL;
   3220     }
   3221   }
   3222 
   3223   builder = ANASTASIS_GTK_get_new_builder (
   3224     ANASTASIS_GTK_project_data (),
   3225     "anastasis_gtk_challenge_iban.ui",
   3226     NULL);
   3227   if (NULL == builder)
   3228   {
   3229     GNUNET_break (0);
   3230     return NULL;
   3231   }
   3232   {
   3233     GtkLabel *label;
   3234 
   3235     label = GTK_LABEL (gtk_builder_get_object (builder,
   3236                                                "debit_account_label"));
   3237     gtk_label_set_text (label,
   3238                         debit_iban_hint);
   3239     label = GTK_LABEL (gtk_builder_get_object (builder,
   3240                                                "credit_account_label"));
   3241     gtk_label_set_text (label,
   3242                         credit_iban);
   3243     label = GTK_LABEL (gtk_builder_get_object (builder,
   3244                                                "provider_name_label"));
   3245     gtk_label_set_text (label,
   3246                         business);
   3247     label = GTK_LABEL (gtk_builder_get_object (builder,
   3248                                                "wire_transfer_subject_label"));
   3249     gtk_label_set_text (label,
   3250                         subject);
   3251     label = GTK_LABEL (gtk_builder_get_object (builder,
   3252                                                "amount_label"));
   3253     gtk_label_set_text (label,
   3254                         TALER_amount2s (&amount));
   3255   }
   3256 
   3257   {
   3258     GtkDialog *ad;
   3259 
   3260     ad = GTK_DIALOG (gtk_builder_get_object (builder,
   3261                                              "anastasis_gtk_c_iban_dialog"));
   3262     return ad;
   3263   }
   3264 }
   3265 
   3266 
   3267 /**
   3268  * The user wants to solve the selected challenge. Launch the
   3269  * dialog to allow the user to enter the solution.
   3270  */
   3271 static void
   3272 action_challenge_solving (void)
   3273 {
   3274   struct
   3275   {
   3276     const char *type;
   3277     GtkDialog *(*ctor)(const json_t *details);
   3278   } type_map [] = {
   3279     { .type = gettext_noop ("question"),
   3280       .ctor = &diag_question },
   3281     { .type = gettext_noop ("sms"),
   3282       .ctor = &diag_code },
   3283     { .type = gettext_noop ("post"),
   3284       .ctor = &diag_code },
   3285     { .type = gettext_noop ("email"),
   3286       .ctor = &diag_code },
   3287     { .type = gettext_noop ("iban"),
   3288       .ctor = &diag_iban },
   3289     { .type = gettext_noop ("totp"),
   3290       .ctor = &diag_totp },
   3291     { .type = NULL,
   3292       .ctor = NULL }
   3293   };
   3294   const char *type;
   3295   GtkDialog *diag;
   3296   const char *uuid;
   3297   const json_t *challenge;
   3298 
   3299   /* This state only puts a dialog on top of the frame the previous state
   3300      rendered, so the progress list is the one thing that must be updated. */
   3301   AG_progress_update ();
   3302   uuid = json_string_value (json_object_get (AG_redux_state,
   3303                                              "selected_challenge_uuid"));
   3304   if (NULL == uuid)
   3305   {
   3306     GNUNET_break (0);
   3307     return;
   3308   }
   3309   challenge = find_challenge_by_uuid (uuid);
   3310   if (NULL == challenge)
   3311   {
   3312     GNUNET_break (0);
   3313     return;
   3314   }
   3315   type = json_string_value (json_object_get (challenge,
   3316                                              "type"));
   3317   if (NULL == type)
   3318   {
   3319     GNUNET_break (0);
   3320     return;
   3321   }
   3322   /* create dialog based on challenge type */
   3323   diag = NULL;
   3324   for (unsigned int i = 0; NULL != type_map[i].type; i++)
   3325   {
   3326     if (0 != strcmp (type_map[i].type,
   3327                      type))
   3328       continue;
   3329     diag = type_map[i].ctor (challenge);
   3330     break;
   3331   }
   3332   if (NULL == diag)
   3333   {
   3334     GNUNET_break (0);
   3335     return;
   3336   }
   3337   /* show dialog */
   3338   {
   3339     GtkRoot *toplevel;
   3340     GtkWidget *box;
   3341 
   3342     box = GTK_WIDGET (GCG_get_main_window_object (
   3343                         "anastasis_gtk_policy_vbox"));
   3344     toplevel = gtk_widget_get_root (box);
   3345     gtk_window_set_transient_for (GTK_WINDOW (diag),
   3346                                   GTK_WINDOW (toplevel));
   3347     gtk_window_present (GTK_WINDOW (diag));
   3348   }
   3349 }
   3350 
   3351 
   3352 /**
   3353  * The recovery process was finished. Show the recovered secret to the
   3354  * user.
   3355  */
   3356 static void
   3357 action_recovery_finished (void)
   3358 {
   3359   const char *mime = NULL;
   3360   const char *text = NULL;
   3361   const char *name = NULL;
   3362   void *data = NULL;
   3363   size_t data_size = 0;
   3364   const json_t *cs;
   3365   struct GNUNET_JSON_Specification spec[] = {
   3366     GNUNET_JSON_spec_mark_optional (
   3367       GNUNET_JSON_spec_string ("mime",
   3368                                &mime),
   3369       NULL),
   3370     GNUNET_JSON_spec_mark_optional (
   3371       GNUNET_JSON_spec_string ("text",
   3372                                &text),
   3373       NULL),
   3374     GNUNET_JSON_spec_mark_optional (
   3375       GNUNET_JSON_spec_varsize ("value",
   3376                                 &data,
   3377                                 &data_size),
   3378       NULL),
   3379     GNUNET_JSON_spec_end ()
   3380   };
   3381   GdkPixbuf *pb;
   3382   GtkImage *img;
   3383 
   3384   AG_hide_all_frames ();
   3385   name = json_string_value (json_object_get (json_object_get (AG_redux_state,
   3386                                                               "recovery_information"),
   3387                                              "secret_name"));
   3388 
   3389   cs = json_object_get (AG_redux_state,
   3390                         "core_secret");
   3391   GNUNET_assert (NULL != cs);
   3392   GNUNET_assert (GNUNET_OK ==
   3393                  GNUNET_JSON_parse (cs,
   3394                                     spec,
   3395                                     NULL, NULL));
   3396   AG_hide ("anastasis_gtk_secret_copy_button");
   3397   update_label ("anastasis_gtk_secret_value_label",
   3398                 text);
   3399   if ( (NULL != name) &&
   3400        (0 != strlen (name)) )
   3401     update_label ("recovery_secret_name_value_label",
   3402                   name);
   3403   else
   3404     update_label ("recovery_secret_name_value_label",
   3405                   _ ("You did not name this secret"));
   3406   if ( (0 == strncasecmp (mime,
   3407                           "text/",
   3408                           strlen ("text/"))) ||
   3409        (0 == strncasecmp (mime,
   3410                           "image/",
   3411                           strlen ("image/"))) ||
   3412        (NULL != text) )
   3413   {
   3414     /* images and text can be copied */
   3415     AG_show ("anastasis_gtk_secret_copy_button");
   3416   }
   3417   pb = NULL;
   3418   img = GTK_IMAGE (GCG_get_main_window_object (
   3419                      "anastasis_gtk_secret_qr_image"));
   3420   if (NULL != text)
   3421   {
   3422     pb = AG_setup_qrcode (GTK_WIDGET (img),
   3423                           text,
   3424                           strlen (text));
   3425   }
   3426   else
   3427   {
   3428     pb = AG_setup_qrcode (GTK_WIDGET (img),
   3429                           data,
   3430                           data_size);
   3431   }
   3432   if (NULL != pb)
   3433   {
   3434     AG_image_set_from_pixbuf (img,
   3435                               pb);
   3436     g_object_unref (pb);
   3437   }
   3438   else
   3439   {
   3440     AG_hide ("anastasis_gtk_secret_qr_image");
   3441   }
   3442   GNUNET_JSON_parse_free (spec);
   3443   AG_hide ("anastasis_gtk_progress_vbox");
   3444   AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
   3445   AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
   3446   AG_show ("anastasis_gtk_completed_frame");
   3447   AG_hide ("anastasis_gtk_backup_complete_box");
   3448   AG_hide ("anastasis_gtk_success_backup_label");
   3449   AG_show ("anastasis_gtk_success_recovery_box");
   3450   AG_show ("anastasis_gtk_main_control_vbox");
   3451   AG_hide ("anastasis_gtk_main_window_save_as_button");
   3452   AG_show ("anastasis_gtk_restart_button");
   3453   AG_show ("anastasis_gtk_main_window_quit_button");
   3454   AG_hide ("anastasis_gtk_main_window_prev_button");
   3455   AG_hide ("anastasis_gtk_main_window_forward_button");
   3456 }
   3457 
   3458 
   3459 /**
   3460  * Function called with the results of #ANASTASIS_redux_action.
   3461  *
   3462  * @param cls closure
   3463  * @param error_code Error code
   3464  * @param response new state as result or config information of a provider
   3465  */
   3466 void
   3467 AG_action_cb (void *cls,
   3468               enum TALER_ErrorCode error_code,
   3469               json_t *response)
   3470 {
   3471   struct DispatchItem actions[] = {
   3472     { .state = "CONTINENT_SELECTING",
   3473       .action = &action_continent_selecting },
   3474     { .state = "COUNTRY_SELECTING",
   3475       .action = &action_country_selecting },
   3476     { .state = "USER_ATTRIBUTES_COLLECTING",
   3477       .action = &action_user_attributes_collecting },
   3478     { .state = "AUTHENTICATIONS_EDITING",
   3479       .action = &action_authentications_editing },
   3480     { .state = "POLICIES_REVIEWING",
   3481       .action = &action_policies_reviewing },
   3482     { .state = "SECRET_EDITING",
   3483       .action = &action_secret_editing },
   3484     { .state = "TRUTHS_PAYING",
   3485       .action = &action_truths_paying },
   3486     { .state = "POLICIES_PAYING",
   3487       .action = &action_policies_paying },
   3488     { .state = "BACKUP_FINISHED",
   3489       .action = &action_backup_finished },
   3490     { .state = "SECRET_SELECTING",
   3491       .action = &action_secret_selecting },
   3492     { .state = "CHALLENGE_SELECTING",
   3493       .action = &action_challenge_selecting },
   3494     { .state = "CHALLENGE_PAYING",
   3495       .action = &action_challenge_paying },
   3496     { .state = "CHALLENGE_SOLVING",
   3497       .action = &action_challenge_solving },
   3498     { .state = "RECOVERY_FINISHED",
   3499       .action = &action_recovery_finished },
   3500     { .state = NULL,
   3501       .action = NULL }
   3502   };
   3503 
   3504   (void) cls;
   3505   AG_ra = NULL;
   3506   AG_thaw ();
   3507 #if DEBUG
   3508   fprintf (stderr,
   3509            "Action result %d\n",
   3510            error_code);
   3511   json_dumpf (response,
   3512               stderr,
   3513               JSON_INDENT (2));
   3514   fprintf (stderr,
   3515            "END action result %d\n",
   3516            error_code);
   3517 #endif
   3518   if (TALER_EC_NONE != error_code)
   3519   {
   3520     /* FIXME: maybe also render 'detail'
   3521        if present in state?
   3522        See for example ~ anastasis_api_backup_redux.c:3082 */
   3523     AG_error ("Error #%d: %s\n",
   3524               (int) error_code,
   3525               TALER_ErrorCode_get_hint (error_code));
   3526     if (AG_in_action)
   3527     {
   3528       GNUNET_break (0);
   3529       return;
   3530     }
   3531   }
   3532   if ( (NULL != json_object_get (response,
   3533                                  "backup_state")) ||
   3534        (NULL != json_object_get (response,
   3535                                  "recovery_state")) )
   3536   {
   3537     json_decref (AG_redux_state);
   3538     AG_stop_long_action ();
   3539     AG_redux_state = json_incref (response);
   3540   }
   3541   if ( (TALER_EC_ANASTASIS_TRUTH_UNKNOWN == error_code) ||
   3542        (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED == error_code) )
   3543   {
   3544     /* special case: do not remain in previous (challenge selected)
   3545        state but revert to challenge selecting */
   3546     GNUNET_assert (0 ==
   3547                    json_object_set_new (AG_redux_state,
   3548                                         "recovery_state",
   3549                                         json_string ("CHALLENGE_SELECTING")));
   3550   }
   3551   if ( (TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED == error_code) ||
   3552        (TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED == error_code) ||
   3553        (TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED == error_code) )
   3554   {
   3555     /* special case: do not remain in previous (enter identity)
   3556        state but advance to secret selecting */
   3557     GNUNET_assert (0 ==
   3558                    json_object_set_new (AG_redux_state,
   3559                                         "recovery_state",
   3560                                         json_string ("SECRET_SELECTING")));
   3561   }
   3562   AG_in_action = true;
   3563   if (GNUNET_OK ==
   3564       AG_dispatch (actions))
   3565   {
   3566     AG_in_action = false;
   3567     return;
   3568   }
   3569   AG_in_action = false;
   3570   AG_error ("Unhandled state `%s/%s'",
   3571             json_string_value (json_object_get (AG_redux_state,
   3572                                                 "backup_state")),
   3573             json_string_value (json_object_get (AG_redux_state,
   3574                                                 "recovery_state")));
   3575   json_dumpf (AG_redux_state,
   3576               stderr,
   3577               JSON_INDENT (2));
   3578   json_decref (AG_redux_state);
   3579   AG_redux_state = NULL;
   3580   AG_hide_all_frames ();
   3581   AG_show ("anastasis_gtk_start_frame");
   3582 }