diff options
Diffstat (limited to 'src')
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, + ¤cy, + -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 (®ex, + 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 (®ex, + qs, + 0, + NULL, + 0); + regfree (®ex); + 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 (®ex, + 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 (®ex, + qs, + 0, + NULL, + 0); + regfree (®ex); + 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 (®ex, + 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 (®ex, + input, + 0, + NULL, + 0)) + { + regfree (®ex); + return false; + } + regfree (®ex); + 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, ®ex, + -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 (>k_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 |