summaryrefslogtreecommitdiff
path: root/src/anastasis/anastasis-gtk_pe-edit-policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/anastasis/anastasis-gtk_pe-edit-policy.c')
-rw-r--r--src/anastasis/anastasis-gtk_pe-edit-policy.c566
1 files changed, 566 insertions, 0 deletions
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 */