anastasis-gtk

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

anastasis-gtk_action.c (105705B)


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