anastasis-gtk

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

anastasis-gtk_action.c (105337B)


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