anastasis-gtk

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

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


      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 from the open-file dialog upon completion.
     43  *
     44  * @param dialog the secret selection dialog
     45  * @param response_id response code from the dialog
     46  * @param user_data the builder of the dialog
     47  */
     48 void
     49 open_secret_dialog_response_cb (GtkDialog *dialog,
     50                                 gint response_id,
     51                                 gpointer user_data)
     52 {
     53   GtkBuilder *builder = GTK_BUILDER (user_data);
     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   if (GTK_RESPONSE_OK != response_id)
     63   {
     64     gtk_widget_destroy (GTK_WIDGET (dialog));
     65     g_object_unref (G_OBJECT (builder));
     66     return;
     67   }
     68   filename =
     69     ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
     70   gtk_widget_destroy (GTK_WIDGET (dialog));
     71   g_object_unref (G_OBJECT (builder));
     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_entry_get_text (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   GtkWidget *ad;
    186   GtkBuilder *builder;
    187 
    188   (void) button;
    189   (void) user_data;
    190   builder = ANASTASIS_GTK_get_new_builder (
    191     ANASTASIS_GTK_project_data (),
    192     "anastasis_gtk_open_secret_dialog.glade",
    193     NULL);
    194   if (NULL == builder)
    195   {
    196     GNUNET_break (0);
    197     return;
    198   }
    199   ad = GTK_WIDGET (gtk_builder_get_object (builder,
    200                                            "open_file_dialog"));
    201   {
    202     GtkWidget *toplevel;
    203 
    204     toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
    205     gtk_window_set_transient_for (GTK_WINDOW (ad),
    206                                   GTK_WINDOW (toplevel));
    207     gtk_window_present (GTK_WINDOW (ad));
    208   }
    209 }
    210 
    211 
    212 /**
    213  * Function called from the open-directory dialog upon completion.
    214  *
    215  * @param dialog the pseudonym selection dialog
    216  * @param response_id response code from the dialog
    217  * @param user_data the builder of the dialog
    218  */
    219 void
    220 save_secret_dialog_response_cb (GtkDialog *dialog,
    221                                 gint response_id,
    222                                 gpointer user_data)
    223 {
    224   GtkBuilder *builder = GTK_BUILDER (user_data);
    225   char *filename;
    226   size_t data_len = 0;
    227   const char *text = NULL;
    228   void *data = NULL;
    229   json_t *cs;
    230   struct GNUNET_JSON_Specification cspec[] = {
    231     GNUNET_JSON_spec_mark_optional (
    232       GNUNET_JSON_spec_string ("text",
    233                                &text),
    234       NULL),
    235     GNUNET_JSON_spec_mark_optional (
    236       GNUNET_JSON_spec_varsize ("value",
    237                                 &data,
    238                                 &data_len),
    239       NULL),
    240     GNUNET_JSON_spec_end ()
    241   };
    242 
    243   if (GTK_RESPONSE_ACCEPT != response_id)
    244   {
    245     gtk_widget_destroy (GTK_WIDGET (dialog));
    246     g_object_unref (G_OBJECT (builder));
    247     return;
    248   }
    249   filename =
    250     ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
    251   gtk_widget_destroy (GTK_WIDGET (dialog));
    252   g_object_unref (G_OBJECT (builder));
    253   cs = json_object_get (AG_redux_state,
    254                         "core_secret");
    255   GNUNET_assert (NULL != cs);
    256   if (GNUNET_OK !=
    257       GNUNET_JSON_parse (cs,
    258                          cspec,
    259                          NULL, NULL))
    260   {
    261     GNUNET_break (0);
    262     return;
    263   }
    264   {
    265     enum GNUNET_GenericReturnValue ret;
    266 
    267     ret = GNUNET_DISK_fn_write (filename,
    268                                 (NULL == data)
    269                                 ? text
    270                                 : data,
    271                                 (NULL == data)
    272                                 ? strlen (text)
    273                                 : data_len,
    274                                 GNUNET_DISK_PERM_USER_READ);
    275     switch (ret)
    276     {
    277     case GNUNET_OK:
    278       break;
    279     case GNUNET_NO:
    280       AG_error ("File `%s' exists",
    281                 filename);
    282       break;
    283     case GNUNET_SYSERR:
    284       AG_error ("Writing to file `%s' failed: %s",
    285                 filename,
    286                 strerror (errno));
    287       break;
    288     }
    289   }
    290   GNUNET_JSON_parse_free (cspec);
    291   GNUNET_free (filename);
    292 }
    293 
    294 
    295 /**
    296  * User clicked the "save as" button in the dialog with the recovered secret.
    297  *
    298  * @param button the button
    299  * @param user_data unused
    300  */
    301 void
    302 anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
    303                                                 gpointer user_data)
    304 {
    305   static const struct
    306   {
    307     const char *mime;
    308     const char *fn;
    309   } mime_map [] = {
    310     { .mime = "text/plain",
    311       .fn = "untitled.txt" },
    312     { .mime = "text/html",
    313       .fn = "untitled.html" },
    314     { .mime = "text/xml",
    315       .fn = "untitled.xml" },
    316     { .mime = "text/csv",
    317       .fn = "untitled.csv" },
    318     { .mime = "image/jpeg",
    319       .fn = "untitled.jpeg" },
    320     { .mime = "image/png",
    321       .fn = "untitled.png" },
    322     { .mime = "application/pgp-keys",
    323       .fn = "untitled.pgp" },
    324     { .mime = "application/json",
    325       .fn = "untitled.json" },
    326     { .mime = "application/taler-wallet-secret",
    327       .fn = "untitled.tws" },
    328     { .mime = "application/taler-wallet",
    329       .fn = "untitled.twd" },
    330     { .mime = NULL,
    331       .fn = NULL }
    332   };
    333 
    334   GtkWidget *ad;
    335   GtkBuilder *builder;
    336   const char *mime = NULL;
    337   const char *fn = NULL;
    338   json_t *cs;
    339   struct GNUNET_JSON_Specification spec[] = {
    340     GNUNET_JSON_spec_mark_optional (
    341       GNUNET_JSON_spec_string ("filename",
    342                                &fn),
    343       NULL),
    344     GNUNET_JSON_spec_mark_optional (
    345       GNUNET_JSON_spec_string ("mime",
    346                                &mime),
    347       NULL),
    348     GNUNET_JSON_spec_end ()
    349   };
    350 
    351   (void) button;
    352   (void) user_data;
    353   cs = json_object_get (AG_redux_state,
    354                         "core_secret");
    355   GNUNET_assert (NULL != cs);
    356   GNUNET_assert (GNUNET_OK ==
    357                  GNUNET_JSON_parse (cs,
    358                                     spec,
    359                                     NULL, NULL));
    360   builder = ANASTASIS_GTK_get_new_builder (
    361     ANASTASIS_GTK_project_data (),
    362     "anastasis_gtk_save_secret_dialog.glade",
    363     NULL);
    364   if (NULL == builder)
    365   {
    366     GNUNET_break (0);
    367     return;
    368   }
    369   ad = GTK_WIDGET (gtk_builder_get_object (builder,
    370                                            "save_file_dialog"));
    371   if ( (NULL == fn) &&
    372        (NULL != mime) )
    373   {
    374     fn = "untitled.secret";
    375     for (unsigned int i = 0; NULL != mime_map[i].mime; i++)
    376     {
    377       if (0 != strcmp (mime_map[i].mime,
    378                        mime))
    379         continue;
    380       fn = mime_map[i].fn;
    381       break;
    382     }
    383   }
    384   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
    385                                      fn);
    386   {
    387     GtkWidget *toplevel;
    388 
    389     toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
    390     gtk_window_set_transient_for (GTK_WINDOW (ad),
    391                                   GTK_WINDOW (toplevel));
    392     gtk_window_present (GTK_WINDOW (ad));
    393   }
    394 }
    395 
    396 
    397 /**
    398  * User clicked the "copy" button in the dialog with the recovered secret.
    399  *
    400  * @param button the button
    401  * @param user_data unused
    402  */
    403 void
    404 anastasis_gtk_secret_copy_button_clicked_cb (GtkButton *button,
    405                                              gpointer user_data)
    406 {
    407   size_t data_len = 0;
    408   void *data = NULL;
    409   const char *mime = NULL;
    410   const char *text = NULL;
    411   json_t *cs;
    412   struct GNUNET_JSON_Specification spec[] = {
    413     GNUNET_JSON_spec_mark_optional (
    414       GNUNET_JSON_spec_varsize ("value",
    415                                 &data,
    416                                 &data_len),
    417       NULL),
    418     GNUNET_JSON_spec_mark_optional (
    419       GNUNET_JSON_spec_string ("mime",
    420                                &mime),
    421       NULL),
    422     GNUNET_JSON_spec_mark_optional (
    423       GNUNET_JSON_spec_string ("text",
    424                                &text),
    425       NULL),
    426     GNUNET_JSON_spec_end ()
    427   };
    428   GtkClipboard *cb;
    429 
    430   (void) button;
    431   (void) user_data;
    432   cs = json_object_get (AG_redux_state,
    433                         "core_secret");
    434   GNUNET_assert (NULL != cs);
    435   GNUNET_assert (GNUNET_OK ==
    436                  GNUNET_JSON_parse (cs,
    437                                     spec,
    438                                     NULL, NULL));
    439   cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
    440   GNUNET_assert (NULL != cb);
    441   if (NULL != text)
    442   {
    443     gtk_clipboard_set_text (cb,
    444                             text,
    445                             strlen (text));
    446   }
    447   else
    448   {
    449     if (0 == strncasecmp (mime,
    450                           "text/",
    451                           strlen ("text/")))
    452     {
    453       gtk_clipboard_set_text (cb,
    454                               data,
    455                               data_len);
    456     }
    457     else if (0 == strncasecmp (mime,
    458                                "image/",
    459                                strlen ("image/")))
    460     {
    461       GdkPixbufLoader *loader;
    462 
    463       loader = gdk_pixbuf_loader_new_with_mime_type (mime,
    464                                                      NULL);
    465       if (NULL != loader)
    466       {
    467         GdkPixbuf *pb;
    468 
    469         gdk_pixbuf_loader_write (loader,
    470                                  data,
    471                                  data_len,
    472                                  NULL);
    473         pb = gdk_pixbuf_loader_get_pixbuf (loader);
    474         if (NULL != pb)
    475         {
    476           gtk_clipboard_set_image (cb,
    477                                    pb);
    478           g_object_unref (pb);
    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 }