anastasis-gtk

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

anastasis-gtk_handle-secret-buttons.c (14646B)


      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  * @file src/anastasis/anastasis-gtk_handle-secret-buttons.c
     22  * @brief Main function of anastasis-gtk
     23  * @author Christian Grothoff
     24  * @author Dennis Neufeld
     25  */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include "anastasis_gtk_util.h"
     28 #include "anastasis-gtk_action.h"
     29 #include "anastasis-gtk_handle-expiration-change.h"
     30 #include "anastasis-gtk_helper.h"
     31 #include <jansson.h>
     32 #include <magic.h>
     33 
     34 
     35 /**
     36  * Global handle to MAGIC data.
     37  */
     38 static magic_t magic;
     39 
     40 
     41 /**
     42  * Function called once the user picked the file holding the secret.
     43  *
     44  * @param source the file dialog
     45  * @param res the result of the user's choice
     46  * @param user_data unused
     47  */
     48 static void
     49 open_secret_ready_cb (GObject *source,
     50                       GAsyncResult *res,
     51                       gpointer user_data)
     52 {
     53   GFile *file;
     54   char *filename;
     55   const char *fn;
     56   size_t data_size;
     57   void *data;
     58   const char *mime;
     59   GtkEntry *entry;
     60   const char *name;
     61 
     62   (void) user_data;
     63   file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source),
     64                                      res,
     65                                      NULL);
     66   if (NULL == file)
     67     return; /* user aborted */
     68   filename = ANASTASIS_GTK_file_get_path_utf8 (file);
     69   g_object_unref (file);
     70   if (NULL == filename)
     71     return;
     72   fn = strrchr (filename,
     73                 '/');
     74   if (NULL == fn)
     75     fn = filename;
     76   else
     77     fn++; /* skip '/' itself */
     78   {
     79     struct GNUNET_DISK_FileHandle *fh;
     80     off_t size;
     81     enum GNUNET_GenericReturnValue ret;
     82 
     83     fh = GNUNET_DISK_file_open (filename,
     84                                 GNUNET_DISK_OPEN_READ,
     85                                 GNUNET_DISK_PERM_NONE);
     86     if (NULL == fh)
     87     {
     88       AG_error ("Failed to open file `%s': %s",
     89                 filename,
     90                 strerror (errno));
     91       GNUNET_free (filename);
     92       return;
     93     }
     94     ret = GNUNET_DISK_file_handle_size (fh,
     95                                         &size);
     96     if (GNUNET_OK != ret)
     97     {
     98       AG_error ("Failed to obtain file size `%s': %s",
     99                 filename,
    100                 strerror (errno));
    101       GNUNET_free (filename);
    102       GNUNET_DISK_file_close (fh);
    103       return;
    104     }
    105     data_size = (size_t) size;
    106     data = GNUNET_malloc_large (data_size);
    107     if (NULL == data)
    108     {
    109       AG_error ("Failed to allocate memory for file `%s': %s",
    110                 filename,
    111                 strerror (errno));
    112       GNUNET_free (filename);
    113       GNUNET_DISK_file_close (fh);
    114       return;
    115     }
    116     if (size !=
    117         GNUNET_DISK_file_read (fh,
    118                                data,
    119                                data_size))
    120     {
    121       AG_error ("Failed read file `%s': %s",
    122                 filename,
    123                 strerror (errno));
    124       GNUNET_free (data);
    125       GNUNET_free (filename);
    126       GNUNET_DISK_file_close (fh);
    127       return;
    128     }
    129     GNUNET_DISK_file_close (fh);
    130   }
    131   entry = GTK_ENTRY (GCG_get_main_window_object (
    132                        "anastasis_gtk_secret_name_entry"));
    133   name = gtk_editable_get_text (GTK_EDITABLE (entry));
    134   mime = magic_buffer (magic,
    135                        data,
    136                        data_size);
    137   {
    138     json_t *arguments;
    139     struct GNUNET_TIME_Timestamp expiration;
    140 
    141     expiration = AG_get_desired_expiration ();
    142     if (GNUNET_TIME_absolute_is_zero (expiration.abs_time))
    143     {
    144       GNUNET_free (data);
    145       GNUNET_free (filename);
    146       return; /* failured */
    147     }
    148     arguments = json_pack ("{s:s?,s:{s:o,s:s,s:s?},s:o}",
    149                            "name",
    150                            name,
    151                            "secret",
    152                            "value",
    153                            GNUNET_JSON_from_data (data,
    154                                                   data_size),
    155                            "filename",
    156                            fn,
    157                            "mime",
    158                            mime,
    159                            "expiration",
    160                            GNUNET_JSON_from_timestamp (expiration));
    161     GNUNET_free (filename);
    162     GNUNET_free (data);
    163     GNUNET_assert (NULL != arguments);
    164     AG_freeze ();
    165     AG_ra = ANASTASIS_redux_action (AG_redux_state,
    166                                     "enter_secret",
    167                                     arguments,
    168                                     &AG_action_cb,
    169                                     NULL);
    170     json_decref (arguments);
    171   }
    172 }
    173 
    174 
    175 /**
    176  * User clicked the "open" button in the dialog where the secret is entered.
    177  *
    178  * @param button the button
    179  * @param user_data unused
    180  */
    181 void
    182 anastasis_gtk_enter_secret_open_button_clicked_cb (GtkButton *button,
    183                                                    gpointer user_data)
    184 {
    185   GtkFileDialog *dialog;
    186 
    187   (void) user_data;
    188   dialog = gtk_file_dialog_new ();
    189   gtk_file_dialog_set_title (dialog,
    190                              _ ("Open file with secret to back up"));
    191   gtk_file_dialog_open (dialog,
    192                         GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
    193                         NULL,
    194                         &open_secret_ready_cb,
    195                         NULL);
    196   g_object_unref (dialog);
    197 }
    198 
    199 
    200 /**
    201  * Function called once the user picked the file to save the secret in.
    202  *
    203  * @param source the file dialog
    204  * @param res the result of the user's choice
    205  * @param user_data unused
    206  */
    207 static void
    208 save_secret_ready_cb (GObject *source,
    209                       GAsyncResult *res,
    210                       gpointer user_data)
    211 {
    212   GFile *file;
    213   char *filename;
    214   size_t data_len = 0;
    215   const char *text = NULL;
    216   void *data = NULL;
    217   json_t *cs;
    218   struct GNUNET_JSON_Specification cspec[] = {
    219     GNUNET_JSON_spec_mark_optional (
    220       GNUNET_JSON_spec_string ("text",
    221                                &text),
    222       NULL),
    223     GNUNET_JSON_spec_mark_optional (
    224       GNUNET_JSON_spec_varsize ("value",
    225                                 &data,
    226                                 &data_len),
    227       NULL),
    228     GNUNET_JSON_spec_end ()
    229   };
    230 
    231   (void) user_data;
    232   file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source),
    233                                       res,
    234                                       NULL);
    235   if (NULL == file)
    236     return; /* user aborted */
    237   filename = ANASTASIS_GTK_file_get_path_utf8 (file);
    238   g_object_unref (file);
    239   if (NULL == filename)
    240     return;
    241   cs = json_object_get (AG_redux_state,
    242                         "core_secret");
    243   GNUNET_assert (NULL != cs);
    244   if (GNUNET_OK !=
    245       GNUNET_JSON_parse (cs,
    246                          cspec,
    247                          NULL, NULL))
    248   {
    249     GNUNET_break (0);
    250     return;
    251   }
    252   {
    253     enum GNUNET_GenericReturnValue ret;
    254 
    255     ret = GNUNET_DISK_fn_write (filename,
    256                                 (NULL == data)
    257                                 ? text
    258                                 : data,
    259                                 (NULL == data)
    260                                 ? strlen (text)
    261                                 : data_len,
    262                                 GNUNET_DISK_PERM_USER_READ);
    263     switch (ret)
    264     {
    265     case GNUNET_OK:
    266       break;
    267     case GNUNET_NO:
    268       AG_error ("File `%s' exists",
    269                 filename);
    270       break;
    271     case GNUNET_SYSERR:
    272       AG_error ("Writing to file `%s' failed: %s",
    273                 filename,
    274                 strerror (errno));
    275       break;
    276     }
    277   }
    278   GNUNET_JSON_parse_free (cspec);
    279   GNUNET_free (filename);
    280 }
    281 
    282 
    283 /**
    284  * User clicked the "save as" button in the dialog with the recovered secret.
    285  *
    286  * @param button the button
    287  * @param user_data unused
    288  */
    289 void
    290 anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
    291                                                 gpointer user_data)
    292 {
    293   static const struct
    294   {
    295     const char *mime;
    296     const char *fn;
    297   } mime_map [] = {
    298     { .mime = "text/plain",
    299       .fn = "untitled.txt" },
    300     { .mime = "text/html",
    301       .fn = "untitled.html" },
    302     { .mime = "text/xml",
    303       .fn = "untitled.xml" },
    304     { .mime = "text/csv",
    305       .fn = "untitled.csv" },
    306     { .mime = "image/jpeg",
    307       .fn = "untitled.jpeg" },
    308     { .mime = "image/png",
    309       .fn = "untitled.png" },
    310     { .mime = "application/pgp-keys",
    311       .fn = "untitled.pgp" },
    312     { .mime = "application/json",
    313       .fn = "untitled.json" },
    314     { .mime = "application/taler-wallet-secret",
    315       .fn = "untitled.tws" },
    316     { .mime = "application/taler-wallet",
    317       .fn = "untitled.twd" },
    318     { .mime = NULL,
    319       .fn = NULL }
    320   };
    321 
    322   GtkFileDialog *dialog;
    323   const char *mime = NULL;
    324   const char *fn = NULL;
    325   json_t *cs;
    326   struct GNUNET_JSON_Specification spec[] = {
    327     GNUNET_JSON_spec_mark_optional (
    328       GNUNET_JSON_spec_string ("filename",
    329                                &fn),
    330       NULL),
    331     GNUNET_JSON_spec_mark_optional (
    332       GNUNET_JSON_spec_string ("mime",
    333                                &mime),
    334       NULL),
    335     GNUNET_JSON_spec_end ()
    336   };
    337 
    338   (void) user_data;
    339   cs = json_object_get (AG_redux_state,
    340                         "core_secret");
    341   GNUNET_assert (NULL != cs);
    342   GNUNET_assert (GNUNET_OK ==
    343                  GNUNET_JSON_parse (cs,
    344                                     spec,
    345                                     NULL, NULL));
    346   if ( (NULL == fn) &&
    347        (NULL != mime) )
    348   {
    349     fn = "untitled.secret";
    350     for (unsigned int i = 0; NULL != mime_map[i].mime; i++)
    351     {
    352       if (0 != strcmp (mime_map[i].mime,
    353                        mime))
    354         continue;
    355       fn = mime_map[i].fn;
    356       break;
    357     }
    358   }
    359   dialog = gtk_file_dialog_new ();
    360   gtk_file_dialog_set_title (dialog,
    361                              _ ("Save recovered secret"));
    362   {
    363     GtkFileFilter *filter;
    364     GListStore *filters;
    365 
    366     filter = gtk_file_filter_new ();
    367     gtk_file_filter_set_name (filter,
    368                               _ ("Secrets"));
    369     gtk_file_filter_add_pattern (filter,
    370                                  "*.secret");
    371     filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
    372     g_list_store_append (filters,
    373                          filter);
    374     g_object_unref (filter);
    375     gtk_file_dialog_set_filters (dialog,
    376                                  G_LIST_MODEL (filters));
    377     g_object_unref (filters);
    378   }
    379   if (NULL != fn)
    380     gtk_file_dialog_set_initial_name (dialog,
    381                                       fn);
    382   gtk_file_dialog_save (dialog,
    383                         GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
    384                         NULL,
    385                         &save_secret_ready_cb,
    386                         NULL);
    387   g_object_unref (dialog);
    388 }
    389 
    390 
    391 /**
    392  * User clicked the "copy" button in the dialog with the recovered secret.
    393  *
    394  * @param button the button
    395  * @param user_data unused
    396  */
    397 void
    398 anastasis_gtk_secret_copy_button_clicked_cb (GtkButton *button,
    399                                              gpointer user_data)
    400 {
    401   size_t data_len = 0;
    402   void *data = NULL;
    403   const char *mime = NULL;
    404   const char *text = NULL;
    405   json_t *cs;
    406   struct GNUNET_JSON_Specification spec[] = {
    407     GNUNET_JSON_spec_mark_optional (
    408       GNUNET_JSON_spec_varsize ("value",
    409                                 &data,
    410                                 &data_len),
    411       NULL),
    412     GNUNET_JSON_spec_mark_optional (
    413       GNUNET_JSON_spec_string ("mime",
    414                                &mime),
    415       NULL),
    416     GNUNET_JSON_spec_mark_optional (
    417       GNUNET_JSON_spec_string ("text",
    418                                &text),
    419       NULL),
    420     GNUNET_JSON_spec_end ()
    421   };
    422   GdkClipboard *cb;
    423 
    424   (void) user_data;
    425   cs = json_object_get (AG_redux_state,
    426                         "core_secret");
    427   GNUNET_assert (NULL != cs);
    428   GNUNET_assert (GNUNET_OK ==
    429                  GNUNET_JSON_parse (cs,
    430                                     spec,
    431                                     NULL, NULL));
    432   cb = gtk_widget_get_clipboard (GTK_WIDGET (button));
    433   GNUNET_assert (NULL != cb);
    434   if (NULL != text)
    435   {
    436     gdk_clipboard_set_text (cb,
    437                             text);
    438   }
    439   else
    440   {
    441     if (0 == strncasecmp (mime,
    442                           "text/",
    443                           strlen ("text/")))
    444     {
    445       char *str;
    446 
    447       /* the secret is not necessarily 0-terminated */
    448       str = g_strndup (data,
    449                        data_len);
    450       gdk_clipboard_set_text (cb,
    451                               str);
    452       g_free (str);
    453     }
    454     else if (0 == strncasecmp (mime,
    455                                "image/",
    456                                strlen ("image/")))
    457     {
    458       GdkPixbufLoader *loader;
    459 
    460       loader = gdk_pixbuf_loader_new_with_mime_type (mime,
    461                                                      NULL);
    462       if (NULL != loader)
    463       {
    464         GdkPixbuf *pb;
    465 
    466         gdk_pixbuf_loader_write (loader,
    467                                  data,
    468                                  data_len,
    469                                  NULL);
    470         pb = gdk_pixbuf_loader_get_pixbuf (loader);
    471         if (NULL != pb)
    472         {
    473           GdkTexture *texture;
    474 
    475           texture = gdk_texture_new_for_pixbuf (pb);
    476           gdk_clipboard_set_texture (cb,
    477                                      texture);
    478           g_object_unref (texture);
    479         }
    480         else
    481         {
    482           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    483                       "Failed to parse secret image data.\n");
    484         }
    485         g_object_unref (loader);
    486       }
    487       else
    488       {
    489         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    490                     "Unsupported image mime type `%s'\n",
    491                     mime);
    492       }
    493     }
    494     else
    495     {
    496       GNUNET_break (0);
    497     }
    498   }
    499   GNUNET_JSON_parse_free (spec);
    500 }
    501 
    502 
    503 /**
    504  * Constructor for the library.  Loads the magic file.
    505  */
    506 void __attribute__ ((constructor))
    507 mime_ltdl_init ()
    508 {
    509   magic = magic_open (MAGIC_MIME_TYPE);
    510   if (0 != magic_load (magic,
    511                        NULL))
    512   {
    513     GNUNET_break (0);
    514   }
    515 }
    516 
    517 
    518 /**
    519  * Destructor for the library, cleans up.
    520  */
    521 void __attribute__ ((destructor))
    522 mime_ltdl_fini ()
    523 {
    524   if (NULL != magic)
    525   {
    526     magic_close (magic);
    527     magic = NULL;
    528   }
    529 }