summaryrefslogtreecommitdiff
path: root/src/anastasis/anastasis-gtk_action.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/anastasis/anastasis-gtk_action.c')
-rw-r--r--src/anastasis/anastasis-gtk_action.c3109
1 files changed, 3109 insertions, 0 deletions
diff --git a/src/anastasis/anastasis-gtk_action.c b/src/anastasis/anastasis-gtk_action.c
new file mode 100644
index 0000000..f0c28c3
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_action.c
@@ -0,0 +1,3109 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020, 2021 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Anastasis; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ * @file src/anastasis/anastasis-gtk_action.c
+ * @brief Handle redux action results
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_dispatch.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include "anastasis-gtk_progress.h"
+#include <jansson.h>
+#include <qrencode.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+
+/**
+ * Are we currently processing an action?
+ */
+bool AG_in_action;
+
+/**
+ * Are we currently editing the secret?
+ */
+bool AG_in_secret_editing;
+
+/**
+ * Are we currently editing the secret name?
+ */
+bool AG_in_secret_name_editing;
+
+
+#define DEBUG 0
+
+/**
+ * Prepare window for selection of the continent.
+ */
+static void
+action_continent_selecting (void)
+{
+ GtkListStore *country_liststore = GTK_LIST_STORE (
+ GCG_get_main_window_object ("country_liststore"));
+
+ AG_hide_all_frames ();
+ gtk_list_store_clear (country_liststore);
+ {
+ GtkListStore *continent_liststore;
+ json_t *continents;
+
+ continent_liststore
+ = GTK_LIST_STORE (
+ GCG_get_main_window_object ("continent_liststore"));
+ gtk_list_store_clear (continent_liststore);
+ continents = json_object_get (AG_redux_state,
+ "continents");
+ if (NULL != continents)
+ {
+ json_t *continent;
+ size_t index;
+
+ json_array_foreach (continents,
+ index,
+ continent)
+ {
+ const char *name;
+ const char *name_i18n;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("name",
+ &name),
+ TALER_JSON_spec_i18n_str ("name",
+ &name_i18n),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (continent,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+
+ gtk_list_store_insert_with_values (continent_liststore,
+ NULL,
+ -1,
+ AG_CMC_CONTINENT_NAME,
+ name,
+ AG_CMC_CONTINENT_NAME_I18N,
+ name_i18n,
+ -1);
+ GNUNET_JSON_parse_free (spec);
+ }
+ }
+ }
+
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ if (NULL != json_object_get (AG_redux_state,
+ "backup_state"))
+ {
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ }
+ else
+ {
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ }
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_continent_frame");
+ AG_show ("anastasis_gtk_continent_selection_image");
+}
+
+
+/**
+ * Prepare window for selection of the country.
+ */
+static void
+action_country_selecting (void)
+{
+ GtkListStore *country_liststore;
+ json_t *countries;
+ const char *selected_country;
+
+ AG_hide_all_frames ();
+ countries = json_object_get (AG_redux_state,
+ "countries");
+ selected_country
+ = json_string_value (json_object_get (AG_redux_state,
+ "selected_country"));
+ country_liststore = GTK_LIST_STORE (
+ GCG_get_main_window_object ("country_liststore"));
+ gtk_list_store_clear (country_liststore);
+ {
+ json_t *country;
+ size_t index;
+
+ json_array_foreach (countries, index, country)
+ {
+ GtkTreeIter iter;
+ const char *code;
+ const char *name;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("code",
+ &code),
+ TALER_JSON_spec_i18n_str ("name",
+ &name),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (country,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+
+ gtk_list_store_insert_with_values (
+ country_liststore,
+ &iter,
+ -1,
+ AG_CCMC_COUNTRY_NAME,
+ name,
+ AG_CCMC_COUNTRY_CODE,
+ code,
+ -1);
+ if ( (NULL != selected_country) &&
+ (NULL != code) &&
+ (0 == strcmp (code,
+ selected_country)) )
+ {
+ GtkTreeView *tv;
+ GtkTreeSelection *sel;
+
+ tv = GTK_TREE_VIEW (GCG_get_main_window_object (
+ "anastasis_gtk_country_treeview"));
+ sel = gtk_tree_view_get_selection (tv);
+ gtk_tree_selection_select_iter (sel,
+ &iter);
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ }
+
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ if (NULL != json_object_get (AG_redux_state,
+ "backup_state"))
+ {
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ }
+ else
+ {
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ }
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_country_selection_image");
+ AG_show ("anastasis_gtk_continent_frame");
+ AG_show ("anastasis_gtk_continent_selection_image");
+ AG_show ("anastasis_gtk_country_selection_image");
+}
+
+
+/**
+ * Create widget for "string" type user attributes.
+ *
+ * @param details not used
+ * @return widget to be used for string entry
+ */
+static GtkWidget *
+ctor_entry (const json_t *details)
+{
+ (void) details;
+ return gtk_entry_new ();
+}
+
+
+/**
+ * Create widget for "date" type user attributes.
+ *
+ * @param details not used
+ * @return widget to be used for date entry
+ */
+static GtkWidget *
+ctor_date (const json_t *details)
+{
+ (void) details;
+ return gtk_calendar_new ();
+}
+
+
+/**
+ * Create widget of @a type under @a uuid with @a label and @a tooltip
+ * for the identity attribute editing dialog. Stores all created widgets
+ * in the #AG_entry_attributes and ensures that we never create the same
+ * widget (by @a uuid) twice.
+ *
+ * @param uh hash of unique ID of the widget, only create one per UUID
+ * @param type type of the widget to create
+ * @param label label to use
+ * @param tooltip tooltip to use
+ * @param id_attr potential additional inputs for the widget creation
+ * @return created widget
+ */
+static GtkWidget *
+create_attribute_widget (const struct GNUNET_HashCode *uh,
+ const char *type,
+ const char *label,
+ const char *tooltip,
+ const json_t *id_attr)
+{
+ static struct
+ {
+ const char *type;
+ GtkWidget *(*ctor)(const json_t *details);
+ } type_map [] = {
+ { .type = "string",
+ .ctor = &ctor_entry },
+ { .type = "date",
+ .ctor = &ctor_date },
+ { .type = NULL,
+ .ctor = NULL }
+ };
+ GtkWidget *w;
+
+ w = GNUNET_CONTAINER_multihashmap_get (AG_entry_attributes,
+ uh);
+ if (NULL != w)
+ {
+ GtkWidget *p;
+
+ gtk_widget_show (w);
+ p = gtk_widget_get_parent (w);
+ gtk_widget_show (p);
+ p = gtk_widget_get_parent (p);
+ gtk_widget_show (p);
+ return w;
+ }
+ for (unsigned int i = 0; NULL != type_map[i].type; i++)
+ {
+ GtkBox *box;
+ GtkBox *vbox;
+
+ if (0 != strcmp (type_map[i].type,
+ type))
+ continue;
+ w = type_map[i].ctor (id_attr);
+ GNUNET_assert (NULL != w);
+ gtk_widget_show (w);
+ box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
+ 5 /* spacing in pixels */));
+ vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL,
+ 5 /* spacing in pixels */));
+ {
+ GtkWidget *glabel;
+
+ glabel = gtk_label_new (label);
+ gtk_box_pack_start (box, /* parent */
+ glabel, /* child */
+ false, /* expand */
+ false, /* fill */
+ 5); /* padding */
+ gtk_widget_show (glabel);
+ }
+ GNUNET_assert (0 <
+ g_signal_connect (w,
+ "changed",
+ G_CALLBACK (&AG_identity_changed),
+ NULL));
+ gtk_widget_set_tooltip_text (w,
+ tooltip);
+ gtk_box_pack_start (box, /* parent */
+ w, /* child */
+ false, /* expand */
+ false, /* fill */
+ 5); /* padding */
+ gtk_widget_show (GTK_WIDGET (box));
+ gtk_box_pack_start (vbox, /* parent */
+ GTK_WIDGET (box), /* child */
+ false, /* expand */
+ false, /* fill */
+ 5); /* padding */
+ {
+ GtkWidget *private_widget;
+ GtkBuilder *builder;
+ GtkBin *bin;
+
+ builder =
+ GNUNET_GTK_get_new_builder ("this_stays_private.glade",
+ NULL);
+ GNUNET_break (NULL != builder);
+ /* load frame */
+ bin = GTK_BIN (gtk_builder_get_object (builder,
+ "private_dummy_window"));
+ GNUNET_break (NULL != bin);
+ private_widget = gtk_bin_get_child (bin);
+ GNUNET_break (NULL != private_widget);
+ g_object_ref (private_widget);
+ gtk_container_remove (GTK_CONTAINER (bin),
+ private_widget);
+ gtk_widget_destroy (GTK_WIDGET (bin));
+ g_object_unref (G_OBJECT (builder));
+ gtk_box_pack_start (vbox, /* parent */
+ private_widget, /* child */
+ false, /* expand */
+ false, /* fill */
+ 5); /* padding */
+ }
+ gtk_widget_show (GTK_WIDGET (vbox));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes,
+ uh,
+ w,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ {
+ GtkBox *pbox;
+
+ pbox = GTK_BOX (GCG_get_main_window_object (
+ "anastasis_gtk_identity_vbox"));
+ gtk_box_pack_start (pbox, /* parent */
+ GTK_WIDGET (vbox), /* child */
+ false, /* expand */
+ false, /* fill */
+ 5); /* padding */
+
+ }
+ return w;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "FATAL: required attribute type `%s' not supported\n",
+ type);
+ GNUNET_assert (0);
+ return NULL;
+}
+
+
+/**
+ * Expand base @a name of a widget based on the @a type to
+ * create the name of the widget with the data.
+ *
+ * @param name base name of the widget
+ * @param type type of the widget
+ * @return NULL on error
+ */
+static char *
+expand_name (const char *name,
+ const char *type)
+{
+ static struct
+ {
+ const char *type;
+ const char *suffix;
+ } type_map [] = {
+ { .type = "string",
+ .suffix = "entry" },
+ { .type = "date",
+ .suffix = "cal" },
+ { .type = NULL,
+ .suffix = NULL }
+ };
+ char *data_widget;
+
+ for (unsigned int i = 0; NULL != type_map[i].type; i++)
+ {
+ if (0 != strcmp (type_map[i].type,
+ type))
+ continue;
+ GNUNET_asprintf (&data_widget,
+ "%s_%s",
+ name,
+ type_map[i].suffix);
+ return data_widget;
+ }
+ return NULL;
+}
+
+
+/**
+ * Update GtkLabel @a name, setting text to @a value.
+ *
+ * @param name Glade-name of widget to update
+ * @param value value to set
+ */
+static void
+update_label (const char *name,
+ const char *value)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (GCG_get_main_window_object (name));
+ if (NULL == label)
+ return;
+ if (NULL == value)
+ {
+ gtk_widget_hide (GTK_WIDGET (label));
+ }
+ else
+ {
+ gtk_label_set_text (label,
+ value);
+ gtk_widget_show (GTK_WIDGET (label));
+ }
+}
+
+
+/**
+ * FIXME.
+ */
+static void
+action_user_attributes_collecting (void)
+{
+ const json_t *id_attributes;
+
+ AG_hide_all_frames ();
+ id_attributes = json_object_get (AG_redux_state,
+ "required_attributes");
+ GNUNET_assert (NULL != id_attributes);
+ AG_hide_children ("anastasis_gtk_identity_vbox");
+ {
+ size_t index;
+ json_t *id_attr;
+
+ json_array_foreach (id_attributes, index, id_attr)
+ {
+ const char *widget_name = NULL;
+ const char *attr_tooltip = NULL;
+ const char *attr_label = NULL;
+ const char *attr_type;
+ const char *attr_uuid;
+ const char *attr_name;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("widget",
+ &widget_name)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("tooltip",
+ &attr_tooltip)),
+ GNUNET_JSON_spec_string ("type",
+ &attr_type),
+ GNUNET_JSON_spec_string ("uuid",
+ &attr_uuid),
+ GNUNET_JSON_spec_string ("name",
+ &attr_name),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_i18n_str ("label",
+ &attr_label)),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_HashCode uh;
+ GtkWidget *w = NULL;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (id_attr,
+ spec,
+ NULL, NULL));
+ GNUNET_CRYPTO_hash (attr_uuid,
+ strlen (attr_uuid),
+ &uh);
+ if (NULL != widget_name)
+ {
+ char *data_name;
+
+ data_name = expand_name (widget_name,
+ attr_type);
+ w = GTK_WIDGET (GCG_get_main_window_object (data_name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Widget `%s' not found, will try to create dynamic replacement\n",
+ data_name);
+ }
+ GNUNET_free (data_name);
+ }
+ if ( (NULL != widget_name) &&
+ (NULL != w) &&
+ (NULL != attr_label) )
+ {
+ char *label_widget;
+
+ GNUNET_asprintf (&label_widget,
+ "%s_label",
+ widget_name);
+ update_label (label_widget,
+ attr_label);
+ GNUNET_free (label_widget);
+ }
+ if ( (NULL != widget_name) &&
+ (NULL != w) )
+ {
+ char *box_widget;
+ GObject *box;
+
+ GNUNET_asprintf (&box_widget,
+ "%s_box",
+ widget_name);
+ box = GCG_get_main_window_object (box_widget);
+ if (NULL == box)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Widget `%s' not found, cannot show entry element. BAD.\n",
+ box_widget);
+ }
+ else
+ {
+ AG_show (box_widget);
+ AG_show_children (box_widget);
+ }
+ GNUNET_free (box_widget);
+ }
+ if ( (NULL != w) &&
+ (! GNUNET_CONTAINER_multihashmap_contains (AG_entry_attributes,
+ &uh)) )
+ {
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes,
+ &uh,
+ w,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+ if (NULL == w)
+ w = create_attribute_widget (&uh,
+ attr_type,
+ attr_label,
+ attr_tooltip,
+ id_attr);
+ if (NULL != w)
+ {
+ json_t *ia;
+ json_t *val;
+
+ ia = json_object_get (AG_redux_state,
+ "identity_attributes");
+ val = json_object_get (ia,
+ attr_name);
+ if ( (NULL != val) &&
+ (! json_is_null (val)) )
+ AG_import_attribute_data (w,
+ attr_type,
+ val);
+ }
+ }
+ }
+
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_identity_changed ();
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ if (NULL != json_object_get (AG_redux_state,
+ "backup_state"))
+ {
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ }
+ else
+ {
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ }
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_identity_frame");
+ AG_focus ("anastasis_gtk_ia_full_name_entry");
+ AG_show ("anastasis_gtk_user_attributes_image");
+}
+
+
+static void
+activate_by_method (json_t *methods)
+{
+ size_t index;
+ const json_t *method;
+
+ json_array_foreach (methods,
+ index,
+ method)
+ {
+ const char *type;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (method,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+
+ {
+ char btn[64];
+
+ GNUNET_snprintf (btn,
+ sizeof (btn),
+ "anastasis_gtk_btn_add_auth_%s",
+ type);
+ AG_sensitive (btn);
+ }
+ }
+}
+
+
+static void
+action_authentications_editing (void)
+{
+ json_t *aps;
+ bool have_auth;
+
+ AG_hide_all_frames ();
+ AG_insensitive_children ("anastasis_gtk_add_auth_button_box");
+ aps = json_object_get (AG_redux_state,
+ "authentication_providers");
+ {
+ const json_t *ap;
+ const char *provider_url;
+
+ json_object_foreach (aps,
+ provider_url,
+ ap)
+ {
+ uint32_t ec = 0;
+ uint32_t hc = 0;
+ json_t *methods;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("error_code",
+ &ec)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("methods",
+ &methods)),
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &hc),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ap,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ switch (hc)
+ {
+ case MHD_HTTP_OK:
+ if (NULL == methods)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Provider `%s' has no authentication methods?\n",
+ provider_url);
+ break;
+ }
+ activate_by_method (methods);
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Status of provider `%s' is %u/%u\n",
+ provider_url,
+ (unsigned int) ec,
+ (unsigned int) hc);
+ break;
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ }
+
+ have_auth = false;
+ {
+ GtkListStore *ls;
+ json_t *ams;
+ size_t index;
+ json_t *am;
+
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "authentication_methods_liststore"));
+ gtk_list_store_clear (ls);
+ ams = json_object_get (AG_redux_state,
+ "authentication_methods");
+ json_array_foreach (ams,
+ index,
+ am)
+ {
+ const char *type;
+ const char *instructions;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_string ("instructions",
+ &instructions),
+ GNUNET_JSON_spec_end ()
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (am,
+ spec,
+ NULL, NULL));
+ gtk_list_store_insert_with_values (
+ ls,
+ NULL,
+ -1,
+ AG_AMMC_TYPE, type,
+ AG_AMMC_VISUALIZATION, instructions,
+ AG_AMMC_INDEX, (guint) index,
+ -1);
+ have_auth = true;
+ }
+ }
+
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ if (have_auth)
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ else
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_b_authentication_frame");
+ AG_show ("anastasis_gtk_b_authentication_methods_image");
+}
+
+
+/**
+ * Lookup @a method_cost of authentication method @a type at @a provider in our
+ * #AG_redux_state.
+ *
+ * @param provider URL of provider
+ * @param type authentication method to look for
+ * @param[out] method_cost cost to return
+ * @return #GNUNET_OK on success
+ */
+static int
+lookup_recovery_cost (const char *provider,
+ const char *type,
+ struct TALER_Amount *method_cost)
+{
+ json_t *aps;
+ json_t *ap;
+ json_t *methods;
+ size_t index;
+ json_t *method;
+
+ memset (method_cost,
+ 0,
+ sizeof (struct TALER_Amount));
+ aps = json_object_get (AG_redux_state,
+ "authentication_providers");
+ GNUNET_assert (NULL != aps);
+ ap = json_object_get (aps,
+ provider);
+ if (NULL == ap)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ methods = json_object_get (ap,
+ "methods");
+ json_array_foreach (methods, index, method)
+ {
+ struct TALER_Amount fee;
+ const char *mtype;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("usage_fee",
+ &fee),
+ GNUNET_JSON_spec_string ("type",
+ &mtype),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (method,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcmp (mtype,
+ type))
+ {
+ *method_cost = fee;
+ return GNUNET_OK;
+ }
+ }
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+static void
+action_policies_reviewing (void)
+{
+ json_t *policies;
+ size_t pindex;
+ json_t *policy;
+ GtkTreeStore *ts;
+
+ AG_hide_all_frames ();
+ ts = GTK_TREE_STORE (GCG_get_main_window_object ("policy_review_treestore"));
+ gtk_tree_store_clear (ts);
+ policies = json_object_get (AG_redux_state,
+ "policies");
+ GNUNET_assert (NULL != policies);
+ json_array_foreach (policies, pindex, policy)
+ {
+ GtkTreeIter piter;
+ json_t *methods;
+ struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_json ("methods",
+ &methods),
+ GNUNET_JSON_spec_end ()
+ };
+ size_t mindex;
+ json_t *method;
+ char *summary;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (policy,
+ pspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ gtk_tree_store_insert_with_values (ts,
+ &piter,
+ NULL, /* no parent */
+ -1, /* append */
+ -1);
+
+ summary = NULL;
+ json_array_foreach (methods, mindex, method)
+ {
+ uint32_t imethod;
+ const char *provider;
+ struct GNUNET_JSON_Specification mspec[] = {
+ GNUNET_JSON_spec_string ("provider",
+ &provider),
+ GNUNET_JSON_spec_uint32 ("authentication_method",
+ &imethod),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *jmethods;
+ json_t *jmethod;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (method,
+ mspec,
+ NULL, NULL))
+ {
+ json_dumpf (method,
+ stderr,
+ JSON_INDENT (2));
+ GNUNET_break (0);
+ continue;
+ }
+ jmethods = json_object_get (AG_redux_state,
+ "authentication_methods");
+ jmethod = json_array_get (jmethods,
+ imethod);
+ {
+ GtkTreeIter miter;
+ const char *instructions;
+ const char *type;
+ struct GNUNET_JSON_Specification tspec[] = {
+ GNUNET_JSON_spec_string ("instructions",
+ &instructions),
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_end ()
+ };
+ struct TALER_Amount method_cost;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jmethod,
+ tspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (GNUNET_OK !=
+ lookup_recovery_cost (provider,
+ type,
+ &method_cost))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ gtk_tree_store_insert_with_values (
+ ts,
+ &miter,
+ &piter, /* parent */
+ -1, /* append */
+ AG_PRMC_POLICY_NAME,
+ instructions,
+ AG_PRMC_METHOD_TYPE,
+ type,
+ AG_PRMC_COST,
+ TALER_amount2s (&method_cost),
+ AG_PRMC_PROVIDER_URL,
+ provider,
+ AG_PRMC_EXPIRATION_TIME_STR,
+ "N/A",
+ AG_PRMC_POLICY_INDEX,
+ (guint) pindex,
+ AG_PRMC_IS_CHALLENGE,
+ TRUE,
+ AG_PRMC_METHOD_INDEX,
+ (guint) mindex,
+ -1);
+ if (NULL == summary)
+ {
+ summary = GNUNET_strdup (type);
+ }
+ else
+ {
+ char *tmp;
+
+ GNUNET_asprintf (&tmp,
+ "%s + %s",
+ summary,
+ type);
+ GNUNET_free (summary);
+ summary = tmp;
+ }
+ }
+ GNUNET_JSON_parse_free (mspec);
+ }
+ if (NULL != summary)
+ {
+ gtk_tree_store_set (ts,
+ &piter,
+ AG_PRMC_POLICY_NAME, summary,
+ AG_PRMC_EXPIRATION_TIME_STR,
+ "N/A",
+ AG_PRMC_POLICY_INDEX,
+ (guint) pindex,
+ AG_PRMC_IS_CHALLENGE,
+ FALSE,
+ -1);
+ GNUNET_free (summary);
+ }
+ GNUNET_JSON_parse_free (pspec);
+ }
+ {
+ GtkTreeView *tv;
+
+ tv = GTK_TREE_VIEW (GCG_get_main_window_object (
+ "anastasis_gtk_review_policy_treeview"));
+ gtk_tree_view_expand_all (tv);
+ }
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_show ("anastasis_gtk_b_policy_frame");
+ AG_show ("anastasis_gtk_b_policies_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * Update GtkEntry @a name, setting text to @a value.
+ *
+ * @param name Glade-name of widget to update
+ * @param value value to set
+ */
+static void
+update_entry (const char *name,
+ const char *value)
+{
+ GtkEntry *entry;
+ const char *old;
+
+ if (NULL == value)
+ value = "";
+ entry = GTK_ENTRY (GCG_get_main_window_object (name));
+ if (NULL == entry)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "`%s' is not a GtkEntry!\n",
+ name);
+ return;
+ }
+ old = gtk_entry_get_text (entry);
+ if (NULL == old)
+ old = "";
+ if (0 != strcmp (old,
+ value))
+ gtk_entry_set_text (entry,
+ value);
+}
+
+
+/**
+ * Function called when we begin editing the secret.
+ */
+static void
+action_secret_editing (void)
+{
+ struct GNUNET_TIME_Absolute exp_time;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_absolute_time ("expiration",
+ &exp_time),
+ GNUNET_JSON_spec_end ()
+ };
+ struct tm tv;
+ bool is_free = false;
+
+ AG_hide_all_frames ();
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (AG_redux_state,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ AG_error ("State did not parse correctly: lacks expiration");
+ return;
+ }
+
+ {
+ time_t t;
+
+ t = exp_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+ GNUNET_assert (NULL !=
+ localtime_r (&t,
+ &tv));
+ }
+
+ {
+ json_t *fees;
+
+ fees = json_object_get (AG_redux_state,
+ "upload_fees");
+ if (0 == json_array_size (fees))
+ {
+ update_label ("backup_fee_value_label",
+ _ ("gratis"));
+ is_free = true;
+ }
+ else
+ {
+ char *val = GNUNET_strdup ("");
+ size_t pos;
+ json_t *fee;
+ struct TALER_Amount a;
+
+ json_array_foreach (fees, pos, fee)
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("fee",
+ &a),
+ GNUNET_JSON_spec_end ()
+ };
+ char *tmp;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (fees,
+ stderr,
+ JSON_INDENT (2));
+ continue;
+ }
+
+ GNUNET_asprintf (&tmp,
+ "%s%s%llu.%u %s",
+ val,
+ strlen (val) > 0 ? "\n" : "",
+ (unsigned long long) a.value,
+ (unsigned int) a.fraction,
+ a.currency);
+ GNUNET_free (val);
+ val = tmp;
+ }
+ update_label ("backup_fee_value_label",
+ val);
+ GNUNET_free (val);
+ }
+ }
+ {
+ char estr[128];
+
+ if (is_free)
+ GNUNET_assert (sizeof (estr) >
+ strftime (estr,
+ sizeof (estr),
+ "%d %B %Y",
+ &tv));
+ else
+ GNUNET_assert (sizeof (estr) >
+ strftime (estr,
+ sizeof (estr),
+ "%d %B",
+ &tv));
+ update_label ("expiration_date_without_year_label",
+ estr);
+ }
+
+ {
+ GtkSpinButton *sb;
+ unsigned int this_year;
+ unsigned int exp_year;
+
+ sb = GTK_SPIN_BUTTON (GCG_get_main_window_object (
+ "expiration_year_spin_button"));
+ if (is_free)
+ gtk_widget_hide (GTK_WIDGET (sb));
+ else
+ gtk_widget_show (GTK_WIDGET (sb));
+ this_year = GNUNET_TIME_get_current_year ();
+ /* We allow at most 5 years into the future */
+ gtk_spin_button_set_range (sb,
+ this_year + 1,
+ this_year + 6);
+ exp_year = tv.tm_year + 1900;
+ gtk_spin_button_set_value (sb,
+ (double) exp_year);
+ }
+
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ AG_sensitive ("anastasis_gtk_enter_secret_open_button");
+ AG_sensitive ("anastasis_gtk_enter_secret_entry");
+ AG_hide ("anastasis_gtk_secret_clear_file_button");
+ AG_hide ("anastasis_gtk_secret_clear_text_button");
+ AG_hide ("anastasis_gtk_secret_file_name_hbox");
+ AG_show ("anastasis_gtk_secret_file_chooser_hbox");
+ {
+ const char *name = "";
+ json_t *jsecret = NULL;
+ const char *filename = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("core_secret",
+ &jsecret)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("secret_name",
+ &name)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (AG_redux_state,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (AG_redux_state,
+ stderr,
+ JSON_INDENT (2));
+ AG_error ("State did not parse correctly: invalid secret data");
+ return;
+ }
+ if (! AG_in_secret_name_editing)
+ update_entry ("anastasis_gtk_secret_name_entry",
+ name);
+ if (NULL != jsecret)
+ {
+ const char *mime = NULL;
+ const char *text = NULL;
+ struct GNUNET_JSON_Specification sspec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("text",
+ &text)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("mime",
+ &mime)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("filename",
+ &filename)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jsecret,
+ sspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (AG_redux_state,
+ stderr,
+ JSON_INDENT (2));
+ AG_error ("State did not parse correctly: invalid secret data");
+ return;
+ }
+ if ( (NULL != text) &&
+ (0 == strlen (text)) )
+ text = NULL;
+ if (! AG_in_secret_editing)
+ update_entry ("anastasis_gtk_enter_secret_entry",
+ text);
+ update_label ("anastasis_gtk_secret_file_name_label",
+ filename);
+ if ( (NULL != text) ||
+ (NULL != filename) )
+ {
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ }
+ if (NULL != text)
+ {
+ AG_insensitive ("anastasis_gtk_enter_secret_open_button");
+ AG_show ("anastasis_gtk_secret_clear_text_button");
+ }
+ if (NULL != filename)
+ {
+ AG_insensitive ("anastasis_gtk_enter_secret_entry");
+ AG_show ("anastasis_gtk_secret_clear_file_button");
+ AG_show ("anastasis_gtk_secret_file_name_hbox");
+ AG_hide ("anastasis_gtk_secret_file_chooser_hbox");
+ }
+ GNUNET_JSON_parse_free (sspec);
+ }
+ else
+ {
+ /* secret is NULL */
+ update_entry ("anastasis_gtk_enter_secret_entry",
+ NULL);
+
+ }
+ if ( (NULL == name) ||
+ (0 == strlen (name) ) )
+ AG_focus ("anastasis_gtk_secret_name_entry");
+ else if (NULL == filename)
+ AG_focus ("anastasis_gtk_enter_secret_entry");
+ GNUNET_JSON_parse_free (spec);
+ }
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_show ("anastasis_gtk_enter_secret_frame");
+ AG_show ("anastasis_gtk_enter_secret_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * Create the QR code image for our zone.
+ *
+ * @param scale factor for scaling up the size of the image to create
+ * @param text text to encode
+ * @return NULL on error
+ */
+static GdkPixbuf *
+create_qrcode (unsigned int scale,
+ const char *text,
+ size_t text_size)
+{
+ QRinput *qri;
+ QRcode *qrc;
+ GdkPixbuf *pb;
+ guchar *pixels;
+ int n_channels;
+ const char *dir;
+ char *fn;
+ unsigned int size;
+
+ qri = QRinput_new2 (0, QR_ECLEVEL_M);
+ if (NULL == qri)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2");
+ return NULL;
+ }
+ /* first try encoding as uppercase-only alpha-numerical
+ QR code (much smaller encoding); if that fails, also
+ try using binary encoding (in case nick contains
+ special characters). */
+ if ((0 != QRinput_append (qri,
+ QR_MODE_AN,
+ text_size,
+ (unsigned char *) text)) &&
+ (0 != QRinput_append (qri,
+ QR_MODE_8,
+ text_size,
+ (unsigned char *) text)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRinput_append");
+ return NULL;
+ }
+ qrc = QRcode_encodeInput (qri);
+ if (NULL == qrc)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRcode_encodeInput");
+ QRinput_free (qri);
+ return NULL;
+ }
+ /* We use a trick to create a pixbuf in a way that works for both Gtk2 and
+ Gtk3 by loading a dummy file from disk; all other methods are not portable
+ to both Gtk2 and Gtk3. */
+ dir = GNUNET_GTK_get_data_dir ();
+ GNUNET_asprintf (&fn,
+ "%s%s",
+ dir,
+ "qr_dummy.png");
+ size = (qrc->width + 8) * scale;
+ size += 8 - (size % 8);
+ pb = gdk_pixbuf_new_from_file_at_size (fn,
+ size,
+ size,
+ NULL);
+ GNUNET_free (fn);
+ if (NULL == pb)
+ {
+ QRcode_free (qrc);
+ QRinput_free (qri);
+ return NULL;
+ }
+ pixels = gdk_pixbuf_get_pixels (pb);
+ n_channels = gdk_pixbuf_get_n_channels (pb);
+ for (unsigned int x = 4 * scale; x < size - 4 * scale; x++)
+ for (unsigned int y = 4 * scale; y < size - 4 * scale; y++)
+ {
+ unsigned int xx = x - 4 * scale;
+ unsigned int yy = y - 4 * scale;
+ unsigned int ss = size - 8 * scale;
+ unsigned int off =
+ (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width;
+ for (int c = 0; c < n_channels; c++)
+ pixels[(y * size + x) * n_channels + c] =
+ (0 == (qrc->data[off] & 1)) ? 0xFF : 0;
+ }
+ QRcode_free (qrc);
+ QRinput_free (qri);
+ return pb;
+}
+
+
+/**
+ * Create the QR code image for our zone.
+ *
+ * @param text text to encode
+ * @return NULL on error
+ */
+static GdkPixbuf *
+setup_qrcode (const char *widget,
+ const char *text,
+ size_t text_size)
+{
+ GtkWidget *image;
+ GdkScreen *screen;
+ GtkSettings *settings;
+ gint dpi;
+ int scale;
+
+ image = GTK_WIDGET (GCG_get_main_window_object (widget));
+ if (NULL == image)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ /* adjust scale to screen resolution */
+ screen = gtk_widget_get_screen (GTK_WIDGET (image));
+ settings = gtk_settings_get_for_screen (screen);
+ g_object_get (G_OBJECT (settings),
+ "gtk-xft-dpi",
+ &dpi,
+ NULL);
+ if (-1 == dpi)
+ scale = 2;
+ else if (dpi >= 122800)
+ scale = 4;
+ else if (dpi >= 98304)
+ scale = 3;
+ else
+ scale = 2;
+ return create_qrcode (3 * scale,
+ text,
+ text_size);
+}
+
+
+static void
+action_truths_paying (void)
+{
+ json_t *pprs;
+ size_t index;
+ json_t *pt;
+ GtkListStore *ls;
+
+ AG_hide_all_frames ();
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "unpaid_qrcodes_liststore"));
+ gtk_list_store_clear (ls);
+ pprs = json_object_get (AG_redux_state,
+ "payments");
+ json_array_foreach (pprs, index, pt)
+ {
+ const char *payto = json_string_value (pt);
+ GdkPixbuf *pb;
+
+ if (NULL == payto)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ pb = setup_qrcode ("unpaid_qr_treeview",
+ payto,
+ strlen (payto));
+ if (NULL == pb)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
+ payto);
+ continue;
+ }
+
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1, /* append */
+ AG_UQRMC_QR_IMAGE, pb,
+ AG_UQRMC_URL, payto,
+ AG_UQRMC_PROVIDER, "",
+ -1);
+ g_object_unref (pb);
+ }
+
+ {
+ json_t *args;
+ struct GNUNET_TIME_Relative timeout;
+
+ timeout = GNUNET_TIME_UNIT_MINUTES;
+ GNUNET_assert (NULL == AG_ra);
+ args = json_pack ("{s:o}",
+ "timeout",
+ GNUNET_JSON_from_time_rel (timeout));
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "pay",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+ AG_show ("anastasis_gtk_pay_frame");
+ AG_show ("anastasis_gtk_pay_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+static void
+action_policies_paying (void)
+{
+ json_t *pprs;
+ size_t index;
+ json_t *ppr;
+ GtkListStore *ls;
+
+ AG_hide_all_frames ();
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "unpaid_qrcodes_liststore"));
+ gtk_list_store_clear (ls);
+ pprs = json_object_get (AG_redux_state,
+ "policy_payment_requests");
+ json_array_foreach (pprs, index, ppr)
+ {
+ const char *provider;
+ const char *payto;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("provider",
+ &provider),
+ GNUNET_JSON_spec_string ("payto",
+ &payto),
+ GNUNET_JSON_spec_end ()
+ };
+ GdkPixbuf *pb;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ppr,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ pb = setup_qrcode ("unpaid_qr_treeview",
+ payto,
+ strlen (payto));
+ if (NULL == pb)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
+ payto);
+ continue;
+ }
+
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1, /* append */
+ AG_UQRMC_QR_IMAGE, pb,
+ AG_UQRMC_URL, payto,
+ AG_UQRMC_PROVIDER, provider,
+ -1);
+ g_object_unref (pb);
+ }
+ {
+ json_t *args;
+ struct GNUNET_TIME_Relative timeout;
+
+ timeout = GNUNET_TIME_UNIT_MINUTES;
+ GNUNET_assert (NULL == AG_ra);
+ args = json_pack ("{s:o}",
+ "timeout",
+ GNUNET_JSON_from_time_rel (timeout));
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "pay",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+ AG_show ("anastasis_gtk_pay_frame");
+ AG_show ("anastasis_gtk_pay_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * The backup has finished, show the providers, policy version and
+ * expiration dates.
+ */
+static void
+action_backup_finished (void)
+{
+ json_t *det;
+ json_t *se;
+ const char *url;
+ GtkListStore *ls;
+ struct GNUNET_TIME_Absolute mexp;
+
+ AG_hide_all_frames ();
+ det = json_object_get (AG_redux_state,
+ "success_details");
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "backup_provider_liststore"));
+ gtk_list_store_clear (ls);
+ mexp = GNUNET_TIME_UNIT_FOREVER_ABS;
+ json_object_foreach (det, url, se)
+ {
+ struct GNUNET_TIME_Absolute pexp;
+ uint64_t version;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("policy_version",
+ &version),
+ GNUNET_JSON_spec_absolute_time ("policy_expiration",
+ &pexp),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (se,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ AG_error ("State did not parse correctly");
+ return;
+ }
+ mexp = GNUNET_TIME_absolute_min (mexp,
+ pexp);
+ gtk_list_store_insert_with_values (
+ ls,
+ NULL,
+ -1, /* append */
+ AG_BPC_PROVIDER_URL,
+ url,
+ AG_BPC_BACKUP_VERSION,
+ (guint64) version,
+ AG_BPC_EXPIRATION_TIME_STR,
+ GNUNET_STRINGS_absolute_time_to_string (pexp),
+ AG_BPC_SUCCESS_FLAG,
+ true,
+ -1);
+ }
+ {
+ struct tm tv;
+ char estr[128];
+ time_t t;
+
+ /* be more conservative in what we show */
+ mexp = GNUNET_TIME_absolute_subtract (mexp,
+ GNUNET_TIME_UNIT_DAYS);
+ t = mexp.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+ GNUNET_assert (NULL !=
+ localtime_r (&t,
+ &tv));
+ GNUNET_assert (sizeof (estr) >
+ strftime (estr,
+ sizeof (estr),
+ "%d %B %Y",
+ &tv));
+ update_label ("backup_expiration_date_label",
+ GNUNET_STRINGS_absolute_time_to_string (mexp));
+ }
+ AG_hide ("anastasis_gtk_progress_vbox");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_completed_frame");
+ AG_show ("anastasis_gtk_backup_complete_box");
+ AG_hide ("anastasis_gtk_success_recovery_box");
+ AG_show ("anastasis_gtk_success_backup_label");
+ AG_hide ("anastasis_gtk_success_recovery_box");
+ AG_show ("anastasis_gtk_completed_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_hide ("anastasis_gtk_main_window_save_as_button");
+ AG_show ("anastasis_gtk_restart_button");
+ AG_show ("anastasis_gtk_main_window_quit_button");
+ AG_hide ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+static const json_t *
+find_challenge_by_uuid (const char *uuid)
+{
+ json_t *rd;
+ json_t *cs;
+ size_t index;
+ json_t *c;
+
+ rd = json_object_get (AG_redux_state,
+ "recovery_document");
+ cs = json_object_get (rd,
+ "cs");
+ json_array_foreach (cs, index, c)
+ {
+ const char *u;
+
+ u = json_string_value (json_object_get (c,
+ "uuid"));
+ if (NULL == u)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcmp (u,
+ uuid))
+ return c;
+ }
+ return NULL;
+}
+
+
+/**
+ * Find out offset of challenge with the given @a uuid in the
+ * "cs" array.
+ *
+ * @param[out] roff set to the offset
+ * @param[out] cost set to the cost of the challenge
+ */
+static int
+get_challenge_offset (const char *uuid,
+ guint *roff,
+ struct TALER_Amount *cost)
+{
+ const json_t *recdoc;
+ const json_t *cs;
+ const json_t *c;
+ size_t off;
+
+ recdoc = json_object_get (AG_redux_state,
+ "recovery_document");
+ GNUNET_assert (NULL != recdoc);
+ cs = json_object_get (recdoc,
+ "cs");
+ GNUNET_assert (NULL != cs);
+ json_array_foreach (cs, off, c)
+ {
+ const char *provider;
+ const char *type;
+ const char *u;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_string ("url",
+ &provider),
+ GNUNET_JSON_spec_string ("uuid",
+ &u),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (c,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcmp (uuid,
+ u))
+ {
+ *roff = off;
+ return lookup_recovery_cost (provider,
+ type,
+ cost);
+ }
+ }
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Translate the @a state into a localized, human-readable
+ * string.
+ *
+ * @param state a challenge state, as a string
+ */
+static const char *
+translate_state (const char *state)
+{
+ struct
+ {
+ const char *in;
+ const char *out;
+ } state_map [] = {
+ { .in = "solved",
+ .out = _ ("challenge solved") },
+ { .in = "payment",
+ .out = _ ("make payment") },
+ { .in = "body",
+ .out = _ ("unexpected reply") },
+ { .in = "hint",
+ .out = _ ("read hint") },
+ { .in = "details",
+ .out = _ ("read feedback") },
+ { .in = "redirect",
+ .out = _ ("open link") },
+ { .in = "server-failure",
+ .out = _ ("wait, provider failed") },
+ { .in = "truth-unknown",
+ .out = _ ("challenge unknown") },
+ { .in = "rate-limit-exceeded",
+ .out = _ ("wait, tries exceeded") },
+ { .in = NULL,
+ .out = NULL }
+ };
+
+ for (unsigned int i = 0; NULL != state_map[i].in; i++)
+ {
+ if (0 != strcmp (state_map[i].in,
+ state))
+ continue;
+ return state_map[i].out;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not localize unexpected state `%s'\n",
+ state);
+ return state;
+}
+
+
+/**
+ * Test if the given @a uuid is already in @a model,
+ * and if so, return the position at @a iter.
+ *
+ * @param model the list store with the challenges
+ * @param uuid challenge UUID to look for
+ * @param[out] iter iter to set
+ * @return true if @a iter was set
+ */
+static bool
+challenge_ls_has_uuid (GtkTreeModel *model,
+ const char *uuid,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter pos;
+
+ if (gtk_tree_model_get_iter_first (model,
+ &pos))
+ do {
+ char *u;
+
+ gtk_tree_model_get (model,
+ &pos,
+ AG_CSM_CHALLENGE_UUID, &u,
+ -1);
+ if (0 == strcmp (uuid,
+ u))
+ {
+ g_free (u);
+ if (NULL != iter)
+ *iter = pos;
+ return true;
+ }
+ g_free (u);
+ }
+ while (gtk_tree_model_iter_next (model,
+ &pos));
+ return false;
+}
+
+
+/**
+ * Update the list store with the challenge feedback.
+ */
+static void
+show_challenge_feedback (void)
+{
+ GtkListStore *ls;
+ json_t *cf;
+ const json_t *f;
+ const char *uuid;
+
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "challenge_status_liststore"));
+ cf = json_object_get (AG_redux_state,
+ "challenge_feedback");
+ json_object_foreach (cf, uuid, f)
+ {
+ const char *state;
+ const char *redirect_url = NULL;
+ const char *hint = NULL;
+ json_t *details = NULL;
+ const char *taler_pay_uri = NULL;
+ uint32_t ec = 0;
+ uint32_t http_status = 0;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("state",
+ &state),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("taler_pay_uri",
+ &taler_pay_uri)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("details",
+ &details)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("redirect_url",
+ &redirect_url)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("hint",
+ &hint)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("error_code",
+ &ec)),
+ GNUNET_JSON_spec_end ()
+ };
+ struct TALER_Amount cost;
+ guint off;
+ GdkPixbuf *qr = NULL;
+ const char *emsg = NULL;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (f,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (f,
+ stderr,
+ JSON_INDENT (2));
+ continue;
+ }
+ if (GNUNET_OK !=
+ get_challenge_offset (uuid,
+ &off,
+ &cost))
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ continue;
+ }
+ if (NULL != taler_pay_uri)
+ {
+ qr = setup_qrcode ("anastasis_gtk_challenge_status_treeview",
+ taler_pay_uri,
+ strlen (taler_pay_uri));
+ }
+ if (TALER_EC_NONE != ec)
+ emsg = TALER_ErrorCode_get_hint (ec);
+ if (0 == strcmp (state,
+ "hint"))
+ emsg = dgettext ("taler-exchange",
+ hint);
+ if (0 == strcmp (state,
+ "details"))
+ {
+ emsg = dgettext ("taler-exchange",
+ json_string_value (json_object_get (details,
+ "hint")));
+ }
+ {
+ GtkTreeIter iter;
+ bool found;
+
+ found = challenge_ls_has_uuid (GTK_TREE_MODEL (ls),
+ uuid,
+ &iter);
+ if (found)
+ gtk_list_store_set (
+ ls,
+ &iter,
+ AG_CSM_SOLVED, 0 == strcmp (state, "solved"),
+ AG_CSM_STATUS, translate_state (state),
+ AG_CSM_PAYMENT_QR_CODE, qr,
+ AG_CSM_ERROR_MESSAGE, emsg,
+ AG_CSM_PAYTO_URI, taler_pay_uri,
+ AG_CSM_PAYING, NULL != taler_pay_uri,
+ AG_CSM_HAS_ERROR, NULL != emsg,
+ AG_CSM_COST, TALER_amount2s (&cost),
+ AG_CSM_REDIRECT_URL, redirect_url,
+ AG_CSM_HAVE_REDIRECT, NULL != redirect_url,
+ AG_CSM_NOT_SOLVED, 0 != strcmp (state, "solved"),
+ -1);
+ else
+ gtk_list_store_insert_with_values (
+ ls,
+ NULL,
+ -1, /* append */
+ AG_CSM_CHALLENGE_OFFSET, (guint) (off + 1),
+ AG_CSM_CHALLENGE_UUID, uuid,
+ AG_CSM_SOLVED, 0 == strcmp (state, "solved"),
+ AG_CSM_STATUS, translate_state (state),
+ AG_CSM_PAYMENT_QR_CODE, qr,
+ AG_CSM_ERROR_MESSAGE, emsg,
+ AG_CSM_PAYTO_URI, taler_pay_uri,
+ AG_CSM_PAYING, NULL != taler_pay_uri,
+ AG_CSM_HAS_ERROR, NULL != emsg,
+ AG_CSM_COST, TALER_amount2s (&cost),
+ AG_CSM_REDIRECT_URL, redirect_url,
+ AG_CSM_HAVE_REDIRECT, NULL != redirect_url,
+ AG_CSM_NOT_SOLVED, 0 != strcmp (state, "solved"),
+ -1);
+ GNUNET_JSON_parse_free (spec);
+ }
+ }
+}
+
+
+/**
+ * FIXME.
+ */
+static void
+action_secret_selecting (void)
+{
+ json_t *ri;
+ json_t *re;
+
+ AG_hide ("anastasis_gtk_start_frame");
+ if (AG_have_error)
+ AG_show ("anastasis_gtk_error_label");
+ AG_hide ("anastasis_gtk_challenge_frame");
+ AG_hide ("anastasis_gtk_identity_frame");
+ AG_hide ("anastasis_gtk_secret_identification_vbox");
+ re = json_object_get (AG_redux_state,
+ "recovery_error");
+ if (NULL != re)
+ {
+ bool offline;
+ const char *hint;
+ struct GNUNET_JSON_Specification espec[] = {
+ GNUNET_JSON_spec_bool ("offline",
+ &offline),
+ GNUNET_JSON_spec_string ("hint",
+ &hint),
+ GNUNET_JSON_spec_end ()
+ };
+
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (re,
+ espec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ AG_error ("'recovery_error' did not parse correctly");
+ return;
+ }
+ AG_error ("%s",
+ dgettext ("taler-exchange",
+ hint));
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_save_as_button");
+ AG_show ("anastasis_gtk_select_secret_frame");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_quit_button");
+ return;
+ }
+ else
+ {
+ json_t *aps;
+ GtkComboBoxText *bt;
+ const json_t *ap;
+ const char *provider_url;
+
+ bt = GTK_COMBO_BOX_TEXT (GCG_get_main_window_object (
+ "anastasis_gtk_provider_url_combo_box_text"));
+ gtk_combo_box_text_remove_all (bt);
+ aps = json_object_get (AG_redux_state,
+ "authentication_providers");
+ json_object_foreach (aps,
+ provider_url,
+ ap)
+ {
+ gtk_combo_box_text_insert_text (bt,
+ -1, /* append */
+ provider_url);
+ }
+ }
+ ri = json_object_get (AG_redux_state,
+ "recovery_information");
+ if (NULL != ri)
+ {
+ uint64_t version;
+ const char *provider_url;
+ struct GNUNET_JSON_Specification vspec[] = {
+ GNUNET_JSON_spec_uint64 ("version",
+ &version),
+ GNUNET_JSON_spec_string ("provider_url",
+ &provider_url),
+ GNUNET_JSON_spec_end ()
+ };
+ GtkSpinButton *sb;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ri,
+ vspec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ AG_error ("'recovery_information' did not parse correctly");
+ return;
+ }
+ sb = GTK_SPIN_BUTTON (GCG_get_main_window_object (
+ "anastasis_gtk_policy_version_spin_button"));
+ gtk_spin_button_set_value (sb,
+ version);
+ if (NULL == re)
+ update_entry ("anastasis_gtk_provider_url_entry",
+ provider_url);
+ }
+ else
+ {
+ GtkWidget *ge;
+
+ ge = GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_provider_url_entry"));
+ if (! gtk_widget_has_focus (ge))
+ gtk_widget_grab_focus (ge);
+ }
+ {
+ json_t *rd;
+ const char *sn;
+
+ rd = json_object_get (AG_redux_state,
+ "recovery_document");
+ if (NULL == rd)
+ {
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ }
+ else
+ {
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ sn = json_string_value (json_object_get (rd,
+ "secret_name"));
+ if (NULL != sn)
+ {
+ update_label ("anastasis_gtk_secret_name_label",
+ sn);
+ }
+ else
+ {
+ update_label ("anastasis_gtk_secret_name_label",
+ _ ("<not set>"));
+ }
+ AG_show ("anastasis_gtk_secret_identification_vbox");
+ }
+ }
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_save_as_button");
+ AG_show ("anastasis_gtk_select_secret_frame");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_quit_button");
+ AG_show ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * FIXME.
+ */
+static void
+action_challenge_selecting (void)
+{
+ json_t *rd;
+
+ AG_hide_all_frames ();
+ rd = json_object_get (AG_redux_state,
+ "recovery_document");
+ {
+ json_t *challenges;
+ size_t index;
+ json_t *challenge;
+ GtkListStore *ls;
+
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "challenge_status_liststore"));
+ gtk_list_store_clear (ls);
+ challenges = json_object_get (rd,
+ "cs");
+ json_array_foreach (challenges, index, challenge)
+ {
+ const char *instructions;
+ const char *provider;
+ const char *type;
+ const char *uuid;
+ struct TALER_Amount cost;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("instructions",
+ &instructions),
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_string ("url",
+ &provider),
+ GNUNET_JSON_spec_string ("uuid",
+ &uuid),
+ GNUNET_JSON_spec_end ()
+ };
+
+ {
+ const json_t *ks;
+
+ ks = json_object_get (challenge,
+ "key_share");
+ if ( (NULL != ks) &&
+ (! json_is_null (ks)) )
+ continue; /* already solved */
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (challenge,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (GNUNET_OK !=
+ lookup_recovery_cost (provider,
+ type,
+ &cost))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (challenge_ls_has_uuid (GTK_TREE_MODEL (ls),
+ uuid,
+ NULL))
+ continue;
+
+ gtk_list_store_insert_with_values (
+ ls,
+ NULL,
+ -1, /* append */
+ AG_CSM_CHALLENGE_OFFSET, (guint) (index + 1),
+ AG_CSM_CHALLENGE_UUID, uuid,
+ AG_CSM_SOLVED, false,
+ AG_CSM_STATUS, _ ("new"),
+ AG_CSM_PAYMENT_QR_CODE, NULL,
+ AG_CSM_ERROR_MESSAGE, NULL,
+ AG_CSM_PAYTO_URI, NULL,
+ AG_CSM_PAYING, false,
+ AG_CSM_HAS_ERROR, false,
+ AG_CSM_COST, TALER_amount2s (&cost),
+ AG_CSM_REDIRECT_URL, NULL,
+ AG_CSM_HAVE_REDIRECT, false,
+ AG_CSM_NOT_SOLVED, true,
+ AG_CSM_TYPE, type,
+ AG_CSM_INSTRUCTIONS, instructions,
+ AG_CSM_PROVIDER_URL, provider,
+ -1);
+ }
+ }
+ show_challenge_feedback ();
+
+ {
+ GtkTreeStore *ts;
+ json_t *policies;
+ size_t pindex;
+ json_t *policy;
+ char *summary = NULL;
+
+ ts = GTK_TREE_STORE (GCG_get_main_window_object (
+ "policy_review_treestore"));
+ gtk_tree_store_clear (ts);
+ policies = json_object_get (rd,
+ "dps");
+ GNUNET_assert (NULL != policies);
+ json_array_foreach (policies, pindex, policy)
+ {
+ json_t *challenges;
+ size_t index;
+ json_t *challenge;
+ GtkTreeIter piter;
+
+ gtk_tree_store_insert (ts,
+ &piter,
+ NULL, /* no parent */
+ -1 /* append */);
+ challenges = json_object_get (policy,
+ "challenges");
+ if (NULL == challenges)
+ {
+ GNUNET_break_op (0);
+ AG_error ("Policy did not parse correctly");
+ return;
+ }
+ json_array_foreach (challenges, index, challenge)
+ {
+ const char *uuid = json_string_value (json_object_get (challenge,
+ "uuid"));
+ const json_t *cs;
+ const char *type;
+ const char *provider;
+ const char *instructions;
+ bool solved = false;
+ struct GNUNET_JSON_Specification cspec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &type),
+ GNUNET_JSON_spec_string ("url",
+ &provider),
+ GNUNET_JSON_spec_string ("instructions",
+ &instructions),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("solved",
+ &solved)),
+ GNUNET_JSON_spec_end ()
+ };
+ struct TALER_Amount recovery_cost;
+
+ GNUNET_assert (NULL != uuid);
+ cs = find_challenge_by_uuid (uuid);
+ if (NULL == cs)
+ {
+ GNUNET_break_op (0);
+ AG_error ("Policy did not parse correctly");
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cs,
+ cspec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ AG_error ("Policy did not parse correctly");
+ return;
+ }
+
+ if (GNUNET_OK !=
+ lookup_recovery_cost (provider,
+ type,
+ &recovery_cost))
+ {
+ GNUNET_break_op (0);
+ AG_error ("Policy did not parse correctly");
+ return;
+ }
+ gtk_tree_store_insert_with_values (ts,
+ NULL,
+ &piter, /* parent */
+ -1, /* append */
+ AG_PRMC_POLICY_NAME,
+ instructions,
+ AG_PRMC_METHOD_TYPE,
+ type,
+ AG_PRMC_COST,
+ TALER_amount2s (&recovery_cost),
+ AG_PRMC_PROVIDER_URL,
+ provider,
+ AG_PRMC_WAS_SOLVED,
+ solved,
+ -1);
+ if (NULL == summary)
+ {
+ summary = GNUNET_strdup (type);
+ }
+ else
+ {
+ char *tmp;
+
+ GNUNET_asprintf (&tmp,
+ "%s + %s",
+ summary,
+ type);
+ GNUNET_free (summary);
+ summary = tmp;
+ }
+ } /* for each challenge */
+ if (NULL != summary)
+ {
+ gtk_tree_store_set (ts,
+ &piter,
+ AG_PRMC_POLICY_NAME, summary,
+ -1);
+ GNUNET_free (summary);
+ }
+ } /* for each policy */
+ }
+ {
+ GtkTreeView *tv;
+
+ tv = GTK_TREE_VIEW (GCG_get_main_window_object (
+ "anastasis_gtk_choose_policy_treeview"));
+ gtk_tree_view_expand_all (tv);
+ }
+ AG_sensitive ("anastasis_gtk_review_policy_treeview");
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_main_window_save_as_button");
+ AG_show ("anastasis_gtk_challenge_frame");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_quit_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+static void
+action_challenge_paying (void)
+{
+ json_t *pprs;
+ json_t *ppr;
+ GtkListStore *ls;
+ const char *uuid;
+ bool found = false;
+ const char *ps;
+
+ AG_hide_all_frames ();
+ ls = GTK_LIST_STORE (GCG_get_main_window_object (
+ "unpaid_qrcodes_liststore"));
+ gtk_list_store_clear (ls);
+ pprs = json_object_get (AG_redux_state,
+ "challenge_feedback");
+ json_object_foreach (pprs, uuid, ppr)
+ {
+ const char *state;
+ const char *payto = NULL;
+ const char *provider = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("state",
+ &state),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("taler_pay_uri",
+ &payto)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("provider",
+ &provider)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("payment_secret",
+ &ps)),
+ GNUNET_JSON_spec_end ()
+ };
+ GdkPixbuf *pb;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ppr,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (ppr,
+ stderr,
+ JSON_INDENT (2));
+ continue;
+ }
+ if (NULL == payto)
+ continue;
+ if (0 != strcmp (state,
+ "payment"))
+ continue;
+ found = true;
+ pb = setup_qrcode ("unpaid_qr_treeview",
+ payto,
+ strlen (payto));
+ if (NULL == pb)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
+ payto);
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1, /* append */
+ AG_UQRMC_QR_IMAGE, pb,
+ AG_UQRMC_URL, payto,
+ AG_UQRMC_PROVIDER, provider,
+ -1);
+ g_object_unref (pb);
+ break;
+ }
+
+ if (found)
+ {
+ json_t *args;
+ struct GNUNET_TIME_Relative timeout;
+
+ timeout = GNUNET_TIME_UNIT_MINUTES;
+ GNUNET_assert (NULL == AG_ra);
+ args = json_pack ("{s:o, s:s}",
+ "timeout",
+ GNUNET_JSON_from_time_rel (timeout),
+ "payment_secret",
+ ps);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "pay",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+ else
+ {
+ AG_error ("ERROR: Internal error: should pay, but do not know what");
+ }
+ AG_show ("anastasis_gtk_progress_vbox");
+ AG_progress_update ();
+ AG_show ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_pay_frame");
+ AG_show ("anastasis_gtk_pay_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_sensitive ("anastasis_gtk_main_window_prev_button");
+ AG_show ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * Render challenge feedback for challenge @a uuid_str in a dialog of
+ * @a builder in the label under @a target_widget.
+ *
+ * @param builder a builder to get widgets from
+ * @param target_widget the widget to upate
+ * @param uuid_str the UUID to render feedback for
+ */
+static void
+render_feedback (GtkBuilder *builder,
+ const char *target_widget,
+ const char *uuid_str)
+{
+ json_t *cf;
+ json_t *cs;
+ const char *state;
+ const char *redirect_url = NULL;
+ const char *hint = NULL;
+ json_t *details = NULL;
+ const char *taler_pay_uri = NULL;
+ uint32_t ec = 0;
+ uint32_t http_status = 0;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("state",
+ &state),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("taler_pay_uri",
+ &taler_pay_uri)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("details",
+ &details)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("redirect_url",
+ &redirect_url)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("hint",
+ &hint)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("error_code",
+ &ec)),
+ GNUNET_JSON_spec_end ()
+ };
+ GtkLabel *elabel;
+ char *msg;
+
+ cf = json_object_get (AG_redux_state,
+ "challenge_feedback");
+ cs = json_object_get (cf,
+ uuid_str);
+ if (NULL == cs)
+ return;
+
+ elabel = GTK_LABEL (gtk_builder_get_object (builder,
+ target_widget));
+ if (NULL == elabel)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ gtk_label_set_text (elabel,
+ _ ("INTERNAL ERROR: could not parse state"));
+ gtk_widget_show (GTK_WIDGET (elabel));
+ return;
+ }
+ if ( (0 == strcmp (state,
+ "hint")) &&
+ (NULL != hint) )
+ {
+ GNUNET_asprintf (&msg,
+ _ ("Hint (#%u): %s"),
+ (unsigned int) http_status,
+ dgettext ("taler-exchange",
+ hint));
+ }
+ else if ( (0 == strcmp (state,
+ "details")) &&
+ (NULL != details) )
+ {
+ uint32_t code;
+ const char *hint = NULL;
+ const char *detail = NULL;
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_uint32 ("code",
+ &code),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("hint",
+ &hint)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("detail",
+ &detail)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (details,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (details,
+ stderr,
+ JSON_INDENT (2));
+ msg = GNUNET_strdup (
+ _ ("ERROR: failed to parse server JSON instructions"));
+ }
+ else
+ {
+ const char *ihint;
+
+ ihint = TALER_ErrorCode_get_hint (code);
+ if ( (NULL != hint) &&
+ ( (NULL == ihint) ||
+ ('<' == ihint[0])) )
+ ihint = hint; /* use server hint */
+ ihint = dgettext ("taler-exchange",
+ ihint);
+ if (NULL == detail)
+ {
+ if (NULL == ihint)
+ GNUNET_asprintf (&msg,
+ "Error #%u",
+ (unsigned int) code);
+ else
+ GNUNET_asprintf (&msg,
+ "Error #%u: %s",
+ (unsigned int) code,
+ ihint);
+ }
+ else
+ {
+ if (NULL == ihint)
+ GNUNET_asprintf (&msg,
+ "Error #%u (%s)",
+ (unsigned int) code,
+ detail);
+ else
+ GNUNET_asprintf (&msg,
+ "Error #%u: %s (%s)",
+ (unsigned int) code,
+ ihint,
+ detail);
+ }
+ }
+ }
+ else
+ {
+ GNUNET_asprintf (&msg,
+ "ERROR: state `%s` with HTTP Status %u",
+ state,
+ (unsigned int) http_status);
+ }
+ gtk_label_set_text (elabel,
+ msg);
+ GNUNET_free (msg);
+ gtk_widget_show (GTK_WIDGET (elabel));
+ GNUNET_JSON_parse_free (spec);
+}
+
+
+/**
+ * Open dialog to allow user to answer security question.
+ *
+ * @param details details about the challenge
+ * @return the dialog object, or NULL on error
+ */
+static GtkDialog *
+diag_question (const json_t *details)
+{
+ GtkBuilder *builder;
+ GtkDialog *ad;
+
+ builder = GNUNET_GTK_get_new_builder (
+ "anastasis_gtk_challenge_question.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ ad = GTK_DIALOG (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_question_dialog"));
+ {
+ GtkLabel *label;
+ const char *instructions;
+
+ label = GTK_LABEL (gtk_builder_get_object (builder,
+ "security_question_label"));
+ instructions = json_string_value (json_object_get (details,
+ "instructions"));
+ gtk_label_set_text (label,
+ instructions);
+ }
+ {
+ const char *uuid_str;
+
+ uuid_str = json_string_value (json_object_get (details,
+ "uuid"));
+ render_feedback (builder,
+ "anastasis_gtk_c_question_error_label",
+ uuid_str);
+ }
+ return ad;
+}
+
+
+/**
+ * Create a dialog for the user to enter a PIN code.
+ *
+ * @param details details about the dialog to render
+ * @return dialog object
+ */
+static GtkDialog *
+diag_code (const json_t *details)
+{
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_challenge_code.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ {
+ GtkLabel *label;
+ const char *instructions;
+
+ label = GTK_LABEL (gtk_builder_get_object (builder,
+ "challenge_instructions_label"));
+ instructions = json_string_value (json_object_get (details,
+ "instructions"));
+ gtk_label_set_text (label,
+ instructions);
+ }
+ {
+ GtkLabel *clabel;
+ const char *uuid_str;
+
+ clabel = GTK_LABEL (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_challenge_label"));
+ uuid_str = json_string_value (json_object_get (details,
+ "uuid"));
+ gtk_label_set_text (clabel,
+ uuid_str);
+ render_feedback (builder,
+ "anastasis_gtk_c_code_error_label",
+ uuid_str);
+ }
+ {
+ GtkDialog *ad;
+
+ ad = GTK_DIALOG (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_code_dialog"));
+ return ad;
+ }
+}
+
+
+/**
+ * The user wants to solve the selected challenge. Launch the
+ * dialog to allow the user to enter the solution.
+ */
+static void
+action_challenge_solving (void)
+{
+ struct
+ {
+ const char *type;
+ GtkDialog *(*ctor)(const json_t *details);
+ } type_map [] = {
+ { .type = gettext_noop ("question"),
+ .ctor = &diag_question },
+ { .type = gettext_noop ("sms"),
+ .ctor = &diag_code },
+ { .type = gettext_noop ("post"),
+ .ctor = &diag_code },
+ { .type = gettext_noop ("email"),
+ .ctor = &diag_code },
+ { .type = NULL,
+ .ctor = NULL }
+ };
+ const char *type;
+ GtkDialog *diag;
+ const char *uuid;
+ const json_t *challenge;
+
+ uuid = json_string_value (json_object_get (AG_redux_state,
+ "selected_challenge_uuid"));
+ if (NULL == uuid)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ challenge = find_challenge_by_uuid (uuid);
+ if (NULL == challenge)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ type = json_string_value (json_object_get (challenge,
+ "type"));
+ if (NULL == type)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ /* create dialog based on challenge type */
+ diag = NULL;
+ for (unsigned int i = 0; NULL != type_map[i].type; i++)
+ {
+ if (0 != strcmp (type_map[i].type,
+ type))
+ continue;
+ diag = type_map[i].ctor (challenge);
+ break;
+ }
+ if (NULL == diag)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ /* show dialog */
+ {
+ GtkWidget *toplevel;
+ GtkBox *box;
+
+ box = GTK_BOX (GCG_get_main_window_object (
+ "anastasis_gtk_open_challenge_box"));
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (box));
+ gtk_window_set_transient_for (GTK_WINDOW (diag),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (diag));
+ }
+}
+
+
+/**
+ * FIXME.
+ */
+static void
+action_recovery_finished (void)
+{
+ const char *mime = NULL;
+ const char *text = NULL;
+ const char *name = NULL;
+ void *data = NULL;
+ size_t data_size = 0;
+ const json_t *cs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("mime",
+ &mime)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("text",
+ &text)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_varsize ("value",
+ &data,
+ &data_size)),
+ GNUNET_JSON_spec_end ()
+ };
+ GdkPixbuf *pb;
+ GtkImage *img;
+
+ AG_hide_all_frames ();
+ name = json_string_value (json_object_get (json_object_get (AG_redux_state,
+ "recovery_information"),
+ "secret_name"));
+
+ cs = json_object_get (AG_redux_state,
+ "core_secret");
+ GNUNET_assert (NULL != cs);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL));
+ AG_hide ("anastasis_gtk_secret_copy_button");
+ update_label ("anastasis_gtk_secret_value_label",
+ text);
+ if ( (NULL != name) &&
+ (0 != strlen (name)) )
+ update_label ("recovery_secret_name_value_label",
+ name);
+ else
+ update_label ("recovery_secret_name_value_label",
+ _("You did not name this secret"));
+ if ( (0 == strncasecmp (mime,
+ "text/",
+ strlen ("text/"))) ||
+ (0 == strncasecmp (mime,
+ "image/",
+ strlen ("image/"))) ||
+ (NULL != text) )
+ {
+ /* images and text can be copied */
+ AG_show ("anastasis_gtk_secret_copy_button");
+ }
+ pb = NULL;
+ if (NULL != text)
+ {
+ pb = setup_qrcode ("anastasis_gtk_secret_qr_image",
+ text,
+ strlen (text));
+ }
+ else
+ {
+ pb = setup_qrcode ("anastasis_gtk_secret_qr_image",
+ data,
+ data_size);
+ }
+ if (NULL != pb)
+ {
+ img = GTK_IMAGE (GCG_get_main_window_object (
+ "anastasis_gtk_secret_qr_image"));
+ gtk_image_set_from_pixbuf (img,
+ pb);
+ g_object_unref (pb);
+ }
+ else
+ {
+ AG_hide ("anastasis_gtk_secret_qr_image");
+ }
+ GNUNET_JSON_parse_free (spec);
+ AG_hide ("anastasis_gtk_progress_vbox");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_show ("anastasis_gtk_completed_frame");
+ AG_hide ("anastasis_gtk_backup_complete_box");
+ AG_hide ("anastasis_gtk_success_backup_label");
+ AG_show ("anastasis_gtk_success_recovery_box");
+ AG_show ("anastasis_gtk_completed_image");
+ AG_show ("anastasis_gtk_main_control_vbox");
+ AG_hide ("anastasis_gtk_main_window_save_as_button");
+ AG_show ("anastasis_gtk_restart_button");
+ AG_show ("anastasis_gtk_main_window_quit_button");
+ AG_hide ("anastasis_gtk_main_window_prev_button");
+ AG_hide ("anastasis_gtk_main_window_forward_button");
+}
+
+
+/**
+ * Function called with the results of #ANASTASIS_redux_action.
+ *
+ * @param cls closure
+ * @param error_code Error code
+ * @param response new state as result or config information of a provider
+ */
+void
+AG_action_cb (void *cls,
+ enum TALER_ErrorCode error_code,
+ json_t *response)
+{
+ struct DispatchItem actions[] = {
+ { .state = "CONTINENT_SELECTING",
+ .action = &action_continent_selecting },
+ { .state = "COUNTRY_SELECTING",
+ .action = &action_country_selecting },
+ { .state = "USER_ATTRIBUTES_COLLECTING",
+ .action = &action_user_attributes_collecting },
+ { .state = "AUTHENTICATIONS_EDITING",
+ .action = &action_authentications_editing },
+ { .state = "POLICIES_REVIEWING",
+ .action = &action_policies_reviewing },
+ { .state = "SECRET_EDITING",
+ .action = &action_secret_editing },
+ { .state = "TRUTHS_PAYING",
+ .action = &action_truths_paying },
+ { .state = "POLICIES_PAYING",
+ .action = &action_policies_paying },
+ { .state = "BACKUP_FINISHED",
+ .action = &action_backup_finished },
+ { .state = "SECRET_SELECTING",
+ .action = &action_secret_selecting },
+ { .state = "CHALLENGE_SELECTING",
+ .action = &action_challenge_selecting },
+ { .state = "CHALLENGE_PAYING",
+ .action = &action_challenge_paying },
+ { .state = "CHALLENGE_SOLVING",
+ .action = &action_challenge_solving },
+ { .state = "RECOVERY_FINISHED",
+ .action = &action_recovery_finished },
+ { .state = NULL,
+ .action = NULL }
+ };
+
+ (void) cls;
+ AG_ra = NULL;
+ AG_thaw ();
+#if DEBUG
+ fprintf (stderr,
+ "Action result %d\n",
+ error_code);
+ json_dumpf (response,
+ stderr,
+ JSON_INDENT (2));
+ fprintf (stderr,
+ "END action result %d\n",
+ error_code);
+#endif
+ if (TALER_EC_NONE != error_code)
+ {
+ AG_error ("Error #%d: %s\n",
+ (int) error_code,
+ TALER_ErrorCode_get_hint (error_code));
+ if (AG_in_action)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ }
+ else
+ {
+ json_decref (AG_redux_state);
+ AG_redux_state = json_incref (response);
+ }
+ if ( (TALER_EC_ANASTASIS_TRUTH_UNKNOWN == error_code) ||
+ (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED == error_code) )
+ {
+ /* special case: do not remain in previous (challenge selected)
+ state but revert to challenge selecting */
+ GNUNET_assert (0 ==
+ json_object_set_new (AG_redux_state,
+ "recovery_state",
+ json_string ("CHALLENGE_SELECTING")));
+ }
+ if ( (TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED == error_code) ||
+ (TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED == error_code) ||
+ (TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED == error_code) )
+ {
+ /* special case: do not remain in previous (enter identity)
+ state but advance to secret selecting */
+ GNUNET_assert (0 ==
+ json_object_set_new (AG_redux_state,
+ "recovery_state",
+ json_string ("SECRET_SELECTING")));
+ }
+ AG_in_action = true;
+ if (GNUNET_OK ==
+ AG_dispatch (actions))
+ {
+ AG_in_action = false;
+ return;
+ }
+ AG_in_action = false;
+ AG_error ("Unhandled state `%s/%s'",
+ json_string_value (json_object_get (AG_redux_state,
+ "backup_state")),
+ json_string_value (json_object_get (AG_redux_state,
+ "recovery_state")));
+ json_dumpf (AG_redux_state,
+ stderr,
+ JSON_INDENT (2));
+ json_decref (AG_redux_state);
+ AG_redux_state = NULL;
+ AG_hide_all_frames ();
+ AG_show ("anastasis_gtk_start_frame");
+}