diff --git a/src/fileman.c b/src/fileman.c index b859517..d55b3a6 100644 --- a/src/fileman.c +++ b/src/fileman.c @@ -1,1315 +1,1313 @@ /* fileman.c - The GNU Privacy Assistant Copyright (C) 2000, 2001 G-N-U GmbH. Copyright (C) 2007, 2008 g10 Code GmbH This file is part of GPA. GPA 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 of the License, or (at your option) any later version. GPA 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 this program; if not, see . */ /* The file encryption/decryption/sign window. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "gpawidgets.h" #include "siglist.h" #include "helpmenu.h" #include "icons.h" #include "fileman.h" #include "gpafiledecryptop.h" #include "gpafileencryptop.h" #include "gpafilesignop.h" #include "gpafileverifyop.h" #if ! GTK_CHECK_VERSION (2, 10, 0) #define GTK_STOCK_SELECT_ALL "gtk-select-all" #endif /* Object and class definition. */ struct _GpaFileManager { GtkWindow parent; GtkWidget *window; GtkWidget *list_files; GList *selection_sensitive_actions; }; struct _GpaFileManagerClass { GtkWindowClass parent_class; }; /* There is only one instance of the file manage class. Use a global variable to keep track of it. */ static GpaFileManager *instance; /* We also need to save the parent class. */ static GObjectClass *parent_class; /* Definition of the sensitivity function type. */ typedef gboolean (*sensitivity_func_t)(gpointer); /* Constants to define the file list. */ enum { FILE_NAME_COLUMN, FILE_N_COLUMNS }; #define DND_TARGET_URI_LIST 1 /* Drag and drop target list. */ static GtkTargetEntry dnd_target_list[] = { { "text/uri-list", 0, DND_TARGET_URI_LIST } }; /* Local prototypes */ static GObject *gpa_file_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); /* * GtkWidget boilerplate. */ static void gpa_file_manager_finalize (GObject *object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static void gpa_file_manager_init (GpaFileManager *fileman) { fileman->selection_sensitive_actions = NULL; } static void gpa_file_manager_class_init (GpaFileManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = gpa_file_manager_constructor; object_class->finalize = gpa_file_manager_finalize; } GType gpa_file_manager_get_type (void) { static GType fileman_type = 0; if (!fileman_type) { static const GTypeInfo fileman_info = { sizeof (GpaFileManagerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gpa_file_manager_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaFileManager), 0, /* n_preallocs */ (GInstanceInitFunc) gpa_file_manager_init, }; fileman_type = g_type_register_static (GTK_TYPE_WINDOW, "GpaFileManager", &fileman_info, 0); } return fileman_type; } /* * File manager methods */ /* Return the currently selected files as a new list of filenames * structs. The list and the texts must be freed by the caller. */ static GList * get_selected_files (GtkWidget *list) { GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list)); GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); GList *selection = gtk_tree_selection_get_selected_rows (sel, &model); GList *files = NULL; while (selection) { gpa_file_item_t file_item; gchar *filename; GtkTreeIter iter; gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) selection->data); gtk_tree_model_get (model, &iter, FILE_NAME_COLUMN, &filename, -1); file_item = g_malloc0 (sizeof (*file_item)); file_item->filename_in = filename; files = g_list_append (files, file_item); selection = g_list_next (selection); } /* Free the selection */ g_list_foreach (selection, (GFunc) gtk_tree_path_free, NULL); g_list_free (selection); return files; } /* Add file FILENAME to the file list of FILEMAN and select it */ static gboolean add_file (GpaFileManager *fileman, const gchar *filename) { GtkListStore *store; GtkTreeIter iter; GtkTreePath *path; GtkTreeSelection *sel; gchar *filename_utf8; /* The tree contains filenames in the UTF-8 encoding. */ filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); /* Try to convert from the current locale as fallback. This is important for windows where g_filename_to_utf8 does not take locale into account because the filedialogs already convert to utf8. */ if (!filename_utf8) { filename_utf8 = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL); } /* Last fallback is guranteed to never be NULL so in doubt we can still fail later showing a filename that can't be found to the user etc.*/ if (!filename_utf8) { filename_utf8 = g_filename_display_name (filename); } store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fileman->list_files))); /* Check for duplicates. */ path = gtk_tree_path_new_first (); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) do { gchar *tmp; gboolean exists; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, FILE_NAME_COLUMN, &tmp, -1); exists = g_str_equal (filename_utf8, tmp); g_free (tmp); if (exists) { g_free (filename_utf8); gtk_tree_path_free (path); return FALSE; /* This file is already in our list. */ } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); gtk_tree_path_free (path); /* Append it to our list. */ gtk_list_store_append (store, &iter); /* FIXME: Add the file status when/if gpgme supports it */ gtk_list_store_set (store, &iter, FILE_NAME_COLUMN, filename_utf8, -1); /* Select the row */ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files)); gtk_tree_selection_select_iter (sel, &iter); return TRUE; } /* Add a file created by an operation to the list */ static void file_created_cb (GpaFileOperation *op, gpa_file_item_t item, gpointer data) { GpaFileManager *fileman = data; add_file (fileman, item->filename_out); } /* Do whatever is required with a file operation, to ensure proper clean up */ static void register_operation (GpaFileManager *fileman, GpaFileOperation *op) { g_signal_connect (G_OBJECT (op), "created_file", G_CALLBACK (file_created_cb), fileman); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), NULL); } /* Management of the selection sensitive actions. */ /* Return true if a selection is active. */ static gboolean has_selection (gpointer param) { GpaFileManager *fileman = param; GtkTreeSelection *sel; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files)); return (gtk_tree_selection_count_selected_rows (sel) > 0); } /* Add WIDGET to the list of sensitive actions of FILEMAN. */ static void add_selection_sensitive_action (GpaFileManager *fileman, GSimpleAction *action, sensitivity_func_t callback) { g_object_set_data (G_OBJECT (action), "gpa_sensitivity", callback); fileman->selection_sensitive_actions = g_list_append (fileman->selection_sensitive_actions, action); } /* Update the sensitivity of the widget DATA and pass PARAM through to the sensitivity callback. Usable as an iterator function in g_list_foreach. */ static void update_selection_sensitive_action (gpointer data, gpointer param) { sensitivity_func_t func; func = g_object_get_data (G_OBJECT (data), "gpa_sensitivity"); g_simple_action_set_enabled (G_SIMPLE_ACTION (data), func (param)); } /* Call update_selection_sensitive_action for all widgets in the list of sensitive actions and pass FILEMAN through as the user data parameter. */ static void update_selection_sensitive_actions (GpaFileManager *fileman) { g_list_foreach (fileman->selection_sensitive_actions, update_selection_sensitive_action, (gpointer) fileman); } /* Actions as called by the menu items. */ static GSList * get_load_file_name (GtkWidget *parent, const gchar *title, const gchar *directory) { static GtkWidget *dialog; GtkResponseType response; GSList *filenames = NULL; if (! dialog) { dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); } if (directory) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), directory); gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog)); /* Run the dialog until there is a valid response. */ response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response == GTK_RESPONSE_OK) filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog)); gtk_widget_hide (dialog); return filenames; } /* Handle menu item "File/Open". */ void open_file_one (gpointer data, gpointer user_data) { GpaFileManager *fileman = user_data; gchar *filename = (gchar *) data; /* FIXME: We are ignoring errors here. */ add_file (fileman, filename); g_free (filename); } static void file_open (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GSList *filenames; filenames = get_load_file_name (GTK_WIDGET (fileman), _("Open File"), NULL); if (! filenames) return; g_slist_foreach (filenames, open_file_one, fileman); g_slist_free (filenames); } /* Handle menu item "File/Clear". */ static void file_clear (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fileman->list_files))); gtk_list_store_clear (store); } /* Handle menu item "File/Verify". */ static void file_verify (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileVerifyOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_verify_operation_new (GTK_WIDGET (fileman), files); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Sign". */ static void file_sign (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList * files; GpaFileSignOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_sign_operation_new (GTK_WIDGET (fileman), files, FALSE); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Encrypt". */ static void file_encrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileEncryptOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_encrypt_operation_new (GTK_WIDGET (fileman), files, FALSE); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Decrypt". */ static void file_decrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileDecryptOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_decrypt_verify_operation_new (GTK_WIDGET (fileman), files); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Close". */ static void file_close (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; gtk_widget_destroy (GTK_WIDGET (fileman)); } /* Handle menu item "File/Quit". */ static void file_quit (GSimpleAction *simple, GVariant *parameter, gpointer param) { g_application_quit (G_APPLICATION (get_gpa_application ())); } /* Handle menu item "Edit/Select All". */ static void edit_select_all (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; gtk_tree_selection_select_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files))); } /* Construct the file manager menu and toolbar widgets and return them. */ static void fileman_action_new (GpaFileManager *fileman, GtkWidget **menubar, GtkWidget **toolbar) { static const GActionEntry entries[] = { { "File", NULL }, { "Edit", NULL }, { "file_open", file_open }, { "file_clear", file_clear }, { "file_sign", file_sign }, { "file_verify", file_verify }, { "file_encrypt", file_encrypt }, { "file_decrypt", file_decrypt }, { "file_close", file_close }, { "file_quit", file_quit }, { "edit_select_all", edit_select_all }, }; /* static const GtkActionEntry old_entries[] = { // Toplevel. { "File", NULL, N_("_File"), NULL }, { "Edit", NULL, N_("_Edit"), NULL }, // File menu. { "FileOpen", GTK_STOCK_OPEN, NULL, NULL, N_("Open a file"), G_CALLBACK (file_open) }, { "FileClear", GTK_STOCK_CLEAR, NULL, NULL, N_("Close all files"), G_CALLBACK (file_clear) }, { "FileSign", GPA_STOCK_SIGN, NULL, NULL, N_("Sign the selected file"), G_CALLBACK (file_sign) }, { "FileVerify", GPA_STOCK_VERIFY, NULL, NULL, N_("Check signatures of selected file"), G_CALLBACK (file_verify) }, { "FileEncrypt", GPA_STOCK_ENCRYPT, NULL, NULL, N_("Encrypt the selected file"), G_CALLBACK (file_encrypt) }, { "FileDecrypt", GPA_STOCK_DECRYPT, NULL, NULL, N_("Decrypt the selected file"), G_CALLBACK (file_decrypt) }, { "FileClose", GTK_STOCK_CLOSE, NULL, NULL, N_("Close the window"), G_CALLBACK (file_close) }, { "FileQuit", GTK_STOCK_QUIT, NULL, NULL, N_("Quit the program"), G_CALLBACK (g_application_quit) }, // Edit menu. { "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, "A", N_("Select all files"), G_CALLBACK (edit_select_all) } }; */ /* static const char *ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif " " " " #if 0 " " #endif " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif #if 0 " " #endif " " ""; */ static const char *menu_string = { "" "" "" "_File" "
" "" "Open" "app.file_open" "" "" "Clear" "app.file_clear" "" "
" "
" "" "Sign" "app.file_sign" "" "" "Verify" "app.file_verify" "" "" "Encrypt" "app.file_encrypt" "" "" "Decrypt" "app.file_decrypt" "" "
" "
" "" "Close" "app.file_close" "" "" "Quit" "app.file_quit" "" "
" "
" "" "Edit" "
" "" "Mark All" "app.file_close" "" "
" "
" "" "Backend Settings" "app.edit_backend_preferences" "" "" "Settings" "app.edit_preferences" "" "
" "
" "" "Window" "" "Keyring Manager" "app.windows_keyring_editor" "" "" "File Manager" "app.windows_file_manager" "" "" "Clipboard" "app.windows_clipboard" "" "" "Card Manager" "app.windows_card_manager" "" "" "" "Help" "" "About" "app.help_about" "" "" "
" "" "True" "False" "False" "" "" "True" "False" "True" "document-open" "app.file_open" "true" "Open a file" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-clear" "app.file_close" "true" "Close all files" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "sign" "app.file_sign" "true" "Sign the selected file" "" "" "False" "True" "" "" "" "" "True" "False" "True" "verify" "app.file_verify" "true" "Check signatures of selected files" "" "" "False" "True" "" "" "" "" "True" "False" "True" "encrypt" "app.file_encrypt" "true" "Encrypt the selected file" "" "" "False" "True" "" "" "" "" "True" "False" "True" "decrypt" "app.file_decrypt" "true" "Decrypt the selected file" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "preferences-desktop" "app.edit_preferences" "true" "Configure the application" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "keyring" "app.windows_keyring_editor" "true" "Open the keyring editor" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-paste" "app.windows_clipboard" "true" "Open the clipboard" "" "" "False" "True" "" "" "" "" "True" "False" "True" "smartcard" "app.windows_card_manager" "true" "Open the card manager" "" "" "False" "True" "" "" "" "
" }; /* GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkAction *action; GtkUIManager *ui_manager; GError *error; action_group = gtk_action_group_new ("MenuActions"); gtk_action_group_set_translation_domain (action_group, PACKAGE); gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), fileman); gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries, G_N_ELEMENTS (gpa_help_menu_action_entries), fileman); gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries, G_N_ELEMENTS (gpa_windows_menu_action_entries), fileman); gtk_action_group_add_actions (action_group, gpa_preferences_menu_action_entries, G_N_ELEMENTS (gpa_preferences_menu_action_entries), fileman); ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (fileman), accel_group); if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error)) { g_message ("building fileman menus failed: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } // Fixup the icon theme labels which are too long for the toolbar. action = gtk_action_group_get_action (action_group, "WindowsKeyringEditor"); g_object_set (action, "short_label", _("Keyring"), NULL); #ifdef ENABLE_CARD_MANAGER action = gtk_action_group_get_action (action_group, "WindowsCardManager"); g_object_set (action, "short_label", _("Card"), NULL); #endif // Take care of sensitiveness of widgets. action = gtk_action_group_get_action (action_group, "FileSign"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileVerify"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileEncrypt"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileDecrypt"); add_selection_sensitive_action (fileman, action, has_selection); *menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); *toolbar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); gpa_toolbar_set_homogeneous (GTK_TOOLBAR (*toolbar), FALSE); */ GError **err; GtkBuilder *gtk_builder = gtk_builder_new_from_string (menu_string, -1); if (gtk_builder_add_from_string( gtk_builder, menu_string , -1, err) == 0) { printf("ERROR: %s \n", (*err)->message); } GMenuModel *menu_bar_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "main_menu")); *menubar = gtk_menu_bar_new_from_model (menu_bar_model); /* GMenuModel *popup_menu_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "popupmenu")); *popup = gtk_menu_new_from_model (popup_menu_model); */ GObject *grid = gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar"); GtkCssProvider *css_provider = gtk_css_provider_new(); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen (display); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_css_provider_load_from_data(css_provider, "#toolbar {\n" //" padding-left: 55px;\n" // " padding-right: 5px;\n" // " border-radius: 3px;\n" "}\n", -1, NULL); GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (GTK_WIDGET (grid)); //gtk_widget_add_css_class (grid, "toolbar"); *toolbar = grid; // We must set the name to the toolbar for css to recognize it gtk_widget_set_name(*toolbar, "toolbar"); gtk_style_context_add_class (style_context, "toolbar"); GtkApplication *gpa_app = get_gpa_application (); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_windows_menu_g_action_entries, G_N_ELEMENTS (gpa_windows_menu_g_action_entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), entries, G_N_ELEMENTS (entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_help_menu_g_action_entries, G_N_ELEMENTS (gpa_help_menu_g_action_entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_preferences_menu_g_action_entries, G_N_ELEMENTS (gpa_preferences_menu_g_action_entries), fileman); GSimpleAction *action; action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_sign"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_verify"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_encrypt"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_decrypt"); add_selection_sensitive_action (fileman, action, has_selection); } /* Drag and Drop handler. */ /* Handler for "drag-drop". This signal is emitted when the user drops the selection. */ static gboolean dnd_drop_handler (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint tim, gpointer user_data) { GdkAtom target_type = gdk_atom_intern ("text/uri-list", FALSE); GList *targets = gdk_drag_context_list_targets(context); /* If the source offers a target we request the data from her. */ if (targets && g_list_find (targets, GDK_ATOM_TO_POINTER (target_type))) { gtk_drag_get_data (widget, context, target_type, tim); return TRUE; } return FALSE; } /* Handler for "drag-data-received". This signal is emitted when the data has been received from the source. */ static void dnd_data_received_handler (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint target_type, guint tim, gpointer user_data) { GpaFileManager *fileman = user_data; gboolean dnd_success = FALSE; gboolean delete_selection_data = FALSE; // const guchar *our_selection_data = gtk_selection_data_get_data(selection_data); /* Is that usable by us? */ if (selection_data && gtk_selection_data_get_length(selection_data) >= 0 ) { GdkDragAction drag_action = gdk_drag_context_get_suggested_action(context); if (drag_action == GDK_ACTION_MOVE) delete_selection_data = TRUE; /* Check that we got a format we can use. */ if (target_type == DND_TARGET_URI_LIST) { //char *p = (char *) selection_data->data; char *p = (char *)gtk_selection_data_get_data(selection_data); char **list; int i; list = g_uri_list_extract_uris (p); for (i=0; list && list[i]; i++) { char *name = g_filename_from_uri (list[i], NULL, NULL); if (name) { /* Canonical line endings are required for an uri-list. */ if ((p = strchr (name, '\r'))) *p = 0; add_file (fileman, name); g_free (name); } } g_strfreev (list); dnd_success = TRUE; } } /* Finish the DnD processing. */ gtk_drag_finish (context, dnd_success, delete_selection_data, tim); } /* Construct the file list object. */ static GtkWidget * file_list_new (GpaFileManager * fileman) { GtkWidget *scrollerFile; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *sel; GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); GtkWidget *list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("File"), renderer, "text", FILE_NAME_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE); g_signal_connect_swapped (sel, "changed", G_CALLBACK (update_selection_sensitive_actions), fileman); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); - scrollerFile = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollerFile), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollerFile), GTK_SHADOW_IN); fileman->list_files = list; gtk_widget_grab_focus (list); gtk_container_add (GTK_CONTAINER (scrollerFile), list); return scrollerFile; } /* Callback for the destroy signal. */ static void file_manager_closed (GtkWidget *widget, gpointer param) { instance = NULL; } /* Construct a new class object of GpaFileManager. */ static GObject* gpa_file_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; GpaFileManager *fileman; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *icon; gchar *markup; GtkWidget *menubar; GtkWidget *file_box; GtkWidget *file_frame; GtkWidget *toolbar; GtkWidget *align; guint pt, pb, pl, pr; /* Invoke parent's constructor. */ object = parent_class->constructor (type, n_construct_properties, construct_properties); fileman = GPA_FILE_MANAGER (object); /* Initialize. */ gpa_window_set_title (GTK_WINDOW (fileman), _("File Manager")); gtk_window_set_default_size (GTK_WINDOW (fileman), 640, 480); /* Realize the window so that we can create pixmaps without warnings. */ gtk_widget_realize (GTK_WIDGET (fileman)); /* Use a vbox to show the menu, toolbar and the file container. */ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); /* Get the menu and the toolbar. */ fileman_action_new (fileman, &menubar, &toolbar); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Add a fancy label that tells us: This is the file manager. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5); icon = gtk_image_new_from_icon_name ("folder", GTK_ICON_SIZE_DND); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); label = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("File Manager")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10); gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START); gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_CENTER); /* Third a hbox with the file list. */ align = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr); gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb + 5, pl + 5, pr + 5); gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); file_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); file_frame = file_list_new (fileman); gtk_box_pack_start (GTK_BOX (file_box), file_frame, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (align), file_box); gtk_container_add (GTK_CONTAINER (fileman), vbox); g_signal_connect (object, "destroy", G_CALLBACK (file_manager_closed), object); /* Make the file box a DnD destination. */ gtk_drag_dest_set (file_box, (GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT), dnd_target_list, DIM (dnd_target_list), GDK_ACTION_COPY); g_signal_connect (file_box, "drag-drop", G_CALLBACK (dnd_drop_handler), fileman); g_signal_connect (file_box, "drag-data-received", G_CALLBACK (dnd_data_received_handler), fileman); return object; } static GpaFileManager * gpa_fileman_new () { GpaFileManager *fileman; fileman = g_object_new (GPA_FILE_MANAGER_TYPE, NULL); update_selection_sensitive_actions (fileman); return fileman; } /* API */ GtkWidget * gpa_file_manager_get_instance (void) { if (!instance) { instance = gpa_fileman_new (); } return GTK_WIDGET (instance); } gboolean gpa_file_manager_is_open (void) { return (instance != NULL); } void gpa_file_manager_open_file (GpaFileManager *fileman, const gchar *filename) { if (!add_file (fileman, filename)) gpa_window_error (_("The file is already open."), GTK_WIDGET (fileman)); /* FIXME: Release filename? */ } diff --git a/src/gpa-tofu-list.c b/src/gpa-tofu-list.c index 0ac153d..ddbea23 100644 --- a/src/gpa-tofu-list.c +++ b/src/gpa-tofu-list.c @@ -1,266 +1,265 @@ /* gpa-tofu-list.c - A list to show TOFU information. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPA * * GPA 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 of the License, or * (at your option) any later version. * * GPA 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 this program; if not, see . */ #include #include "gpa.h" #include "convert.h" #include "gtktools.h" #include "keytable.h" #include "gpa-tofu-list.h" #ifdef ENABLE_TOFU_INFO static gboolean tofu_list_query_tooltip_cb (GtkWidget *wdiget, int x, int y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data); typedef enum { TOFU_ADDRESS, TOFU_VALIDITY, TOFU_POLICY, TOFU_COUNT, TOFU_FIRSTSIGN, TOFU_LASTSIGN, TOFU_FIRSTENCR, TOFU_LASTENCR, TOFU_N_COLUMNS } SubkeyListColumn; /* Create a new subkey list. */ GtkWidget * gpa_tofu_list_new (void) { GtkListStore *store; GtkWidget *list; GtkTreeViewColumn *column; GtkCellRenderer *renderer; /* Init the model */ store = gtk_list_store_new (TOFU_N_COLUMNS, G_TYPE_STRING, /* address */ G_TYPE_STRING, /* validity */ G_TYPE_STRING, /* policy */ G_TYPE_STRING, /* count */ G_TYPE_STRING, /* firstsign */ G_TYPE_STRING, /* lastsign */ G_TYPE_STRING, /* firstencr */ G_TYPE_STRING /* lastencr */ ); /* The view */ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); /* Add the columns */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_ADDRESS, NULL); gpa_set_column_title (column, _("Address"), _("The mail address.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_VALIDITY, NULL); gpa_set_column_title (column, _("Validity"), _("The TOFU validity of the mail address:\n" " Minimal = Only little history available\n")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_POLICY, NULL); gpa_set_column_title (column, _("Policy"), _("The TOFU policy set for this mail address.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_COUNT, NULL); gpa_set_column_title (column, _("Count"), _("The number of signatures seen for this address\n" "and the number of encryption done to this address.") ); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_FIRSTSIGN, NULL); gpa_set_column_title (column, _("First Sig"), _("The date the first signature was verified.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_LASTSIGN, NULL); gpa_set_column_title (column, _("Last Sig"), _("The most recent date a signature was verified.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_FIRSTENCR, NULL); gpa_set_column_title (column, _("First Enc"), _("The date the first encrypted mail was sent.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", TOFU_LASTENCR, NULL); gpa_set_column_title (column, _("Last Enc"), _("The most recent date an encrypted mail was sent.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); g_object_set (list, "has-tooltip", TRUE, NULL); g_signal_connect (list, "query-tooltip", G_CALLBACK (tofu_list_query_tooltip_cb), list); return list; } static const gchar * tofu_validity_str (gpgme_tofu_info_t tofu) { switch (tofu->validity) { case 0: return _("Conflict"); case 1: return _("Unknown"); case 2: return _("Minimal"); case 3: return _("Basic"); case 4: return _("Full"); default: return "?"; } } static const gchar * tofu_policy_str (gpgme_tofu_info_t tofu) { switch (tofu->policy) { case GPGME_TOFU_POLICY_NONE: return _("None"); case GPGME_TOFU_POLICY_AUTO: return _("Auto"); case GPGME_TOFU_POLICY_GOOD: return _("Good"); case GPGME_TOFU_POLICY_UNKNOWN: return _("Unknown"); case GPGME_TOFU_POLICY_BAD: return _("Bad"); case GPGME_TOFU_POLICY_ASK: return _("Ask"); } return "?"; } /* Set the key whose subkeys should be displayed. */ void gpa_tofu_list_set_key (GtkWidget *list, gpgme_key_t key) { GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); GtkTreeIter iter; gpgme_user_id_t uid; gpgme_tofu_info_t tofu; char *countstr, *firstsign, *lastsign, *firstencr, *lastencr; /* Empty the list */ gtk_list_store_clear (store); if (!key || !key->uids) return; for (uid = key->uids; uid; uid = uid->next) { if (!uid->address || !uid->tofu) continue; /* No address or tofu info. */ tofu = uid->tofu; /* Note that we do not need to filter ADDRESS like we do with * user ids because GPGME checked that it is a valid mail * address. */ countstr = g_strdup_printf ("%hu/%hu", tofu->signcount, tofu->encrcount); firstsign = gpa_date_string (tofu->signfirst); lastsign = gpa_date_string (tofu->signlast); firstencr = gpa_date_string (tofu->encrfirst); lastencr = gpa_date_string (tofu->encrlast); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, TOFU_ADDRESS, uid->address, TOFU_VALIDITY, tofu_validity_str (tofu), TOFU_POLICY, tofu_policy_str (tofu), TOFU_COUNT, countstr, TOFU_FIRSTSIGN,firstsign, TOFU_LASTSIGN, lastsign, TOFU_FIRSTENCR,firstencr, TOFU_LASTENCR, lastencr, -1); g_free (countstr); g_free (firstsign); g_free (lastsign); g_free (firstencr); g_free (lastencr); } } /* Tooltip display callback. */ static gboolean tofu_list_query_tooltip_cb (GtkWidget *widget, int x, int y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer user_data) { GtkTreeView *tv = GTK_TREE_VIEW (widget); GtkTreeViewColumn *column; char *text; (void)user_data; if (!gtk_tree_view_get_tooltip_context (tv, &x, &y, keyboard_tip, NULL, NULL, NULL)) return FALSE; /* Not at a row - do not show a tooltip. */ if (!gtk_tree_view_get_path_at_pos (tv, x, y, NULL, &column, NULL, NULL)) return FALSE; widget = gtk_tree_view_column_get_widget (column); text = widget? gtk_widget_get_tooltip_text (widget) : NULL; if (!text) return FALSE; /* No tooltip desired. */ gtk_tooltip_set_text (tooltip, text); g_free (text); return TRUE; /* Show tooltip. */ } #endif /*ENABLE_TOFU_INFO*/ diff --git a/src/gpa-uid-list.c b/src/gpa-uid-list.c index 0136e57..afe5fd1 100644 --- a/src/gpa-uid-list.c +++ b/src/gpa-uid-list.c @@ -1,169 +1,168 @@ /* gpa-uid-list.c - A list to show User ID information. * Copyright (C) 2018 g10 Code GmbH * * This file is part of GPA * * GPA 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 of the License, or * (at your option) any later version. * * GPA 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 this program; if not, see . */ #include #include "gpa.h" #include "convert.h" #include "gtktools.h" #include "keytable.h" #include "gpa-uid-list.h" static gboolean uid_list_query_tooltip_cb (GtkWidget *wdiget, int x, int y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data); typedef enum { UID_ADDRESS, UID_VALIDITY, UID_UPDATE, UID_FULLUID, UID_N_COLUMNS } UidListColumn; /* Create a new user id list. */ GtkWidget * gpa_uid_list_new (void) { GtkListStore *store; GtkWidget *list; GtkTreeViewColumn *column; GtkCellRenderer *renderer; /* Init the model */ store = gtk_list_store_new (UID_N_COLUMNS, G_TYPE_STRING, /* address */ G_TYPE_STRING, /* validity */ G_TYPE_STRING, /* updated */ G_TYPE_STRING /* fulluid */ ); /* The view */ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); /* Add the columns */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", UID_ADDRESS, NULL); gpa_set_column_title (column, _("Address"), _("The mail address.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", UID_VALIDITY, NULL); gpa_set_column_title (column, _("Validity"), _("The validity of the mail address\n")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", UID_UPDATE, NULL); gpa_set_column_title (column, _("Update"), _("The date the key was last updated via this mail address.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", UID_FULLUID, NULL); gpa_set_column_title (column, _("User ID"), _("The full user ID.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); g_object_set (list, "has-tooltip", TRUE, NULL); g_signal_connect (list, "query-tooltip", G_CALLBACK (uid_list_query_tooltip_cb), list); return list; } /* Set the key whose user ids shall be displayed. */ void gpa_uid_list_set_key (GtkWidget *list, gpgme_key_t key) { GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); GtkTreeIter iter; gpgme_user_id_t uid; /* Empty the list */ gtk_list_store_clear (store); if (!key || !key->uids) return; for (uid = key->uids; uid; uid = uid->next) { char *lupd = gpa_update_origin_string (uid->last_update, uid->origin); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, UID_ADDRESS, uid->address? uid->address : "", UID_VALIDITY, gpa_uid_validity_string (uid), UID_UPDATE, lupd, UID_FULLUID, uid->uid, -1); g_free (lupd); } } /* Tooltip display callback. */ static gboolean uid_list_query_tooltip_cb (GtkWidget *widget, int x, int y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer user_data) { GtkTreeView *tv = GTK_TREE_VIEW (widget); GtkTreeViewColumn *column; char *text; (void)user_data; if (!gtk_tree_view_get_tooltip_context (tv, &x, &y, keyboard_tip, NULL, NULL, NULL)) return FALSE; /* Not at a row - do not show a tooltip. */ if (!gtk_tree_view_get_path_at_pos (tv, x, y, NULL, &column, NULL, NULL)) return FALSE; widget = gtk_tree_view_column_get_widget (column); text = widget? gtk_widget_get_tooltip_text (widget) : NULL; if (!text) return FALSE; /* No tooltip desired. */ gtk_tooltip_set_text (tooltip, text); g_free (text); return TRUE; /* Show tooltip. */ } diff --git a/src/recipientdlg.c b/src/recipientdlg.c index 8c723b2..eecf23d 100644 --- a/src/recipientdlg.c +++ b/src/recipientdlg.c @@ -1,1245 +1,1244 @@ /* recipientdlg.c - A dialog to select a mail recipient. Copyright (C) 2008 g10 Code GmbH. This file is part of GPA GPA 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 of the License, or (at your option) any later version. GPA 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 this program; if not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include "gpa.h" #include "i18n.h" #include "gtktools.h" #include "selectkeydlg.h" #include "recipientdlg.h" struct _RecipientDlg { GtkDialog parent; GtkWidget *clist_keys; GtkWidget *statushint; GtkWidget *radio_pgp; GtkWidget *radio_x509; GtkWidget *radio_auto; GtkWidget *popup_menu; /* Flag to disable updates of the status hint. This is actual a counter with updates only allowed if it is zero. */ int freeze_update_statushint; /* Flag to disable any key selection. This is used while a key selection is active. Implemented as a counter. */ int freeze_key_selection; /* Set if this dialog has usable key to be passed back to the caller. You need to call update_statushint to set it. */ int usable; /* The selected protocol. This is also set by update_statushint. */ gpgme_protocol_t selected_protocol; }; struct _RecipientDlgClass { GtkDialogClass parent_class; }; /* The parent class. */ static GObjectClass *parent_class; /* Indentifiers for our properties. */ enum { PROP_0, PROP_WINDOW, PROP_FORCE_ARMOR }; /* For performance reasons we truncate the listing of ambiguous keys at a reasonable value. */ #define TRUNCATE_KEYSEARCH_AT 40 /* An object to keep information about keys. */ struct keyinfo_s { /* An array with associated key(s) or NULL if none found/selected. */ gpgme_key_t *keys; /* The allocated size of the KEYS array. This includes the terminating NULL entry. */ unsigned int dimof_keys; /* If set, indicates that the KEYS array has been truncated. */ int truncated:1; }; /* Management information for each recipient. This data is used per recipient. */ struct userdata_s { /* The recipient's address. */ char *mailbox; /* Information about PGP keys. */ struct keyinfo_s pgp; /* Information about X.509 keys. */ struct keyinfo_s x509; /* If the user has set this field, no encryption key will be required for the recipient. */ int ignore_recipient; }; /* Identifiers for the columns of the RECPLIST. */ enum { RECPLIST_MAILBOX, /* The rfc822 mailbox to whom a key needs to be associated. */ RECPLIST_HAS_PGP, /* A PGP certificate is available. */ RECPLIST_HAS_X509, /* An X.509 certificate is available. */ RECPLIST_KEYID, /* The key ID of the associated key. */ RECPLIST_USERDATA, /* Pointer to management information (struct userdata_s *). */ RECPLIST_N_COLUMNS }; /* Create the main list of this dialog. */ static GtkWidget * recplist_window_new (void) { GtkListStore *store; GtkWidget *list; GtkCellRenderer *renderer; GtkTreeViewColumn *column; /* Create a model and associate a view. */ store = gtk_list_store_new (RECPLIST_N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); /* Define the columns. */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "markup", RECPLIST_MAILBOX, NULL); gpa_set_column_title (column, _("Recipient"), _("Shows the recipients of the message." " A key needs to be assigned to each recipient.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_toggle_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "active", RECPLIST_HAS_PGP, NULL); gpa_set_column_title (column, "PGP", _("Checked if at least one matching" " OpenPGP certificate has been found.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_toggle_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "active", RECPLIST_HAS_X509, NULL); gpa_set_column_title (column, "X.509", _("Checked if at least one matching" " X.509 certificate for use with S/MIME" " has been found.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", RECPLIST_KEYID, NULL); gpa_set_column_title (column, _("Key ID"), _("Shows the key ID of the selected key or" " an indication that a key needs to be selected.")); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); return list; } /* Get an interator for the selected row. Store it in ITER and returns the mdeol. if nothing is selected NULL is return and ITER is not valid. */ static GtkTreeModel * get_selected_row (RecipientDlg *dialog, GtkTreeIter *iter) { GtkTreeSelection *selection; GtkTreeModel *model; g_return_val_if_fail (dialog, NULL); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->clist_keys)); if (gtk_tree_selection_count_selected_rows (selection) == 1 && gtk_tree_selection_get_selected (selection, &model, iter)) return model; return NULL; } /* Compute and display a new help text for the statushint. */ static void update_statushint (RecipientDlg *dialog) { gpgme_protocol_t req_protocol, sel_protocol; GtkTreeModel *model; GtkTreeIter iter; int missing_keys = 0; int ambiguous_pgp_keys = 0; int ambiguous_x509_keys = 0; int n_pgp_keys = 0; int n_x509_keys = 0; int n_keys = 0; const char *hint; int okay = 0; if (dialog->freeze_update_statushint) return; model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys)); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp))) req_protocol = GPGME_PROTOCOL_OpenPGP; else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_x509))) req_protocol = GPGME_PROTOCOL_CMS; else req_protocol = GPGME_PROTOCOL_UNKNOWN; sel_protocol = GPGME_PROTOCOL_UNKNOWN; if (gtk_tree_model_get_iter_first (model, &iter)) { do { gboolean has_pgp, has_x509; struct userdata_s *info; gtk_tree_model_get (model, &iter, RECPLIST_HAS_PGP, &has_pgp, RECPLIST_HAS_X509, &has_x509, RECPLIST_USERDATA, &info, -1); if (!info) missing_keys++; /* Oops */ else if (info->ignore_recipient) ; else if (!info->pgp.keys && !info->x509.keys) missing_keys++; else if ((req_protocol == GPGME_PROTOCOL_OpenPGP && !has_pgp) ||(req_protocol == GPGME_PROTOCOL_CMS && !has_x509)) ; /* Not of the requested protocol. */ else { n_keys++; if (info->pgp.keys && info->pgp.keys[0]) { n_pgp_keys++; if (info->pgp.keys[1]) ambiguous_pgp_keys++; } if (info->x509.keys && info->x509.keys[0]) { n_x509_keys++; if (info->x509.keys[1]) ambiguous_x509_keys++; } } } while (gtk_tree_model_iter_next (model, &iter)); } if (req_protocol == GPGME_PROTOCOL_UNKNOWN) { /* We select the protocol with the most available keys, preferring PGP. */ if (n_pgp_keys >= n_x509_keys) sel_protocol = GPGME_PROTOCOL_OpenPGP; else if (n_x509_keys) sel_protocol = GPGME_PROTOCOL_CMS; } else sel_protocol = req_protocol; if (missing_keys) hint = _("You need to select a key for each recipient.\n" "To select a key right-click on the respective line."); else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP && ambiguous_pgp_keys) || (sel_protocol == GPGME_PROTOCOL_CMS && ambiguous_x509_keys) || (sel_protocol == GPGME_PROTOCOL_UNKNOWN && (ambiguous_pgp_keys || ambiguous_x509_keys ))) hint = _("You need to select exactly one key for each recipient.\n" "To select a key right-click on the respective line."); else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP && n_keys != n_pgp_keys) || (sel_protocol == GPGME_PROTOCOL_CMS && n_keys != n_x509_keys) || (sel_protocol == GPGME_PROTOCOL_UNKNOWN)) hint = _("Although you selected keys for all recipients " "a common encryption protocol can't be used. " "Please decide on one protocol by clicking one " "of the above radio buttons."); else if (n_pgp_keys && sel_protocol == GPGME_PROTOCOL_OpenPGP) { hint = _("Using OpenPGP for encryption."); okay = 1; } else if (n_x509_keys && sel_protocol == GPGME_PROTOCOL_CMS) { hint = _("Using S/MIME for encryption."); okay = 1; } else hint = _("No recipients - encryption is not possible"); gtk_label_set_text (GTK_LABEL (dialog->statushint), hint); gtk_label_set_line_wrap (GTK_LABEL (dialog->statushint), TRUE); dialog->usable = okay; dialog->selected_protocol = sel_protocol; gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, okay); } /* Add KEY to the keyarray of KEYINFO. Ownership of KEY is moved to KEYARRAY. Returns the number of keys in KEYINFO. */ static unsigned int append_key_to_keyinfo (struct keyinfo_s *keyinfo, gpgme_key_t key) { unsigned int nkeys; if (!keyinfo->keys) { keyinfo->dimof_keys = 5; /* Space for 4 keys. */ keyinfo->keys = g_new (gpgme_key_t, keyinfo->dimof_keys); keyinfo->keys[0] = NULL; } for (nkeys=0; keyinfo->keys[nkeys]; nkeys++) ; /* Note that we silently skip a KEY of NULL because we can't store a NULL in the array. */ if (key) { if (nkeys+1 >= keyinfo->dimof_keys) { keyinfo->dimof_keys += 10; keyinfo->keys = g_renew (gpgme_key_t, keyinfo->keys, keyinfo->dimof_keys); } keyinfo->keys[nkeys++] = key; keyinfo->keys[nkeys] = NULL; } return nkeys; } /* Clear the content of a keyinfo object. */ static void clear_keyinfo (struct keyinfo_s *keyinfo) { unsigned int nkeys; if (keyinfo) { if (keyinfo->keys) { for (nkeys=0; keyinfo->keys[nkeys]; nkeys++) gpgme_key_unref (keyinfo->keys[nkeys]); g_free (keyinfo->keys); keyinfo->keys = NULL; } keyinfo->dimof_keys = 0; keyinfo->truncated = 0; } } /* Update the row in the list described by by STORE and ITER. The new data shall be taken from INFO. */ static void update_recplist_row (GtkListStore *store, GtkTreeIter *iter, struct userdata_s *info) { char *infostr = NULL; char *oldinfostr = NULL; int any_pgp = 0, any_x509 = 0; gpgme_key_t key; char *mailbox; char *oldmailbox; if (info->pgp.keys && info->pgp.keys[0]) any_pgp = 1; if (info->x509.keys && info->x509.keys[0]) any_x509 = 1; if (info->ignore_recipient) infostr = NULL; else if (any_pgp && any_x509 && info->pgp.keys[1] && info->x509.keys[1]) infostr = g_strdup (_("[Ambiguous keys. Right-click to select]")); else if (any_pgp && info->pgp.keys[1]) infostr = g_strdup (_("[Ambiguous PGP key. Right-click to select]")); else if (any_x509 && info->x509.keys[1]) infostr = g_strdup (_("[Ambiguous X.509 key. Right-click to select]")); else if (any_pgp && !info->pgp.keys[1]) { /* Exactly one key found. */ key = info->pgp.keys[0]; infostr = gpa_gpgme_key_get_userid (key->uids); } else if (any_x509 && !info->x509.keys[1]) { key = info->x509.keys[0]; infostr = gpa_gpgme_key_get_userid (key->uids); } else infostr = g_strdup (_("[Right-click to select]")); mailbox = g_markup_printf_escaped ("%s", info->ignore_recipient? "true":"false", info->mailbox); g_print (" mbox=`%s' fmt=`%s'\n", info->mailbox, mailbox); gtk_tree_model_get (GTK_TREE_MODEL (store), iter, RECPLIST_MAILBOX, &oldmailbox, RECPLIST_KEYID, &oldinfostr, -1); gtk_list_store_set (store, iter, RECPLIST_MAILBOX, mailbox, RECPLIST_HAS_PGP, any_pgp, RECPLIST_HAS_X509, any_x509, RECPLIST_KEYID, infostr, -1); g_free (oldmailbox); g_free (mailbox); g_free (infostr); g_free (oldinfostr); } /* Parse one recipient, this is the working horse of parse_recipeints. */ static void parse_one_recipient (gpgme_ctx_t ctx, GtkListStore *store, GtkTreeIter *iter, struct userdata_s *info) { static int have_locate = -1; gpgme_key_t key = NULL; gpgme_keylist_mode_t mode; if (have_locate == -1) have_locate = is_gpg_version_at_least ("2.0.10"); g_return_if_fail (info); clear_keyinfo (&info->pgp); gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); mode = gpgme_get_keylist_mode (ctx); if (have_locate) gpgme_set_keylist_mode (ctx, (mode | (GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_EXTERN))); if (!gpgme_op_keylist_start (ctx, info->mailbox, 0)) { while (!gpgme_op_keylist_next (ctx, &key)) { if (key->revoked || key->disabled || key->expired || !key->can_encrypt) gpgme_key_unref (key); else if (append_key_to_keyinfo (&info->pgp, key) >= TRUNCATE_KEYSEARCH_AT) { /* Note that the truncation flag is not 100% correct. In case the next iteration would not yield a new key we have not actually truncated the search. */ info->pgp.truncated = 1; break; } } } gpgme_op_keylist_end (ctx); gpgme_set_keylist_mode (ctx, mode); clear_keyinfo (&info->x509); gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS); if (!gpgme_op_keylist_start (ctx, info->mailbox, 0)) { while (!gpgme_op_keylist_next (ctx, &key)) { if (key->revoked || key->disabled || key->expired || !key->can_encrypt) gpgme_key_unref (key); else if (append_key_to_keyinfo (&info->x509,key) >= TRUNCATE_KEYSEARCH_AT) { info->x509.truncated = 1; break; } } } gpgme_op_keylist_end (ctx); update_recplist_row (store, iter, info); } /* Parse the list of recipients, find possible keys and update the store. */ static void parse_recipients (GtkListStore *store) { GtkTreeModel *model; GtkTreeIter iter; gpg_error_t err; gpgme_ctx_t ctx; err = gpgme_new (&ctx); if (err) gpa_gpgme_error (err); model = GTK_TREE_MODEL (store); /* Walk through the list, reading each row. */ if (gtk_tree_model_get_iter_first (model, &iter)) do { struct userdata_s *info; gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1); /* Do something with the data */ /*g_print ("parsing mailbox `%s'\n", info? info->mailbox:"(null)");*/ parse_one_recipient (ctx, store, &iter,info); } while (gtk_tree_model_iter_next (model, &iter)); gpgme_release (ctx); } /* Callback for the row-activated signal. */ static void recplist_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { /*RecipientDlg *dialog = user_data;*/ GtkTreeIter iter; GtkTreeModel *model; char *mailbox; model = gtk_tree_view_get_model (tree_view); if (!gtk_tree_model_get_iter (model, &iter, path)) return; gtk_tree_model_get (model, &iter, RECPLIST_MAILBOX, &mailbox, -1); g_free (mailbox); } static void recplist_row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *changediter, gpointer user_data) { RecipientDlg *dialog = user_data; g_return_if_fail (dialog); update_statushint (dialog); } static void rbutton_toggled_cb (GtkToggleButton *button, gpointer user_data) { RecipientDlg *dialog = user_data; GtkTreeViewColumn *column; int pgp = FALSE; int x509 = FALSE; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp))) { pgp = TRUE; } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_x509))) { x509 = TRUE; } else { pgp = TRUE; x509 = TRUE; } column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys), RECPLIST_HAS_PGP); gtk_tree_view_column_set_visible (column, pgp); column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys), RECPLIST_HAS_X509); gtk_tree_view_column_set_visible (column, x509); update_statushint (dialog); } /* The select key selection has returned. */ static void select_key_response_cb (SelectKeyDlg *seldlg, int response, void *user_data) { RecipientDlg *dialog = user_data; gpgme_key_t key; if (response != GTK_RESPONSE_OK) { /* The dialog was canceled */ gtk_widget_destroy (GTK_WIDGET (seldlg)); dialog->freeze_key_selection--; return; } key = select_key_dlg_get_key (seldlg); if (key) { GtkTreeModel *model; GtkTreeIter iter; struct userdata_s *info = NULL; char *uidstr = gpa_gpgme_key_get_userid (key->uids); g_free (uidstr); if ((model = get_selected_row (dialog, &iter))) { gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1); if (info) { if (key->protocol == GPGME_PROTOCOL_OpenPGP) { clear_keyinfo (&info->pgp); gpgme_key_ref (key); append_key_to_keyinfo (&info->pgp, key); } else if (key->protocol == GPGME_PROTOCOL_CMS) { clear_keyinfo (&info->x509); gpgme_key_ref (key); append_key_to_keyinfo (&info->x509, key); } update_recplist_row (GTK_LIST_STORE (model), &iter, info); update_statushint (dialog); } } gpgme_key_unref (key); } gtk_widget_destroy (GTK_WIDGET (seldlg)); dialog->freeze_key_selection--; } static void do_select_key (RecipientDlg *dialog, gpgme_protocol_t protocol) { GtkTreeModel *model; GtkTreeIter iter; struct userdata_s *info = NULL; SelectKeyDlg *seldlg; if ((model = get_selected_row (dialog, &iter))) { gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1); if (info) { gpgme_key_t *keys; if (protocol == GPGME_PROTOCOL_OpenPGP) keys = info->pgp.keys; else if (protocol == GPGME_PROTOCOL_CMS) keys = info->x509.keys; else keys = NULL; seldlg = select_key_dlg_new_with_keys (GTK_WIDGET (dialog), protocol, keys, info->mailbox); g_signal_connect (G_OBJECT (seldlg), "response", G_CALLBACK (select_key_response_cb), dialog); gtk_widget_show_all (GTK_WIDGET (seldlg)); } } else dialog->freeze_key_selection--; } static void recplist_popup_pgp (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { RecipientDlg *dialog = user_data; do_select_key (dialog, GPGME_PROTOCOL_OpenPGP); } static void recplist_popup_x509 (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { RecipientDlg *dialog = user_data; do_select_key (dialog, GPGME_PROTOCOL_CMS); } static void recplist_popup_ignore (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; struct userdata_s *info; RecipientDlg *dialog = user_data; if ((model = get_selected_row (dialog, &iter))) { gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1); info->ignore_recipient = !info->ignore_recipient; update_recplist_row (GTK_LIST_STORE (model), &iter, info); update_statushint (dialog); } dialog->freeze_key_selection--; } /* Left Click on the list auto selects an action. */ static void recplist_default_action (RecipientDlg *dialog) { dialog->freeze_key_selection--; } static gint recplist_display_popup_menu (RecipientDlg *dialog, GdkEvent *event, GtkListStore *list) { GtkMenu *menu; GdkEventButton *event_button; g_return_val_if_fail (dialog, FALSE); g_return_val_if_fail (event, FALSE); menu = GTK_MENU (dialog->popup_menu); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 1 || event_button->button == 3) { GtkTreeSelection *selection; GtkTreePath *path; GtkTreeIter iter; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); /* Make sure the clicked key is selected. */ if (!dialog->freeze_key_selection && gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (list), event_button->x, event_button->y, &path, NULL, NULL, NULL)) { dialog->freeze_key_selection++; gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (list)), &iter, path); if (!gtk_tree_selection_iter_is_selected (selection, &iter)) { gtk_tree_selection_unselect_all (selection); gtk_tree_selection_select_path (selection, path); } if (event_button->button == 1) recplist_default_action (dialog); else gtk_menu_popup_at_pointer (menu, NULL); } return TRUE; } } return FALSE; } /* Create the popup menu for the recipient list. */ static GtkWidget * recplist_popup_menu_new (GtkWidget *window, RecipientDlg *dialog) { static const GActionEntry entries [] = { { "select_gpg_key", recplist_popup_pgp }, { "select_cms_key", recplist_popup_x509 }, { "toggle_ignore_flag", recplist_popup_ignore }, }; static const char *menu_string = "" "" "
" "" "Select _PGP key" "app.select_pgp_key" "" "" "Select _S\\/MIME key" "app.select_cms_key" "" "" "Toggle _Ignore flag" "app.toggle_ignore_flag" "" "
" "
" "
"; GError **err; GtkWidget *popup_menu_widget; GtkBuilder *gtk_builder = gtk_builder_new (); if (gtk_builder_add_from_string( gtk_builder, menu_string , -1, err) == 0) { printf("ERROR menu: %s \n", (*err)->message); } GMenuModel *popup_menu_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "popup_menu")); popup_menu_widget = gtk_menu_new_from_model (popup_menu_model); GApplication *gpa_app = G_APPLICATION (get_gpa_application ()); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), entries, G_N_ELEMENTS (entries), window); return popup_menu_widget; } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void recipient_dlg_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { RecipientDlg *dialog = RECIPIENT_DLG (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (dialog))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void recipient_dlg_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { RecipientDlg *dialog = RECIPIENT_DLG (object); switch (prop_id) { case PROP_WINDOW: gtk_window_set_transient_for (GTK_WINDOW (dialog), g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void recipient_dlg_finalize (GObject *object) { /* Fixme: Release the store. */ G_OBJECT_CLASS (parent_class)->finalize (object); } static void recipient_dlg_init (RecipientDlg *dialog) { } static GObject* recipient_dlg_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; RecipientDlg *dialog; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *widget; GtkWidget *labelKeys; GtkWidget *scrollerKeys; GtkWidget *clistKeys; object = parent_class->constructor (type, n_construct_properties, construct_properties); dialog = RECIPIENT_DLG (object); gpa_window_set_title (GTK_WINDOW (dialog), _("Select keys for recipients")); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); //vbox = GTK_DIALOG (dialog)->vbox; vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); labelKeys = gtk_label_new_with_mnemonic (_("_Recipient list")); gtk_widget_set_halign (GTK_WIDGET (labelKeys), GTK_ALIGN_START); gtk_widget_set_valign (GTK_WIDGET (labelKeys), GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (vbox), labelKeys, FALSE, FALSE, 0); scrollerKeys = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollerKeys), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox), scrollerKeys, TRUE, TRUE, 0); gtk_widget_set_size_request (scrollerKeys, 400, 200); clistKeys = recplist_window_new (); dialog->clist_keys = clistKeys; gtk_container_add (GTK_CONTAINER (scrollerKeys), clistKeys); gtk_label_set_mnemonic_widget (GTK_LABEL (labelKeys), clistKeys); dialog->popup_menu = recplist_popup_menu_new (dialog->clist_keys, dialog); g_signal_connect_swapped (G_OBJECT (dialog->clist_keys), "button_press_event", G_CALLBACK (recplist_display_popup_menu), dialog); dialog->radio_pgp = gtk_radio_button_new_with_mnemonic (NULL, _("Use _PGP")); dialog->radio_x509 = gtk_radio_button_new_with_mnemonic (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)), _("Use _X.509")); dialog->radio_auto = gtk_radio_button_new_with_mnemonic (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)), _("_Auto selection")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radio_auto), TRUE); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_pgp, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_x509, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_auto, FALSE, FALSE, 0); widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); dialog->statushint = gtk_label_new (NULL); gtk_box_pack_start (GTK_BOX (vbox), dialog->statushint, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (GTK_TREE_VIEW (dialog->clist_keys)), "row-activated", G_CALLBACK (recplist_row_activated_cb), dialog); g_signal_connect (G_OBJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys))), "row-changed", G_CALLBACK (recplist_row_changed_cb), dialog); g_signal_connect (G_OBJECT (dialog->radio_pgp), "toggled", G_CALLBACK (rbutton_toggled_cb), dialog); g_signal_connect (G_OBJECT (dialog->radio_x509), "toggled", G_CALLBACK (rbutton_toggled_cb), dialog); g_signal_connect (G_OBJECT (dialog->radio_auto), "toggled", G_CALLBACK (rbutton_toggled_cb), dialog); return object; } static void recipient_dlg_class_init (RecipientDlgClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = recipient_dlg_constructor; object_class->finalize = recipient_dlg_finalize; object_class->set_property = recipient_dlg_set_property; object_class->get_property = recipient_dlg_get_property; g_object_class_install_property (object_class, PROP_WINDOW, g_param_spec_object ("window", "Parent window", "Parent window", GTK_TYPE_WIDGET, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } GType recipient_dlg_get_type (void) { static GType this_type; if (!this_type) { static const GTypeInfo this_info = { sizeof (RecipientDlgClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) recipient_dlg_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (RecipientDlg), 0, /* n_preallocs */ (GInstanceInitFunc) recipient_dlg_init, }; this_type = g_type_register_static (GTK_TYPE_DIALOG, "RecipientDlg", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ RecipientDlg * recipient_dlg_new (GtkWidget *parent) { RecipientDlg *dialog; dialog = g_object_new (RECIPIENT_DLG_TYPE, "window", parent, NULL); return dialog; } /* Put RECIPIENTS into the list. PROTOCOL select the default protocol. */ void recipient_dlg_set_recipients (RecipientDlg *dialog, GSList *recipients, gpgme_protocol_t protocol) { GtkListStore *store; GSList *recp; GtkTreeIter iter; const char *name; GtkWidget *widget; g_return_if_fail (dialog); dialog->freeze_update_statushint++; if (protocol == GPGME_PROTOCOL_OpenPGP) widget = dialog->radio_pgp; else if (protocol == GPGME_PROTOCOL_CMS) widget = dialog->radio_x509; else widget = dialog->radio_auto; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); if (widget != dialog->radio_auto) { gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_pgp), FALSE); gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_x509), FALSE); gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_auto), FALSE); } store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys))); gtk_list_store_clear (store); for (recp = recipients; recp; recp = g_slist_next (recp)) { name = recp->data; if (name && *name) { struct userdata_s *info = g_malloc0 (sizeof *info); info->mailbox = g_strdup (name); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, RECPLIST_MAILBOX, g_strdup (""), RECPLIST_HAS_PGP, FALSE, RECPLIST_HAS_X509, FALSE, RECPLIST_KEYID, NULL, RECPLIST_USERDATA, info, -1); } } parse_recipients (store); dialog->freeze_update_statushint--; update_statushint (dialog); } /* Return the selected keys as well as the selected protocol. */ gpgme_key_t * recipient_dlg_get_keys (RecipientDlg *dialog, gpgme_protocol_t *r_protocol) { GtkTreeModel *model; GtkTreeIter iter; size_t idx, nkeys; gpgme_key_t key, *keyarray; gpgme_protocol_t protocol; g_return_val_if_fail (dialog, NULL); if (!dialog->usable) return NULL; /* No valid keys available. */ protocol = dialog->selected_protocol; model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys)); /* Count the number of possible keys. */ nkeys = 0; if (gtk_tree_model_get_iter_first (model, &iter)) { do nkeys++; while (gtk_tree_model_iter_next (model, &iter)); } keyarray = g_new (gpgme_key_t, nkeys+1); idx = 0; if (gtk_tree_model_get_iter_first (model, &iter)) { do { char *mailbox; struct userdata_s *info; if (idx >= nkeys) { g_debug ("key list grew unexpectedly\n"); break; } gtk_tree_model_get (model, &iter, RECPLIST_MAILBOX, &mailbox, RECPLIST_USERDATA, &info, -1); if (info && !info->ignore_recipient) { if (protocol == GPGME_PROTOCOL_OpenPGP && info->pgp.keys) key = info->pgp.keys[0]; else if (protocol == GPGME_PROTOCOL_CMS && info->x509.keys) key = info->x509.keys[0]; else key = NULL; if (key) { gpgme_key_ref (key); keyarray[idx++] = key; } } g_free (mailbox); } while (gtk_tree_model_iter_next (model, &iter)); } g_assert (idx < nkeys+1); keyarray[idx] = NULL; if (r_protocol) *r_protocol = protocol; return keyarray; } diff --git a/src/verifydlg.c b/src/verifydlg.c index 36609e4..01dafb3 100644 --- a/src/verifydlg.c +++ b/src/verifydlg.c @@ -1,446 +1,445 @@ /* fileverifydlg.c - Dialog for verifying files. Copyright (C) 2000, 2001 G-N-U GmbH. Copyright (C) 2005, 2012 g10 Code GmbH. This file is part of GPA GPA 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 2 of the License, or (at your option) any later version. GPA 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 GPA; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "gpawidgets.h" #include "verifydlg.h" /* Properties */ enum { PROP_0, PROP_WINDOW, }; static GObjectClass *parent_class = NULL; static void gpa_file_verify_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (dialog))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gpa_file_verify_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object); switch (prop_id) { case PROP_WINDOW: gtk_window_set_transient_for (GTK_WINDOW (dialog), g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gpa_file_verify_dialog_finalize (GObject *object) { GpaFileVerifyDialog *dialog = GPA_FILE_VERIFY_DIALOG (object); g_object_unref (dialog->ctx); G_OBJECT_CLASS (parent_class)->finalize (object); } static void gpa_file_verify_dialog_init (GpaFileVerifyDialog *dialog) { } static GObject* gpa_file_verify_dialog_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; GpaFileVerifyDialog *dialog; /* Invoke parent's constructor */ object = parent_class->constructor (type, n_construct_properties, construct_properties); dialog = GPA_FILE_VERIFY_DIALOG (object); /* Initialize */ dialog->ctx = gpa_context_new (); /* Set up the dialog */ gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Close"), GTK_RESPONSE_CLOSE, NULL); gpa_window_set_title (GTK_WINDOW (dialog), _("Verify documents")); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); dialog->notebook = gtk_notebook_new (); //vboxEncrypt = gtk_dialog_get_content_area(dialog); GtkWidget *box = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_box_pack_start (GTK_BOX (box), dialog->notebook, TRUE, TRUE, 0); /* Hide on response */ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_hide), NULL); return object; } static void gpa_file_verify_dialog_class_init (GpaFileVerifyDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = gpa_file_verify_dialog_constructor; object_class->finalize = gpa_file_verify_dialog_finalize; object_class->set_property = gpa_file_verify_dialog_set_property; object_class->get_property = gpa_file_verify_dialog_get_property; /* Properties */ g_object_class_install_property (object_class, PROP_WINDOW, g_param_spec_object ("window", "Parent window", "Parent window", GTK_TYPE_WIDGET, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)); } GType gpa_file_verify_dialog_get_type (void) { static GType verify_dialog_type = 0; if (!verify_dialog_type) { static const GTypeInfo verify_dialog_info = { sizeof (GpaFileVerifyDialogClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gpa_file_verify_dialog_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaFileVerifyDialog), 0, /* n_preallocs */ (GInstanceInitFunc) gpa_file_verify_dialog_init, }; verify_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GpaFileVerifyDialog", &verify_dialog_info, 0); } return verify_dialog_type; } /* Internal */ typedef struct { gchar *fpr; gpgme_key_t key; gpgme_validity_t validity; unsigned long summary; time_t created; time_t expire; char *sigdesc; char *keydesc; } SignatureData; typedef enum { SIG_KEYID_COLUMN, SIG_STATUS_COLUMN, SIG_USERID_COLUMN, SIG_DESC_COLUMN, SIG_N_COLUMNS } SignatureListColumn; /* Return the text of the "status" column in pango markup language. */ static gchar* signature_status_label (SignatureData *data) { gchar *text = NULL; gchar *color = NULL; gchar *label = NULL; if (data->summary & GPGME_SIGSUM_VALID) { text = _("Valid"); color = "green"; } else if (data->summary & GPGME_SIGSUM_RED) { text = _("Bad"); color = "red"; } else if (data->summary & GPGME_SIGSUM_KEY_MISSING) { text = _("Unknown Key"); color = "red"; } else if (data->summary & GPGME_SIGSUM_KEY_REVOKED) { text = _("Revoked Key"); color = "red"; } else if (data->summary & GPGME_SIGSUM_KEY_EXPIRED) { text = _("Expired Key"); color = "orange"; } else { /* If we arrived here we know the key is available, the signature is * not bad, but it's not completely valid. So, the signature is good * but the key is not valid. */ text = _("Key NOT valid"); color = "orange"; } label = g_strdup_printf ("%s", color, text); return label; } /* Add a signature to the list */ static void add_signature_to_model (GtkListStore *store, SignatureData *data) { GtkTreeIter iter; const gchar *keyid; gchar *userid; gchar *status; if (data->key) { keyid = gpa_gpgme_key_get_short_keyid (data->key); } else if (data->fpr && strlen (data->fpr) > 8) { /* We use the last 8 bytes of fingerprint for the keyID. */ keyid = data->fpr + strlen (data->fpr) - 8; } else { keyid = ""; } status = signature_status_label (data); userid = data->keydesc; if (!userid) userid = _("[Unknown user ID]"); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, SIG_KEYID_COLUMN, keyid, SIG_STATUS_COLUMN, status, SIG_USERID_COLUMN, userid, SIG_DESC_COLUMN, data->sigdesc, -1); g_free (status); gpgme_key_unref (data->key); g_free (data->sigdesc); g_free (data->keydesc); g_free (data); } /* Fill the list of signatures with the data from the verification */ static void fill_sig_model (GtkListStore *store, gpgme_signature_t sigs, gpgme_ctx_t ctx) { SignatureData *data; gpgme_signature_t sig; for (sig = sigs; sig; sig = sig->next) { data = g_malloc (sizeof (SignatureData)); data->fpr = sig->fpr? g_strdup (sig->fpr) : NULL; data->validity = sig->validity; data->summary = sig->summary; data->created = sig->timestamp; data->expire = sig->exp_timestamp; data->sigdesc = gpa_gpgme_get_signature_desc (ctx, sig, &data->keydesc, &data->key); add_signature_to_model (store, data); } } /* Create the list of signatures */ static GtkWidget * signature_list (gpgme_signature_t sigs, gpgme_ctx_t ctx) { GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkListStore *store; GtkWidget *list; store = gtk_list_store_new (SIG_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); gtk_widget_set_size_request (list, 400, 100); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Key ID"), renderer, "text", SIG_KEYID_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Status"), renderer, "markup", SIG_STATUS_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("User Name"), renderer, "text", SIG_USERID_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Description"), renderer, "text", SIG_DESC_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); fill_sig_model (store, sigs, ctx); return list; } static GtkWidget * verify_file_page (gpgme_signature_t sigs, const gchar *signed_file, const gchar *signature_file, gpgme_ctx_t ctx) { GtkWidget *vbox; GtkWidget *list; GtkWidget *label; GtkWidget *scrolled; vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); if (signed_file) { gchar *text = g_strdup_printf (_("Verified data in file: %s"), signed_file); label = gtk_label_new (text); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); g_free (text); text = g_strdup_printf (_("Signature: %s"), signature_file); label = gtk_label_new (text); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); g_free (text); } label = gtk_label_new (_("Signatures:")); gtk_widget_set_halign (GTK_WIDGET (label), 0.0); gtk_widget_set_valign (GTK_WIDGET (label), 0.5); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); list = signature_list (sigs, ctx); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (scrolled), list); gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); return vbox; } /* API */ GtkWidget *gpa_file_verify_dialog_new (GtkWidget *parent) { GpaFileVerifyDialog *dialog; dialog = g_object_new (GPA_FILE_VERIFY_DIALOG_TYPE, "window", parent, NULL); return GTK_WIDGET(dialog); } void gpa_file_verify_dialog_set_title (GpaFileVerifyDialog *dialog, const char *title) { if (dialog && title && *title) gpa_window_set_title (GTK_WINDOW (dialog), title); } void gpa_file_verify_dialog_add_file (GpaFileVerifyDialog *dialog, const gchar *filename, const gchar *signed_file, const gchar *signature_file, gpgme_signature_t sigs) { GtkWidget *page; page = verify_file_page (sigs, signed_file, signature_file, dialog->ctx->ctx); gtk_notebook_append_page (GTK_NOTEBOOK (dialog->notebook), page, gtk_label_new (filename)); }