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 }