anastasis-gtk

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

anastasis-gtk_pe-edit-policy.c (14998B)


      1 /*
      2      This file is part of anastasis-gtk.
      3      Copyright (C) 2021 Anastasis SARL
      4 
      5      Anastasis is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      Anastasis is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with Anastasis; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @file src/anastasis/anastasis-gtk_pe-edit-policy.c
     23  * @brief Handle request to interactively edit policy
     24  * @author Christian Grothoff
     25  */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <strings.h>
     28 #include "anastasis_gtk_util.h"
     29 #include "anastasis-gtk_action.h"
     30 #include "anastasis-gtk_helper.h"
     31 #include "anastasis-gtk_pe.h"
     32 #include <jansson.h>
     33 
     34 
     35 /**
     36  * Context for the edit dialog.
     37  */
     38 struct EditDialogContext;
     39 
     40 /**
     41  * Information we track per line in the grid.
     42  */
     43 struct LineContext
     44 {
     45   /**
     46    * Kept in a DLL.
     47    */
     48   struct LineContext *next;
     49 
     50   /**
     51    * Kept in a DLL.
     52    */
     53   struct LineContext *prev;
     54 
     55   /**
     56    * Context this line context belongs with.
     57    */
     58   struct EditDialogContext *edc;
     59 
     60   /**
     61    * Our combo box.
     62    */
     63   GtkComboBox *cb;
     64 
     65   /**
     66    * Model for our combo box.
     67    */
     68   GtkTreeModel *model;
     69 
     70   /**
     71    * Challenge index for this line.
     72    */
     73   unsigned int cindex;
     74 
     75   /**
     76    * Is this challenge used?
     77    */
     78   bool on;
     79 };
     80 
     81 
     82 /**
     83  * Context for the edit dialog.
     84  */
     85 struct EditDialogContext
     86 {
     87   /**
     88    * Builder of the dialog.
     89    */
     90   GtkBuilder *builder;
     91 
     92   /**
     93    * Head of line contexts for this dialog
     94    */
     95   struct LineContext *lc_head;
     96 
     97   /**
     98    * Tail of line contexts for this dialog
     99    */
    100   struct LineContext *lc_tail;
    101 
    102   /**
    103    * Policy index. UINT_MAX for a new policy.
    104    */
    105   unsigned int pindex;
    106 
    107 };
    108 
    109 
    110 /**
    111  * Handle the response from the edit dialog.
    112  *
    113  * @param dialog the dialog
    114  * @param response_id what the user's action was
    115  * @param user_data a `struct EditDialogContext`
    116  */
    117 void
    118 anastasis_gtk_policy_edit_dialog_response_cb (
    119   GtkDialog *dialog,
    120   gint response_id,
    121   gpointer user_data)
    122 {
    123   struct EditDialogContext *edc = user_data;
    124 
    125   if (GTK_RESPONSE_OK == response_id)
    126   {
    127     json_t *policy;
    128 
    129     policy = json_array ();
    130     GNUNET_assert (NULL != policy);
    131     for (struct LineContext *lctx = edc->lc_head;
    132          NULL != lctx;
    133          lctx = lctx->next)
    134     {
    135       GtkTreeIter iter;
    136       gchar *url;
    137 
    138       if (! lctx->on)
    139         continue;
    140       if (! gtk_combo_box_get_active_iter (lctx->cb,
    141                                            &iter))
    142       {
    143         GNUNET_break (0);
    144         continue;
    145       }
    146       gtk_tree_model_get (lctx->model,
    147                           &iter,
    148                           0, &url,
    149                           -1);
    150       GNUNET_assert (0 ==
    151                      json_array_append_new (
    152                        policy,
    153                        GNUNET_JSON_PACK (
    154                          GNUNET_JSON_pack_uint64 ("authentication_method",
    155                                                   lctx->cindex),
    156                          GNUNET_JSON_pack_string ("provider",
    157                                                   url))));
    158       g_free (url);
    159     }
    160     if (UINT_MAX == edc->pindex)
    161     {
    162       json_t *args;
    163 
    164       args = GNUNET_JSON_PACK (
    165         GNUNET_JSON_pack_array_steal ("policy",
    166                                       policy));
    167       AG_ra = ANASTASIS_redux_action (AG_redux_state,
    168                                       "add_policy",
    169                                       args,
    170                                       &AG_action_cb,
    171                                       NULL);
    172       json_decref (args);
    173     }
    174     else
    175     {
    176       json_t *args;
    177 
    178       args = GNUNET_JSON_PACK (
    179         GNUNET_JSON_pack_uint64 ("policy_index",
    180                                  edc->pindex),
    181         GNUNET_JSON_pack_array_steal ("policy",
    182                                       policy));
    183       AG_ra = ANASTASIS_redux_action (AG_redux_state,
    184                                       "update_policy",
    185                                       args,
    186                                       &AG_action_cb,
    187                                       NULL);
    188       json_decref (args);
    189     }
    190   }
    191   /* clean up */
    192   {
    193     struct LineContext *lctx;
    194 
    195     while (NULL != (lctx = edc->lc_head))
    196     {
    197       GNUNET_CONTAINER_DLL_remove (edc->lc_head,
    198                                    edc->lc_tail,
    199                                    lctx);
    200       GNUNET_free (lctx);
    201     }
    202   }
    203   gtk_widget_destroy (GTK_WIDGET (dialog));
    204   g_object_unref (G_OBJECT (edc->builder));
    205   GNUNET_free (edc);
    206 }
    207 
    208 
    209 /**
    210  * The user changed an entry in the combo boxy of an edit
    211  * dialog. Update the ability to confirm the selection:
    212  * if at least one authentication method is selected, the
    213  * OK button should be sensitive.
    214  *
    215  * @param widget the combo box that was changed
    216  * @param user_data the `struct EditDialogContext`
    217  */
    218 static void
    219 combo_box_changed_cb (
    220   GtkComboBox *widget,
    221   gpointer user_data)
    222 {
    223   struct LineContext *lc = user_data;
    224   struct EditDialogContext *edc = lc->edc;
    225 
    226   /* Update our line context's on/off flag */
    227   {
    228     GtkTreeIter iter;
    229 
    230     if (! gtk_combo_box_get_active_iter (lc->cb,
    231                                          &iter))
    232     {
    233       GNUNET_break (0);
    234     }
    235     else
    236     {
    237       gchar *url;
    238 
    239       gtk_tree_model_get (lc->model,
    240                           &iter,
    241                           0, &url,
    242                           -1);
    243       lc->on = (0 !=
    244                 strcmp (_ ("<off>"),
    245                         url));
    246       g_free (url);
    247     }
    248   }
    249   /* finally, update "OK" button sensitivity */
    250   {
    251     GtkWidget *ok_button;
    252     bool legal = false;
    253 
    254     for (struct LineContext *lctx = edc->lc_head;
    255          NULL != lctx;
    256          lctx = lctx->next)
    257       legal |= lctx->on;
    258     ok_button = GTK_WIDGET (gtk_builder_get_object (edc->builder,
    259                                                     "ok_button"));
    260     gtk_widget_set_sensitive (ok_button,
    261                               legal);
    262   }
    263 }
    264 
    265 
    266 /**
    267  * Check if the authentication provider @a ap offers
    268  * authentications of type @a type. If so, return true.
    269  *
    270  * @param type method to check for
    271  * @param ap provider to check against
    272  * @return true if @a ap offers @a type
    273  */
    274 static bool
    275 ap_matches (const char *type,
    276             json_t *ap)
    277 {
    278   json_t *methods;
    279   size_t index;
    280   json_t *method;
    281   const char *status;
    282 
    283   status = json_string_value (json_object_get (ap,
    284                                                "status"));
    285   if (0 != strcasecmp (status,
    286                        "ok"))
    287   {
    288     /* provider is down, cannot proceed */
    289     return false;
    290   }
    291   methods = json_object_get (ap,
    292                              "methods");
    293   GNUNET_break (NULL != methods);
    294   json_array_foreach (methods, index, method)
    295   {
    296     const char *offer;
    297 
    298     offer = json_string_value (json_object_get (method,
    299                                                 "type"));
    300     if (NULL == offer)
    301     {
    302       GNUNET_break (0);
    303       continue;
    304     }
    305     if (0 == strcasecmp (type,
    306                          offer))
    307       return true;
    308   }
    309   return false;
    310 }
    311 
    312 
    313 /**
    314  * Create a GtkListStore containing all of the URLs
    315  * of Anastasis providers offering @a type as an
    316  * authentication method.
    317  *
    318  * @return the model
    319  */
    320 static GtkTreeModel *
    321 make_model (const char *type)
    322 {
    323   GtkListStore *ls;
    324   json_t *aps;
    325   const char *url;
    326   json_t *ap;
    327 
    328   ls = gtk_list_store_new (1,
    329                            G_TYPE_STRING);
    330   gtk_list_store_insert_with_values (ls,
    331                                      NULL,
    332                                      -1,
    333                                      0, _ ("<off>"),
    334                                      -1);
    335   aps = json_object_get (AG_redux_state,
    336                          "authentication_providers");
    337   GNUNET_break (NULL != aps);
    338   json_object_foreach (aps, url, ap) {
    339     if (ap_matches (type,
    340                     ap))
    341     {
    342       gtk_list_store_insert_with_values (ls,
    343                                          NULL,
    344                                          -1,
    345                                          0, url,
    346                                          -1);
    347     }
    348   }
    349 
    350   return GTK_TREE_MODEL (ls);
    351 }
    352 
    353 
    354 /**
    355  * Select entry in @a cb based on the @a url.
    356  *
    357  * @param url provider to select
    358  * @param lctx line to update
    359  */
    360 static void
    361 select_by_url (const char *url,
    362                struct LineContext *lctx)
    363 {
    364   GtkTreeIter iter;
    365 
    366   if (! gtk_tree_model_get_iter_first (lctx->model,
    367                                        &iter))
    368   {
    369     GNUNET_break (0);
    370     return;
    371   }
    372   do {
    373     gchar *have;
    374 
    375     gtk_tree_model_get (lctx->model,
    376                         &iter,
    377                         0, &have,
    378                         -1);
    379     if (0 == strcmp (have,
    380                      url))
    381     {
    382       gtk_combo_box_set_active_iter (lctx->cb,
    383                                      &iter);
    384       lctx->on = true;
    385       g_free (have);
    386       return;
    387     }
    388     g_free (have);
    389   } while (gtk_tree_model_iter_next (lctx->model,
    390                                      &iter));
    391   GNUNET_break (0); /* not found */
    392 }
    393 
    394 
    395 /**
    396  * Select entry in @a cb based on the @a methods for
    397  * challenge @a cindex.
    398  *
    399  * @param methods methods of policy to base selection on
    400  * @param lctx line to update
    401  */
    402 static void
    403 select_by_policy (const json_t *methods,
    404                   struct LineContext *lctx)
    405 {
    406   size_t index;
    407   json_t *method;
    408   GtkTreeIter iter;
    409 
    410   if (! gtk_tree_model_get_iter_first (lctx->model,
    411                                        &iter))
    412   {
    413     GNUNET_break (0);
    414     return;
    415   }
    416   gtk_combo_box_set_active_iter (lctx->cb,
    417                                  &iter);
    418   json_array_foreach (methods, index, method) {
    419     json_int_t am = json_integer_value (json_object_get (method, \
    420                                                          "authentication_method"));
    421     const char *url;
    422 
    423     if (am != lctx->cindex)
    424       continue;
    425     url = json_string_value (json_object_get (method,
    426                                               "provider"));
    427     select_by_url (url,
    428                    lctx);
    429     break;
    430   }
    431 }
    432 
    433 
    434 void
    435 AG_edit_policy (guint pindex)
    436 {
    437   struct EditDialogContext *edc;
    438   GtkGrid *grid;
    439   json_t *methods = NULL;
    440 
    441   edc = GNUNET_new (struct EditDialogContext);
    442   edc->builder = ANASTASIS_GTK_get_new_builder (
    443     ANASTASIS_GTK_project_data (),
    444     "anastasis_gtk_edit_policy.glade",
    445     edc);
    446   edc->pindex = pindex;
    447   if (NULL == edc->builder)
    448   {
    449     GNUNET_break (0);
    450     GNUNET_free (edc);
    451     return;
    452   }
    453   if (UINT_MAX != pindex)
    454   {
    455     json_t *policies;
    456     json_t *policy;
    457 
    458     policies = json_object_get (AG_redux_state,
    459                                 "policies");
    460     policy = json_array_get (policies,
    461                              pindex);
    462     methods = json_object_get (policy,
    463                                "methods");
    464     GNUNET_break (NULL != methods);
    465   }
    466   grid = GTK_GRID (gtk_builder_get_object (edc->builder,
    467                                            "policy_grid"));
    468   {
    469     json_t *ams = json_object_get (AG_redux_state,
    470                                    "authentication_methods");
    471     json_t *am;
    472     size_t index;
    473     gint row = 1;
    474 
    475     json_array_foreach (ams, index, am) {
    476       const char *type;
    477       const char *instructions;
    478       struct GNUNET_JSON_Specification spec[] = {
    479         GNUNET_JSON_spec_string ("type",
    480                                  &type),
    481         GNUNET_JSON_spec_string ("instructions",
    482                                  &instructions),
    483         GNUNET_JSON_spec_end ()
    484       };
    485       char *labels;
    486       GtkWidget *label;
    487       GtkWidget *cb;
    488       struct LineContext *lctx;
    489 
    490       if (GNUNET_OK !=
    491           GNUNET_JSON_parse (am,
    492                              spec,
    493                              NULL, NULL))
    494       {
    495         GNUNET_break (0);
    496         continue;
    497       }
    498       lctx = GNUNET_new (struct LineContext);
    499       lctx->cindex = index;
    500       GNUNET_asprintf (&labels,
    501                        "<b>%s</b>: %s",
    502                        type,
    503                        instructions);
    504       label = gtk_label_new (NULL);
    505       gtk_label_set_markup (GTK_LABEL (label),
    506                             labels);
    507       GNUNET_free (labels);
    508       lctx->model = make_model (type);
    509       cb = gtk_combo_box_new_with_model (lctx->model);
    510       lctx->cb = GTK_COMBO_BOX (cb);
    511       {
    512         GtkCellRenderer *renderer;
    513 
    514         renderer = gtk_cell_renderer_text_new ();
    515         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb),
    516                                     renderer,
    517                                     true);
    518         gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb),
    519                                        renderer,
    520                                        "text",
    521                                        0);
    522       }
    523       lctx->edc = edc;
    524       GNUNET_CONTAINER_DLL_insert (edc->lc_head,
    525                                    edc->lc_tail,
    526                                    lctx);
    527       g_object_connect (cb,
    528                         "signal::changed",
    529                         &combo_box_changed_cb, lctx,
    530                         NULL);
    531       if (NULL != methods)
    532         select_by_policy (methods,
    533                           lctx);
    534       gtk_grid_insert_row (grid,
    535                            row);
    536       gtk_widget_show (label);
    537       gtk_grid_attach (grid,
    538                        label,
    539                        0,
    540                        row,
    541                        1,
    542                        1);
    543       g_object_set (cb,
    544                     "expand",
    545                     TRUE,
    546                     NULL);
    547       gtk_widget_show (cb);
    548       gtk_grid_attach (grid,
    549                        cb,
    550                        1,
    551                        row,
    552                        1,
    553                        1);
    554       row++;
    555     }
    556   }
    557   {
    558     GtkWidget *toplevel;
    559     GtkWidget *ad;
    560     GtkWidget *anchor;
    561     GtkRequisition req;
    562 
    563     anchor = GTK_WIDGET (GCG_get_main_window_object (
    564                            "anastasis_gtk_main_window_quit_button"));
    565     toplevel = gtk_widget_get_toplevel (anchor);
    566     ad = GTK_WIDGET (gtk_builder_get_object (edc->builder,
    567                                              "anastasis_gtk_policy_edit_dialog")
    568                      );
    569     gtk_widget_get_preferred_size (ad,
    570                                    NULL,
    571                                    &req);
    572     gtk_window_resize (GTK_WINDOW (ad),
    573                        req.width,
    574                        req.height);
    575     gtk_window_set_transient_for (GTK_WINDOW (ad),
    576                                   GTK_WINDOW (toplevel));
    577     gtk_window_present (GTK_WINDOW (ad));
    578   }
    579 }
    580 
    581 
    582 /* end of anastasis-gtk_pe-edit-policy.c */