summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/anastasis/.gitignore1
-rw-r--r--src/anastasis/Makefile.am80
-rw-r--r--src/anastasis/anastasis-gtk.c312
-rw-r--r--src/anastasis/anastasis-gtk.h68
-rw-r--r--src/anastasis/anastasis-gtk_about.c65
-rw-r--r--src/anastasis/anastasis-gtk_action.c3109
-rw-r--r--src/anastasis/anastasis-gtk_action.h63
-rw-r--r--src/anastasis/anastasis-gtk_attributes.c247
-rw-r--r--src/anastasis/anastasis-gtk_attributes.h55
-rw-r--r--src/anastasis/anastasis-gtk_backup.c140
-rw-r--r--src/anastasis/anastasis-gtk_dispatch.c42
-rw-r--r--src/anastasis/anastasis-gtk_dispatch.h56
-rw-r--r--src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c89
-rw-r--r--src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c254
-rw-r--r--src/anastasis/anastasis-gtk_handle-backup-button-clicked.c52
-rw-r--r--src/anastasis/anastasis-gtk_handle-challenge-code.c120
-rw-r--r--src/anastasis/anastasis-gtk_handle-challenge-question.c98
-rw-r--r--src/anastasis/anastasis-gtk_handle-challenge-row-activated.c189
-rw-r--r--src/anastasis/anastasis-gtk_handle-clear-secret-clicked.c48
-rw-r--r--src/anastasis/anastasis-gtk_handle-continent-selected.c76
-rw-r--r--src/anastasis/anastasis-gtk_handle-core-secret-changed.c80
-rw-r--r--src/anastasis/anastasis-gtk_handle-core-secret-name-changed.c60
-rw-r--r--src/anastasis/anastasis-gtk_handle-country-activated.c116
-rw-r--r--src/anastasis/anastasis-gtk_handle-country-unselected.c51
-rw-r--r--src/anastasis/anastasis-gtk_handle-currency-changed.c78
-rw-r--r--src/anastasis/anastasis-gtk_handle-expiration-change.c108
-rw-r--r--src/anastasis/anastasis-gtk_handle-expiration-change.h36
-rw-r--r--src/anastasis/anastasis-gtk_handle-identity-changed.c92
-rw-r--r--src/anastasis/anastasis-gtk_handle-identity-changed.h37
-rw-r--r--src/anastasis/anastasis-gtk_handle-main-window-back-clicked.c113
-rw-r--r--src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c214
-rw-r--r--src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.h34
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-email.c272
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-post.c203
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-question.c160
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-sms.c252
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-video.c160
-rw-r--r--src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c69
-rw-r--r--src/anastasis/anastasis-gtk_handle-policy-activate.c66
-rw-r--r--src/anastasis/anastasis-gtk_handle-policy-button.c77
-rw-r--r--src/anastasis/anastasis-gtk_handle-policy-meta.c374
-rw-r--r--src/anastasis/anastasis-gtk_handle-policy-version-changed.c179
-rw-r--r--src/anastasis/anastasis-gtk_handle-recovery-button-clicked.c52
-rw-r--r--src/anastasis/anastasis-gtk_handle-secret-buttons.c520
-rw-r--r--src/anastasis/anastasis-gtk_helper.c265
-rw-r--r--src/anastasis/anastasis-gtk_helper.h479
-rw-r--r--src/anastasis/anastasis-gtk_io.c248
-rw-r--r--src/anastasis/anastasis-gtk_pe-add-policy.c39
-rw-r--r--src/anastasis/anastasis-gtk_pe-delete-challenge.c50
-rw-r--r--src/anastasis/anastasis-gtk_pe-delete-policy.c48
-rw-r--r--src/anastasis/anastasis-gtk_pe-edit-policy.c566
-rw-r--r--src/anastasis/anastasis-gtk_pe.h66
-rw-r--r--src/anastasis/anastasis-gtk_progress.c153
-rw-r--r--src/anastasis/anastasis-gtk_progress.h36
-rw-r--r--src/anastasis/os_installation.c56
-rw-r--r--src/testing/Makefile.am9
-rwxr-xr-xsrc/testing/scat2
-rw-r--r--src/testing/test_anastasis_reducer_1.conf9
-rw-r--r--src/testing/test_anastasis_reducer_2.conf9
-rw-r--r--src/testing/test_anastasis_reducer_3.conf9
-rw-r--r--src/testing/test_anastasis_reducer_4.conf27
-rw-r--r--src/testing/test_anastasis_reducer_4_free.conf8
-rwxr-xr-xsrc/testing/test_prepare.sh322
-rw-r--r--src/testing/test_reducer.conf189
65 files changed, 11159 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..72fc3e0
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,2 @@
+# This Makefile.am is in the public domain
+SUBDIRS = . anastasis testing
diff --git a/src/anastasis/.gitignore b/src/anastasis/.gitignore
new file mode 100644
index 0000000..e4b8b25
--- /dev/null
+++ b/src/anastasis/.gitignore
@@ -0,0 +1 @@
+anastasis-gtk
diff --git a/src/anastasis/Makefile.am b/src/anastasis/Makefile.am
new file mode 100644
index 0000000..af3ed4e
--- /dev/null
+++ b/src/anastasis/Makefile.am
@@ -0,0 +1,80 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/ \
+ -I$(top_srcdir)/src/include \
+ @GTK_CFLAGS@ \
+ @GNUNET_CFLAGS@ \
+ @GLADE_CFLAGS@
+
+bin_PROGRAMS = anastasis-gtk
+
+anastasis_gtk_SOURCES = \
+ anastasis-gtk.c \
+ anastasis-gtk_about.c \
+ anastasis-gtk_action.c anastasis-gtk_action.h \
+ anastasis-gtk_attributes.c anastasis-gtk_attributes.h \
+ anastasis-gtk_dispatch.c anastasis-gtk_dispatch.h \
+ anastasis-gtk_io.c \
+ anastasis-gtk_handle-auth-delete-button-clicked.c \
+ anastasis-gtk_handle-auth-edit-provider-clicked.c \
+ anastasis-gtk_handle-backup-button-clicked.c \
+ anastasis-gtk_handle-challenge-code.c \
+ anastasis-gtk_handle-challenge-row-activated.c \
+ anastasis-gtk_handle-challenge-question.c \
+ anastasis-gtk_handle-clear-secret-clicked.c \
+ anastasis-gtk_handle-continent-selected.c \
+ anastasis-gtk_handle-country-activated.c \
+ anastasis-gtk_handle-country-unselected.c \
+ anastasis-gtk_handle-currency-changed.c \
+ anastasis-gtk_handle-core-secret-changed.c \
+ anastasis-gtk_handle-core-secret-name-changed.c \
+ anastasis-gtk_handle-expiration-change.c \
+ anastasis-gtk_handle-expiration-change.h \
+ anastasis-gtk_handle-identity-changed.c \
+ anastasis-gtk_handle-identity-changed.h \
+ anastasis-gtk_handle-recovery-button-clicked.c \
+ anastasis-gtk_handle-secret-buttons.c \
+ anastasis-gtk_handle-main-window-forward-clicked.c \
+ anastasis-gtk_handle-main-window-forward-clicked.h \
+ anastasis-gtk_handle-main-window-back-clicked.c \
+ anastasis-gtk_handle-method-email.c \
+ anastasis-gtk_handle-method-post.c \
+ anastasis-gtk_handle-method-question.c \
+ anastasis-gtk_handle-method-sms.c \
+ anastasis-gtk_handle-payqr-selection-changed.c \
+ anastasis-gtk_handle-policy-activate.c \
+ anastasis-gtk_handle-policy-button.c \
+ anastasis-gtk_handle-policy-meta.c \
+ anastasis-gtk_handle-policy-version-changed.c \
+ anastasis-gtk_helper.c anastasis-gtk_helper.h \
+ anastasis-gtk_pe.h \
+ anastasis-gtk_pe-add-policy.c \
+ anastasis-gtk_pe-delete-challenge.c \
+ anastasis-gtk_pe-delete-policy.c \
+ anastasis-gtk_pe-edit-policy.c \
+ anastasis-gtk_progress.c anastasis-gtk_progress.h \
+ os_installation.c
+
+anastasis_gtk_LDADD = \
+ @GTK_LIBS@ \
+ @GLADE_LIBS@ @GNUNET_LIBS@ \
+ @QR_LIBS@ \
+ -lanastasisrest \
+ -lanastasisredux \
+ -lanastasisutil \
+ -ltalerjson \
+ -ltalerutil \
+ -lgnunetgtk \
+ -lgnunetutil \
+ -lgnunetcurl \
+ -lgnunetjson \
+ -ljansson \
+ -lmicrohttpd \
+ -lmagic \
+ $(INTLLIBS)
+anastasis_gtk_CFLAGS = \
+ @QR_CFLAGS@
+anastasis_gtk_LDFLAGS = \
+ -export-dynamic
diff --git a/src/anastasis/anastasis-gtk.c b/src/anastasis/anastasis-gtk.c
new file mode 100644
index 0000000..4023b16
--- /dev/null
+++ b/src/anastasis/anastasis-gtk.c
@@ -0,0 +1,312 @@
+/*
+ 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.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+/**
+ * Handle to our main loop.
+ */
+struct GNUNET_GTK_MainLoop *AG_ml;
+
+/**
+ * Our configuration.
+ */
+const struct GNUNET_CONFIGURATION_Handle *AG_cfg;
+
+/**
+ * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+/**
+ * Hash map from UUID hashes to GtkWidgets.
+ */
+struct GNUNET_CONTAINER_MultiHashMap *AG_entry_attributes;
+
+/**
+ * Curl context for communication with taler backend
+ */
+static struct GNUNET_CURL_Context *ctx;
+
+/**
+ * Handle to an ongoing action.
+ */
+struct ANASTASIS_ReduxAction *AG_ra;
+
+/**
+ * Actual state.
+ */
+json_t *AG_redux_state;
+
+
+/**
+ * Callback invoked if the the "show animation"-menuitem (Help) is clicked.
+ *
+ * @param menuitem the object which received the signal.
+ * @param user_data user data set when the signal handler was connected.
+ */
+void
+anastasis_gtk_animation_activate_cb (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ static const struct
+ {
+ const char *png;
+ const char *widget;
+ } map[] = {
+ { .png = "continent_selection.jpg",
+ .widget = "anastasis_gtk_continent_selection_image" },
+ { .png = "country_selection.jpg",
+ .widget = "anastasis_gtk_country_selection_image" },
+ { .png = "user_attributes.png",
+ .widget = "anastasis_gtk_user_attributes_image" },
+ { .png = "authentication_methods.png",
+ .widget = "anastasis_gtk_b_authentication_methods_image" },
+ { .png = "policy_confirmation.png",
+ .widget = "anastasis_gtk_b_policies_image" },
+ { .png = "enter_secret.jpg",
+ .widget = "anastasis_gtk_enter_secret_image" },
+ { .png = "pay_with_taler.png",
+ .widget = "anastasis_gtk_pay_image" },
+ { .png = NULL,
+ .widget = NULL },
+ { .png = NULL,
+ .widget = "anastasis_gtk_completed_image" }
+ };
+ char *path;
+
+ if (gtk_widget_is_visible (GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_illustration_vbox"))))
+ {
+ AG_hide ("anastasis_gtk_illustration_vbox");
+ return;
+ }
+ AG_show ("anastasis_gtk_illustration_vbox");
+ path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_PREFIX);
+ if (NULL == path)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ for (unsigned int i = 0; NULL != map[i].png; i++)
+ {
+ GObject *img;
+
+ img = GCG_get_main_window_object (map[i].widget);
+ if (NULL == img)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not find widget `%s' to set image path\n",
+ map[i].widget);
+ }
+ else
+ {
+ char *ip;
+
+ GNUNET_asprintf (&ip,
+ "%s/share/anastasis/%s",
+ path,
+ map[i].png);
+ gtk_image_set_from_file (GTK_IMAGE (img),
+ ip);
+ GNUNET_free (ip);
+ }
+ }
+ GNUNET_free (path);
+}
+
+
+/**
+ * Task run on shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+ (void) cls;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Shutdown initiated\n");
+ ANASTASIS_redux_done ();
+ if (NULL != AG_ra)
+ {
+ ANASTASIS_redux_action_cancel (AG_ra);
+ AG_ra = NULL;
+ }
+ if (NULL != ctx)
+ {
+ GNUNET_CURL_fini (ctx);
+ ctx = NULL;
+ }
+ if (NULL != rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (rc);
+ rc = NULL;
+ }
+ GNUNET_GTK_main_loop_quit (AG_ml);
+ AG_ml = NULL;
+ GNUNET_CONTAINER_multihashmap_destroy (AG_entry_attributes);
+ AG_entry_attributes = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Shutdown complete\n");
+}
+
+
+/**
+ * Callback invoked if the application is supposed to exit.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_quit_cb (GObject *object,
+ gpointer user_data)
+{
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * User clicked the "quit" button.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_main_window_quit_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+void
+AG_load (const char *filename)
+{
+ json_error_t error;
+ json_t *in;
+
+ in = json_load_file (filename,
+ JSON_REJECT_DUPLICATES,
+ &error);
+ if (NULL == in)
+ {
+ AG_error ("Failed to parse file `%s' at %d:%d: %s\n",
+ filename,
+ error.line,
+ error.column,
+ error.text);
+ return;
+ }
+ AG_action_cb (NULL,
+ TALER_EC_NONE,
+ in);
+ json_decref (in);
+}
+
+
+/**
+ * Actual main function run right after GNUnet's scheduler
+ * is initialized. Initializes up GTK and Glade.
+ *
+ * @param cls NULL
+ */
+static void
+run (void *cls)
+{
+ GtkWidget *main_window;
+ int argc;
+ char *const *argv;
+
+ AG_ml = cls;
+ AG_entry_attributes = GNUNET_CONTAINER_multihashmap_create (16,
+ GNUNET_NO);
+ GNUNET_GTK_set_icon_search_path ();
+ GNUNET_OS_init (ANASTASIS_project_data_default ());
+ GNUNET_GTK_setup_nls ();
+ if (GNUNET_OK !=
+ GNUNET_GTK_main_loop_build_window (AG_ml,
+ NULL))
+ return;
+ AG_cfg = GNUNET_GTK_main_loop_get_configuration (AG_ml);
+ GNUNET_GTK_main_loop_get_args (AG_ml,
+ &argc,
+ &argv);
+ /* setup main window */
+ main_window = GTK_WIDGET (
+ GCG_get_main_window_object ("anastasis_gtk_main_window"));
+ gtk_window_maximize (GTK_WINDOW (main_window));
+ /* make GUI visible */
+ gtk_widget_show (main_window);
+ gtk_window_present (GTK_WINDOW (main_window));
+ GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+ NULL);
+ /* initialize HTTP client */
+ ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &rc);
+ rc = GNUNET_CURL_gnunet_rc_create (ctx);
+ ANASTASIS_redux_init (ctx);
+ if (0 != argc)
+ AG_load (argv[0]);
+}
+
+
+/**
+ * Main function of anastasis-gtk.
+ *
+ * @param argc number of arguments
+ * @param argv arguments
+ * @return 0 on success
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+ int ret;
+
+ if (GNUNET_OK !=
+ GNUNET_GTK_main_loop_start ("anastasis-gtk",
+ "GTK GUI for Anastasis",
+ argc,
+ argv,
+ options,
+ "anastasis_gtk_main_window.glade",
+ &run))
+ ret = 1;
+ else
+ ret = 0;
+ return ret;
+}
+
+
+/* end of anastasis-gtk.c */
diff --git a/src/anastasis/anastasis-gtk.h b/src/anastasis/anastasis-gtk.h
new file mode 100644
index 0000000..a4672a8
--- /dev/null
+++ b/src/anastasis/anastasis-gtk.h
@@ -0,0 +1,68 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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/include/anastasis-gtk_helper.h
+ * @brief Definition of helpers.
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_H
+#define ANASTASIS_GTK_H
+#include <gnunet-gtk/gnunet_gtk.h>
+#include <gtk/gtk.h>
+#include <anastasis/anastasis_service.h>
+#include <anastasis/anastasis_redux.h>
+
+/**
+ * Handle to our main loop.
+ */
+extern struct GNUNET_GTK_MainLoop *AG_ml;
+
+/**
+ * Our configuration.
+ */
+extern const struct GNUNET_CONFIGURATION_Handle *AG_cfg;
+
+/**
+ * Hash map from UUID hashes to GtkWidgets.
+ */
+extern struct GNUNET_CONTAINER_MultiHashMap *AG_entry_attributes;
+
+/**
+ * Actual state.
+ */
+extern json_t *AG_redux_state;
+
+/**
+ * Handle to an ongoing action.
+ */
+extern struct ANASTASIS_ReduxAction *AG_ra;
+
+
+/**
+ * Load #AG_redux_state from @a filename.
+ *
+ * @param filename to load
+ */
+void
+AG_load (const char *filename);
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_about.c b/src/anastasis/anastasis-gtk_about.c
new file mode 100644
index 0000000..d1c0a61
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_about.c
@@ -0,0 +1,65 @@
+/*
+ This file is part of Anastasis-gtk
+ Copyright (C) 2005-2013, 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_about.c
+ * @author Christian Grothoff
+ *
+ * This file contains the about dialog.
+ */
+#include <gnunet/platform.h>
+#include <gnunet-gtk/gnunet_gtk.h>
+
+
+void
+ANASTASIS_GTK_about_close_response (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = user_data;
+
+ gtk_widget_destroy (dialog);
+ g_object_unref (G_OBJECT (builder));
+}
+
+
+/**
+ * This displays an about window
+ */
+void
+anastasis_gtk_about_imagemenuitem_activate_cb (GtkWidget *dummy,
+ gpointer data)
+{
+ GtkBuilder *builder;
+ GtkWidget *diag;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_about_window.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ diag = GTK_WIDGET (gtk_builder_get_object (builder,
+ "about_window"));
+ gtk_widget_show (diag);
+}
+
+
+/* end of anastasis-gtk_about.c */
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");
+}
diff --git a/src/anastasis/anastasis-gtk_action.h b/src/anastasis/anastasis-gtk_action.h
new file mode 100644
index 0000000..cb74f33
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_action.h
@@ -0,0 +1,63 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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/include/anastasis-gtk_action.h
+ * @brief Definition of redux action result handler
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_ACTION_H
+#define ANASTASIS_GTK_ACTION_H
+#include <gnunet-gtk/gnunet_gtk.h>
+#include <gtk/gtk.h>
+#include <anastasis/anastasis_service.h>
+#include <anastasis/anastasis_redux.h>
+
+
+/**
+ * Are we currently processing an action?
+ */
+extern bool AG_in_action;
+
+/**
+ * Are we currently editing the secret?
+ */
+extern bool AG_in_secret_editing;
+
+/**
+ * Are we currently editing the secret name?
+ */
+extern bool AG_in_secret_name_editing;
+
+/**
+ * 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 provider
+ */
+void
+AG_action_cb (void *cls,
+ enum TALER_ErrorCode error_code,
+ json_t *response);
+
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_attributes.c b/src/anastasis/anastasis-gtk_attributes.c
new file mode 100644
index 0000000..bbf3621
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_attributes.c
@@ -0,0 +1,247 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-identity-changed.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+
+
+static json_t *
+extract_entry (GtkWidget *entry)
+{
+ const gchar *txt;
+
+ txt = gtk_entry_get_text (GTK_ENTRY (entry));
+ if ( (NULL == txt) ||
+ (0 == strlen (txt)) )
+ return NULL;
+ return json_string (txt);
+}
+
+
+static json_t *
+extract_cal (GtkWidget *cal)
+{
+ guint day = 0;
+ guint month = 0;
+ guint year = 0;
+ char txt[12];
+
+ gtk_calendar_get_date (GTK_CALENDAR (cal),
+ &year,
+ &month,
+ &day);
+ if (! (day && month && day))
+ return NULL;
+ GNUNET_snprintf (txt,
+ sizeof (txt),
+ "%04u-%02u-%02u",
+ (unsigned int) year,
+ (unsigned int) month,
+ (unsigned int) day);
+ return json_string (txt);
+}
+
+
+json_t *
+AG_collect_attributes (bool partial)
+{
+ static struct
+ {
+ const char *type;
+ json_t * (*extract)(GtkWidget *w);
+ } e_map [] = {
+ { .type = "string",
+ .extract = &extract_entry },
+ { .type = "date",
+ .extract = &extract_cal },
+ { .type = NULL,
+ .extract = NULL }
+ };
+ const json_t *id_attributes;
+ json_t *result;
+ size_t index;
+ json_t *id_attr;
+
+ id_attributes = json_object_get (AG_redux_state,
+ "required_attributes");
+ GNUNET_assert (NULL != id_attributes);
+ result = json_object ();
+ GNUNET_assert (NULL != result);
+ json_array_foreach (id_attributes, index, id_attr)
+ {
+ json_t *val = NULL;
+ GtkWidget *w;
+ const char *attr_name;
+ const char *attr_type;
+ const char *attr_uuid;
+ int optional = false;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_boolean ("optional",
+ &optional)),
+ GNUNET_JSON_spec_string ("type",
+ &attr_type),
+ GNUNET_JSON_spec_string ("name",
+ &attr_name),
+ GNUNET_JSON_spec_string ("uuid",
+ &attr_uuid),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_HashCode uh;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (id_attr,
+ spec,
+ NULL, NULL));
+ GNUNET_CRYPTO_hash (attr_uuid,
+ strlen (attr_uuid),
+ &uh);
+ w = GNUNET_CONTAINER_multihashmap_get (AG_entry_attributes,
+ &uh);
+ if (NULL == w)
+ {
+ if (partial)
+ continue;
+ json_decref (result);
+ return NULL;
+ }
+ for (unsigned int i = 0; NULL != e_map[i].type; i++)
+ {
+ if (0 != strcmp (e_map[i].type,
+ attr_type))
+ continue;
+ val = e_map[i].extract (w);
+ break;
+ }
+ if (NULL == val)
+ {
+ if (partial)
+ continue;
+ if (optional)
+ continue;
+ json_decref (result);
+ return NULL;
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (result,
+ attr_name,
+ val));
+ }
+ return json_pack ("{s:o}",
+ "identity_attributes",
+ result);
+}
+
+
+/**
+ * Import string value into a GtkEntry.
+ *
+ * @param w should be a GtkEntry
+ * @param value should be a string value
+ */
+static void
+import_entry (GtkWidget *w,
+ const json_t *value)
+{
+ GNUNET_break (json_is_string (value));
+ gtk_entry_set_text (GTK_ENTRY (w),
+ json_string_value (value));
+}
+
+
+/**
+ * Import date value into a GtkCalendar.
+ *
+ * @param w should be a GtkCalendar
+ * @param value should be a date value
+ */
+static void
+import_cal (GtkWidget *w,
+ const json_t *value)
+{
+ const char *s;
+ guint day;
+ guint month;
+ guint year;
+ char dummy;
+
+ s = json_string_value (value);
+ if (NULL == s)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (3 !=
+ sscanf (s,
+ "%04u-%02u-%02u%c",
+ &year,
+ &month,
+ &day,
+ &dummy))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_calendar_select_day (GTK_CALENDAR (w),
+ day);
+ gtk_calendar_select_month (GTK_CALENDAR (w),
+ month,
+ year);
+}
+
+
+void
+AG_import_attribute_data (GtkWidget *w,
+ const char *type,
+ const json_t *value)
+{
+ static struct
+ {
+ const char *type;
+ void (*import)(GtkWidget *w,
+ const json_t *value);
+ } i_map [] = {
+ { .type = "string",
+ .import = &import_entry },
+ { .type = "date",
+ .import = &import_cal },
+ { .type = NULL,
+ .import = NULL }
+ };
+
+ for (unsigned int i = 0; NULL != i_map[i].type; i++)
+ {
+ if (0 != strcmp (i_map[i].type,
+ type))
+ continue;
+ i_map[i].import (w,
+ value);
+ return;
+ }
+
+}
diff --git a/src/anastasis/anastasis-gtk_attributes.h b/src/anastasis/anastasis-gtk_attributes.h
new file mode 100644
index 0000000..13546cb
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_attributes.h
@@ -0,0 +1,55 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_attributes.h
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_ATTRIBUTES_H
+#define ANASTASIS_GTK_ATTRIBUTES_H
+
+#include <jansson.h>
+#include <gtk/gtk.h>
+
+/**
+ * Tries to compile the user's attributes into a JSON object.
+ *
+ * @param partial is partial collection OK?
+ * @return NULL on failure
+ */
+json_t *
+AG_collect_attributes (bool partial);
+
+
+/**
+ * Set widget @a w value from a the @a value.
+ *
+ * @param w a widget to initialize
+ * @param type the attribute type of the widget and the value
+ * @param value the value to restore to the widget @a w
+ */
+void
+AG_import_attribute_data (GtkWidget *w,
+ const char *type,
+ const json_t *value);
+
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_backup.c b/src/anastasis/anastasis-gtk_backup.c
new file mode 100644
index 0000000..7296387
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_backup.c
@@ -0,0 +1,140 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_backup.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the the "video OK"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_b_video_dialog_btn_ok_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkHBox *hbox;
+ GtkBox *vbox = GTK_BOX (GCG_get_main_window_object (
+ "anastasis_gtk_b_authentication_vbox"));
+
+ bool is_box = GTK_IS_BOX (user_data);
+ if (is_box)
+ {
+ hbox = (GtkHBox *) user_data;
+ // if is_box is true, we are editing and have to delete the old method
+ delete_auth_method (user_data);
+ }
+ else
+ hbox = (GtkHBox *) gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ // set labels
+ GtkLabel *label_prefix = (GtkLabel *) gtk_label_new ("VIDEO: ");
+ const gchar *photo_path = gtk_entry_get_text (
+ GTK_ENTRY (GCG_get_main_window_object (
+ "anastasis_gtk_b_video_dialog_photo_path_entry")));
+ GtkLabel *label_photo_path = (GtkLabel *) gtk_label_new (photo_path);
+ {
+ // build json arguments for reducer
+ json_t *arguments = json_object ();
+ json_t *auth_method = json_object ();
+ json_t *method_data = json_object ();
+
+ json_object_set_new (auth_method, "method", json_string ("video"));
+ json_object_set_new (method_data, "picture", json_string (photo_path)); // FIXME: load photo, not only path
+ json_object_set_new (auth_method, "data", method_data);
+ json_object_set_new (arguments, "authentication_method", method_data);
+
+ ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ }
+
+ // set buttons
+ GtkHButtonBox *buttons = (GtkHButtonBox *) gtk_box_new (1, 0);
+ GtkButton *edit_btn = (GtkButton *) gtk_button_new_from_icon_name (
+ "gtk-edit", GTK_ICON_SIZE_BUTTON);
+ g_signal_connect (edit_btn,
+ "clicked",
+ G_CALLBACK (
+ anastasis_gtk_b_auth_method_btn_edit_clicked_cb),
+ hbox);
+ GtkButton *delete_btn = (GtkButton *) gtk_button_new_from_icon_name (
+ "gtk-delete", GTK_ICON_SIZE_BUTTON);
+ g_signal_connect (delete_btn,
+ "clicked",
+ G_CALLBACK (
+ anastasis_gtk_b_auth_method_btn_delete_clicked_cb),
+ hbox);
+ gtk_box_pack_start (GTK_BOX (buttons), GTK_WIDGET (edit_btn), 0, 0, 0);
+ gtk_box_pack_start (GTK_BOX (buttons), GTK_WIDGET (delete_btn), 0, 0, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (label_prefix), 0, 0, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (label_photo_path), 0, 0, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), GTK_WIDGET (buttons), 0, 0, 0);
+
+ if (! is_box)
+ {
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (hbox), 0, 0, 0);
+ }
+
+ gtk_widget_show (GTK_WIDGET (hbox));
+ gtk_widget_show (GTK_WIDGET (label_prefix));
+ gtk_widget_show (GTK_WIDGET (label_photo_path));
+ gtk_widget_show (GTK_WIDGET (buttons));
+ gtk_widget_show (GTK_WIDGET (edit_btn));
+ gtk_widget_show (GTK_WIDGET (delete_btn));
+
+ gtk_entry_set_text (GTK_ENTRY (
+ GCG_get_main_window_object (
+ "anastasis_gtk_b_video_dialog_photo_path_entry")),
+ "");
+ gtk_widget_hide (GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_b_video_dialog")));
+ gtk_widget_set_sensitive (GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_main_window_forward_button")),
+ true);
+}
+
+
+/**
+ * Callback invoked if the the "video"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_video_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ gtk_widget_show (GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_b_video_dialog")));
+}
diff --git a/src/anastasis/anastasis-gtk_dispatch.c b/src/anastasis/anastasis-gtk_dispatch.c
new file mode 100644
index 0000000..67ff210
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_dispatch.c
@@ -0,0 +1,42 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_dispatch.c
+ * @brief Generic state dispatcher
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_dispatch.h"
+
+
+int
+AG_dispatch (const struct DispatchItem *dt)
+{
+ for (unsigned int i = 0; NULL != dt[i].state; i++)
+ {
+ if (! AG_check_state (AG_redux_state,
+ dt[i].state))
+ continue;
+ dt[i].action ();
+ return GNUNET_OK;
+ }
+ return GNUNET_SYSERR;
+}
diff --git a/src/anastasis/anastasis-gtk_dispatch.h b/src/anastasis/anastasis-gtk_dispatch.h
new file mode 100644
index 0000000..06d2fb8
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_dispatch.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_dispatch.h
+ * @brief Generic state dispatcher
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_DISPATCH_H
+#define ANASTASIS_GTK_DISPATCH_H
+
+/**
+ * Dispatch table item.
+ */
+struct DispatchItem
+{
+ /**
+ * State in which to run @a action.
+ */
+ const char *state;
+
+ /**
+ * The action function to execute.
+ */
+ void (*action)(void);
+};
+
+
+/**
+ * Run actions as per the given dispatch table based on the
+ * current #AG_redux_state.
+ *
+ * @param dt dispatching table
+ * @return #GNUNET_OK if an action was run from @a dt
+ */
+int
+AG_dispatch (const struct DispatchItem *dt);
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c b/src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c
new file mode 100644
index 0000000..a431057
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c
@@ -0,0 +1,89 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-auth-delete-button-clicked.c
+ * @brief Support for deletion of authentication methods
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the the "authentication methods delete"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_authentication_method_delete_button_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ json_t *args;
+ guint index;
+ GtkTreeSelection *ts;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
+ "anastasis_gtk_authentication_methods_selection"));
+ if (! gtk_tree_selection_get_selected (ts,
+ &model,
+ &iter))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_tree_model_get (model,
+ &iter,
+ AG_AMMC_INDEX, &index,
+ -1);
+ AG_freeze ();
+ args = json_pack ("{s:I}",
+ "authentication_method",
+ index);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "delete_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+auth_method_selection_changed_cb (
+ GtkTreeSelection *treeselection,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (treeselection,
+ &model,
+ &iter))
+ AG_sensitive ("anastasis_gtk_authentication_method_delete_button");
+ else
+ AG_insensitive ("anastasis_gtk_authentication_method_delete_button");
+}
diff --git a/src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c b/src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c
new file mode 100644
index 0000000..4bc6558
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c
@@ -0,0 +1,254 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_backup.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-main-window-forward-clicked.h"
+#include <jansson.h>
+#include <microhttpd.h>
+
+
+void
+url_add_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkListStore *ls;
+ GtkEntry *entry;
+ const char *url;
+
+ ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+ "provider_liststore"));
+ GNUNET_assert (NULL != ls);
+ entry = GTK_ENTRY (gtk_builder_get_object (builder,
+ "url_entry"));
+ url = gtk_entry_get_text (entry);
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1,
+ AG_PMC_PROVIDER_URL, url,
+ -1);
+ gtk_entry_set_text (entry,
+ "");
+}
+
+
+void
+url_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkWidget *button;
+ const char *url;
+
+ button = GTK_WIDGET (gtk_builder_get_object (builder,
+ "add_button"));
+ url = gtk_entry_get_text (entry);
+ gtk_widget_set_sensitive (button,
+ (0 == strncasecmp (url,
+ "http://",
+ strlen ("http://"))) ||
+ (0 == strncasecmp (url,
+ "https://",
+ strlen ("https://"))));
+}
+
+
+/**
+ * Function called from the edit-provider dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+edit_provider_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkTreeModel *tm;
+ GtkTreeIter iter;
+ const json_t *providers;
+ json_t *urls;
+
+ if (GTK_RESPONSE_APPLY != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+ "provider_liststore"));
+ if (NULL == tm)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ providers = json_object_get (AG_redux_state,
+ "authentication_providers");
+ urls = json_array ();
+ if (gtk_tree_model_get_iter_first (tm,
+ &iter))
+ do {
+ gchar *url;
+
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PMC_PROVIDER_URL, &url,
+ -1);
+ if (NULL == json_object_get (providers,
+ url))
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (urls,
+ json_string (url)));
+ }
+ g_free (url);
+ }
+ while (gtk_tree_model_iter_next (tm,
+ &iter));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ {
+ json_t *args;
+
+ args = json_pack ("{s:o}",
+ "urls",
+ urls);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_provider",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+}
+
+
+/**
+ * Callback invoked if the the "Edit"-provider list button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_edit_provider_list_clicked_cb (GtkButton *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+ GtkListStore *ls;
+ json_t *providers;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_edit_providers.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+ "provider_liststore"));
+ providers = json_object_get (AG_redux_state,
+ "authentication_providers");
+ {
+ const char *url;
+ const json_t *provider;
+ json_object_foreach (providers, url, provider)
+ {
+ uint32_t http_code;
+ uint32_t ec;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_code)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("error_code",
+ &ec)),
+ GNUNET_JSON_spec_end ()
+ };
+ char *status;
+ const char *color;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (provider,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (provider,
+ stderr,
+ JSON_INDENT (2));
+ continue;
+ }
+ if (MHD_HTTP_OK == http_code)
+ {
+ status = GNUNET_strdup (_ ("available"));
+ color = "green";
+ }
+ else if (0 == http_code)
+ {
+ GNUNET_asprintf (&status,
+ _ ("Network failure: %s (#%u)"),
+ TALER_ErrorCode_get_hint (ec),
+ (unsigned int) ec);
+ color = "red";
+ }
+ else
+ {
+ GNUNET_asprintf (&status,
+ _ ("HTTP %s (%u): %s (#%u)"),
+ MHD_get_reason_phrase_for (http_code),
+ (unsigned int) http_code,
+ TALER_ErrorCode_get_hint (ec),
+ (unsigned int) ec);
+ color = "red";
+ }
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1,
+ AG_PMC_PROVIDER_URL, url,
+ AG_PMC_PROVIDER_STATUS, status,
+ AG_PMC_PROVIDER_STATUS_COLOR, color,
+ -1);
+ GNUNET_free (status);
+ }
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "edit_provider_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-backup-button-clicked.c b/src/anastasis/anastasis-gtk_handle-backup-button-clicked.c
new file mode 100644
index 0000000..487d6d5
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-backup-button-clicked.c
@@ -0,0 +1,52 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_backup.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the the "backup"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_backup_button_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ json_t *j;
+
+ AG_freeze ();
+ j = ANASTASIS_backup_start (AG_cfg);
+ AG_action_cb (NULL,
+ TALER_EC_NONE,
+ j);
+ json_decref (j);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-challenge-code.c b/src/anastasis/anastasis-gtk_handle-challenge-code.c
new file mode 100644
index 0000000..046e29e
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-challenge-code.c
@@ -0,0 +1,120 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-challenge-code.c
+ * @brief Handle dialogs for code returned to challenge address (Email, SMS, POST)
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Function called from the secure question challenge dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_c_code_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ json_t *args;
+ unsigned long long pin;
+ char dummy;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ GNUNET_assert (NULL == AG_ra);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "back",
+ NULL,
+ &AG_action_cb,
+ NULL);
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_code_entry"));
+ qs = gtk_entry_get_text (q);
+ if ( (NULL != qs) &&
+ (0 == strncasecmp ("a-", qs, 2)) )
+ qs += 2; /* skip "A-" prefix if present */
+ if (1 != sscanf (qs,
+ "%llu%c",
+ &pin,
+ &dummy))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ args = json_pack ("{s:I}",
+ "pin",
+ (json_int_t) pin);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "solve_challenge",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_c_code_dialog_answer_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *a;
+ const char *as;
+ unsigned int pin;
+ char dummy;
+ bool ok;
+
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_code_entry"));
+ as = gtk_entry_get_text (a);
+ if ( (NULL != as) &&
+ (0 == strncasecmp ("a-", as, 2)) )
+ as += 2; /* skip "A-" prefix if present */
+ ok = ( (NULL != as) &&
+ (1 == sscanf (as,
+ "%u%c",
+ &pin,
+ &dummy)) );
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_code_dialog_btn_ok")),
+ ok);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-challenge-question.c b/src/anastasis/anastasis-gtk_handle-challenge-question.c
new file mode 100644
index 0000000..a947745
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-challenge-question.c
@@ -0,0 +1,98 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-challenge-question.c
+ * @brief Handle dialogs for secure question
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Function called from the secure question challenge dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_c_question_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ json_t *args;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ GNUNET_assert (NULL == AG_ra);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "back",
+ NULL,
+ &AG_action_cb,
+ NULL);
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_question_dialog_answer_entry"));
+ qs = gtk_entry_get_text (q);
+ args = json_pack ("{s:s}",
+ "answer",
+ qs);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ GNUNET_assert (NULL == AG_ra);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "solve_challenge",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_c_question_dialog_answer_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *a;
+ const char *as;
+
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_question_dialog_answer_entry"));
+ as = gtk_entry_get_text (a);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_c_question_dialog_btn_ok")),
+ (NULL != as) &&
+ (0 < strlen (as)));
+}
diff --git a/src/anastasis/anastasis-gtk_handle-challenge-row-activated.c b/src/anastasis/anastasis-gtk_handle-challenge-row-activated.c
new file mode 100644
index 0000000..0f1023c
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-challenge-row-activated.c
@@ -0,0 +1,189 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-challenge-row-activated.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+static void
+start_solve (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ char *uuid;
+ gboolean solved;
+ json_t *args;
+
+ gtk_tree_model_get (model,
+ iter,
+ AG_CSM_CHALLENGE_UUID, &uuid,
+ AG_CSM_SOLVED, &solved,
+ -1);
+ if (solved)
+ {
+ g_free (uuid);
+ return;
+ }
+ args = json_pack ("{s:s}",
+ "uuid",
+ uuid);
+ g_free (uuid);
+ GNUNET_assert (NULL != args);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "select_challenge",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+/**
+ * The user activated a row in the challenge list.
+ * If the row contains an unsolved challenge, start
+ * the process to solve the challenge.
+ *
+ * @param selection the selected data
+ * @param user_data unused
+ */
+void
+anastasis_gtk_challenge_status_treeview_row_activated_cb (
+ GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ (void) path;
+ (void) column;
+ (void) user_data;
+ selection = gtk_tree_view_get_selection (tree_view);
+ if (gtk_tree_selection_get_selected (selection,
+ &model,
+ &iter))
+ {
+ start_solve (model,
+ &iter);
+ }
+ else
+ {
+ /* How can this be? */
+ GNUNET_break (0);
+ }
+}
+
+
+/**
+ * The user clicked one of the challenge buttons, select the
+ * challenge.
+ *
+ * @param button the button that was clicked
+ * @param user_data a `json *` with the challenge
+ */
+void
+anastasis_gtk_challenge_status_solved_toggled_cb (
+ GtkCellRendererToggle *cell_renderer,
+ gchar *path,
+ gpointer user_data)
+{
+ GtkTreePath *p;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = GTK_TREE_MODEL (GCG_get_main_window_object (
+ "challenge_status_liststore"));
+ p = gtk_tree_path_new_from_string (path);
+ gtk_tree_model_get_iter (model,
+ &iter,
+ p);
+ gtk_tree_path_free (p);
+ start_solve (model,
+ &iter);
+}
+
+
+/**
+ * The user selected another row in the challenge list.
+ * If the row has data that might be interesting for the
+ * clipboard, copy it there.
+ *
+ * @param selection the selected data
+ * @param user_data unused
+ */
+void
+anastasis_gtk_challenge_status_treeselection_changed_cb (
+ GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkClipboard *cb;
+
+ (void) user_data;
+ cb = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+ GNUNET_assert (NULL != cb);
+ if (gtk_tree_selection_get_selected (selection,
+ &model,
+ &iter))
+ {
+ char *uri;
+ char *url;
+ gboolean paying;
+ gboolean have_redir;
+
+ gtk_tree_model_get (model,
+ &iter,
+ AG_CSM_PAYTO_URI, &uri,
+ AG_CSM_PAYING, &paying,
+ AG_CSM_REDIRECT_URL, &url,
+ AG_CSM_HAVE_REDIRECT, &have_redir,
+ -1);
+ if (paying && (NULL != uri))
+ gtk_clipboard_set_text (cb,
+ uri,
+ strlen (uri));
+ else if (have_redir && (NULL != url))
+ gtk_clipboard_set_text (cb,
+ url,
+ strlen (url));
+ else
+ gtk_clipboard_set_text (cb,
+ "",
+ 0);
+ g_free (url);
+ }
+ else
+ {
+ gtk_clipboard_set_text (cb,
+ "",
+ 0);
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-clear-secret-clicked.c b/src/anastasis/anastasis-gtk_handle-clear-secret-clicked.c
new file mode 100644
index 0000000..5857d8a
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-clear-secret-clicked.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-clear-secret-clicked.c
+ * @brief Handle user clicking a 'clear' button in the enter secret dialog
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the the "backup"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_secret_clear_button_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "clear_secret",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-continent-selected.c b/src/anastasis/anastasis-gtk_handle-continent-selected.c
new file mode 100644
index 0000000..8122893
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-continent-selected.c
@@ -0,0 +1,76 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-continent-selected.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if a continent is selected.
+ *
+ * @param selection A GtkTreeSelection.
+ * @param user_data user data set when the signal handler was connected.
+ */
+void
+anastasis_gtk_continent_selection_changed_cb (GtkTreeSelection *treeselection,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *continent_name;
+ json_t *arguments;
+
+ if (! gtk_tree_selection_get_selected (treeselection,
+ &model,
+ &iter))
+ {
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "unselect_continent",
+ NULL,
+ &AG_action_cb,
+ NULL);
+ return;
+ }
+ gtk_tree_model_get (model,
+ &iter,
+ AG_CMC_CONTINENT_NAME, &continent_name,
+ -1);
+ arguments = json_pack ("{s:s}",
+ "continent",
+ continent_name);
+ GNUNET_assert (NULL != arguments);
+ g_free (continent_name);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "select_continent",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ json_decref (arguments);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-core-secret-changed.c b/src/anastasis/anastasis-gtk_handle-core-secret-changed.c
new file mode 100644
index 0000000..cc4f787
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-core-secret-changed.c
@@ -0,0 +1,80 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-core-secret.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include "anastasis-gtk_handle-expiration-change.h"
+#include <jansson.h>
+
+
+void
+anastasis_gtk_enter_secret_entry_changed_cb (GtkEditable *entry,
+ gpointer user_data)
+{
+ GtkEntry *e = GTK_ENTRY (entry);
+ const char *text = gtk_entry_get_text (e);
+ json_t *arguments;
+ struct GNUNET_TIME_Absolute expiration;
+
+ if (AG_in_action)
+ return;
+ AG_in_secret_editing = true;
+ expiration = AG_get_desired_expiration ();
+ if (0 == expiration.abs_value_us)
+ return; /* failured */
+ if ( (NULL == text) ||
+ (0 == strlen (text)) )
+ {
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "clear_secret",
+ NULL,
+ &AG_action_cb,
+ NULL);
+ AG_focus ("anastasis_gtk_enter_secret_entry");
+ return;
+ }
+ arguments = json_pack ("{s:{s:s,s:s},s:o}",
+ "secret",
+ "text",
+ text,
+ "mime",
+ "text/plain",
+ "expiration",
+ GNUNET_JSON_from_time_abs (expiration));
+ GNUNET_assert (NULL != arguments);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "enter_secret",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ json_decref (arguments);
+ AG_focus ("anastasis_gtk_enter_secret_entry");
+ AG_in_secret_editing = false;
+}
diff --git a/src/anastasis/anastasis-gtk_handle-core-secret-name-changed.c b/src/anastasis/anastasis-gtk_handle-core-secret-name-changed.c
new file mode 100644
index 0000000..285251f
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-core-secret-name-changed.c
@@ -0,0 +1,60 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-core-secret-name-changed.c
+ * @brief The user changed the name of the core secret. Update state.
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include "anastasis-gtk_handle-expiration-change.h"
+#include <jansson.h>
+
+
+void
+anastasis_gtk_enter_secret_name_entry_changed_cb (GtkEditable *entry,
+ gpointer user_data)
+{
+ GtkEntry *ne = GTK_ENTRY (entry);
+ const char *name = gtk_entry_get_text (ne);
+ json_t *arguments;
+
+ if (AG_in_action)
+ return;
+ arguments = json_pack ("{s:s}",
+ "name",
+ name);
+ GNUNET_assert (NULL != arguments);
+ AG_freeze ();
+ AG_in_secret_name_editing = true;
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "enter_secret_name",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ GNUNET_break (NULL == AG_ra);
+ AG_focus ("anastasis_gtk_secret_name_entry");
+ AG_in_secret_name_editing = false;
+ json_decref (arguments);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-country-activated.c b/src/anastasis/anastasis-gtk_handle-country-activated.c
new file mode 100644
index 0000000..dc16e97
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-country-activated.c
@@ -0,0 +1,116 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-country-activated.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_handle-main-window-forward-clicked.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if a country is selected.
+ *
+ * @param treeselection selection object
+ * @param user_data NULL
+ */
+void
+anastasis_gtk_country_selection_changed_cb (GtkTreeSelection *treeselection,
+ gpointer user_data)
+{
+ GtkTreeSelection *currency_selection;
+ GtkTreeModel *currency_model;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ char *scode;
+
+ (void) user_data;
+ if (! gtk_tree_selection_get_selected (treeselection,
+ &model,
+ &iter))
+ {
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ return;
+ }
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ gtk_tree_model_get (model,
+ &iter,
+ AG_CCMC_COUNTRY_CODE,
+ &scode,
+ -1);
+ /* select all currencies matching current country */
+ currency_selection = GTK_TREE_SELECTION (
+ GCG_get_main_window_object ("anastasis_gtk_currency_selection"));
+ gtk_tree_selection_unselect_all (currency_selection);
+ currency_model = GTK_TREE_MODEL (
+ GCG_get_main_window_object ("currency_liststore"));
+ {
+ json_t *countries;
+ json_t *country;
+ size_t index;
+
+ countries = json_object_get (AG_redux_state,
+ "countries");
+ json_array_foreach (countries, index, country)
+ {
+ const char *code;
+ const char *currency;
+
+ code = json_string_value (json_object_get (country,
+ "code"));
+ GNUNET_assert (NULL != code);
+ if (0 != strcmp (code,
+ scode))
+ continue;
+ currency = json_string_value (json_object_get (country,
+ "currency"));
+ GNUNET_assert (NULL != currency);
+ {
+ GtkTreeIter citer;
+ char *pcur;
+
+ if (gtk_tree_model_get_iter_first (currency_model,
+ &citer))
+ do {
+ gtk_tree_model_get (currency_model,
+ &citer,
+ AG_CMC_CURRENCY_NAME,
+ &pcur,
+ -1);
+ if (0 == strcasecmp (pcur,
+ currency))
+ {
+ gtk_tree_selection_select_iter (currency_selection,
+ &citer);
+ }
+ g_free (pcur);
+ } while (gtk_tree_model_iter_next (currency_model,
+ &citer));
+ }
+ }
+ }
+ g_free (scode);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-country-unselected.c b/src/anastasis/anastasis-gtk_handle-country-unselected.c
new file mode 100644
index 0000000..5ddbc17
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-country-unselected.c
@@ -0,0 +1,51 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-country-unselected.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if a country is unselected (unselected signal).
+ *
+ * @param selection A GtkTreeSelection.
+ * @param user_data user data set when the signal handler was connected.
+ */
+void
+anastasis_gtk_country_unselected (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection,
+ &model,
+ NULL))
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ else
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+}
diff --git a/src/anastasis/anastasis-gtk_handle-currency-changed.c b/src/anastasis/anastasis-gtk_handle-currency-changed.c
new file mode 100644
index 0000000..75be227
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-currency-changed.c
@@ -0,0 +1,78 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-country-activated.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_handle-main-window-forward-clicked.h"
+#include <jansson.h>
+
+
+/**
+ * Function called on each selected item.
+ * Sets @a data to true if called.
+ *
+ * @param model unused
+ * @param path unused
+ * @param iter unused
+ * @param[out] data pointer to a `bool` to set to true
+ */
+static void
+select_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ bool *ptr = data;
+
+ (void) model;
+ (void) path;
+ (void) iter;
+ *ptr = true;
+}
+
+
+/**
+ * Callback invoked if the currency selection changed.
+ *
+ * @param treeselection selection object
+ * @param user_data NULL
+ */
+void
+anastasis_gtk_currency_selection_changed_cb (GtkTreeSelection *treeselection,
+ gpointer user_data)
+{
+ bool have_sel = false;
+
+ (void) user_data;
+ gtk_tree_selection_selected_foreach (treeselection,
+ &select_cb,
+ &have_sel);
+ if (have_sel)
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ else
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+}
diff --git a/src/anastasis/anastasis-gtk_handle-expiration-change.c b/src/anastasis/anastasis-gtk_handle-expiration-change.c
new file mode 100644
index 0000000..99b5412
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-expiration-change.c
@@ -0,0 +1,108 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-expiration-change.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include <jansson.h>
+
+
+struct GNUNET_TIME_Absolute
+AG_get_desired_expiration ()
+{
+ GtkSpinButton *spin_button;
+ gint value;
+ struct GNUNET_TIME_Absolute res;
+ 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;
+
+ 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 GNUNET_TIME_UNIT_ZERO_ABS;
+ }
+
+ {
+ time_t t;
+
+ t = exp_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+ GNUNET_assert (NULL !=
+ localtime_r (&t,
+ &tv));
+ }
+
+ spin_button = GTK_SPIN_BUTTON (GCG_get_main_window_object (
+ "expiration_year_spin_button"));
+ value = gtk_spin_button_get_value_as_int (spin_button);
+ tv.tm_year = value - 1900;
+ {
+ time_t t = mktime (&tv);
+
+ res.abs_value_us = t * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+ }
+ return res;
+}
+
+
+/**
+ * Callback invoked if the user changed when the
+ * backup may expire. Update cost.
+ *
+ * @param spin_button the spin button that changed
+ * @param user_data NULL
+ */
+void
+expiration_year_spin_button_value_changed_cb (
+ GtkSpinButton *spin_button,
+ gpointer user_data)
+{
+ json_t *arg;
+ struct GNUNET_TIME_Absolute expiration;
+
+ expiration = AG_get_desired_expiration ();
+ if (0 == expiration.abs_value_us)
+ return; /* failured */
+ (void) GNUNET_TIME_round_abs (&expiration);
+ arg = json_pack ("{s:o}",
+ "expiration",
+ GNUNET_JSON_from_time_abs (expiration));
+ GNUNET_assert (NULL != arg);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "update_expiration",
+ arg,
+ &AG_action_cb,
+ NULL);
+ json_decref (arg);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-expiration-change.h b/src/anastasis/anastasis-gtk_handle-expiration-change.h
new file mode 100644
index 0000000..28defc3
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-expiration-change.h
@@ -0,0 +1,36 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-expiration-change.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#ifndef ANASTASIS_GTK_HANDLE_EXPIRATION_CHANGE_H
+#define ANASTASIS_GTK_HANDLE_EXPIRATION_CHANGE_H
+
+
+/**
+ * Return the current expiration time desired by the
+ * user (in the 'edit secret' dialog).
+ */
+struct GNUNET_TIME_Absolute
+AG_get_desired_expiration (void);
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_handle-identity-changed.c b/src/anastasis/anastasis-gtk_handle-identity-changed.c
new file mode 100644
index 0000000..ea9c8b2
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-identity-changed.c
@@ -0,0 +1,92 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-identity-changed.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * 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 provider
+ */
+static void
+test_ok_cb (void *cls,
+ enum TALER_ErrorCode error_code,
+ json_t *response)
+{
+ bool *result = cls;
+
+ if (TALER_EC_NONE == error_code)
+ *result = true;
+}
+
+
+/**
+ * Function to ckeck if required attributes are set.
+ *
+ * @return true if user-provided attributes satisfy the constraints
+ */
+static bool
+check_attributes_fullfilled (void)
+{
+ struct ANASTASIS_ReduxAction *ta;
+ json_t *args;
+ bool result;
+
+ args = AG_collect_attributes (false);
+ if (NULL == args)
+ return false;
+ result = false;
+ ta = ANASTASIS_redux_action (AG_redux_state,
+ "enter_user_attributes",
+ args,
+ &test_ok_cb,
+ &result);
+ if (NULL != ta)
+ {
+ result = true;
+ ANASTASIS_redux_action_cancel (ta);
+ }
+ json_decref (args);
+ return result;
+}
+
+
+void
+AG_identity_changed (void)
+{
+ if (check_attributes_fullfilled ())
+ AG_sensitive ("anastasis_gtk_main_window_forward_button");
+ else
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+}
diff --git a/src/anastasis/anastasis-gtk_handle-identity-changed.h b/src/anastasis/anastasis-gtk_handle-identity-changed.h
new file mode 100644
index 0000000..826b69c
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-identity-changed.h
@@ -0,0 +1,37 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-identity-changed.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_HANDLE_IDENTITY_CHANGED_H
+#define ANASTASIS_GTK_HANDLE_IDENTITY_CHANGED_H
+
+/**
+ * Function called when the user changed anything about its identity.
+ * Check whether the current input is valid and enables/disables the
+ * 'forward' button accordingly.
+ */
+void
+AG_identity_changed (void);
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_handle-main-window-back-clicked.c b/src/anastasis/anastasis-gtk_handle-main-window-back-clicked.c
new file mode 100644
index 0000000..863706f
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-main-window-back-clicked.c
@@ -0,0 +1,113 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-main-window-back-clicked.c
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Start interaction from the beginning.
+ */
+static void
+fresh_start (void)
+{
+ AG_hide_all_frames ();
+ json_decref (AG_redux_state);
+ AG_redux_state = NULL;
+ AG_hide ("anastasis_gtk_progress_vbox");
+ AG_hide ("anastasis_gtk_backup_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_recovery_progress_scrolled_window");
+ AG_hide ("anastasis_gtk_restart_button");
+ AG_hide ("anastasis_gtk_main_control_vbox");
+ AG_show ("anastasis_gtk_start_frame");
+}
+
+
+/**
+ * Callback invoked if the "back"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_main_window_back_clicked (GObject *object,
+ gpointer user_data)
+{
+ const char *state;
+
+ (void) object;
+ (void) user_data;
+ if (NULL != AG_ra)
+ {
+ /* This happens if we were long polling for payment */
+ ANASTASIS_redux_action_cancel (AG_ra);
+ AG_ra = NULL;
+ }
+ state = json_string_value (json_object_get (AG_redux_state,
+ "recovery_state"));
+ if (NULL == state)
+ state = json_string_value (json_object_get (AG_redux_state,
+ "backup_state"));
+
+ if ( (0 == strcasecmp (state,
+ "CONTINENT_SELECTING")) ||
+ (0 == strcasecmp (state,
+ "COUNTRY_SELECTING")) )
+ {
+ AG_hide ("anastasis_gtk_country_selection_image");
+ AG_hide ("anastasis_gtk_continent_frame");
+ AG_hide ("anastasis_gtk_continent_selection_image");
+ AG_hide ("anastasis_gtk_country_selection_image");
+ fresh_start ();
+ return;
+ }
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "back",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
+
+
+/**
+ * Callback invoked if the "restart"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_restart_button_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ (void) object;
+ (void) user_data;
+ AG_hide ("anastasis_gtk_restart_button");
+ fresh_start ();
+}
diff --git a/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c b/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c
new file mode 100644
index 0000000..a85a039
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c
@@ -0,0 +1,214 @@
+/*
+ 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_handle-main-window-forward-clicked.c
+ * @brief
+ * @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 <jansson.h>
+
+
+/**
+ * Function called on each selected currency. Appends
+ * the currency to the JSON array.
+ *
+ * @param model the model of the currencies
+ * @param path a path (unused)
+ * @param iter selected currency position
+ * @param data a `json *` with the JSON array to expand
+ */
+static void
+append_currency (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ json_t *currencies = data;
+ gchar *currency;
+
+ (void) path;
+ gtk_tree_model_get (model,
+ iter,
+ AG_CMC_CURRENCY_NAME,
+ &currency,
+ -1);
+ GNUNET_break (0 ==
+ json_array_append_new (currencies,
+ json_string (currency)));
+ g_free (currency);
+}
+
+
+/**
+ * The user selected the 'forward' button. Move on with the
+ * country and currency selection.
+ */
+static void
+forward_country_selecting (void)
+{
+ GtkTreeIter iter;
+ GtkTreeView *tv;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ gchar *country_name;
+ gchar *country_code;
+ json_t *arguments;
+ json_t *currencies;
+
+ tv = GTK_TREE_VIEW (GCG_get_main_window_object (
+ "anastasis_gtk_country_treeview"));
+ sel = gtk_tree_view_get_selection (tv);
+ if (! gtk_tree_selection_get_selected (sel,
+ &model,
+ &iter))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ currencies = json_array ();
+ GNUNET_assert (NULL != currencies);
+ gtk_tree_selection_selected_foreach (
+ GTK_TREE_SELECTION (
+ GCG_get_main_window_object ("anastasis_gtk_currency_selection")),
+ &append_currency,
+ currencies);
+ gtk_tree_model_get (model,
+ &iter,
+ AG_CCMC_COUNTRY_NAME, &country_name,
+ AG_CCMC_COUNTRY_CODE, &country_code,
+ -1);
+ arguments = json_pack ("{s:s, s:s, s:o}",
+ "country", country_name,
+ "country_code", country_code,
+ "currencies", currencies);
+ GNUNET_assert (NULL != arguments);
+ g_free (country_name);
+ g_free (country_code);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "select_country",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ json_decref (arguments);
+}
+
+
+void
+AG_forward_user_attributes_collecting (void)
+{
+ json_t *args;
+
+ AG_freeze ();
+ args = AG_collect_attributes (false);
+ GNUNET_assert (NULL != args);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "enter_user_attributes",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+static void
+forward_authentications_editing (void)
+{
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "next",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
+
+
+static void
+forward_policies_reviewing (void)
+{
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "next",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
+
+
+static void
+forward_secret_editing (void)
+{
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "next",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
+
+
+static void
+forward_secret_selecting (void)
+{
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "next",
+ NULL,
+ &AG_action_cb,
+ NULL);
+}
+
+
+/**
+ * Callback invoked if the the "forward"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_main_window_forward_clicked (GObject *object,
+ gpointer user_data)
+{
+ struct DispatchItem actions[] = {
+ { .state = "COUNTRY_SELECTING",
+ .action = &forward_country_selecting },
+ { .state = "USER_ATTRIBUTES_COLLECTING",
+ .action = &AG_forward_user_attributes_collecting },
+ { .state = "AUTHENTICATIONS_EDITING",
+ .action = &forward_authentications_editing },
+ { .state = "POLICIES_REVIEWING",
+ .action = &forward_policies_reviewing },
+ { .state = "SECRET_EDITING",
+ .action = &forward_secret_editing },
+ { .state = "SECRET_SELECTING",
+ .action = &forward_secret_selecting },
+ { .state = NULL,
+ .action = NULL }
+ };
+
+ AG_dispatch (actions);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.h b/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.h
new file mode 100644
index 0000000..ce5f0ee
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.h
@@ -0,0 +1,34 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-main-window-forward-clicked.h
+ * @brief
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_HANDLE_MAIN_WINDOW_FORDWARD_CLICKED_H
+#define ANASTASIS_GTK_HANDLE_MAIN_WINDOW_FORDWARD_CLICKED_H
+
+
+void
+AG_forward_user_attributes_collecting (void);
+
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_handle-method-email.c b/src/anastasis/anastasis-gtk_handle-method-email.c
new file mode 100644
index 0000000..36e4d4d
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-email.c
@@ -0,0 +1,272 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-method-email.c
+ * @brief Handle dialogs for security email
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Return obfuscated variant of an e-mail address.
+ *
+ * @param email input address
+ * @return obfuscated version, NULL on errors
+ */
+static char *
+mask_email (const char *email)
+{
+ char *at;
+ char *tld;
+ size_t nlen;
+ char *result;
+
+ result = GNUNET_strdup (email);
+ at = strchr (result, '@');
+ if (NULL == at)
+ {
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ }
+ tld = strrchr (result, '.');
+ if (NULL == tld)
+ {
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ }
+ if ( (ssize_t) (at - tld) > 0)
+ {
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ }
+ nlen = at - result;
+ switch (nlen)
+ {
+ case 0:
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ case 1:
+ result[0] = '?';
+ break;
+ case 2:
+ case 3:
+ result[0] = '?';
+ result[1] = '?';
+ break;
+ default:
+ for (unsigned int i = 1; i<nlen - 2; i++)
+ result[i] = '?';
+ break;
+ }
+ nlen = tld - at;
+ switch (nlen)
+ {
+ case 1:
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ case 2:
+ at[1] = '?';
+ break;
+ case 3:
+ at[1] = '?';
+ at[2] = '?';
+ break;
+ case 4:
+ at[2] = '?';
+ at[3] = '?';
+ break;
+ default:
+ for (unsigned int i = 2; i<nlen - 2; i++)
+ at[i] = '?';
+ break;
+ }
+
+ /* shorten multiple consecutive "?" to "*" */
+ {
+ bool star = false;
+ bool qmark = false;
+ size_t woff = 0;
+
+ for (unsigned int i = 0; i<strlen (result); i++)
+ {
+ result[woff++] = result[i];
+ if ('?' == result[i])
+ {
+ if (star)
+ {
+ /* more than two "??" in a row */
+ woff--;
+ continue;
+ }
+ if (qmark)
+ {
+ /* two "??", combine to "*" */
+ result[--woff - 1] = '*';
+ star = true;
+ continue;
+ }
+ /* first qmark */
+ qmark = true;
+ }
+ else
+ {
+ star = false;
+ qmark = false;
+ }
+ }
+ result[woff] = '\0';
+ }
+ return result;
+}
+
+
+/**
+ * Function called from the security-email dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_email_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ json_t *args;
+ char *ins;
+ char *pe;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_email_dialog_mailaddress_entry"));
+ qs = gtk_entry_get_text (q);
+ pe = mask_email (qs);
+ GNUNET_asprintf (&ins,
+ _ ("e-mail address %s"),
+ pe);
+ GNUNET_free (pe);
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "email",
+ "challenge",
+ GNUNET_JSON_from_data (qs,
+ strlen (qs)),
+ "instructions",
+ ins);
+ GNUNET_free (ins);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_b_email_dialog_mailaddress_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ regex_t regex;
+ int regex_result;
+ const char *regexp = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}";
+
+ regex_result = regcomp (&regex,
+ regexp,
+ REG_EXTENDED);
+ if (0 < regex_result)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_email_dialog_mailaddress_entry"));
+ qs = gtk_entry_get_text (q);
+ regex_result = regexec (&regex,
+ qs,
+ 0,
+ NULL,
+ 0);
+ regfree (&regex);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_email_dialog_btn_ok")),
+ 0 == regex_result);
+}
+
+
+/**
+ * Callback invoked if the the "secure email"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_email_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_email.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_email_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-method-post.c b/src/anastasis/anastasis-gtk_handle-method-post.c
new file mode 100644
index 0000000..5269548
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-post.c
@@ -0,0 +1,203 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-method-post.c
+ * @brief Handle dialogs for security post
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Get text of @a widget_name from builder @a b.
+ *
+ * @param b a builder
+ * @param widget_name name of an entry widget of @a b
+ * @return associated text
+ */
+static const char *
+gt (GtkBuilder *b,
+ const char *widget_name)
+{
+ GtkEntry *q;
+
+ q = GTK_ENTRY (gtk_builder_get_object (b,
+ widget_name));
+ if (NULL == q)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not find GtkEntry widget `%s'\n",
+ widget_name);
+ }
+ return gtk_entry_get_text (q);
+}
+
+
+/**
+ * Function called from the security-post dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_post_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ json_t *args;
+ char *addr_s;
+ char *ins;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ GNUNET_asprintf (&ins,
+ _ ("postal address %s"),
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_postcode_entry"));
+ {
+ json_t *addr;
+
+ addr = json_pack ("{s:s,s:s,s:s,s:s,s:s}",
+ "full_name",
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_full_name_entry"),
+ "street",
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_street_entry"),
+ "city",
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_city_entry"),
+ "postcode",
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_postcode_entry"),
+ "country",
+ gt (builder,
+ "anastasis_gtk_b_post_dialog_country_entry"));
+ GNUNET_assert (NULL != addr);
+ addr_s = json_dumps (addr,
+ JSON_COMPACT | JSON_SORT_KEYS);
+ json_decref (addr);
+ }
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "post",
+ "challenge",
+ GNUNET_JSON_from_data (addr_s,
+ strlen (addr_s)),
+ "instructions",
+ ins);
+ free (addr_s);
+ GNUNET_free (ins);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_b_post_dialog_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ const char *fields[] = {
+ "anastasis_gtk_b_post_dialog_full_name_entry",
+ "anastasis_gtk_b_post_dialog_street_entry",
+ "anastasis_gtk_b_post_dialog_city_entry",
+ "anastasis_gtk_b_post_dialog_postcode_entry",
+ "anastasis_gtk_b_post_dialog_country_entry",
+ NULL
+ };
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ bool sensitive = true;
+
+ for (unsigned int i = 0; NULL != fields[i]; i++)
+ {
+ const char *qs;
+
+ qs = gt (builder,
+ fields[i]);
+ if ( (NULL == qs) ||
+ (0 == strlen (qs)) )
+ {
+ sensitive = false;
+ break;
+ }
+ }
+
+ {
+ GtkWidget *button;
+
+ button = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_post_dialog_btn_ok"));
+ gtk_widget_set_sensitive (button,
+ sensitive);
+ }
+}
+
+
+/**
+ * Callback invoked if the the "secure post"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_post_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_post.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_post_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-method-question.c b/src/anastasis/anastasis-gtk_handle-method-question.c
new file mode 100644
index 0000000..ea327aa
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-question.c
@@ -0,0 +1,160 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-method-question.c
+ * @brief Handle dialogs for security question
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Function called from the security-question dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_question_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ GtkEntry *a;
+ const char *qs;
+ const char *as;
+ json_t *args;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_question_entry"));
+ qs = gtk_entry_get_text (q);
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_answer_entry"));
+ as = gtk_entry_get_text (a);
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "question",
+ "challenge",
+ GNUNET_JSON_from_data (as,
+ strlen (as)),
+ "instructions",
+ qs);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+static void
+update_sensitivity (GtkBuilder *builder)
+{
+ GtkEntry *q;
+ GtkEntry *a;
+ const char *qs;
+ const char *as;
+
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_question_entry"));
+ qs = gtk_entry_get_text (q);
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_answer_entry"));
+ as = gtk_entry_get_text (a);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_btn_ok")),
+ ( (NULL != qs) &&
+ (0 < strlen (qs)) &&
+ (NULL != as) &&
+ (0 < strlen (as)) ));
+}
+
+
+void
+anastasis_gtk_b_question_dialog_question_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+
+ update_sensitivity (builder);
+}
+
+
+void
+anastasis_gtk_b_question_dialog_answer_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+
+ update_sensitivity (builder);
+}
+
+
+/**
+ * Callback invoked if the the "secure question"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_question_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_question.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-method-sms.c b/src/anastasis/anastasis-gtk_handle-method-sms.c
new file mode 100644
index 0000000..026a5fc
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-sms.c
@@ -0,0 +1,252 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-method-sms.c
+ * @brief Handle dialogs for security sms
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Return obfuscated variant of a phone number.
+ *
+ * @param number input address
+ * @return obfuscated version, NULL on errors
+ */
+static char *
+mask_number (const char *number)
+{
+ char *at;
+ size_t nlen;
+ char *result;
+
+ result = GNUNET_strdup (number);
+ at = strchr (result, '+');
+ if ( (NULL != at) &&
+ (result != at) )
+ {
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ }
+ nlen = strlen (number);
+ if (NULL != at)
+ {
+ if (strlen (at) < 3)
+ {
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ }
+ at += 3;
+ nlen -= 3;
+ }
+ else
+ {
+ at = result;
+ }
+ switch (nlen)
+ {
+ case 0:
+ GNUNET_break (0);
+ GNUNET_free (result);
+ return NULL;
+ case 1:
+ at[0] = '?';
+ break;
+ case 2:
+ case 3:
+ case 4:
+ at[0] = '?';
+ at[1] = '?';
+ break;
+ default:
+ for (unsigned int i = 1; i<nlen - 3; i++)
+ at[i] = '?';
+ break;
+ }
+
+ /* shorten multiple consecutive "?" to "*" */
+ {
+ bool star = false;
+ bool qmark = false;
+ size_t woff = 0;
+
+ for (unsigned int i = 0; i<strlen (result); i++)
+ {
+ result[woff++] = result[i];
+ if ('?' == result[i])
+ {
+ if (star)
+ {
+ /* more than two "??" in a row */
+ woff--;
+ continue;
+ }
+ if (qmark)
+ {
+ /* two "??", combine to "*" */
+ result[--woff - 1] = '*';
+ star = true;
+ continue;
+ }
+ /* first qmark */
+ qmark = true;
+ }
+ else
+ {
+ star = false;
+ qmark = false;
+ }
+ }
+ result[woff] = '\0';
+ }
+ return result;
+}
+
+
+/**
+ * Function called from the security-sms dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_sms_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ json_t *args;
+ char *mn;
+ char *ins;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_sms_dialog_phonenumber_entry"));
+ qs = gtk_entry_get_text (q);
+ mn = mask_number (qs);
+ GNUNET_asprintf (&ins,
+ _ ("phone number %s"),
+ mn);
+ GNUNET_free (mn);
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "sms",
+ "challenge",
+ GNUNET_JSON_from_data (qs,
+ strlen (qs)),
+ "instructions",
+ ins);
+ GNUNET_free (ins);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_b_sms_dialog_phonenumber_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *qs;
+ regex_t regex;
+ int regex_result;
+ const char *regexp = "^\\+?[0-9]+$";
+
+ regex_result = regcomp (&regex,
+ regexp,
+ REG_EXTENDED);
+ if (0 < regex_result)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_sms_dialog_phonenumber_entry"));
+ qs = gtk_entry_get_text (q);
+ regex_result = regexec (&regex,
+ qs,
+ 0,
+ NULL,
+ 0);
+ regfree (&regex);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_sms_dialog_btn_ok")),
+ 0 == regex_result);
+}
+
+
+/**
+ * Callback invoked if the the "secure sms"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_sms_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_sms.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_sms_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-method-video.c b/src/anastasis/anastasis-gtk_handle-method-video.c
new file mode 100644
index 0000000..64841b8
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-video.c
@@ -0,0 +1,160 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-method-question.c
+ * @brief Handle dialogs for security question
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Function called from the security-question dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_question_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ GtkEntry *a;
+ const char *qs;
+ const char *as;
+ json_t *args;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_question_entry"));
+ qs = gtk_entry_get_text (q);
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_answer_entry"));
+ as = gtk_entry_get_text (a);
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "question",
+ "challenge",
+ GNUNET_JSON_from_data (as,
+ strlen (as)),
+ "instructions",
+ qs);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_freeze ();
+ ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+static void
+update_sensitivity (GtkBuilder *builder)
+{
+ GtkEntry *q;
+ GtkEntry *a;
+ const char *qs;
+ const char *as;
+
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_question_entry"));
+ qs = gtk_entry_get_text (q);
+ a = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_answer_entry"));
+ as = gtk_entry_get_text (a);
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog_btn_ok")),
+ ( (NULL != qs) &&
+ (0 < strlen (qs)) &&
+ (NULL != as) &&
+ (0 < strlen (as)) ));
+}
+
+
+void
+anastasis_gtk_b_question_dialog_question_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+
+ update_sensitivity (builder);
+}
+
+
+void
+anastasis_gtk_b_question_dialog_answer_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+
+ update_sensitivity (builder);
+}
+
+
+/**
+ * Callback invoked if the the "secure question"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_question_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_question.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_question_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c b/src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c
new file mode 100644
index 0000000..8ecea63
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c
@@ -0,0 +1,69 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-payqr-selection-changed.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the QR code selection changed.
+ *
+ * @param selection A GtkTreeSelection.
+ * @param user_data user data set when the signal handler was connected.
+ */
+void
+unpaid_qr_tree_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkClipboard *cb;
+
+ cb = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+ GNUNET_assert (NULL != cb);
+ if (gtk_tree_selection_get_selected (selection,
+ &model,
+ &iter))
+ {
+ char *uri;
+
+ gtk_tree_model_get (model,
+ &iter,
+ AG_UQRMC_URL, &uri,
+ -1);
+ gtk_clipboard_set_text (cb,
+ uri,
+ strlen (uri));
+ g_free (uri);
+ }
+ else
+ {
+ gtk_clipboard_set_text (cb,
+ "",
+ 0);
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-activate.c b/src/anastasis/anastasis-gtk_handle-policy-activate.c
new file mode 100644
index 0000000..ddb89ec
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-policy-activate.c
@@ -0,0 +1,66 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-policy-activate.c
+ * @brief Handle double-click in policy review
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+void
+anastasis_gtk_review_policy_treeview_row_activated_cb (
+ GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ GtkTreeModel *tm = gtk_tree_view_get_model (tree_view);
+ GtkTreeIter iter;
+ guint pindex;
+ gboolean is_challenge;
+
+ if (NULL == path)
+ return;
+ if (! gtk_tree_model_get_iter (tm,
+ &iter,
+ path))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRMC_POLICY_INDEX,
+ &pindex,
+ AG_PRMC_IS_CHALLENGE,
+ &is_challenge,
+ -1);
+ if (! is_challenge)
+ return;
+ AG_edit_policy (pindex);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-button.c b/src/anastasis/anastasis-gtk_handle-policy-button.c
new file mode 100644
index 0000000..1d29a8a
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-policy-button.c
@@ -0,0 +1,77 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-policy-button.c
+ * @brief Handle right-click context menu in policy review
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+gboolean
+anastasis_gtk_review_policy_treeview_key_press_event_cb (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GtkTreeView *tv;
+ GtkTreeSelection *ts;
+ GtkTreeModel *tm;
+ GtkTreeIter iter;
+ guint pindex;
+ gboolean is_challenge;
+ guint mindex;
+
+ if ( (GDK_KEY_PRESS != event->type) ||
+ (GDK_KEY_Delete != ((GdkEventKey *) event)->keyval) )
+ return FALSE;
+ tv = GTK_TREE_VIEW (GCG_get_main_window_object (
+ "anastasis_gtk_review_policy_treeview"));
+ ts = gtk_tree_view_get_selection (tv);
+ if (! gtk_tree_selection_get_selected (ts,
+ &tm,
+ &iter))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Nothing selected, cannot delete\n");
+ return FALSE;
+ }
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRMC_POLICY_INDEX,
+ &pindex,
+ AG_PRMC_IS_CHALLENGE,
+ &is_challenge,
+ AG_PRMC_METHOD_INDEX,
+ &mindex,
+ -1);
+ if (is_challenge)
+ AG_delete_challenge (pindex,
+ mindex);
+ else
+ AG_delete_policy (pindex);
+ return TRUE;
+}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-meta.c b/src/anastasis/anastasis-gtk_handle-policy-meta.c
new file mode 100644
index 0000000..5ec16ed
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-policy-meta.c
@@ -0,0 +1,374 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-policy-meta.c
+ * @brief Handle right-click context menu in policy review
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+/**
+ * Context for menu callbacks.
+ */
+struct MenuContext
+{
+ /**
+ * Reference to the row that the user right-clicked.
+ */
+ GtkTreeRowReference *rr;
+
+};
+
+
+/**
+ * The user selected the 'add policy' menu.
+ *
+ * @param menuitem the selected menu
+ * @param user_data a `struct MenuContext`
+ */
+static void
+add_from_ctx_menu (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ (void) user_data;
+ (void) menuitem;
+ AG_add_policy ();
+}
+
+
+/**
+ * The user selected the 'delete challenge' menu item.
+ *
+ * @param menuitem the selected menu
+ * @param user_data a `struct MenuContext`
+ */
+static void
+delete_challenge_from_ctx_menu (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ struct MenuContext *ctx = user_data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path (ctx->rr);
+ GtkTreeModel *tm = gtk_tree_row_reference_get_model (ctx->rr);
+ GtkTreeIter iter;
+ guint pindex;
+ gboolean is_challenge;
+ guint mindex;
+
+ if (NULL == path)
+ return;
+ if (! gtk_tree_model_get_iter (tm,
+ &iter,
+ path))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRMC_POLICY_INDEX,
+ &pindex,
+ AG_PRMC_IS_CHALLENGE,
+ &is_challenge,
+ AG_PRMC_METHOD_INDEX,
+ &mindex,
+ -1);
+ if (! is_challenge)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ AG_delete_challenge (pindex,
+ mindex);
+}
+
+
+/**
+ * The user selected the 'delete policy' menu item.
+ *
+ * @param menuitem the selected menu
+ * @param user_data a `struct MenuContext`
+ */
+static void
+delete_policy_from_ctx_menu (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ struct MenuContext *ctx = user_data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path (ctx->rr);
+ GtkTreeModel *tm = gtk_tree_row_reference_get_model (ctx->rr);
+ GtkTreeIter iter;
+ guint pindex;
+
+ if (NULL == path)
+ return;
+ if (! gtk_tree_model_get_iter (tm,
+ &iter,
+ path))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRMC_POLICY_INDEX,
+ &pindex,
+ -1);
+ AG_delete_policy (pindex);
+}
+
+
+/**
+ * The user selected the 'edit policy' menu.
+ *
+ * @param menuitem the selected menu
+ * @param user_data a `struct MenuContext`
+ */
+static void
+edit_from_ctx_menu (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ struct MenuContext *ctx = user_data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path (ctx->rr);
+ GtkTreeModel *tm = gtk_tree_row_reference_get_model (ctx->rr);
+ GtkTreeIter iter;
+ guint pindex;
+
+ if (NULL == path)
+ return;
+ if (! gtk_tree_model_get_iter (tm,
+ &iter,
+ path))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRMC_POLICY_INDEX,
+ &pindex,
+ -1);
+ AG_edit_policy (pindex);
+}
+
+
+/**
+ * An item was selected from the context menu; destroy the menu shell.
+ *
+ * @param menushell menu to destroy
+ * @param user_data the 'struct MenuContext' of the menu
+ */
+static void
+context_popup_selection_done (GtkMenuShell *menushell,
+ gpointer user_data)
+{
+ struct MenuContext *ctx = user_data;
+
+ gtk_widget_destroy (GTK_WIDGET (menushell));
+ if (NULL != ctx->rr)
+ {
+ gtk_tree_row_reference_free (ctx->rr);
+ ctx->rr = NULL;
+ }
+ GNUNET_free (ctx);
+}
+
+
+/**
+ * Context menu was requested for the policy. Compute which menu
+ * items are applicable and display an appropriate menu.
+ *
+ * @param tm tree model underlying the tree view where the event happened
+ * @param iter location in the tree model selected at the time
+ * @return NULL if no menu could be created,
+ * otherwise the a pop-up menu
+ */
+static GtkMenu *
+get_popup (GtkTreeModel *tm,
+ GtkTreeIter *iter)
+{
+ GtkMenu *menu;
+ struct MenuContext *ctx;
+
+ ctx = GNUNET_new (struct MenuContext);
+ menu = GTK_MENU (gtk_menu_new ());
+ if (NULL != iter)
+ {
+ gboolean is_challenge;
+
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (tm,
+ iter);
+ ctx->rr = gtk_tree_row_reference_new (tm,
+ path);
+ gtk_tree_path_free (path);
+ }
+ gtk_tree_model_get (tm,
+ iter,
+ AG_PRMC_IS_CHALLENGE,
+ &is_challenge,
+ -1);
+ if (! is_challenge)
+ {
+ GtkWidget *child;
+
+ /* only show 'edit' entry for lines that
+ are for an entire policy */
+ child = gtk_menu_item_new_with_label (_ ("_Edit policy..."));
+ g_signal_connect (child,
+ "activate",
+ G_CALLBACK (&edit_from_ctx_menu),
+ ctx);
+ gtk_label_set_use_underline (GTK_LABEL (
+ gtk_bin_get_child (GTK_BIN (child))),
+ TRUE);
+ gtk_widget_show (child);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ child);
+ }
+ {
+ GtkWidget *child;
+ const char *label;
+
+ if (is_challenge)
+ label = _ ("Delete challenge");
+ else
+ label = _ ("Delete policy");
+ child = gtk_menu_item_new_with_label (label);
+ g_signal_connect (child,
+ "activate",
+ G_CALLBACK (is_challenge
+ ? &delete_challenge_from_ctx_menu
+ : &delete_policy_from_ctx_menu),
+ ctx);
+ gtk_label_set_use_underline (GTK_LABEL (
+ gtk_bin_get_child (GTK_BIN (child))),
+ TRUE);
+ gtk_widget_show (child);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
+ }
+
+ {
+ GtkWidget *child;
+
+ /* Insert a separator */
+ child = gtk_separator_menu_item_new ();
+ gtk_widget_show (child);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
+ }
+ }
+
+ {
+ GtkWidget *child;
+
+ /* only show 'edit' entry for lines that
+ are for an entire policy */
+ child = gtk_menu_item_new_with_label (_ ("_Add policy..."));
+ g_signal_connect (child,
+ "activate",
+ G_CALLBACK (&add_from_ctx_menu),
+ ctx);
+ gtk_label_set_use_underline (GTK_LABEL (
+ gtk_bin_get_child (GTK_BIN (child))),
+ TRUE);
+ gtk_widget_show (child);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ child);
+ }
+
+ g_signal_connect (menu,
+ "selection-done",
+ G_CALLBACK (&context_popup_selection_done),
+ ctx);
+ return menu;
+}
+
+
+/**
+ * We got a button-click on the policy treeview. If it was a
+ * right-click, display the context menu.
+ *
+ * @param widget the GtkTreeView with the search result list
+ * @param event the event, we only care about button events
+ * @param user_data NULL
+ * @return FALSE to propagate the event further,
+ * TRUE to stop the propagation
+ */
+gboolean
+anastasis_gtk_review_policy_treeview_button_press_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GtkTreeView *tv = GTK_TREE_VIEW (widget);
+ GdkEventButton *event_button = (GdkEventButton *) event;
+ GtkTreeModel *tm;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GtkMenu *menu;
+
+ if ((GDK_BUTTON_PRESS != event->type) ||
+ (3 != event_button->button))
+ return FALSE; /* not a right-click */
+ if (! gtk_tree_view_get_path_at_pos (tv,
+ event_button->x,
+ event_button->y,
+ &path,
+ NULL,
+ NULL,
+ NULL))
+ {
+ menu = get_popup (NULL,
+ NULL);
+ }
+ else
+ {
+ tm = gtk_tree_view_get_model (tv);
+ if (! gtk_tree_model_get_iter (tm,
+ &iter,
+ path))
+ {
+ /* not sure how we got a path but no iter... */
+ GNUNET_break (0);
+ return FALSE;
+ }
+ gtk_tree_path_free (path);
+
+ menu = get_popup (tm,
+ &iter);
+ }
+ if (NULL == menu)
+ {
+ GNUNET_break (0);
+ return FALSE;
+ }
+ gtk_menu_popup_at_pointer (menu,
+ event);
+ return FALSE;
+}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-version-changed.c b/src/anastasis/anastasis-gtk_handle-policy-version-changed.c
new file mode 100644
index 0000000..3f4a14a
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-policy-version-changed.c
@@ -0,0 +1,179 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_handle-policy-version-changed.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * 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
+ */
+static void
+change_action_cb (void *cls,
+ enum TALER_ErrorCode error_code,
+ json_t *response)
+{
+ (void) cls;
+ AG_ra = NULL;
+ if (TALER_EC_NONE != error_code)
+ {
+ AG_error ("Error: %s (%d)\n",
+ TALER_ErrorCode_get_hint (error_code),
+ error_code);
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ return;
+ }
+ AG_action_cb (NULL,
+ TALER_EC_NONE,
+ response);
+}
+
+
+/**
+ * The version or provider URL was edited by the user. Try to
+ * download the specified version from the specified provider.
+ */
+static void
+update_policy (void)
+{
+ GtkSpinButton *sb;
+ GtkEntry *ge;
+ gint version;
+ const char *provider_url;
+ GtkWidget *toplevel;
+
+ if (AG_in_action)
+ return;
+ toplevel = gtk_widget_get_toplevel (
+ GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_main_window")));
+ if (NULL != AG_ra)
+ {
+ ANASTASIS_redux_action_cancel (AG_ra);
+ AG_ra = NULL;
+ }
+
+ if (NULL !=
+ json_object_get (AG_redux_state,
+ "challenge_feedback"))
+ {
+ GtkWidget *diag;
+ gint ret;
+
+ diag = gtk_message_dialog_new (
+ GTK_WINDOW (toplevel),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK_CANCEL,
+ _ ("This action will reset all of your challenge solving progress!"));
+ ret = gtk_dialog_run (GTK_DIALOG (diag));
+ gtk_widget_destroy (diag);
+ switch (ret)
+ {
+ case GTK_RESPONSE_OK:
+ break;
+ default:
+ {
+ /* call action to reset view */
+ json_t *cp = json_incref (AG_redux_state);
+
+ AG_action_cb (NULL,
+ TALER_EC_NONE,
+ cp);
+ json_decref (cp);
+ }
+ /* user aborted */
+ return;
+ }
+ }
+
+ sb = GTK_SPIN_BUTTON (GCG_get_main_window_object (
+ "anastasis_gtk_policy_version_spin_button"));
+ ge = GTK_ENTRY (GCG_get_main_window_object (
+ "anastasis_gtk_provider_url_entry"));
+ provider_url = gtk_entry_get_text (ge);
+ if (! ( ( (0 == strncasecmp (provider_url,
+ "https://",
+ strlen ("https://"))) &&
+ (strlen (provider_url) >= strlen ("https://X/")) ) ||
+ ( (0 == strncasecmp (provider_url,
+ "http://",
+ strlen ("http://"))) &&
+ (strlen (provider_url) >= strlen ("http://X/")) ) ) )
+ {
+ AG_error ("Notice: URL must begin with 'http://' or 'https://'.");
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ return;
+ }
+ if ( (0 == strlen (provider_url)) ||
+ ('/' != provider_url[strlen (provider_url) - 1]) )
+ {
+ AG_error ("Notice: URL must end with '/'.");
+ AG_insensitive ("anastasis_gtk_main_window_forward_button");
+ return;
+ }
+ version = gtk_spin_button_get_value_as_int (sb);
+
+ {
+ json_t *args;
+
+ args = json_pack ("{s:I, s:s}",
+ "version",
+ (json_int_t) version,
+ "provider_url",
+ provider_url);
+ GNUNET_assert (NULL != args);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "change_version",
+ args,
+ &change_action_cb,
+ NULL);
+ json_decref (args);
+ }
+}
+
+
+void
+anastasis_gtk_policy_version_spin_button_changed_cb (GtkEditable *entry,
+ gpointer user_data)
+{
+ update_policy ();
+}
+
+
+void
+anastasis_gtk_provider_url_entry_changed_cb (GtkEditable *entry,
+ gpointer user_data)
+{
+ update_policy ();
+}
diff --git a/src/anastasis/anastasis-gtk_handle-recovery-button-clicked.c b/src/anastasis/anastasis-gtk_handle-recovery-button-clicked.c
new file mode 100644
index 0000000..1a05f02
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-recovery-button-clicked.c
@@ -0,0 +1,52 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-recovery-button-clicked.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Callback invoked if the "recovery"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_recovery_button_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ json_t *j;
+
+ AG_freeze ();
+ j = ANASTASIS_recovery_start (AG_cfg);
+ AG_action_cb (NULL,
+ TALER_EC_NONE,
+ j);
+ json_decref (j);
+}
diff --git a/src/anastasis/anastasis-gtk_handle-secret-buttons.c b/src/anastasis/anastasis-gtk_handle-secret-buttons.c
new file mode 100644
index 0000000..c7bfe30
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-secret-buttons.c
@@ -0,0 +1,520 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_handle-secret-buttons.c
+ * @brief Main function of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_handle-expiration-change.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+#include <magic.h>
+
+
+/**
+ * Global handle to MAGIC data.
+ */
+static magic_t magic;
+
+
+/**
+ * Function called from the open-file dialog upon completion.
+ *
+ * @param dialog the secret selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+open_secret_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ char *filename;
+ const char *fn;
+ size_t data_size;
+ void *data;
+ const char *mime;
+ GtkEntry *entry;
+ const char *name;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ filename =
+ GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ fn = strrchr (filename,
+ '/');
+ if (NULL == fn)
+ fn = filename;
+ else
+ fn++; /* skip '/' itself */
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+ off_t size;
+ enum GNUNET_GenericReturnValue ret;
+
+ fh = GNUNET_DISK_file_open (filename,
+ GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (NULL == fh)
+ {
+ AG_error ("Failed to open file `%s': %s",
+ filename,
+ strerror (errno));
+ GNUNET_free (filename);
+ return;
+ }
+ ret = GNUNET_DISK_file_handle_size (fh,
+ &size);
+ if (GNUNET_OK != ret)
+ {
+ AG_error ("Failed to obtain file size `%s': %s",
+ filename,
+ strerror (errno));
+ GNUNET_free (filename);
+ GNUNET_DISK_file_close (fh);
+ return;
+ }
+ data_size = (size_t) size;
+ data = GNUNET_malloc_large (data_size);
+ if (GNUNET_OK != ret)
+ {
+ AG_error ("Failed to allocate memory for file `%s': %s",
+ filename,
+ strerror (errno));
+ GNUNET_free (filename);
+ GNUNET_DISK_file_close (fh);
+ return;
+ }
+ if (size !=
+ GNUNET_DISK_file_read (fh,
+ data,
+ data_size))
+ {
+ AG_error ("Failed read file `%s': %s",
+ filename,
+ strerror (errno));
+ GNUNET_free (data);
+ GNUNET_free (filename);
+ GNUNET_DISK_file_close (fh);
+ return;
+ }
+ GNUNET_DISK_file_close (fh);
+ }
+ entry = GTK_ENTRY (GCG_get_main_window_object (
+ "anastasis_gtk_secret_name_entry"));
+ name = gtk_entry_get_text (entry);
+ mime = magic_buffer (magic,
+ data,
+ data_size);
+ {
+ json_t *arguments;
+ struct GNUNET_TIME_Absolute expiration;
+
+ expiration = AG_get_desired_expiration ();
+ if (0 == expiration.abs_value_us)
+ {
+ GNUNET_free (data);
+ GNUNET_free (filename);
+ return; /* failured */
+ }
+ arguments = json_pack ("{s:s?,s:{s:o,s:s,s:s?},s:o}",
+ "name",
+ name,
+ "secret",
+ "value",
+ GNUNET_JSON_from_data (data,
+ data_size),
+ "filename",
+ fn,
+ "mime",
+ mime,
+ "expiration",
+ GNUNET_JSON_from_time_abs (expiration));
+ GNUNET_free (filename);
+ GNUNET_free (data);
+ GNUNET_assert (NULL != arguments);
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "enter_secret",
+ arguments,
+ &AG_action_cb,
+ NULL);
+ json_decref (arguments);
+ }
+}
+
+
+/**
+ * User clicked the "open" button in the dialog where the secret is entered.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_enter_secret_open_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ (void) button;
+ (void) user_data;
+ builder = GNUNET_GTK_get_new_builder (
+ "anastasis_gtk_open_secret_dialog.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "open_file_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
+
+
+/**
+ * Function called from the open-directory dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+save_secret_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ char *filename;
+ size_t data_len;
+ const char *text;
+ void *data;
+ json_t *cs;
+ struct GNUNET_JSON_Specification cspec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("text",
+ &text)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_varsize ("value",
+ &data,
+ &data_len)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GTK_RESPONSE_ACCEPT != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ filename =
+ GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ cs = json_object_get (AG_redux_state,
+ "core_secret");
+ GNUNET_assert (NULL != cs);
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cs,
+ cspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = GNUNET_DISK_fn_write (filename,
+ (NULL == data)
+ ? text
+ : data,
+ (NULL == data)
+ ? strlen (text)
+ : data_len,
+ GNUNET_DISK_PERM_USER_READ);
+ switch (ret)
+ {
+ case GNUNET_OK:
+ break;
+ case GNUNET_NO:
+ AG_error ("File `%s' exists",
+ filename);
+ break;
+ case GNUNET_SYSERR:
+ AG_error ("Writing to file `%s' failed: %s",
+ filename,
+ strerror (errno));
+ break;
+ }
+ }
+ GNUNET_JSON_parse_free (cspec);
+ GNUNET_free (filename);
+}
+
+
+/**
+ * User clicked the "save as" button in the dialog with the recovered secret.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ static const struct
+ {
+ const char *mime;
+ const char *fn;
+ } mime_map [] = {
+ { .mime = "text/plain",
+ .fn = "untitled.txt" },
+ { .mime = "text/html",
+ .fn = "untitled.html" },
+ { .mime = "text/xml",
+ .fn = "untitled.xml" },
+ { .mime = "text/csv",
+ .fn = "untitled.csv" },
+ { .mime = "image/jpeg",
+ .fn = "untitled.jpeg" },
+ { .mime = "image/png",
+ .fn = "untitled.png" },
+ { .mime = "application/pgp-keys",
+ .fn = "untitled.pgp" },
+ { .mime = "application/json",
+ .fn = "untitled.json" },
+ { .mime = "application/taler-wallet-secret",
+ .fn = "untitled.tws" },
+ { .mime = "application/taler-wallet",
+ .fn = "untitled.twd" },
+ { .mime = NULL,
+ .fn = NULL }
+ };
+
+ GtkWidget *ad;
+ GtkBuilder *builder;
+ const char *mime;
+ const char *fn;
+ json_t *cs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("filename",
+ &fn)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("mime",
+ &mime)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ (void) button;
+ (void) user_data;
+ cs = json_object_get (AG_redux_state,
+ "core_secret");
+ GNUNET_assert (NULL != cs);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL));
+ builder = GNUNET_GTK_get_new_builder (
+ "anastasis_gtk_save_secret_dialog.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "save_file_dialog"));
+ if ( (NULL == fn) &&
+ (NULL != mime) )
+ {
+ fn = "untitled.secret";
+ for (unsigned int i = 0; NULL != mime_map[i].mime; i++)
+ {
+ if (0 != strcmp (mime_map[i].mime,
+ mime))
+ continue;
+ fn = mime_map[i].fn;
+ break;
+ }
+ }
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
+ fn);
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
+
+
+/**
+ * User clicked the "copy" button in the dialog with the recovered secret.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_secret_copy_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ size_t data_len;
+ void *data;
+ const char *mime;
+ const char *text;
+ json_t *cs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_varsize ("value",
+ &data,
+ &data_len)),
+ 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_end ()
+ };
+ GtkClipboard *cb;
+
+ (void) button;
+ (void) user_data;
+ cs = json_object_get (AG_redux_state,
+ "core_secret");
+ GNUNET_assert (NULL != cs);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL));
+ cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ GNUNET_assert (NULL != cb);
+ if (NULL != text)
+ {
+ gtk_clipboard_set_text (cb,
+ text,
+ strlen (text));
+ }
+ else
+ {
+ if (0 == strncasecmp (mime,
+ "text/",
+ strlen ("text/")))
+ {
+ gtk_clipboard_set_text (cb,
+ data,
+ data_len);
+ }
+ else if (0 == strncasecmp (mime,
+ "image/",
+ strlen ("image/")))
+ {
+ GdkPixbufLoader *loader;
+
+ loader = gdk_pixbuf_loader_new_with_mime_type (mime,
+ NULL);
+ if (NULL != loader)
+ {
+ GdkPixbuf *pb;
+
+ gdk_pixbuf_loader_write (loader,
+ data,
+ data_len,
+ NULL);
+ pb = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (NULL != pb)
+ {
+ gtk_clipboard_set_image (cb,
+ pb);
+ g_object_unref (pb);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to parse secret image data.\n");
+ }
+ g_object_unref (loader);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unsupported image mime type `%s'\n",
+ mime);
+ }
+ }
+ else
+ {
+ GNUNET_break (0);
+ }
+ }
+ GNUNET_JSON_parse_free (spec);
+}
+
+
+/**
+ * Constructor for the library. Loads the magic file.
+ */
+void __attribute__ ((constructor))
+mime_ltdl_init ()
+{
+ magic = magic_open (MAGIC_MIME_TYPE);
+ if (0 != magic_load (magic,
+ NULL))
+ {
+ GNUNET_break (0);
+ }
+}
+
+
+/**
+ * Destructor for the library, cleans up.
+ */
+void __attribute__ ((destructor))
+mime_ltdl_fini ()
+{
+ if (NULL != magic)
+ {
+ magic_close (magic);
+ magic = NULL;
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_helper.c b/src/anastasis/anastasis-gtk_helper.c
new file mode 100644
index 0000000..5760f00
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_helper.c
@@ -0,0 +1,265 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_helper.c
+ * @brief Helper functions of anastasis-gtk
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * true if we are currently showing an error message.
+ */
+bool AG_have_error;
+
+
+void
+AG_thaw ()
+{
+ AG_error_clear ();
+ AG_sensitive ("anastasis_gtk_main_window");
+ GNUNET_assert (NULL == AG_ra);
+}
+
+
+void
+AG_freeze ()
+{
+ AG_insensitive ("anastasis_gtk_main_window");
+ GNUNET_assert (NULL == AG_ra);
+}
+
+
+void
+AG_sensitive (const char *name)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Widget `%s' not found, cannot make it sensitive!\n",
+ name);
+ return;
+ }
+ gtk_widget_set_sensitive (w,
+ true);
+}
+
+
+void
+AG_focus (const char *name)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Widget `%s' not found, cannot focus on it!\n",
+ name);
+ return;
+ }
+ gtk_widget_grab_focus (w);
+}
+
+
+void
+AG_insensitive (const char *name)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Widget `%s' not found, cannot make it sensitive!\n",
+ name);
+ return;
+ }
+ gtk_widget_set_sensitive (w,
+ false);
+}
+
+
+void
+AG_hide (const char *name)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Widget `%s' not found, cannot hide it!\n",
+ name);
+ return;
+ }
+ gtk_widget_hide (w);
+}
+
+
+void
+AG_show (const char *name)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (name));
+ if (NULL == w)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Widget `%s' not found, cannot show it!\n",
+ name);
+ return;
+ }
+ gtk_widget_show (w);
+}
+
+
+void
+AG_insensitive_children (const char *name)
+{
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (
+ GCG_get_main_window_object (name)));
+ for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
+ gtk_widget_set_sensitive (GTK_WIDGET (iter->data),
+ false);
+ g_list_free (children);
+}
+
+
+void
+AG_hide_children (const char *name)
+{
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (
+ GCG_get_main_window_object (name)));
+ for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
+ gtk_widget_hide (GTK_WIDGET (iter->data));
+ g_list_free (children);
+}
+
+
+void
+AG_show_children (const char *name)
+{
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (
+ GCG_get_main_window_object (name)));
+ for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
+ gtk_widget_show (GTK_WIDGET (iter->data));
+ g_list_free (children);
+
+}
+
+
+void
+AG_hide_all_frames (void)
+{
+ AG_hide ("anastasis_gtk_start_frame");
+ AG_hide_children ("anastasis_gtk_super_vbox");
+ AG_hide_children ("anastasis_gtk_illustration_vbox");
+ if (AG_have_error)
+ AG_show ("anastasis_gtk_error_label");
+}
+
+
+bool
+AG_check_state (json_t *state,
+ const char *expected_state)
+{
+ const char *state_name = json_string_value (json_object_get (state,
+ "backup_state"));
+ if (NULL == state_name)
+ state_name = json_string_value (json_object_get (state,
+ "recovery_state"));
+ if (NULL == state_name)
+ return false;
+ return (0 == strcasecmp (state_name,
+ expected_state));
+}
+
+
+/**
+ * Get an object from the main window.
+ *
+ * @param name name of the object
+ * @return NULL on error
+ */
+GObject *
+GCG_get_main_window_object (const char *name)
+{
+ if (NULL == AG_ml)
+ return NULL;
+ return GNUNET_GTK_main_loop_get_object (AG_ml,
+ name);
+}
+
+
+void
+AG_error_clear ()
+{
+ AG_have_error = false;
+ AG_hide ("anastasis_gtk_error_label");
+}
+
+
+void
+AG_error (const char *format,
+ ...)
+{
+ va_list ap;
+ char *msg;
+ int ret;
+ GtkLabel *l;
+
+ va_start (ap, format);
+ ret = vasprintf (&msg,
+ format,
+ ap);
+ va_end (ap);
+ if (-1 == ret)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ l = GTK_LABEL (GCG_get_main_window_object ("anastasis_gtk_error_label"));
+ if (NULL == l)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_label_set_text (l,
+ msg);
+ free (msg);
+ AG_have_error = true;
+ gtk_widget_show (GTK_WIDGET (l));
+}
diff --git a/src/anastasis/anastasis-gtk_helper.h b/src/anastasis/anastasis-gtk_helper.h
new file mode 100644
index 0000000..28ddc74
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_helper.h
@@ -0,0 +1,479 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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/include/anastasis-gtk_helper.h
+ * @brief Definition of helpers.
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ */
+#ifndef ANASTASIS_GTK_HELPER_H
+#define ANASTASIS_GTK_HELPER_H
+#include <gnunet-gtk/gnunet_gtk.h>
+#include <gtk/gtk.h>
+#include <anastasis/anastasis_service.h>
+#include <anastasis/anastasis_redux.h>
+#include "anastasis-gtk.h"
+
+
+/**
+ * true if we are currently showing an error message.
+ */
+extern bool AG_have_error;
+
+
+/**
+ * Columns of the continent_liststore.
+ */
+enum AG_ContinentsModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_CMC_CONTINENT_NAME = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_CMC_CONTINENT_NAME_I18N = 1
+};
+
+
+/**
+ * Columns of the currency_liststore.
+ */
+enum AG_CurrencyModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_CMC_CURRENCY_NAME = 0
+};
+
+
+/**
+ * Columns of the challenge_status_liststore.
+ */
+enum AG_ChallengeStatusModelColumns
+{
+ /**
+ * A guint.
+ */
+ AG_CSM_CHALLENGE_OFFSET = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_CHALLENGE_UUID = 1,
+
+ /**
+ * A gboolean
+ */
+ AG_CSM_SOLVED = 2,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_STATUS = 3,
+
+ /**
+ * A GdkPixBuf.
+ */
+ AG_CSM_PAYMENT_QR_CODE = 4,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_ERROR_MESSAGE = 5,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_PAYTO_URI = 6,
+
+ /**
+ * A gboolean.
+ */
+ AG_CSM_PAYING = 7,
+
+ /**
+ * A gboolean.
+ */
+ AG_CSM_HAS_ERROR = 8,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_COST = 9,
+
+ /**
+ * A gchararray.
+ */
+ AG_CSM_REDIRECT_URL = 10,
+
+ /**
+ * A gboolean.
+ */
+ AG_CSM_HAVE_REDIRECT = 11,
+
+ /**
+ * A gboolean
+ */
+ AG_CSM_NOT_SOLVED = 12,
+
+ /**
+ * A gchararray
+ */
+ AG_CSM_TYPE = 13,
+
+ /**
+ * A gchararray
+ */
+ AG_CSM_INSTRUCTIONS = 14,
+
+ /**
+ * A gchararray
+ */
+ AG_CSM_PROVIDER_URL = 15
+
+};
+
+
+/**
+ * Columns of the provider_liststore.
+ */
+enum AG_ProviderModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_PMC_PROVIDER_URL = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_PMC_PROVIDER_STATUS = 1,
+
+ /**
+ * A gchararray.
+ */
+ AG_PMC_PROVIDER_STATUS_COLOR = 2
+};
+
+
+/**
+ * Columns of the backup_provider_liststore.
+ */
+enum AG_BackupProviderColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_BPC_PROVIDER_URL = 0,
+
+ /**
+ * A guint64
+ */
+ AG_BPC_BACKUP_VERSION = 1,
+
+ /**
+ * A gchararray. // FIXME: #6823
+ */
+ AG_BPC_EXPIRATION_TIME_STR = 2,
+
+ /**
+ * A gboolean.
+ */
+ AG_BPC_SUCCESS_FLAG = 3
+
+};
+
+/**
+ * Columns of the country_liststore.
+ */
+enum AG_CountryCodeModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_CCMC_COUNTRY_NAME = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_CCMC_COUNTRY_CODE = 1
+
+};
+
+/**
+ * Columns of the authentication_methods_liststore.
+ */
+enum AG_AuthenticationMethodsModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_AMMC_TYPE = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_AMMC_VISUALIZATION = 1,
+
+ /**
+ * A guint.
+ */
+ AG_AMMC_INDEX = 2
+};
+
+
+/**
+ * Columns of the unpaid_qrcodes_liststore.
+ */
+enum AG_UnpaidQrcodesModelColumns
+{
+ /**
+ * A GdkPixbuf.
+ */
+ AG_UQRMC_QR_IMAGE = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_UQRMC_URL = 1,
+
+ /**
+ * A gchararray.
+ */
+ AG_UQRMC_PROVIDER = 2
+};
+
+
+/**
+ * Columns of the policy_review_treestore.
+ */
+enum AG_PolicyReviewModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_PRMC_POLICY_NAME = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRMC_METHOD_TYPE = 1,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRMC_COST = 2,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRMC_PROVIDER_URL = 3,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRMC_EXPIRATION_TIME_STR = 4,
+
+ /**
+ * A guint.
+ */
+ AG_PRMC_POLICY_INDEX = 5,
+
+ /**
+ * A gboolean. True on lines representing challenges.
+ */
+ AG_PRMC_IS_CHALLENGE = 6,
+
+ /**
+ * A guint.
+ */
+ AG_PRMC_METHOD_INDEX = 7,
+
+ /**
+ * A gboolean. True on lines representing solved challenges.
+ */
+ AG_PRMC_WAS_SOLVED
+
+};
+
+
+/**
+ * Columns in the progress model liststores.
+ */
+enum AG_ProgressModelColumns
+{
+ /**
+ * A gchararray.
+ */
+ AG_PRGMC_DESCRIPTION = 0,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRGMC_REGEX = 1,
+
+ /**
+ * A gchararray.
+ */
+ AG_PRGMC_TOOLTIP = 2
+};
+
+
+/**
+ * Hide widget of the given @a name of the main window
+ *
+ * @param name widget to hide
+ */
+void
+AG_hide (const char *name);
+
+
+/**
+ * Show widget of the given @a name of the main window
+ *
+ * @param name widget to show
+ */
+void
+AG_show (const char *name);
+
+
+/**
+ * Make widget of the given @a name of the main window insensitive.
+ *
+ * @param name widget to make insensitive
+ */
+void
+AG_insensitive (const char *name);
+
+
+/**
+ * Make widget of the given @a name of the main window sensitive.
+ *
+ * @param name widget to make sensitive
+ */
+void
+AG_sensitive (const char *name);
+
+
+/**
+ * Make widget of the given @a name the focus.
+ *
+ * @param name widget to focus
+ */
+void
+AG_focus (const char *name);
+
+
+/**
+ * Thaw the user interface.
+ */
+void
+AG_thaw (void);
+
+
+/**
+ * Freeze the user interface while the action completes.
+ */
+void
+AG_freeze (void);
+
+
+/**
+ * Hide all of the children of the container widget @a name in the main window.
+ *
+ * @param name name of the object
+ */
+void
+AG_hide_children (const char *name);
+
+
+/**
+ * Make all of the children of the container widget @a name in the main window
+ * insensitive.
+ *
+ * @param name name of the object
+ */
+void
+AG_insensitive_children (const char *name);
+
+
+/**
+ * Show all of the children of the container widget @a name in the main window.
+ *
+ * @param name name of the object
+ */
+void
+AG_show_children (const char *name);
+
+
+/**
+ * Get an object from the main window.
+ *
+ * @param name name of the object
+ * @return NULL on error
+ */
+GObject *
+GCG_get_main_window_object (const char *name);
+
+
+/**
+ * Checks the actual state. True, if state is correct, else false.
+ *
+ * @param state the state to check
+ * @param expected_state the expected state as string
+ * @return bool
+ */
+bool
+AG_check_state (json_t *state,
+ const char *expected_state);
+
+
+/**
+ * Hides all frames;
+ */
+void
+AG_hide_all_frames (void);
+
+
+/**
+ * Show error message.
+ *
+ * @param format format string
+ * @param ... arguments for format string
+ */
+void
+AG_error (const char *format,
+ ...)
+__attribute__ ((format (printf, 1, 2)));
+
+
+/**
+ * Stop showing error message.
+ */
+void
+AG_error_clear (void);
+
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_io.c b/src/anastasis/anastasis-gtk_io.c
new file mode 100644
index 0000000..95f702e
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_io.c
@@ -0,0 +1,248 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2020 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_io.c
+ * @brief
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_attributes.h"
+#include "anastasis-gtk_dispatch.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+/**
+ * Function called from the open-directory dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+open_directory_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ char *filename;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ filename =
+ GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ AG_load (filename);
+ GNUNET_free (filename);
+}
+
+
+/**
+ * User clicked the "open" button.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_open_state_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_open_file_dialog.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "open_file_dialog"));
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
+
+
+/**
+ * Serialize state of currently shown use attribute editing frame to JSON.
+ */
+static void
+save_user_attributes_collecting (void)
+{
+ json_t *ia;
+
+ ia = AG_collect_attributes (true);
+ if (NULL == ia)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_break (0 ==
+ json_object_set (AG_redux_state,
+ "identity_attributes",
+ json_object_get (ia,
+ "identity_attributes")));
+ json_decref (ia);
+}
+
+
+/**
+ * Function called from the open-directory dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+save_directory_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ static const struct DispatchItem save_state[] = {
+ { .state = "USER_ATTRIBUTES_COLLECTING",
+ .action = &save_user_attributes_collecting },
+ { .state = NULL,
+ .action = NULL }
+ };
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ char *filename;
+
+ if (GTK_RESPONSE_ACCEPT != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ (void) AG_dispatch (save_state);
+ filename =
+ GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+
+ /* check if we should warn the user about writing 'core_secret' to disk */
+ {
+ json_t *cs;
+
+ cs = json_object_get (AG_redux_state,
+ "core_secret");
+ if ( (NULL != cs) &&
+ (! json_is_null (cs)) )
+ {
+ GtkWidget *diag;
+ gint ret;
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (
+ GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_main_window")));
+ diag = gtk_message_dialog_new (
+ GTK_WINDOW (toplevel),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK_CANCEL,
+ _ ("This will write your secret to disk in cleartext!"));
+ ret = gtk_dialog_run (GTK_DIALOG (diag));
+ gtk_widget_destroy (diag);
+ switch (ret)
+ {
+ case GTK_RESPONSE_OK:
+ break;
+ default:
+ /* user aborted */
+ return;
+ }
+ }
+ }
+
+ /* all good, do writing! */
+ {
+ const char *ana;
+
+ ana = strstr (filename,
+ ".ana");
+ if ( (NULL == ana) ||
+ (4 != strlen (ana)) )
+ {
+ char *tmp;
+
+ GNUNET_asprintf (&tmp,
+ "%s.ana",
+ filename);
+ GNUNET_free (filename);
+ filename = tmp;
+ }
+ }
+ if (0 !=
+ json_dump_file (AG_redux_state,
+ filename,
+ JSON_COMPACT))
+ {
+ AG_error ("Failed to write state to `%s'\n",
+ filename);
+ }
+ GNUNET_free (filename);
+}
+
+
+/**
+ * User clicked the "save as" button.
+ *
+ * @param button the button
+ * @param user_data unused
+ */
+void
+anastasis_gtk_main_window_save_as_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_save_file_dialog.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "save_file_dialog"));
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
+ "untitled.ana");
+ {
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_pe-add-policy.c b/src/anastasis/anastasis-gtk_pe-add-policy.c
new file mode 100644
index 0000000..dfb19f4
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_pe-add-policy.c
@@ -0,0 +1,39 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_pe-add-policy.c
+ * @brief Handle request to interactively add new policy
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+
+void
+AG_add_policy (void)
+{
+ AG_edit_policy (UINT_MAX);
+}
diff --git a/src/anastasis/anastasis-gtk_pe-delete-challenge.c b/src/anastasis/anastasis-gtk_pe-delete-challenge.c
new file mode 100644
index 0000000..d41c983
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_pe-delete-challenge.c
@@ -0,0 +1,50 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_pe-delete-challenge.c
+ * @brief Handle request to delete challenge
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include <jansson.h>
+
+
+void
+AG_delete_challenge (guint pindex,
+ guint mindex)
+{
+ json_t *args;
+
+ args = json_pack ("{s:I, s:I}",
+ "policy_index",
+ (json_int_t) pindex,
+ "challenge_index",
+ (json_int_t) mindex);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "delete_challenge",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
diff --git a/src/anastasis/anastasis-gtk_pe-delete-policy.c b/src/anastasis/anastasis-gtk_pe-delete-policy.c
new file mode 100644
index 0000000..5fdfb74
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_pe-delete-policy.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_pe-delete-policy.c
+ * @brief Handle request to delete policy
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+void
+AG_delete_policy (guint pindex)
+{
+ json_t *args;
+
+ args = json_pack ("{s:I}",
+ "policy_index",
+ (json_int_t) pindex);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "delete_policy",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
diff --git a/src/anastasis/anastasis-gtk_pe-edit-policy.c b/src/anastasis/anastasis-gtk_pe-edit-policy.c
new file mode 100644
index 0000000..f0ac3fb
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_pe-edit-policy.c
@@ -0,0 +1,566 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_pe-edit-policy.c
+ * @brief Handle request to interactively edit policy
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_pe.h"
+#include <jansson.h>
+
+
+/**
+ * Context for the edit dialog.
+ */
+struct EditDialogContext;
+
+/**
+ * Information we track per line in the grid.
+ */
+struct LineContext
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct LineContext *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LineContext *prev;
+
+ /**
+ * Context this line context belongs with.
+ */
+ struct EditDialogContext *edc;
+
+ /**
+ * Our combo box.
+ */
+ GtkComboBox *cb;
+
+ /**
+ * Model for our combo box.
+ */
+ GtkTreeModel *model;
+
+ /**
+ * Challenge index for this line.
+ */
+ unsigned int cindex;
+
+ /**
+ * Is this challenge used?
+ */
+ bool on;
+};
+
+
+/**
+ * Context for the edit dialog.
+ */
+struct EditDialogContext
+{
+ /**
+ * Builder of the dialog.
+ */
+ GtkBuilder *builder;
+
+ /**
+ * Head of line contexts for this dialog
+ */
+ struct LineContext *lc_head;
+
+ /**
+ * Tail of line contexts for this dialog
+ */
+ struct LineContext *lc_tail;
+
+ /**
+ * Policy index. UINT_MAX for a new policy.
+ */
+ unsigned int pindex;
+
+};
+
+
+/**
+ * Handle the response from the edit dialog.
+ *
+ * @param dialog the dialog
+ * @param response_id what the user's action was
+ * @param user_data a `struct EditDialogContext`
+ */
+void
+anastasis_gtk_policy_edit_dialog_response_cb (
+ GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ struct EditDialogContext *edc = user_data;
+
+ if (GTK_RESPONSE_OK == response_id)
+ {
+ json_t *policy;
+
+ policy = json_array ();
+ GNUNET_assert (NULL != policy);
+ for (struct LineContext *lctx = edc->lc_head;
+ NULL != lctx;
+ lctx = lctx->next)
+ {
+ GtkTreeIter iter;
+ gchar *url;
+
+ if (! lctx->on)
+ continue;
+ if (! gtk_combo_box_get_active_iter (lctx->cb,
+ &iter))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ gtk_tree_model_get (lctx->model,
+ &iter,
+ 0, &url,
+ -1);
+ GNUNET_break (0 ==
+ json_array_append_new (policy,
+ json_pack ("{s:I, s:s}",
+ "authentication_method",
+ (json_int_t) lctx->cindex,
+ "provider",
+ url)));
+ g_free (url);
+ }
+ if (UINT_MAX == edc->pindex)
+ {
+ json_t *args;
+
+ args = json_pack ("{s:o}",
+ "policy",
+ (json_int_t) policy);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_policy",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+ else
+ {
+ json_t *args;
+
+ args = json_pack ("{s:I, s:o}",
+ "policy_index",
+ (json_int_t) edc->pindex,
+ "policy",
+ policy);
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "update_policy",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+ }
+ }
+ /* clean up */
+ {
+ struct LineContext *lctx;
+
+ while (NULL != (lctx =edc->lc_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (edc->lc_head,
+ edc->lc_tail,
+ lctx);
+ GNUNET_free (lctx);
+ }
+ }
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (edc->builder));
+ GNUNET_free (edc);
+}
+
+
+/**
+ * The user changed an entry in the combo boxy of an edit
+ * dialog. Update the ability to confirm the selection:
+ * if at least one authentication method is selected, the
+ * OK button should be sensitive.
+ *
+ * @param widget the combo box that was changed
+ * @param user_data the `struct EditDialogContext`
+ */
+static void
+combo_box_changed_cb (
+ GtkComboBox *widget,
+ gpointer user_data)
+{
+ struct LineContext *lc = user_data;
+ struct EditDialogContext *edc = lc->edc;
+
+ /* Update our line context's on/off flag */
+ {
+ GtkTreeIter iter;
+
+ if (! gtk_combo_box_get_active_iter (lc->cb,
+ &iter))
+ {
+ GNUNET_break (0);
+ }
+ else
+ {
+ gchar *url;
+
+ gtk_tree_model_get (lc->model,
+ &iter,
+ 0, &url,
+ -1);
+ lc->on = (0 !=
+ strcmp (_("<off>"),
+ url));
+ g_free (url);
+ }
+ }
+ /* finally, update "OK" button sensitivity */
+ {
+ GtkWidget *ok_button;
+ bool legal = false;
+
+ for (struct LineContext *lctx = edc->lc_head;
+ NULL != lctx;
+ lctx = lctx->next)
+ legal |= lctx->on;
+ ok_button = GTK_WIDGET (gtk_builder_get_object (edc->builder,
+ "ok_button"));
+ gtk_widget_set_sensitive (ok_button,
+ legal);
+ }
+}
+
+
+/**
+ * Check if the authentication provider @a ap offers
+ * authentications of type @a type. If so, return true.
+ *
+ * @param type method to check for
+ * @param ap provider to check against
+ * @return true if @a ap offers @a type
+ */
+static bool
+ap_matches (const char *type,
+ json_t *ap)
+{
+ json_t *methods;
+ size_t index;
+ json_t *method;
+
+ methods = json_object_get (ap,
+ "methods");
+ GNUNET_break (NULL != methods);
+ json_array_foreach (methods, index, method)
+ {
+ const char *offer;
+
+ offer = json_string_value (json_object_get (method,
+ "type"));
+ if (NULL == offer)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcasecmp (type,
+ offer))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Create a GtkListStore containing all of the URLs
+ * of Anastasis providers offering @a type as an
+ * authentication method.
+ *
+ * @return the model
+ */
+static GtkTreeModel *
+make_model (const char *type)
+{
+ GtkListStore *ls;
+ json_t *aps;
+ const char *url;
+ json_t *ap;
+
+ ls = gtk_list_store_new (1,
+ G_TYPE_STRING);
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1,
+ 0, _("<off>"),
+ -1);
+ aps = json_object_get (AG_redux_state,
+ "authentication_providers");
+ GNUNET_break (NULL != aps);
+ json_object_foreach (aps, url, ap) {
+ if (ap_matches (type,
+ ap))
+ {
+ gtk_list_store_insert_with_values (ls,
+ NULL,
+ -1,
+ 0, url,
+ -1);
+ }
+ }
+
+ return GTK_TREE_MODEL (ls);
+}
+
+
+/**
+ * Select entry in @a cb based on the @a url.
+ *
+ * @param url provider to select
+ * @param lctx line to update
+ */
+static void
+select_by_url (const char *url,
+ struct LineContext *lctx)
+{
+ GtkTreeIter iter;
+
+ if (! gtk_tree_model_get_iter_first (lctx->model,
+ &iter))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ do {
+ gchar *have;
+
+ gtk_tree_model_get (lctx->model,
+ &iter,
+ 0, &have,
+ -1);
+ if (0 == strcmp (have,
+ url))
+ {
+ gtk_combo_box_set_active_iter (lctx->cb,
+ &iter);
+ lctx->on = true;
+ g_free (have);
+ return;
+ }
+ g_free (have);
+ } while (gtk_tree_model_iter_next (lctx->model,
+ &iter));
+ GNUNET_break (0); /* not found */
+}
+
+
+/**
+ * Select entry in @a cb based on the @a methods for
+ * challenge @a cindex.
+ *
+ * @param methods methods of policy to base selection on
+ * @param lctx line to update
+ */
+static void
+select_by_policy (const json_t *methods,
+ struct LineContext *lctx)
+{
+ size_t index;
+ json_t *method;
+ GtkTreeIter iter;
+
+ if (! gtk_tree_model_get_iter_first (lctx->model,
+ &iter))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ gtk_combo_box_set_active_iter (lctx->cb,
+ &iter);
+ json_array_foreach (methods, index, method) {
+ json_int_t am = json_integer_value (json_object_get (method,\
+ "authentication_method"));
+ const char *url;
+
+ if (am != lctx->cindex)
+ continue;
+ url = json_string_value (json_object_get (method,
+ "provider"));
+ select_by_url (url,
+ lctx);
+ break;
+ }
+}
+
+
+void
+AG_edit_policy (guint pindex)
+{
+ struct EditDialogContext *edc;
+ GtkGrid *grid;
+ json_t *methods = NULL;
+
+ edc = GNUNET_new (struct EditDialogContext);
+ edc->builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_edit_policy.glade",
+ edc);
+ edc->pindex = pindex;
+ if (NULL == edc->builder)
+ {
+ GNUNET_break (0);
+ GNUNET_free (edc);
+ return;
+ }
+ if (UINT_MAX != pindex)
+ {
+ json_t *policies;
+ json_t *policy;
+
+ policies = json_object_get (AG_redux_state,
+ "policies");
+ policy = json_array_get (policies,
+ pindex);
+ methods = json_object_get (policy,
+ "methods");
+ GNUNET_break (NULL != methods);
+ }
+ grid = GTK_GRID (gtk_builder_get_object (edc->builder,
+ "policy_grid"));
+ {
+ json_t *ams = json_object_get (AG_redux_state,
+ "authentication_methods");
+ json_t *am;
+ size_t index;
+ gint row = 1;
+
+ 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 ()
+ };
+ char *labels;
+ GtkWidget *label;
+ GtkWidget *cb;
+ struct LineContext *lctx;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (am,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ lctx = GNUNET_new (struct LineContext);
+ lctx->cindex = index;
+ GNUNET_asprintf (&labels,
+ "<b>%s</b>: %s",
+ type,
+ instructions);
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (label),
+ labels);
+ GNUNET_free (labels);
+ lctx->model = make_model (type);
+ cb = gtk_combo_box_new_with_model (lctx->model);
+ lctx->cb = GTK_COMBO_BOX (cb);
+ {
+ GtkCellRenderer *renderer;
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb),
+ renderer,
+ true);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb),
+ renderer,
+ "text",
+ 0);
+ }
+ lctx->edc = edc;
+ GNUNET_CONTAINER_DLL_insert (edc->lc_head,
+ edc->lc_tail,
+ lctx);
+ g_object_connect (cb,
+ "signal::changed",
+ &combo_box_changed_cb, lctx,
+ NULL);
+ if (NULL != methods)
+ select_by_policy (methods,
+ lctx);
+ gtk_grid_insert_row (grid,
+ row);
+ gtk_widget_show (label);
+ gtk_grid_attach (grid,
+ label,
+ 0,
+ row,
+ 1,
+ 1);
+ g_object_set (cb,
+ "expand",
+ TRUE,
+ NULL);
+ gtk_widget_show (cb);
+ gtk_grid_attach (grid,
+ cb,
+ 1,
+ row,
+ 1,
+ 1);
+ row++;
+ }
+ }
+ {
+ GtkWidget *toplevel;
+ GtkWidget *ad;
+ GtkWidget *anchor;
+ GtkRequisition req;
+
+ anchor = GTK_WIDGET (GCG_get_main_window_object ("anastasis_gtk_main_window_quit_button"));
+ toplevel = gtk_widget_get_toplevel (anchor);
+ ad = GTK_WIDGET (gtk_builder_get_object (edc->builder,
+ "anastasis_gtk_policy_edit_dialog"));
+ gtk_widget_get_preferred_size (ad,
+ NULL,
+ &req);
+ gtk_window_resize (GTK_WINDOW (ad),
+ req.width,
+ req.height);
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
+
+/* end of anastasis-gtk_pe-edit-policy.c */
diff --git a/src/anastasis/anastasis-gtk_pe.h b/src/anastasis/anastasis-gtk_pe.h
new file mode 100644
index 0000000..ae9292f
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_pe.h
@@ -0,0 +1,66 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_pe.h
+ * @brief Subsystem to handle policy editing
+ * @author Christian Grothoff
+ */
+#ifndef ANASTASIS_GTK_PE_H
+#define ANASTASIS_GTK_PE_H
+
+/**
+ * Delete a challenge at @a mindex in the policy
+ * at @a pindex.
+ *
+ * @param pindex policy to edit
+ * @param mindex challenge index to remove
+ */
+void
+AG_delete_challenge (guint pindex,
+ guint mindex);
+
+
+/**
+ * Delete a policy at @a pindex.
+ *
+ * @param pindex index of policy to remove
+ */
+void
+AG_delete_policy (guint pindex);
+
+
+/**
+ * Edit policy at @a pindex.
+ *
+ * @param pindex index of policy to edit
+ */
+void
+AG_edit_policy (guint pindex);
+
+
+/**
+ * Add a new policy.
+ */
+void
+AG_add_policy (void);
+
+
+#endif
diff --git a/src/anastasis/anastasis-gtk_progress.c b/src/anastasis/anastasis-gtk_progress.c
new file mode 100644
index 0000000..714a75d
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_progress.c
@@ -0,0 +1,153 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_progress.c
+ * @brief Functions dealing with the tree views used to show the user where we are in the process
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+
+/**
+ * Ensure signals are ignored where the user
+ * clicks in the widget.
+ */
+gboolean
+anastasis_gtk_backup_progress_treeview_button_press_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+
+/**
+ * Ensure signals are ignored where the user
+ * clicks in the widget.
+ */
+gboolean
+anastasis_gtk_recovery_progress_treeview_button_press_event_cb (
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer
+ user_data)
+{
+ return TRUE;
+}
+
+
+/**
+ * Function to validate an input by regular expression ("validation-regex").
+ *
+ * @param input text to validate
+ * @param regexp regular expression to validate form
+ * @return true if validation passed, else false
+ */
+static bool
+validate_regex (const char *input,
+ const char *regexp)
+{
+ regex_t regex;
+
+ if (0 != regcomp (&regex,
+ regexp,
+ REG_EXTENDED))
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to compile regular expression `%s'.",
+ regexp);
+ return true;
+ }
+ /* check if input has correct form */
+ if (0 != regexec (&regex,
+ input,
+ 0,
+ NULL,
+ 0))
+ {
+ regfree (&regex);
+ return false;
+ }
+ regfree (&regex);
+ return true;
+}
+
+
+void
+AG_progress_update (void)
+{
+ GtkTreeSelection *ts;
+ GtkTreeModel *tm;
+ GtkTreeIter iter;
+ const char *state;
+
+ state = json_string_value (json_object_get (AG_redux_state,
+ "backup_state"));
+ if (NULL == state)
+ {
+ state = json_string_value (json_object_get (AG_redux_state,
+ "recovery_state"));
+ if (NULL == state)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
+ "anastasis_gtk_recovery_progress_tree_selection"));
+ }
+ else
+ {
+ ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
+ "anastasis_gtk_backup_progress_tree_selection"));
+ }
+ gtk_tree_selection_get_selected (ts,
+ &tm,
+ &iter);
+ if (! gtk_tree_model_get_iter_first (tm,
+ &iter))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ do {
+ char *regex;
+
+ gtk_tree_model_get (tm,
+ &iter,
+ AG_PRGMC_REGEX, &regex,
+ -1);
+ if (validate_regex (state,
+ regex))
+ {
+ g_free (regex);
+ gtk_tree_selection_select_iter (ts,
+ &iter);
+ return;
+ }
+ g_free (regex);
+ } while (gtk_tree_model_iter_next (tm,
+ &iter));
+ GNUNET_break (0);
+ return;
+}
diff --git a/src/anastasis/anastasis-gtk_progress.h b/src/anastasis/anastasis-gtk_progress.h
new file mode 100644
index 0000000..4469a7a
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_progress.h
@@ -0,0 +1,36 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 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_progress.h
+ * @brief Generic progress state indication updates
+ * @author Christian Grothoff
+ */
+#ifndef ANASTASIS_GTK_PROGRESS_H
+#define ANASTASIS_GTK_PROGRESS_H
+
+
+/**
+ * Update progress indicators.
+ */
+void
+AG_progress_update (void);
+
+
+#endif
diff --git a/src/anastasis/os_installation.c b/src/anastasis/os_installation.c
new file mode 100644
index 0000000..6a1f94c
--- /dev/null
+++ b/src/anastasis/os_installation.c
@@ -0,0 +1,56 @@
+/*
+ This file is part of Anastasis.
+ Copyright (C) 2020 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/os_installation.c
+ * @brief initialize GNUNET_OS for anastasis-gtk
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+
+/**
+ * Default project data used for installation path detection
+ * for anastasis-gtk.
+ */
+static const struct GNUNET_OS_ProjectData gtk_pd = {
+ .libname = "libanastasisgtk",
+ .project_dirname = "anastasis",
+ .binary_name = "anastasis-gtk",
+ .env_varname = "ANASTASIS_GTK_PREFIX",
+ .env_varname_alt = "ANASTASIS_PREFIX",
+ .base_config_varname = "ANASTASIS_BASE_CONFIG",
+ .bug_email = "anastasis@gnu.org",
+ .homepage = "https://anastasis.lu/",
+ .config_file = "anastasis.conf",
+ .user_config_file = "~/.config/anastasis.conf"
+};
+
+
+/**
+ * Initialize.
+ */
+void __attribute__ ((constructor))
+ANASTASIS_GTK_init ()
+{
+ GNUNET_OS_init (&gtk_pd);
+}
+
+
+/* end of os_installation.c */
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
new file mode 100644
index 0000000..1d91895
--- /dev/null
+++ b/src/testing/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = .
+EXTRA_DIST = \
+ test_anastasis_reducer_1.conf \
+ test_anastasis_reducer_2.conf \
+ test_anastasis_reducer_3.conf \
+ test_anastasis_reducer_4.conf \
+ test_anastasis_reducer_4_free.conf \
+ test_prepare.sh \
+ test_reducer.conf
diff --git a/src/testing/scat b/src/testing/scat
new file mode 100755
index 0000000..7596208
--- /dev/null
+++ b/src/testing/scat
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec cat - 2>&1 >$1
diff --git a/src/testing/test_anastasis_reducer_1.conf b/src/testing/test_anastasis_reducer_1.conf
new file mode 100644
index 0000000..25b001e
--- /dev/null
+++ b/src/testing/test_anastasis_reducer_1.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8086
+SERVER_SALT = AUfO1KGOKYIFlFQ1
+BUSINESS_NAME = "Data loss #1 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck1
diff --git a/src/testing/test_anastasis_reducer_2.conf b/src/testing/test_anastasis_reducer_2.conf
new file mode 100644
index 0000000..48f7dd7
--- /dev/null
+++ b/src/testing/test_anastasis_reducer_2.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8087
+SERVER_SALT = BUfO1KGOKYIFlFQ2
+BUSINESS_NAME = "Data loss #2 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck2
diff --git a/src/testing/test_anastasis_reducer_3.conf b/src/testing/test_anastasis_reducer_3.conf
new file mode 100644
index 0000000..fdfbada
--- /dev/null
+++ b/src/testing/test_anastasis_reducer_3.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8088
+SERVER_SALT = CUfO1KGOKYIFlFQ3
+BUSINESS_NAME = "Data loss #3 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck3
diff --git a/src/testing/test_anastasis_reducer_4.conf b/src/testing/test_anastasis_reducer_4.conf
new file mode 100644
index 0000000..d990d6b
--- /dev/null
+++ b/src/testing/test_anastasis_reducer_4.conf
@@ -0,0 +1,27 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8089
+SERVER_SALT = DUfO1KGOKYIFlFQ4
+BUSINESS_NAME = "Data loss #4 Inc."
+
+ANNUAL_FEE = TESTKUDOS:0.01
+TRUTH_UPLOAD_FEE = TESTKUDOS:0.01
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck4
+
+[authorization-sms]
+COST = TESTKUDOS:0.0
+ENABLED = yes
+COMMAND = /bin/false
+
+[authorization-post]
+COST = TESTKUDOS:1.0
+ENABLED = yes
+COMMAND = /bin/false
+
+[authorization-email]
+COST = TESTKUDOS:0.01
+ENABLED = yes
+COMMAND = scat
diff --git a/src/testing/test_anastasis_reducer_4_free.conf b/src/testing/test_anastasis_reducer_4_free.conf
new file mode 100644
index 0000000..5e987a6
--- /dev/null
+++ b/src/testing/test_anastasis_reducer_4_free.conf
@@ -0,0 +1,8 @@
+@INLINE@ test_anastasis_reducer_4.conf
+
+[anastasis]
+ANNUAL_FEE = TESTKUDOS:0.0
+TRUTH_UPLOAD_FEE = TESTKUDOS:0.0
+
+[authorization-email]
+COST = TESTKUDOS:0.0
diff --git a/src/testing/test_prepare.sh b/src/testing/test_prepare.sh
new file mode 100755
index 0000000..253cb8f
--- /dev/null
+++ b/src/testing/test_prepare.sh
@@ -0,0 +1,322 @@
+#!/bin/bash
+# Shell script to launch Taler components
+# and Anastasis providers for local test
+# using TESTKUDOS.
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -rf $CONF $WALLET_DB $R1FILE $R2FILE $B1FILE $B2FILE $TMP_DIR
+ wait
+}
+
+if test "${1:-}" != "free" -a "${1:-}" != "fees"
+then
+ echo "Launch script with either 'free' or 'fees' argument to launch providers with/without fees."
+ exit 1
+fi
+
+CONF_1="test_anastasis_reducer_1.conf"
+CONF_2="test_anastasis_reducer_2.conf"
+CONF_3="test_anastasis_reducer_3.conf"
+CONF_4="test_anastasis_reducer_4.conf"
+if test $1 = 'free'
+then
+ CONF_4="test_anastasis_reducer_4_free.conf"
+fi
+
+# Exchange configuration file will be edited, so we create one
+# from the template.
+CONF=`mktemp test_reducerXXXXXX.conf`
+cp test_reducer.conf $CONF
+
+TMP_DIR=`mktemp -d keys-tmp-XXXXXX`
+WALLET_DB=`mktemp test_reducer_walletXXXXXX.json`
+B1FILE=`mktemp test_reducer_stateB1XXXXXX`
+B2FILE=`mktemp test_reducer_stateB2XXXXXX`
+R1FILE=`mktemp test_reducer_stateR1XXXXXX`
+R2FILE=`mktemp test_reducer_stateR2XXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+if test $1 = 'fees'
+then
+ echo -n "Testing for taler"
+ taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange required"
+ taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant required"
+ echo " FOUND"
+
+ echo -n "Testing for taler-bank-manage"
+ taler-bank-manage --help >/dev/null </dev/null || exit_skip " MISSING"
+ echo " FOUND"
+
+ echo -n "Testing for taler-wallet-cli"
+ taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
+ echo " FOUND"
+fi
+
+echo -n "Testing for anastasis-httpd"
+anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+echo -n "Initialize anastasis database ..."
+# Name of the Postgres database we will use for the script.
+# Will be dropped, do NOT use anything that might be used
+# elsewhere
+TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+
+dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1"
+anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log
+dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2"
+anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log
+dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3"
+anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log
+dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4"
+anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log
+
+echo " OK"
+
+if test $1 = 'fees'
+then
+
+ echo -n "Generating Taler auditor, exchange and merchant configurations ..."
+
+ DATA_DIR=`taler-config -f -c $CONF -s PATHS -o TALER_HOME`
+ rm -rf $DATA_DIR
+
+ # obtain key configuration data
+ MASTER_PRIV_FILE=`taler-config -f -c $CONF -s EXCHANGE -o MASTER_PRIV_FILE`
+ MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
+ mkdir -p $MASTER_PRIV_DIR
+ gnunet-ecc -g1 $MASTER_PRIV_FILE > /dev/null 2> /dev/null
+ MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
+ EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
+ MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
+ MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
+ BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
+ BANK_URL=http://localhost:${BANK_PORT}/
+ AUDITOR_URL=http://localhost:8083/
+ AUDITOR_PRIV_FILE=`taler-config -f -c $CONF -s AUDITOR -o AUDITOR_PRIV_FILE`
+ AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
+ mkdir -p $AUDITOR_PRIV_DIR
+ gnunet-ecc -g1 $AUDITOR_PRIV_FILE > /dev/null 2> /dev/null
+ AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
+
+ # patch configuration
+ TALER_DB=talercheck
+ taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
+ taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
+ taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TALER_DB
+ taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TALER_DB
+ taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TALER_DB
+ taler-config -c $CONF -s bank -o database -V postgres:///$TALER_DB
+ taler-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/"
+ taler-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/"
+
+ echo " OK"
+
+ echo -n "Setting up exchange ..."
+
+ # reset database
+ dropdb $TALER_DB >/dev/null 2>/dev/null || true
+ createdb $TALER_DB || exit_skip "Could not create database $TALER_DB"
+ taler-exchange-dbinit -c $CONF
+ taler-merchant-dbinit -c $CONF
+ taler-auditor-dbinit -c $CONF
+ taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL
+
+ echo " OK"
+
+ # Launch services
+ echo -n "Launching taler services ..."
+ taler-bank-manage-testing $CONF postgres:///$TALER_DB serve > taler-bank.log 2> taler-bank.err &
+ taler-exchange-secmod-eddsa -c $CONF 2> taler-exchange-secmod-eddsa.log &
+ taler-exchange-secmod-rsa -c $CONF 2> taler-exchange-secmod-rsa.log &
+ taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
+ taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
+ taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
+ taler-auditor-httpd -L INFO -c $CONF 2> taler-auditor-httpd.log &
+
+ echo " OK"
+
+fi
+
+
+echo -n "Launching anastasis services ..."
+# PREFIX="valgrind --log-file=anastasis-httpd.%p.log"
+PREFIX=""
+$PREFIX anastasis-httpd -L INFO -c $CONF_1 2> anastasis-httpd_1.log &
+$PREFIX anastasis-httpd -L INFO -c $CONF_2 2> anastasis-httpd_2.log &
+$PREFIX anastasis-httpd -L INFO -c $CONF_3 2> anastasis-httpd_3.log &
+$PREFIX anastasis-httpd -L INFO -c $CONF_4 2> anastasis-httpd_4.log &
+
+
+if test $1 = 'fees'
+then
+
+ # Wait for bank to be available (usually the slowest)
+ for n in `seq 1 50`
+ do
+ echo -n "."
+ sleep 0.2
+ OK=0
+ # bank
+ wget --tries=1 --timeout=1 http://localhost:8082/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+ done
+
+ if [ 1 != $OK ]
+ then
+ exit_skip "Failed to launch services (bank)"
+ fi
+
+ # Wait for all other taler services to be available
+ for n in `seq 1 50`
+ do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # exchange
+ wget --tries=1 --timeout=1 http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null || continue
+ # merchant
+ wget --tries=1 --timeout=1 http://localhost:9966/ -o /dev/null -O /dev/null >/dev/null || continue
+ # auditor
+ wget --tries=1 --timeout=1 http://localhost:8083/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+ done
+
+ if [ 1 != $OK ]
+ then
+ exit_skip "Failed to launch taler services"
+ fi
+
+ echo "OK"
+
+ echo -n "Setting up keys ..."
+ taler-exchange-offline -c $CONF \
+ download \
+ sign \
+ enable-account payto://x-taler-bank/localhost/Exchange \
+ enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
+ wire-fee now x-taler-bank TESTKUDOS:0.01 TESTKUDOS:0.01 \
+ upload &> taler-exchange-offline.log
+
+ echo -n "."
+
+ for n in `seq 1 3`
+ do
+ echo -n "."
+ OK=0
+ wget --tries=1 --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+ done
+
+ if [ 1 != $OK ]
+ then
+ exit_skip "Failed to setup keys"
+ fi
+
+ echo " OK"
+
+ echo -n "Setting up auditor signatures ..."
+ taler-auditor-offline -c $CONF \
+ download sign upload &> taler-auditor-offline.log
+ echo " OK"
+
+fi
+
+echo -n "Waiting for anastasis services ..."
+
+# Wait for anastasis services to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # anastasis_01
+ wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_02
+ wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_03
+ wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_04
+ wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch anastasis services"
+fi
+echo "OK"
+
+if test $1 = 'fees'
+then
+
+ echo -n "Configuring merchant instance ..."
+ # Setup merchant
+
+ curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_ms" : 3600000},"default_pay_delay":{"d_ms": 3600000}}' http://localhost:9966/private/instances
+
+ echo " OK"
+
+ echo -n "Preparing wallet"
+ rm $WALLET_DB
+ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api 'withdrawTestBalance' \
+ "$(jq -n '
+ {
+ amount: "TESTKUDOS:100",
+ bankBaseUrl: $BANK_URL,
+ exchangeBaseUrl: $EXCHANGE_URL
+ }' \
+ --arg BANK_URL "$BANK_URL" \
+ --arg EXCHANGE_URL "$EXCHANGE_URL"
+ )" 2> /dev/null >/dev/null
+ taler-wallet-cli --wallet-db=$WALLET_DB run-until-done 2>/dev/null >/dev/null
+ echo " OK"
+
+fi
+
+echo "You can now run anastasis-gtk in the shell we are starting now."
+echo "Exit the shell when done to terminate the test environment!"
+
+if test $1 = 'fees'
+then
+ echo "Use 'taler-wallet-cli --wallet-db=\$WALLET_DB handle-uri \$PAY_URI -y' to pay"
+ export WALLET_DB
+fi
+
+bash
+
+exit 0
diff --git a/src/testing/test_reducer.conf b/src/testing/test_reducer.conf
new file mode 100644
index 0000000..763f670
--- /dev/null
+++ b/src/testing/test_reducer.conf
@@ -0,0 +1,189 @@
+[PATHS]
+TALER_HOME = ${PWD}/test_reducer_home/
+TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
+TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
+TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
+TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/taler-system-runtime/
+
+[taler]
+CURRENCY = TESTKUDOS
+CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
+
+[anastasis]
+DB = postgres
+PAYMENT_BACKEND_URL = http://localhost:9966/
+ANNUAL_FEE = TESTKUDOS:0
+# 4.99
+TRUTH_UPLOAD_FEE = TESTKUDOS:0.0
+UPLOAD_LIMIT_MB = 1
+INSURANCE = TESTKUDOS:1.0
+ANNUAL_POLICY_UPLOAD_LIMIT = 42
+
+[authorization-question]
+COST = TESTKUDOS:0.0
+
+
+[exchange]
+MAX_KEYS_CACHING = forever
+DB = postgres
+MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
+SERVE = tcp
+UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
+UNIXPATH_MODE = 660
+PORT = 8081
+BASE_URL = http://localhost:8081/
+SIGNKEY_DURATION = 2 weeks
+SIGNKEY_LEGAL_DURATION = 2 years
+LEGAL_DURATION = 2 years
+LOOKAHEAD_SIGN = 3 weeks 1 day
+LOOKAHEAD_PROVIDE = 2 weeks 1 day
+KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/
+REVOCATION_DIR = ${TALER_DATA_HOME}/exchange/revocations/
+TERMS_ETAG = 0
+PRIVACY_ETAG = 0
+
+[merchant]
+SERVE = tcp
+PORT = 9966
+UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
+UNIXPATH_MODE = 660
+DEFAULT_WIRE_FEE_AMORTIZATION = 1
+DB = postgres
+WIREFORMAT = default
+# Set very low, so we can be sure that the database generated
+# will contain wire transfers "ready" for the aggregator.
+WIRE_TRANSFER_DELAY = 1 minute
+DEFAULT_PAY_DEADLINE = 1 day
+DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
+KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
+DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
+
+# Ensure that merchant reports EVERY deposit confirmation to auditor
+FORCE_AUDIT = YES
+
+[auditor]
+DB = postgres
+AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
+SERVE = tcp
+UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
+UNIXPATH_MODE = 660
+PORT = 8083
+AUDITOR_URL = http://localhost:8083/
+TINY_AMOUNT = TESTKUDOS:0.01
+AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
+BASE_URL = "http://localhost:8083/"
+
+[bank]
+DATABASE = postgres:///taler-auditor-basedb
+MAX_DEBT = TESTKUDOS:50.0
+MAX_DEBT_BANK = TESTKUDOS:100000.0
+HTTP_PORT = 8082
+SUGGESTED_EXCHANGE = http://localhost:8081/
+SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
+ALLOW_REGISTRATIONS = YES
+SERVE = http
+
+[exchangedb]
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+LEGAL_RESERVE_EXPIRATION_TIME = 7 years
+
+[exchange-account-1]
+PAYTO_URI = payto://x-taler-bank/localhost/Exchange
+enable_debit = yes
+enable_credit = yes
+WIRE_GATEWAY_URL = "http://localhost:8082/taler-wire-gateway/Exchange/"
+WIRE_GATEWAY_AUTH_METHOD = basic
+USERNAME = Exchange
+PASSWORD = x
+
+[merchant-exchange-default]
+EXCHANGE_BASE_URL = http://localhost:8081/
+CURRENCY = TESTKUDOS
+
+[coin_kudos_ct_1]
+value = TESTKUDOS:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.01
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_ct_10]
+value = TESTKUDOS:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_1]
+value = TESTKUDOS:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.02
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_2]
+value = TESTKUDOS:2
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+rsa_keysize = 1024
+
+[coin_kudos_4]
+value = TESTKUDOS:4
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+rsa_keysize = 1024
+
+[coin_kudos_5]
+value = TESTKUDOS:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_8]
+value = TESTKUDOS:8
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.05
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.04
+rsa_keysize = 1024
+
+[coin_kudos_10]
+value = TESTKUDOS:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024