summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Walter <stefw@src.gnome.org>2009-01-21 22:08:17 +0000
committerStefan Walter <stefw@src.gnome.org>2009-01-21 22:08:17 +0000
commitcb90a436ed4e25b02c6df9963d64290b740a2ea9 (patch)
tree20a0ef15f13bd4f7e35e5c2e05e01be025b9cf0a
parent21fc592b57f7729ead21efec04a2bfe94f50e296 (diff)
downloadgcr-cb90a436ed4e25b02c6df9963d64290b740a2ea9.tar.gz
Complete importer in gcr library. Make 'gnome-keyring import' use the new
* daemon/ui/gkr-ask-entry.c: (moved) * daemon/ui/gkr-ask-entry.h: (moved) * daemon/ui/gkr-ask-tool.c: * daemon/ui/Makefile.am: * egg/egg-secure-entry.c: (moved from daemon/ui/gkr-ask-entry.c) * egg/egg-secure-entry.h: (moved from daemon/ui/gkr-ask-entry.h) * egg/Makefile.am: * gcr/gcr.h: (added) * gcr/gcr-import-dialog.c: (added) * gcr/gcr-import-dialog.glade: * gcr/gcr-import-dialog.h: (added) * gcr/gcr-importer.c: * gcr/gcr-importer.h: * gcr/gcr-internal.c: (removed) * gcr/gcr-internal.h: * gcr/gcr-library.c: (added) * gcr/gcr-parser.c: * gcr/gcr-parser.h: * gcr/gcr-types.h: * gcr/Makefile.am: * gcr/tests/Makefile.am: * gcr/tests/unit-test-parser.c: * tool/gkr-tool.c: * tool/gkr-tool-import.c: * tool/Makefile.am: Complete importer in gcr library. Make 'gnome-keyring import' use the new importer. svn path=/trunk/; revision=1472
-rw-r--r--egg/Makefile.am11
-rw-r--r--egg/egg-secure-entry.c2733
-rw-r--r--egg/egg-secure-entry.h187
-rw-r--r--gcr/Makefile.am50
-rw-r--r--gcr/gcr-import-dialog.c452
-rw-r--r--gcr/gcr-import-dialog.glade185
-rw-r--r--gcr/gcr-import-dialog.h86
-rw-r--r--gcr/gcr-importer.c749
-rw-r--r--gcr/gcr-importer.h59
-rw-r--r--gcr/gcr-internal.h8
-rw-r--r--gcr/gcr-library.c (renamed from gcr/gcr-internal.c)105
-rw-r--r--gcr/gcr-parser.c163
-rw-r--r--gcr/gcr-parser.h44
-rw-r--r--gcr/gcr-types.h19
-rw-r--r--gcr/gcr.h46
-rw-r--r--gcr/tests/Makefile.am4
-rw-r--r--gcr/tests/unit-test-parser.c2
17 files changed, 4560 insertions, 343 deletions
diff --git a/egg/Makefile.am b/egg/Makefile.am
index ca3637b..a6d0a2d 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -3,7 +3,8 @@ noinst_LTLIBRARIES = \
libegg.la \
libegg-buffer.la \
libegg-creds.la \
- libegg-secure.la
+ libegg-secure.la \
+ libegg-secure-entry.la
BUILT_SOURCES = \
asn1-def-pk.h asn1-def-pkix.h
@@ -45,6 +46,14 @@ DISTCLEANFILES = \
libegg_secure_la_SOURCES = \
egg-secure-memory.c egg-secure-memory.h
+libegg_secure_entry_la_SOURCES = \
+ egg-secure-entry.c egg-secure-entry.h
+
+libegg_secure_entry_la_CFLAGS = \
+ $(GOBJECT_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
libegg_buffer_la_SOURCES = \
egg-buffer.c egg-buffer.h
diff --git a/egg/egg-secure-entry.c b/egg/egg-secure-entry.c
new file mode 100644
index 0000000..1d92c9f
--- /dev/null
+++ b/egg/egg-secure-entry.c
@@ -0,0 +1,2733 @@
+/*
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
+ * <albrecht.dress@arcor.de> Feb. 2004.
+ *
+ * (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
+ * Public License.
+ *
+ * The entry is invisible by default, uses secure memory methods to
+ * allocate the text memory, and all potentially dangerous methods
+ * (copy & paste, popup, etc.) have been removed.
+ */
+
+/*
+ * Modified for inclusion into gnome-keyring by Stef Walter
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "egg-secure-entry.h"
+#include "egg-secure-memory.h"
+
+#define MIN_ASK_ENTRY_WIDTH 150
+#define DRAW_TIMEOUT 20
+#define INNER_BORDER 2
+
+/* Initial size of buffer, in bytes */
+#define MIN_SIZE 16
+
+/* Maximum size of text buffer, in bytes */
+#define MAX_SIZE G_MAXUSHORT
+
+enum {
+ ACTIVATE,
+ MOVE_CURSOR,
+ INSERT_AT_CURSOR,
+ DELETE_FROM_CURSOR,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_CURSOR_POSITION,
+ PROP_SELECTION_BOUND,
+ PROP_MAX_LENGTH,
+ PROP_HAS_FRAME,
+ PROP_INVISIBLE_CHAR,
+ PROP_ACTIVATES_DEFAULT,
+ PROP_WIDTH_CHARS,
+ PROP_SCROLL_OFFSET,
+ PROP_TEXT,
+ PROP_VISIBILITY
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* GObject, GtkObject methods */
+static void egg_secure_entry_class_init (EggSecureEntryClass *klass);
+static void egg_secure_entry_editable_init (GtkEditableClass *iface);
+static void egg_secure_entry_cell_editable_init (GtkCellEditableIface *iface);
+static void egg_secure_entry_init (EggSecureEntry *entry);
+static void egg_secure_entry_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void egg_secure_entry_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+static void egg_secure_entry_finalize (GObject *object);
+
+/* GtkWidget methods */
+static void egg_secure_entry_realize (GtkWidget *widget);
+static void egg_secure_entry_unrealize (GtkWidget *widget);
+static void egg_secure_entry_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void egg_secure_entry_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
+static void egg_secure_entry_draw_frame (GtkWidget *widget);
+static gint egg_secure_entry_expose (GtkWidget *widget, GdkEventExpose *event);
+static gint egg_secure_entry_button_press (GtkWidget *widget, GdkEventButton *event);
+static gint egg_secure_entry_button_release (GtkWidget *widget, GdkEventButton *event);
+static gint egg_secure_entry_motion_notify (GtkWidget *widget, GdkEventMotion *event);
+static gint egg_secure_entry_key_press (GtkWidget *widget, GdkEventKey *event);
+static gint egg_secure_entry_key_release (GtkWidget *widget, GdkEventKey *event);
+static gint egg_secure_entry_focus_in (GtkWidget *widget, GdkEventFocus *event);
+static gint egg_secure_entry_focus_out (GtkWidget *widget, GdkEventFocus *event);
+static void egg_secure_entry_grab_focus (GtkWidget *widget);
+static void egg_secure_entry_style_set (GtkWidget *widget, GtkStyle *previous_style);
+static void egg_secure_entry_direction_changed (GtkWidget *widget, GtkTextDirection previous_dir);
+static void egg_secure_entry_state_changed (GtkWidget *widget, GtkStateType previous_state);
+static void egg_secure_entry_screen_changed (GtkWidget *widget, GdkScreen *old_screen);
+
+/* GtkEditable method implementations */
+static void egg_secure_entry_insert_text (GtkEditable *editable, const gchar *new_text,
+ gint new_text_length, gint *position);
+static void egg_secure_entry_delete_text (GtkEditable *editable, gint start_pos, gint end_pos);
+static void egg_secure_entry_real_set_position (GtkEditable *editable, gint position);
+static gint egg_secure_entry_get_position (GtkEditable *editable);
+static void egg_secure_entry_set_selection_bounds (GtkEditable *editable, gint start, gint end);
+static gboolean egg_secure_entry_get_selection_bounds (GtkEditable *editable, gint *start, gint *end);
+
+/* GtkCellEditable method implementations */
+static void egg_secure_entry_start_editing (GtkCellEditable *cell_editable, GdkEvent *event);
+
+/* Default signal handlers */
+static void egg_secure_entry_real_insert_text (GtkEditable *editable, const gchar *new_text,
+ gint new_text_length, gint *position);
+static void egg_secure_entry_real_delete_text (GtkEditable *editable, gint start_pos, gint end_pos);
+static void egg_secure_entry_move_cursor (EggSecureEntry *entry, GtkMovementStep step,
+ gint count, gboolean extend_selection);
+static void egg_secure_entry_insert_at_cursor (EggSecureEntry *entry, const gchar *str);
+static void egg_secure_entry_delete_from_cursor (EggSecureEntry *entry, GtkDeleteType type, gint count);
+static void egg_secure_entry_real_activate (EggSecureEntry *entry);
+static void egg_secure_entry_keymap_direction_changed (GdkKeymap *keymap, EggSecureEntry *entry);
+
+/* IM Context Callbacks */
+static void egg_secure_entry_commit_cb(GtkIMContext *context, const gchar *str, EggSecureEntry *entry);
+static void egg_secure_entry_preedit_changed_cb (GtkIMContext * context, EggSecureEntry *entry);
+static gboolean egg_secure_entry_retrieve_surrounding_cb (GtkIMContext *context, EggSecureEntry *entry);
+static gboolean egg_secure_entry_delete_surrounding_cb (GtkIMContext *context, gint offset,
+ gint n_chars, EggSecureEntry *entry);
+
+/* Internal routines */
+static void egg_secure_entry_enter_text (EggSecureEntry *entry, const gchar *str);
+static void egg_secure_entry_set_positions (EggSecureEntry *entry, gint current_pos, gint selection_bound);
+static void egg_secure_entry_draw_text (EggSecureEntry *entry);
+static void egg_secure_entry_draw_cursor (EggSecureEntry *entry);
+static PangoLayout *egg_secure_entry_ensure_layout(EggSecureEntry *entry, gboolean include_preedit);
+static void egg_secure_entry_reset_layout (EggSecureEntry *entry);
+static void egg_secure_entry_queue_draw (EggSecureEntry *entry);
+static void egg_secure_entry_reset_im_context (EggSecureEntry *entry);
+static void egg_secure_entry_recompute (EggSecureEntry *entry);
+static gint egg_secure_entry_find_position (EggSecureEntry *entry, gint x);
+static void egg_secure_entry_get_cursor_locations (EggSecureEntry *entry, gint *strong_x, gint *weak_x);
+static void egg_secure_entry_adjust_scroll (EggSecureEntry *entry);
+static gint egg_secure_entry_move_visually (EggSecureEntry *editable, gint start, gint count);
+static gint egg_secure_entry_move_logically (EggSecureEntry *entry, gint start, gint count);
+static gboolean egg_secure_entry_mnemonic_activate (GtkWidget *widget, gboolean group_cycling);
+static void egg_secure_entry_state_changed (GtkWidget *widget, GtkStateType previous_state);
+static void egg_secure_entry_check_cursor_blink (EggSecureEntry *entry);
+static void egg_secure_entry_pend_cursor_blink (EggSecureEntry *entry);
+static void get_text_area_size (EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height);
+static void get_widget_window_size (EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height);
+
+#define _gtk_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
+#define _gtk_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING
+static void _gtk_marshal_VOID__ENUM_INT_BOOLEAN (GClosure *closure, GValue *return_value,
+ guint n_param_values, const GValue *param_values,
+ gpointer invocation_hint, gpointer marshal_data);
+static void _gtk_marshal_VOID__ENUM_INT (GClosure *closure, GValue *return_value, guint n_param_values,
+ const GValue *param_values, gpointer invocation_hint, gpointer marshal_data);
+
+static GtkWidgetClass *parent_class = NULL;
+
+GType
+egg_secure_entry_get_type(void)
+{
+ static GType entry_type = 0;
+
+ if (!entry_type) {
+ static const GTypeInfo entry_info = {
+ sizeof(EggSecureEntryClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) egg_secure_entry_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EggSecureEntry),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_secure_entry_init,
+ };
+
+ static const GInterfaceInfo editable_info = {
+ (GInterfaceInitFunc) egg_secure_entry_editable_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ static const GInterfaceInfo cell_editable_info = {
+ (GInterfaceInitFunc) egg_secure_entry_cell_editable_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ entry_type = g_type_register_static(GTK_TYPE_WIDGET, "EggSecureEntry", &entry_info, 0);
+ g_type_add_interface_static(entry_type, GTK_TYPE_EDITABLE, &editable_info);
+ g_type_add_interface_static(entry_type, GTK_TYPE_CELL_EDITABLE, &cell_editable_info);
+ }
+
+ return entry_type;
+}
+
+static void
+add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask,
+ GtkMovementStep step, gint count)
+{
+ g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
+
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move_cursor", 3,
+ G_TYPE_ENUM, step, G_TYPE_INT, count, G_TYPE_BOOLEAN, FALSE);
+
+ /* Selection-extending version */
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, "move_cursor",
+ 3, G_TYPE_ENUM, step, G_TYPE_INT, count, G_TYPE_BOOLEAN, TRUE);
+}
+
+static void
+egg_secure_entry_class_init(EggSecureEntryClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
+
+ widget_class = (GtkWidgetClass*) class;
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = egg_secure_entry_finalize;
+ gobject_class->set_property = egg_secure_entry_set_property;
+ gobject_class->get_property = egg_secure_entry_get_property;
+
+ widget_class->realize = egg_secure_entry_realize;
+ widget_class->unrealize = egg_secure_entry_unrealize;
+ widget_class->size_request = egg_secure_entry_size_request;
+ widget_class->size_allocate = egg_secure_entry_size_allocate;
+ widget_class->expose_event = egg_secure_entry_expose;
+ widget_class->button_press_event = egg_secure_entry_button_press;
+ widget_class->button_release_event = egg_secure_entry_button_release;
+ widget_class->motion_notify_event = egg_secure_entry_motion_notify;
+ widget_class->key_press_event = egg_secure_entry_key_press;
+ widget_class->key_release_event = egg_secure_entry_key_release;
+ widget_class->focus_in_event = egg_secure_entry_focus_in;
+ widget_class->focus_out_event = egg_secure_entry_focus_out;
+ widget_class->grab_focus = egg_secure_entry_grab_focus;
+ widget_class->style_set = egg_secure_entry_style_set;
+ widget_class->direction_changed = egg_secure_entry_direction_changed;
+ widget_class->state_changed = egg_secure_entry_state_changed;
+ widget_class->screen_changed = egg_secure_entry_screen_changed;
+ widget_class->mnemonic_activate = egg_secure_entry_mnemonic_activate;
+
+ class->move_cursor = egg_secure_entry_move_cursor;
+ class->insert_at_cursor = egg_secure_entry_insert_at_cursor;
+ class->delete_from_cursor = egg_secure_entry_delete_from_cursor;
+ class->activate = egg_secure_entry_real_activate;
+
+ g_object_class_install_property (gobject_class, PROP_CURSOR_POSITION,
+ g_param_spec_int ("cursor_position", "Cursor Position", "The current position of the insertion cursor in chars",
+ 0, MAX_SIZE, 0, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND,
+ g_param_spec_int ("selection_bound", "Selection Bound", "The position of the opposite end of the selection from the cursor in chars",
+ 0, MAX_SIZE, 0, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_MAX_LENGTH,
+ g_param_spec_int ("max_length", "Maximum length", "Maximum number of characters for this entry. Zero if no maximum",
+ 0, MAX_SIZE, 0, G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_HAS_FRAME,
+ g_param_spec_boolean("has_frame", "Has Frame", "FALSE removes outside bevel from entry",
+ TRUE, G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR,
+ g_param_spec_unichar("invisible_char", "Invisible character", "The character to use when masking entry contents (in \"password mode\")",
+ '*', G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_ACTIVATES_DEFAULT,
+ g_param_spec_boolean ("activates_default", "Activates default", "Whether to activate the default widget (such as the default button in a dialog) when Enter is pressed",
+ FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
+ g_param_spec_int ("width_chars", "Width in chars", "Number of characters to leave space for in the entry",
+ -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_SCROLL_OFFSET,
+ g_param_spec_int("scroll_offset", "Scroll offset", "Number of pixels of the entry scrolled off the screen to the left",
+ 0, G_MAXINT, 0, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_TEXT,
+ g_param_spec_string("text", "Text", "The contents of the entry",
+ "", G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class, PROP_VISIBILITY,
+ g_param_spec_boolean ("visibility", "Visibility", "Whether contents are drawn using invisible character",
+ FALSE, G_PARAM_READWRITE));
+
+ /* Action signals */
+
+ signals[ACTIVATE] = g_signal_new ("activate", G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggSecureEntryClass, activate),
+ NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ widget_class->activate_signal = signals[ACTIVATE];
+
+ signals[MOVE_CURSOR] = g_signal_new ("move_cursor", G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggSecureEntryClass, move_cursor),
+ NULL, NULL, _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
+ G_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
+
+ signals[INSERT_AT_CURSOR] = g_signal_new("insert_at_cursor", G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggSecureEntryClass, insert_at_cursor),
+ NULL, NULL, _gtk_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[DELETE_FROM_CURSOR] = g_signal_new("delete_from_cursor", G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggSecureEntryClass, delete_from_cursor),
+ NULL, NULL, _gtk_marshal_VOID__ENUM_INT, G_TYPE_NONE, 2,
+ GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
+
+ /* Key bindings */
+
+ binding_set = gtk_binding_set_by_class(class);
+
+ /* Moving the insertion point */
+ add_move_binding(binding_set, GDK_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+ add_move_binding(binding_set, GDK_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+ add_move_binding(binding_set, GDK_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+ add_move_binding(binding_set, GDK_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+ add_move_binding(binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1);
+ add_move_binding(binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1);
+ add_move_binding(binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1);
+ add_move_binding(binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1);
+ add_move_binding(binding_set, GDK_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ add_move_binding(binding_set, GDK_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+ add_move_binding(binding_set, GDK_KP_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+ add_move_binding(binding_set, GDK_KP_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+ add_move_binding(binding_set, GDK_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1);
+ add_move_binding(binding_set, GDK_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1);
+ add_move_binding(binding_set, GDK_KP_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1);
+ add_move_binding(binding_set, GDK_KP_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+ /* Select all */
+ gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "move_cursor", 3,
+ GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, -1,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "move_cursor", 3,
+ GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, G_TYPE_INT, 1,
+ G_TYPE_BOOLEAN, TRUE);
+
+ /* Activate */
+ gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "activate", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "activate", 0);
+
+ /* Deleting text */
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT, -1);
+ /* Make this do the same as Backspace, to help with mis-typing */
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_SHIFT_MASK, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_CHARS, G_TYPE_INT, -1);
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, G_TYPE_INT, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, G_TYPE_INT, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, "delete_from_cursor", 2,
+ G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, G_TYPE_INT, -1);
+}
+
+static void
+egg_secure_entry_editable_init (GtkEditableClass *iface)
+{
+ iface->do_insert_text = egg_secure_entry_insert_text;
+ iface->do_delete_text = egg_secure_entry_delete_text;
+ iface->insert_text = egg_secure_entry_real_insert_text;
+ iface->delete_text = egg_secure_entry_real_delete_text;
+ iface->set_selection_bounds = egg_secure_entry_set_selection_bounds;
+ iface->get_selection_bounds = egg_secure_entry_get_selection_bounds;
+ iface->set_position = egg_secure_entry_real_set_position;
+ iface->get_position = egg_secure_entry_get_position;
+}
+
+static void
+egg_secure_entry_cell_editable_init (GtkCellEditableIface * iface)
+{
+ iface->start_editing = egg_secure_entry_start_editing;
+}
+
+static void
+egg_secure_entry_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(object);
+
+ switch (prop_id) {
+ case PROP_MAX_LENGTH:
+ egg_secure_entry_set_max_length(entry, g_value_get_int(value));
+ break;
+
+ case PROP_HAS_FRAME:
+ egg_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
+ break;
+
+ case PROP_INVISIBLE_CHAR:
+ egg_secure_entry_set_invisible_char(entry, g_value_get_uint(value));
+ break;
+
+ case PROP_ACTIVATES_DEFAULT:
+ egg_secure_entry_set_activates_default(entry, g_value_get_boolean(value));
+ break;
+
+ case PROP_WIDTH_CHARS:
+ egg_secure_entry_set_width_chars(entry, g_value_get_int(value));
+ break;
+
+ case PROP_TEXT:
+ egg_secure_entry_set_text(entry, g_value_get_string(value));
+ break;
+
+ case PROP_VISIBILITY:
+ egg_secure_entry_set_visibility (entry, g_value_get_boolean (value));
+ break;
+
+ case PROP_SCROLL_OFFSET:
+ case PROP_CURSOR_POSITION:
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_secure_entry_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(object);
+
+ switch (prop_id) {
+ case PROP_CURSOR_POSITION:
+ g_value_set_int(value, entry->current_pos);
+ break;
+ case PROP_SELECTION_BOUND:
+ g_value_set_int(value, entry->selection_bound);
+ break;
+ case PROP_MAX_LENGTH:
+ g_value_set_int(value, entry->text_max_length);
+ break;
+ case PROP_HAS_FRAME:
+ g_value_set_boolean(value, entry->has_frame);
+ break;
+ case PROP_INVISIBLE_CHAR:
+ g_value_set_uint(value, entry->invisible_char);
+ break;
+ case PROP_ACTIVATES_DEFAULT:
+ g_value_set_boolean(value, entry->activates_default);
+ break;
+ case PROP_WIDTH_CHARS:
+ g_value_set_int(value, entry->width_chars);
+ break;
+ case PROP_SCROLL_OFFSET:
+ g_value_set_int(value, entry->scroll_offset);
+ break;
+ case PROP_TEXT:
+ g_value_set_string(value, egg_secure_entry_get_text(entry));
+ break;
+ case PROP_VISIBILITY:
+ g_value_set_boolean (value, egg_secure_entry_get_visibility (entry));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_secure_entry_init (EggSecureEntry *entry)
+{
+ GtkStyle *style;
+ GtkWidget *tempent;
+
+ GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
+
+ /* Get the RC style for a normal GtkEntry */
+ style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (GTK_WIDGET (entry)),
+ NULL, NULL, GTK_TYPE_ENTRY);
+ gtk_widget_set_style (GTK_WIDGET (entry), style);
+
+ entry->text_size = MIN_SIZE;
+ entry->text = egg_secure_alloc (entry->text_size + 1);
+ entry->text[0] = '\0';
+
+ entry->visibility = FALSE;
+ entry->width_chars = -1;
+ entry->is_cell_renderer = FALSE;
+ entry->editing_canceled = FALSE;
+ entry->has_frame = TRUE;
+
+ /* Use the invisible_char from GtkEntry */
+ tempent = gtk_entry_new ();
+ entry->invisible_char = gtk_entry_get_invisible_char (GTK_ENTRY (tempent));
+ g_object_ref_sink (tempent);
+ g_object_unref (tempent);
+
+ /*
+ * This object is completely private. No external entity can gain a reference
+ * to it; so we create it here and destroy it in finalize().
+ */
+ entry->im_context = gtk_im_multicontext_new ();
+
+ g_signal_connect (entry->im_context, "commit",
+ G_CALLBACK (egg_secure_entry_commit_cb), entry);
+ g_signal_connect (entry->im_context, "preedit_changed",
+ G_CALLBACK(egg_secure_entry_preedit_changed_cb), entry);
+ g_signal_connect (entry->im_context, "retrieve_surrounding",
+ G_CALLBACK(egg_secure_entry_retrieve_surrounding_cb), entry);
+ g_signal_connect (entry->im_context, "delete_surrounding",
+ G_CALLBACK(egg_secure_entry_delete_surrounding_cb), entry);
+}
+
+static void
+egg_secure_entry_finalize (GObject *object)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (object);
+
+ if (entry->cached_layout)
+ g_object_unref (entry->cached_layout);
+
+ g_object_unref (entry->im_context);
+
+ if (entry->blink_timeout)
+ g_source_remove (entry->blink_timeout);
+
+ if (entry->recompute_idle)
+ g_source_remove (entry->recompute_idle);
+
+ entry->text_size = 0;
+
+ if (entry->text)
+ egg_secure_free (entry->text);
+ entry->text = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+egg_secure_entry_realize (GtkWidget *widget)
+{
+ EggSecureEntry *entry;
+ GtkEditable *editable;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ entry = EGG_SECURE_ENTRY (widget);
+ editable = GTK_EDITABLE (widget);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+
+ get_widget_window_size (entry, &attributes.x, &attributes.y,
+ &attributes.width, &attributes.height);
+
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
+ GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, entry);
+
+ get_text_area_size (entry, &attributes.x, &attributes.y,
+ &attributes.width, &attributes.height);
+
+ attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_XTERM);
+ attributes_mask |= GDK_WA_CURSOR;
+
+ entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
+ gdk_window_set_user_data (entry->text_area, entry);
+
+ gdk_cursor_unref (attributes.cursor);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_background (widget->window,
+ &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ gdk_window_set_background (entry->text_area,
+ &widget->style->base[GTK_WIDGET_STATE (widget)]);
+
+ gdk_window_show (entry->text_area);
+
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
+
+ egg_secure_entry_adjust_scroll (entry);
+}
+
+static void
+egg_secure_entry_unrealize (GtkWidget *widget)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ egg_secure_entry_reset_layout (entry);
+
+ gtk_im_context_set_client_window (entry->im_context, NULL);
+
+ if (entry->text_area) {
+ gdk_window_set_user_data (entry->text_area, NULL);
+ gdk_window_destroy (entry->text_area);
+ entry->text_area = NULL;
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (*GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+get_borders (EggSecureEntry *entry, gint *xborder, gint *yborder)
+{
+ GtkWidget *widget = GTK_WIDGET (entry);
+ gint focus_width;
+ gboolean interior_focus;
+
+ gtk_widget_style_get (widget, "interior-focus", &interior_focus,
+ "focus-line-width", &focus_width, NULL);
+
+ if (entry->has_frame) {
+ *xborder = widget->style->xthickness;
+ *yborder = widget->style->ythickness;
+ } else {
+ *xborder = 0;
+ *yborder = 0;
+ }
+
+ if (!interior_focus) {
+ *xborder += focus_width;
+ *yborder += focus_width;
+ }
+}
+
+static void
+egg_secure_entry_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+ PangoFontMetrics *metrics;
+ gint xborder, yborder;
+ PangoContext *context;
+
+ context = gtk_widget_get_pango_context (widget);
+ metrics = pango_context_get_metrics (context, widget->style->font_desc,
+ pango_context_get_language (context));
+
+ entry->ascent = pango_font_metrics_get_ascent (metrics);
+ entry->descent = pango_font_metrics_get_descent (metrics);
+
+ get_borders (entry, &xborder, &yborder);
+
+ xborder += INNER_BORDER;
+ yborder += INNER_BORDER;
+
+ if (entry->width_chars < 0)
+ requisition->width = MIN_ASK_ENTRY_WIDTH + xborder * 2;
+ else {
+ gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
+ gint digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
+ gint char_pixels = (MAX (char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
+ requisition->width = char_pixels * entry->width_chars + xborder * 2;
+ }
+
+ requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
+ pango_font_metrics_unref(metrics);
+}
+
+static void
+get_text_area_size(EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height)
+{
+ gint xborder, yborder;
+ GtkRequisition requisition;
+ GtkWidget *widget = GTK_WIDGET (entry);
+
+ gtk_widget_get_child_requisition (widget, &requisition);
+
+ get_borders (entry, &xborder, &yborder);
+
+ if (x)
+ *x = xborder;
+
+ if (y)
+ *y = yborder;
+
+ if (width)
+ *width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
+
+ if (height)
+ *height = requisition.height - yborder * 2;
+}
+
+static void
+get_widget_window_size (EggSecureEntry *entry, gint *x, gint *y, gint *width, gint *height)
+{
+ GtkRequisition requisition;
+ GtkWidget *widget = GTK_WIDGET (entry);
+
+ gtk_widget_get_child_requisition (widget, &requisition);
+
+ if (x)
+ *x = widget->allocation.x;
+
+ if (y) {
+ if (entry->is_cell_renderer)
+ *y = widget->allocation.y;
+ else
+ *y = widget->allocation.y + (widget->allocation.height -
+ requisition.height) / 2;
+ }
+
+ if (width)
+ *width = widget->allocation.width;
+
+ if (height) {
+ if (entry->is_cell_renderer)
+ *height = widget->allocation.height;
+ else
+ *height = requisition.height;
+ }
+}
+
+static void
+egg_secure_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ /*
+ * We call gtk_widget_get_child_requisition, since we want (for
+ * backwards compatibility reasons) the realization here to
+ * be affected by the usize of the entry, if set
+ */
+ gint x, y, width, height;
+
+ get_widget_window_size (entry, &x, &y, &width, &height);
+
+ gdk_window_move_resize (widget->window, x, y, width, height);
+
+ get_text_area_size (entry, &x, &y, &width, &height);
+
+ gdk_window_move_resize (entry->text_area, x, y, width, height);
+
+ egg_secure_entry_recompute (entry);
+ }
+}
+
+static void
+egg_secure_entry_draw_frame (GtkWidget *widget)
+{
+ gint x = 0, y = 0;
+ gint width, height;
+ gboolean interior_focus;
+ gint focus_width;
+
+ gtk_widget_style_get (widget, "interior-focus", &interior_focus,
+ "focus-line-width", &focus_width, NULL);
+
+ gdk_drawable_get_size (widget->window, &width, &height);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus) {
+ x += focus_width;
+ y += focus_width;
+ width -= 2 * focus_width;
+ height -= 2 * focus_width;
+ }
+
+ gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
+ NULL, widget, "entry", x, y, width, height);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus) {
+ x -= focus_width;
+ y -= focus_width;
+ width += 2 * focus_width;
+ height += 2 * focus_width;
+
+ gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE (widget),
+ NULL, widget, "entry", 0, 0, width, height);
+ }
+}
+
+static gint
+egg_secure_entry_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
+
+ if (widget->window == event->window)
+ egg_secure_entry_draw_frame(widget);
+ else if (entry->text_area == event->window) {
+ gint area_width, area_height;
+
+ get_text_area_size(entry, NULL, NULL, &area_width, &area_height);
+
+ gtk_paint_flat_box(widget->style, entry->text_area, GTK_WIDGET_STATE (widget), GTK_SHADOW_NONE,
+ NULL, widget, "entry_bg", 0, 0, area_width, area_height);
+
+ if ((entry->invisible_char != 0) && GTK_WIDGET_HAS_FOCUS (widget) &&
+ entry->selection_bound == entry->current_pos && entry->cursor_visible)
+ egg_secure_entry_draw_cursor (EGG_SECURE_ENTRY (widget));
+
+ egg_secure_entry_draw_text (EGG_SECURE_ENTRY (widget));
+ }
+
+ return FALSE;
+}
+
+static gint
+egg_secure_entry_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+ gint tmp_pos;
+
+ if (event->window != entry->text_area ||
+ (entry->button && event->button != entry->button))
+ return FALSE;
+
+ entry->button = event->button;
+
+ if (!GTK_WIDGET_HAS_FOCUS (widget)) {
+ entry->in_click = TRUE;
+ gtk_widget_grab_focus (widget);
+ entry->in_click = FALSE;
+ }
+
+ tmp_pos = egg_secure_entry_find_position (entry, event->x + entry->scroll_offset);
+
+ if (event->button == 1) {
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ egg_secure_entry_set_positions(entry, tmp_pos, tmp_pos);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+egg_secure_entry_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
+
+ if (event->window != entry->text_area || entry->button != event->button)
+ return FALSE;
+
+ entry->button = 0;
+ return TRUE;
+}
+
+static gint
+egg_secure_entry_motion_notify(GtkWidget *widget, GdkEventMotion *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(widget);
+ gint tmp_pos;
+
+ if (entry->mouse_cursor_obscured) {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display(widget),
+ GDK_XTERM);
+ gdk_window_set_cursor (entry->text_area, cursor);
+ gdk_cursor_unref (cursor);
+ entry->mouse_cursor_obscured = FALSE;
+ }
+
+ if (event->window != entry->text_area || entry->button != 1)
+ return FALSE;
+
+ if (event->is_hint || (entry->text_area != event->window))
+ gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
+
+ {
+ gint height;
+ gdk_drawable_get_size (entry->text_area, NULL, &height);
+
+ if (event->y < 0)
+ tmp_pos = 0;
+ else if (event->y >= height)
+ tmp_pos = entry->text_length;
+ else
+ tmp_pos = egg_secure_entry_find_position (entry,
+ event->x + entry->scroll_offset);
+
+ egg_secure_entry_set_positions (entry, tmp_pos, -1);
+ }
+
+ return TRUE;
+}
+
+static void
+set_invisible_cursor (GdkWindow * window)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor;
+ GdkColor useless;
+ char invisible_cursor_bits[] = { 0x0 };
+
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (window, invisible_cursor_bits, 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap, empty_bitmap, &useless, &useless, 0, 0);
+
+ gdk_window_set_cursor (window, cursor);
+
+ gdk_cursor_unref (cursor);
+
+ g_object_unref (empty_bitmap);
+}
+
+static void
+egg_secure_entry_obscure_mouse_cursor (EggSecureEntry * entry)
+{
+ if (entry->mouse_cursor_obscured)
+ return;
+
+ set_invisible_cursor (entry->text_area);
+
+ entry->mouse_cursor_obscured = TRUE;
+}
+
+static gint
+egg_secure_entry_key_press (GtkWidget *widget, GdkEventKey *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ egg_secure_entry_pend_cursor_blink (entry);
+
+ if (gtk_im_context_filter_keypress (entry->im_context, event)) {
+ egg_secure_entry_obscure_mouse_cursor (entry);
+ entry->need_im_reset = TRUE;
+ return TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ /* Activate key bindings */
+ return TRUE;
+
+ return FALSE;
+}
+
+static gint
+egg_secure_entry_key_release (GtkWidget *widget, GdkEventKey *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ if (gtk_im_context_filter_keypress (entry->im_context, event)) {
+ entry->need_im_reset = TRUE;
+ return TRUE;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
+}
+
+static gint
+egg_secure_entry_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ gtk_widget_queue_draw (widget);
+
+ entry->need_im_reset = TRUE;
+ gtk_im_context_focus_in (entry->im_context);
+
+ g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), "direction_changed",
+ G_CALLBACK (egg_secure_entry_keymap_direction_changed), entry);
+
+ egg_secure_entry_check_cursor_blink (entry);
+
+ return FALSE;
+}
+
+static gint
+egg_secure_entry_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ gtk_widget_queue_draw (widget);
+
+ entry->need_im_reset = TRUE;
+ gtk_im_context_focus_out (entry->im_context);
+
+ egg_secure_entry_check_cursor_blink (entry);
+
+ g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
+ egg_secure_entry_keymap_direction_changed, entry);
+
+ return FALSE;
+}
+
+static void
+egg_secure_entry_grab_focus (GtkWidget *widget)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+ GtkSettings *settings = gtk_widget_get_settings (widget);
+ gboolean select_on_focus = FALSE;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT);
+ GTK_WIDGET_CLASS (parent_class)->grab_focus(widget);
+
+ /* Some versions of GTK don't have this property */
+ if (g_object_class_find_property (G_OBJECT_CLASS (GTK_SETTINGS_GET_CLASS (settings)),
+ "gtk-entry-select-on-focus"))
+ g_object_get (settings, "gtk-entry-select-on-focus", &select_on_focus, NULL);
+
+ if (select_on_focus && !entry->in_click)
+ gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
+}
+
+static void
+egg_secure_entry_direction_changed(GtkWidget *widget, GtkTextDirection previous_dir)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ egg_secure_entry_recompute (entry);
+
+ GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
+}
+
+static void
+egg_secure_entry_state_changed (GtkWidget *widget, GtkStateType previous_state)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ if (GTK_WIDGET_REALIZED (widget)) {
+ gdk_window_set_background (widget->window,
+ &widget->style->base[GTK_WIDGET_STATE(widget)]);
+ gdk_window_set_background (entry->text_area,
+ &widget->style->base[GTK_WIDGET_STATE(widget)]);
+ }
+
+ if (!GTK_WIDGET_IS_SENSITIVE (widget)) {
+ /* Clear any selection */
+ gtk_editable_select_region (GTK_EDITABLE(entry),
+ entry->current_pos, entry->current_pos);
+ }
+
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+egg_secure_entry_screen_changed (GtkWidget *widget, GdkScreen *old_screen)
+{
+ egg_secure_entry_recompute (EGG_SECURE_ENTRY (widget));
+}
+
+/* GtkEditable method implementations */
+
+static void
+egg_secure_entry_insert_text (GtkEditable *editable, const gchar *new_text,
+ gint new_text_length, gint * position)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY(editable);
+ gchar *text;
+
+ if (*position < 0 || *position > entry->text_length)
+ *position = entry->text_length;
+
+ g_object_ref (editable);
+
+ text = egg_secure_alloc (new_text_length + 1);
+
+ strncpy (text, new_text, new_text_length);
+ text[new_text_length] = '\0';
+
+ g_signal_emit_by_name (editable, "insert_text", text,
+ new_text_length, position);
+
+ egg_secure_free (text);
+
+ g_object_unref (editable);
+}
+
+static void
+egg_secure_entry_delete_text (GtkEditable* editable, gint start_pos,
+ gint end_pos)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
+
+ if (end_pos < 0 || end_pos > entry->text_length)
+ end_pos = entry->text_length;
+ if (start_pos < 0)
+ start_pos = 0;
+ if (start_pos > end_pos)
+ start_pos = end_pos;
+
+ g_object_ref(editable);
+
+ g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
+
+ g_object_unref (editable);
+}
+
+static void
+egg_secure_entry_set_position_internal (EggSecureEntry *entry,
+ gint position, gboolean reset_im)
+{
+ if (position < 0 || position > entry->text_length)
+ position = entry->text_length;
+
+ if (position != entry->current_pos || position != entry->selection_bound) {
+ if (reset_im)
+ egg_secure_entry_reset_im_context (entry);
+ egg_secure_entry_set_positions (entry, position, position);
+ }
+}
+
+static void
+egg_secure_entry_real_set_position (GtkEditable *editable, gint position)
+{
+ egg_secure_entry_set_position_internal (EGG_SECURE_ENTRY (editable),
+ position, TRUE);
+}
+
+static gint
+egg_secure_entry_get_position (GtkEditable *editable)
+{
+ return EGG_SECURE_ENTRY (editable)->current_pos;
+}
+
+static void
+egg_secure_entry_set_selection_bounds (GtkEditable *editable,
+ gint start, gint end)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
+
+ if (start < 0)
+ start = entry->text_length;
+ if (end < 0)
+ end = entry->text_length;
+
+ egg_secure_entry_reset_im_context (entry);
+
+ egg_secure_entry_set_positions (entry, MIN (end, entry->text_length),
+ MIN (start, entry->text_length));
+}
+
+static gboolean
+egg_secure_entry_get_selection_bounds (GtkEditable *editable,
+ gint *start, gint *end)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
+
+ *start = entry->selection_bound;
+ *end = entry->current_pos;
+
+ return (entry->selection_bound != entry->current_pos);
+}
+
+static void
+egg_secure_entry_style_set (GtkWidget *widget, GtkStyle *previous_style)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (widget);
+
+ egg_secure_entry_recompute (entry);
+
+ if (previous_style && GTK_WIDGET_REALIZED (widget)) {
+ gdk_window_set_background (widget->window,
+ &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ gdk_window_set_background (entry->text_area,
+ &widget->style-> base[GTK_WIDGET_STATE (widget)]);
+ }
+}
+
+/* GtkCellEditable method implementations
+ */
+static void
+gtk_cell_editable_secure_entry_activated (EggSecureEntry *entry, gpointer data)
+{
+ gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
+ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
+}
+
+static gboolean
+gtk_cell_editable_key_press_event (EggSecureEntry *entry, GdkEventKey *key_event,
+ gpointer data)
+{
+ if (key_event->keyval == GDK_Escape) {
+ entry->editing_canceled = TRUE;
+ gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry));
+ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry));
+ return TRUE;
+ }
+
+ /* override focus */
+ if (key_event->keyval == GDK_Up || key_event->keyval == GDK_Down) {
+ gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
+ gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(entry));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+egg_secure_entry_start_editing (GtkCellEditable *cell_editable,
+ GdkEvent *event)
+{
+ EGG_SECURE_ENTRY(cell_editable)->is_cell_renderer = TRUE;
+
+ g_signal_connect (cell_editable, "activate",
+ G_CALLBACK (gtk_cell_editable_secure_entry_activated), NULL);
+ g_signal_connect (cell_editable, "key_press_event",
+ G_CALLBACK (gtk_cell_editable_key_press_event), NULL);
+}
+
+/* Default signal handlers */
+
+static void
+egg_secure_entry_real_insert_text (GtkEditable *editable, const gchar *new_text,
+ gint new_text_length, gint *position)
+{
+ gint _index;
+ gint n_chars;
+
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
+
+ if (new_text_length < 0)
+ new_text_length = strlen (new_text);
+
+ n_chars = g_utf8_strlen (new_text, new_text_length);
+ if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) {
+ gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (entry)));
+ n_chars = entry->text_max_length - entry->text_length;
+ new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
+ }
+
+ if (new_text_length + entry->n_bytes + 1 > entry->text_size) {
+ while (new_text_length + entry->n_bytes + 1 > entry->text_size) {
+ if (entry->text_size == 0)
+ entry->text_size = MIN_SIZE;
+ else {
+ if (2 * (guint) entry->text_size < MAX_SIZE &&
+ 2 * (guint) entry->text_size > entry->text_size)
+ entry->text_size *= 2;
+ else {
+ entry->text_size = MAX_SIZE;
+ if (new_text_length > (gint) entry->text_size - (gint) entry->n_bytes - 1) {
+ new_text_length = (gint) entry->text_size - (gint) entry->n_bytes - 1;
+ new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text;
+ n_chars = g_utf8_strlen (new_text, new_text_length);
+ }
+ break;
+ }
+ }
+ }
+
+ entry->text = egg_secure_realloc (entry->text, entry->text_size + 1);
+ }
+
+ _index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
+
+ g_memmove (entry->text + _index + new_text_length, entry->text + _index,
+ entry->n_bytes - _index);
+ memcpy (entry->text + _index, new_text, new_text_length);
+
+ entry->n_bytes += new_text_length;
+ entry->text_length += n_chars;
+
+ /* NUL terminate for safety and convenience */
+ entry->text[entry->n_bytes] = '\0';
+
+ if (entry->current_pos > *position)
+ entry->current_pos += n_chars;
+
+ if (entry->selection_bound > *position)
+ entry->selection_bound += n_chars;
+
+ *position += n_chars;
+
+ egg_secure_entry_recompute (entry);
+
+ entry->changed = TRUE;
+ g_signal_emit_by_name (editable, "changed");
+ g_object_notify (G_OBJECT (editable), "text");
+}
+
+static void
+egg_secure_entry_real_delete_text (GtkEditable *editable, gint start_pos,
+ gint end_pos)
+{
+ EggSecureEntry *entry = EGG_SECURE_ENTRY (editable);
+
+ if (start_pos < 0)
+ start_pos = 0;
+ if (end_pos < 0 || end_pos > entry->text_length)
+ end_pos = entry->text_length;
+
+ if (start_pos < end_pos) {
+ gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
+ gint end_index = g_utf8_offset_to_pointer(entry->text, end_pos) - entry->text;
+ gint current_pos;
+ gint selection_bound;
+
+ g_memmove (entry->text + start_index, entry->text + end_index,
+ entry->n_bytes + 1 - end_index);
+ entry->text_length -= (end_pos - start_pos);
+ entry->n_bytes -= (end_index - start_index);
+
+ current_pos = entry->current_pos;
+ if (current_pos > start_pos)
+ current_pos -= MIN(current_pos, end_pos) - start_pos;
+
+ selection_bound = entry->selection_bound;
+ if (selection_bound > start_pos)
+ selection_bound -= MIN(selection_bound, end_pos) - start_pos;
+
+ egg_secure_entry_set_positions(entry, current_pos, selection_bound);
+
+ egg_secure_entry_recompute (entry);
+
+ entry->changed = TRUE;
+ g_signal_emit_by_name (editable, "changed");
+ g_object_notify (G_OBJECT (editable), "text");
+ }
+}
+
+/*
+ * Compute the X position for an offset that corresponds to the "more important
+ * cursor position for that offset. We use this when trying to guess to which
+ * end of the selection we should go to when the user hits the left or
+ * right arrow key.
+ */
+static gint
+get_better_cursor_x (EggSecureEntry *entry, gint offset)
+{
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET(entry)));
+ PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
+ gboolean split_cursor;
+
+ PangoLayout *layout = egg_secure_entry_ensure_layout (entry, TRUE);
+ const gchar *text = pango_layout_get_text(layout);
+ gint _index = g_utf8_offset_to_pointer (text, offset) - text;
+
+ PangoRectangle strong_pos, weak_pos;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
+ "gtk-split-cursor", &split_cursor, NULL);
+
+ pango_layout_get_cursor_pos (layout, _index, &strong_pos, &weak_pos);
+
+ if (split_cursor)
+ return strong_pos.x / PANGO_SCALE;
+ else
+ return (keymap_direction == entry->resolved_dir) ?
+ strong_pos.x / PANGO_SCALE :
+ weak_pos.x / PANGO_SCALE;
+}
+
+static void
+egg_secure_entry_move_cursor (EggSecureEntry *entry, GtkMovementStep step,
+ gint count, gboolean extend_selection)
+{
+ gint new_pos = entry->current_pos;
+
+ egg_secure_entry_reset_im_context (entry);
+
+ if (entry->current_pos != entry->selection_bound && !extend_selection) {
+ /*
+ * If we have a current selection and aren't extending it, move to the
+ * start/or end of the selection as appropriate
+ */
+ switch (step) {
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ {
+ gint current_x = get_better_cursor_x (entry, entry->current_pos);
+ gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
+
+ if (count < 0)
+ new_pos = current_x < bound_x ?
+ entry->current_pos : entry->selection_bound;
+ else
+ new_pos = current_x > bound_x ?
+ entry->current_pos : entry->selection_bound;
+ break;
+ }
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+ case GTK_MOVEMENT_PARAGRAPH_ENDS:
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ new_pos = count < 0 ? 0 : entry->text_length;
+ break;
+ case GTK_MOVEMENT_WORDS:
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ case GTK_MOVEMENT_PARAGRAPHS:
+ case GTK_MOVEMENT_PAGES:
+ case GTK_MOVEMENT_HORIZONTAL_PAGES:
+ break;
+ }
+ } else {
+ switch (step) {
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ new_pos = egg_secure_entry_move_logically (entry, new_pos, count);
+ break;
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ new_pos = egg_secure_entry_move_visually (entry, new_pos, count);
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+ case GTK_MOVEMENT_PARAGRAPH_ENDS:
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ new_pos = count < 0 ? 0 : entry->text_length;
+ break;
+ case GTK_MOVEMENT_WORDS:
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ case GTK_MOVEMENT_PARAGRAPHS:
+ case GTK_MOVEMENT_PAGES:
+ case GTK_MOVEMENT_HORIZONTAL_PAGES:
+ break;
+ }
+ }
+
+ if (extend_selection)
+ gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
+ else
+ gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
+
+ egg_secure_entry_pend_cursor_blink (entry);
+}
+
+static void
+egg_secure_entry_insert_at_cursor(EggSecureEntry *entry, const gchar *str)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint pos = entry->current_pos;
+
+ egg_secure_entry_reset_im_context (entry);
+
+ gtk_editable_insert_text (editable, str, -1, &pos);
+ gtk_editable_set_position (editable, pos);
+}
+
+static void
+egg_secure_entry_delete_from_cursor (EggSecureEntry *entry, GtkDeleteType type,
+ gint count)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start_pos = entry->current_pos;
+ gint end_pos = entry->current_pos;
+
+ egg_secure_entry_reset_im_context (entry);
+
+ if (entry->selection_bound != entry->current_pos) {
+ gtk_editable_delete_selection (editable);
+ return;
+ }
+
+ switch (type) {
+ case GTK_DELETE_CHARS:
+ end_pos = egg_secure_entry_move_logically (entry, entry->current_pos, count);
+ gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
+ break;
+ case GTK_DELETE_DISPLAY_LINE_ENDS:
+ case GTK_DELETE_PARAGRAPH_ENDS:
+ if (count < 0)
+ gtk_editable_delete_text (editable, 0, entry->current_pos);
+ else
+ gtk_editable_delete_text (editable, entry->current_pos, -1);
+ break;
+ case GTK_DELETE_DISPLAY_LINES:
+ case GTK_DELETE_PARAGRAPHS:
+ gtk_editable_delete_text (editable, 0, -1);
+ break;
+ default:
+ break;
+ }
+
+ egg_secure_entry_pend_cursor_blink (entry);
+}
+
+static void
+egg_secure_entry_real_activate (EggSecureEntry *entry)
+{
+ GtkWindow *window;
+ GtkWidget *toplevel;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (entry);
+
+ if (entry->activates_default) {
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (GTK_IS_WINDOW (toplevel)) {
+ window = GTK_WINDOW(toplevel);
+
+ if (window && widget != window->default_widget &&
+ !(widget == window->focus_widget &&
+ (!window->default_widget || !GTK_WIDGET_SENSITIVE(window->default_widget))))
+ gtk_window_activate_default(window);
+ }
+ }
+}
+
+static void
+egg_secure_entry_keymap_direction_changed (GdkKeymap *keymap, EggSecureEntry *entry)
+{
+ egg_secure_entry_recompute (entry);
+}
+
+/* IM Context Callbacks */
+
+static void
+egg_secure_entry_commit_cb (GtkIMContext *context, const gchar *str,
+ EggSecureEntry *entry)
+{
+ egg_secure_entry_enter_text (entry, str);
+}
+
+static void
+egg_secure_entry_preedit_changed_cb (GtkIMContext *context, EggSecureEntry *entry)
+{
+ gchar *preedit_string;
+ gint cursor_pos;
+
+ gtk_im_context_get_preedit_string (entry->im_context, &preedit_string, NULL, &cursor_pos);
+ entry->preedit_length = strlen (preedit_string);
+ cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
+ entry->preedit_cursor = cursor_pos;
+ g_free (preedit_string);
+
+ egg_secure_entry_recompute (entry);
+}
+
+static gboolean
+egg_secure_entry_retrieve_surrounding_cb (GtkIMContext *context, EggSecureEntry *entry)
+{
+ gtk_im_context_set_surrounding (context, entry->text, entry->n_bytes,
+ g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text);
+ return TRUE;
+}
+
+static gboolean
+egg_secure_entry_delete_surrounding_cb (GtkIMContext *slave, gint offset, gint n_chars,
+ EggSecureEntry *entry)
+{
+ gtk_editable_delete_text (GTK_EDITABLE (entry), entry->current_pos + offset,
+ entry->current_pos + offset + n_chars);
+ return TRUE;
+}
+
+/* Internal functions */
+
+/* Used for im_commit_cb and inserting Unicode chars */
+static void
+egg_secure_entry_enter_text (EggSecureEntry *entry, const gchar *str)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint tmp_pos;
+
+ if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
+ gtk_editable_delete_selection (editable);
+ else {
+ if (entry->overwrite_mode)
+ egg_secure_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
+ }
+
+ tmp_pos = entry->current_pos;
+ gtk_editable_insert_text (editable, str, strlen(str), &tmp_pos);
+ egg_secure_entry_set_position_internal (entry, tmp_pos, FALSE);
+}
+
+/*
+ * All changes to entry->current_pos and entry->selection_bound
+ * should go through this function.
+ */
+static void
+egg_secure_entry_set_positions (EggSecureEntry *entry, gint current_pos,
+ gint selection_bound)
+{
+ gboolean changed = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (entry));
+
+ if (current_pos != -1 && entry->current_pos != current_pos) {
+ entry->current_pos = current_pos;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (entry), "cursor_position");
+ }
+
+ if (selection_bound != -1 && entry->selection_bound != selection_bound) {
+ entry->selection_bound = selection_bound;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (entry), "selection_bound");
+ }
+
+ g_object_thaw_notify (G_OBJECT (entry));
+
+ if (changed)
+ egg_secure_entry_recompute (entry);
+}
+
+static void
+egg_secure_entry_reset_layout (EggSecureEntry *entry)
+{
+ if (entry->cached_layout) {
+ g_object_unref (entry->cached_layout);
+ entry->cached_layout = NULL;
+ }
+}
+
+static void
+update_im_cursor_location (EggSecureEntry *entry)
+{
+ GdkRectangle area;
+ gint strong_x;
+ gint strong_xoffset;
+ gint area_width, area_height;
+
+ egg_secure_entry_get_cursor_locations (entry, &strong_x, NULL);
+ get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
+
+ strong_xoffset = strong_x - entry->scroll_offset;
+ if (strong_xoffset < 0) {
+ strong_xoffset = 0;
+ } else if (strong_xoffset > area_width) {
+ strong_xoffset = area_width;
+ }
+ area.x = strong_xoffset;
+ area.y = 0;
+ area.width = 0;
+ area.height = area_height;
+
+ gtk_im_context_set_cursor_location (entry->im_context, &area);
+}
+
+static gboolean
+recompute_idle_func (gpointer data)
+{
+ EggSecureEntry *entry;
+
+ GDK_THREADS_ENTER ();
+
+ entry = EGG_SECURE_ENTRY (data);
+
+ entry->recompute_idle = 0;
+
+ if (gtk_widget_has_screen (GTK_WIDGET (entry))) {
+ egg_secure_entry_adjust_scroll (entry);
+ egg_secure_entry_queue_draw (entry);
+
+ update_im_cursor_location (entry);
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+static void
+egg_secure_entry_recompute (EggSecureEntry *entry)
+{
+ egg_secure_entry_reset_layout (entry);
+ egg_secure_entry_check_cursor_blink (entry);
+
+ if (!entry->recompute_idle) {
+ entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
+ recompute_idle_func, entry, NULL);
+ }
+}
+
+static gunichar
+build_string (EggSecureEntry *entry, GString *str, gint extra)
+{
+ gint i, count, char_len;
+ gunichar invisible_char;
+ gchar buf[7];
+
+ if (entry->visibility) {
+ g_string_append_len (str, entry->text, entry->n_bytes);
+ return 0;
+
+ } else {
+
+ if (entry->invisible_char != 0)
+ invisible_char = entry->invisible_char;
+ else
+ invisible_char = ' '; /* just pick a char */
+
+ count = g_utf8_strlen (entry->text, entry->n_bytes) + extra;
+
+ char_len = g_unichar_to_utf8 (entry->invisible_char, buf);
+ for (i = 0; i < count; i++)
+ g_string_append_len(str, buf, char_len);
+
+ return invisible_char;
+ }
+}
+
+static PangoLayout *
+egg_secure_entry_create_layout (EggSecureEntry * entry, gboolean include_preedit)
+{
+ GtkWidget *widget = GTK_WIDGET (entry);
+ PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL);
+ PangoAttrList *tmp_attrs = pango_attr_list_new ();
+
+ gchar *preedit_string = NULL;
+ gint preedit_length = 0;
+ PangoAttrList *preedit_attrs = NULL;
+
+ pango_layout_set_single_paragraph_mode (layout, TRUE);
+
+ if (include_preedit) {
+ gtk_im_context_get_preedit_string(entry->im_context, &preedit_string,
+ &preedit_attrs, NULL);
+ preedit_length = entry->preedit_length;
+ }
+
+ if (preedit_length) {
+ GString *tmp_string = g_string_new(NULL);
+
+ gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) -
+ entry->text;
+
+ gint preedit_len_chars;
+ gunichar invisible_char;
+
+ preedit_len_chars = g_utf8_strlen (preedit_string, -1);
+ invisible_char = build_string (entry, tmp_string, preedit_len_chars);
+
+ /*
+ * Fix cursor index to point to invisible char corresponding
+ * to the preedit, fix preedit_length to be the length of
+ * the invisible chars representing the preedit
+ */
+ cursor_index =
+ g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) - tmp_string->str;
+ preedit_length = preedit_len_chars * g_unichar_to_utf8 (invisible_char, NULL);
+
+ pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
+
+ pango_attr_list_splice (tmp_attrs, preedit_attrs, cursor_index, preedit_length);
+
+ g_string_free (tmp_string, TRUE);
+ } else {
+ PangoDirection pango_dir;
+
+ pango_dir = pango_find_base_dir (entry->text, entry->n_bytes);
+ if (pango_dir == PANGO_DIRECTION_NEUTRAL) {
+ if (GTK_WIDGET_HAS_FOCUS (widget)) {
+ GdkDisplay *display = gtk_widget_get_display (widget);
+ GdkKeymap *keymap = gdk_keymap_get_for_display (display);
+ pango_dir = gdk_keymap_get_direction (keymap);
+ } else {
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ pango_dir = PANGO_DIRECTION_LTR;
+ else
+ pango_dir = PANGO_DIRECTION_RTL;
+ }
+ }
+
+ pango_context_set_base_dir (gtk_widget_get_pango_context(widget), pango_dir);
+
+ pango_layout_set_alignment (layout, pango_dir);
+
+ entry->resolved_dir = pango_dir;
+
+ {
+ GString *str = g_string_new (NULL);
+ build_string (entry, str, 0);
+ pango_layout_set_text (layout, str->str, str->len);
+ g_string_free (str, TRUE);
+ }
+ }
+
+ pango_layout_set_attributes (layout, tmp_attrs);
+
+ if (preedit_string)
+ g_free (preedit_string);
+ if (preedit_attrs)
+ pango_attr_list_unref (preedit_attrs);
+
+ pango_attr_list_unref (tmp_attrs);
+
+ return layout;
+}
+
+static PangoLayout *
+egg_secure_entry_ensure_layout (EggSecureEntry *entry, gboolean include_preedit)
+{
+ if (entry->preedit_length > 0 && !include_preedit != !entry->cache_includes_preedit)
+ egg_secure_entry_reset_layout (entry);
+
+ if (!entry->cached_layout) {
+ entry->cached_layout = egg_secure_entry_create_layout (entry, include_preedit);
+ entry->cache_includes_preedit = include_preedit;
+ }
+
+ return entry->cached_layout;
+}
+
+static void
+get_layout_position (EggSecureEntry *entry, gint *x, gint *y)
+{
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ gint area_width, area_height;
+ gint y_pos;
+ PangoLayoutLine *line;
+
+ layout = egg_secure_entry_ensure_layout (entry, TRUE);
+
+ get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
+
+ area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
+
+ line = pango_layout_get_lines (layout)->data;
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+
+ /* Align primarily for locale's ascent/descent */
+ y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
+ entry->ascent + logical_rect.y);
+
+ /* Now see if we need to adjust to fit in actual drawn string */
+ if (logical_rect.height > area_height)
+ y_pos = (area_height - logical_rect.height) / 2;
+ else if (y_pos < 0)
+ y_pos = 0;
+ else if (y_pos + logical_rect.height > area_height)
+ y_pos = area_height - logical_rect.height;
+
+ y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
+
+ if (x)
+ *x = INNER_BORDER - entry->scroll_offset;
+
+ if (y)
+ *y = y_pos;
+}
+
+static void
+egg_secure_entry_draw_text(EggSecureEntry *entry)
+{
+ GtkWidget *widget;
+ PangoLayoutLine *line;
+
+ if (entry->invisible_char == 0)
+ return;
+
+ if (GTK_WIDGET_DRAWABLE (entry)) {
+ PangoLayout *layout = egg_secure_entry_ensure_layout (entry, TRUE);
+ gint x, y;
+ gint start_pos, end_pos;
+
+ widget = GTK_WIDGET(entry);
+
+ get_layout_position(entry, &x, &y);
+
+ gdk_draw_layout(entry->text_area, widget->style->text_gc[widget->state], x, y, layout);
+
+ if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos)) {
+ gint *ranges;
+ gint n_ranges, i;
+ PangoRectangle logical_rect;
+ const gchar *text = pango_layout_get_text (layout);
+ gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
+ gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
+ GdkRegion *clip_region = gdk_region_new ();
+ GdkGC *text_gc;
+ GdkGC *selection_gc;
+
+ line = pango_layout_get_lines(layout)->data;
+
+ pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+ pango_layout_get_extents(layout, NULL, &logical_rect);
+
+ if (GTK_WIDGET_HAS_FOCUS(entry)) {
+ selection_gc = widget->style->base_gc[GTK_STATE_SELECTED];
+ text_gc = widget->style->text_gc[GTK_STATE_SELECTED];
+ } else {
+ selection_gc = widget->style->base_gc[GTK_STATE_ACTIVE];
+ text_gc = widget->style->text_gc[GTK_STATE_ACTIVE];
+ }
+
+ for (i = 0; i < n_ranges; i++) {
+ GdkRectangle rect;
+
+ rect.x = INNER_BORDER - entry->scroll_offset +
+ ranges[2 * i] / PANGO_SCALE;
+ rect.y = y;
+ rect.width = (ranges[2 * i + 1] - ranges[2 * i]) / PANGO_SCALE;
+ rect.height = logical_rect.height / PANGO_SCALE;
+
+ gdk_draw_rectangle (entry->text_area, selection_gc, TRUE, rect.x,
+ rect.y, rect.width, rect.height);
+
+ gdk_region_union_with_rect(clip_region, &rect);
+ }
+
+ gdk_gc_set_clip_region(text_gc, clip_region);
+ gdk_draw_layout(entry->text_area, text_gc, x, y, layout);
+ gdk_gc_set_clip_region(text_gc, NULL);
+
+ gdk_region_destroy(clip_region);
+ g_free(ranges);
+ }
+ }
+}
+
+static void
+draw_insertion_cursor (EggSecureEntry *entry, GdkRectangle *cursor_location,
+ gboolean is_primary, PangoDirection direction, gboolean draw_arrow)
+{
+ GtkWidget *widget = GTK_WIDGET (entry);
+ GtkTextDirection text_dir;
+
+ if (direction == PANGO_DIRECTION_LTR)
+ text_dir = GTK_TEXT_DIR_LTR;
+ else
+ text_dir = GTK_TEXT_DIR_RTL;
+
+ gtk_draw_insertion_cursor (widget, entry->text_area, NULL, cursor_location,
+ is_primary, text_dir, draw_arrow);
+}
+
+static void
+egg_secure_entry_draw_cursor (EggSecureEntry * entry)
+{
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET(entry)));
+ PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
+
+ if (GTK_WIDGET_DRAWABLE (entry)) {
+ GtkWidget *widget = GTK_WIDGET(entry);
+ GdkRectangle cursor_location;
+ gboolean split_cursor;
+
+ gint xoffset = INNER_BORDER - entry->scroll_offset;
+ gint strong_x, weak_x;
+ gint text_area_height;
+ PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
+ PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
+ gint x1 = 0;
+ gint x2 = 0;
+
+ gdk_drawable_get_size (entry->text_area, NULL, &text_area_height);
+
+ egg_secure_entry_get_cursor_locations (entry, &strong_x, &weak_x);
+
+ g_object_get (gtk_widget_get_settings (widget), "gtk-split-cursor", &split_cursor, NULL);
+
+ dir1 = entry->resolved_dir;
+
+ if (split_cursor) {
+ x1 = strong_x;
+
+ if (weak_x != strong_x) {
+ dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ?
+ PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
+ x2 = weak_x;
+ }
+ } else {
+ if (keymap_direction == entry->resolved_dir)
+ x1 = strong_x;
+ else
+ x1 = weak_x;
+ }
+
+ cursor_location.x = xoffset + x1;
+ cursor_location.y = INNER_BORDER;
+ cursor_location.width = 0;
+ cursor_location.height = text_area_height - 2 * INNER_BORDER;
+
+ draw_insertion_cursor (entry, &cursor_location, TRUE, dir1,
+ dir2 != PANGO_DIRECTION_NEUTRAL);
+
+ if (dir2 != PANGO_DIRECTION_NEUTRAL) {
+ cursor_location.x = xoffset + x2;
+ draw_insertion_cursor(entry, &cursor_location, FALSE, dir2, TRUE);
+ }
+ }
+}
+
+static void
+egg_secure_entry_queue_draw (EggSecureEntry *entry)
+{
+ if (GTK_WIDGET_REALIZED (entry))
+ gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
+}
+
+static void
+egg_secure_entry_reset_im_context (EggSecureEntry *entry)
+{
+ if (entry->need_im_reset) {
+ entry->need_im_reset = 0;
+ gtk_im_context_reset (entry->im_context);
+ }
+}
+
+static gint
+egg_secure_entry_find_position (EggSecureEntry *entry, gint x)
+{
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+ gint _index;
+ gint pos;
+ gboolean trailing;
+ const gchar *text;
+ gint cursor_index;
+
+ layout = egg_secure_entry_ensure_layout (entry, TRUE);
+ text = pango_layout_get_text (layout);
+ cursor_index = g_utf8_offset_to_pointer (text, entry->current_pos) - text;
+
+ line = pango_layout_get_lines (layout)->data;
+ pango_layout_line_x_to_index (line, x * PANGO_SCALE, &_index, &trailing);
+
+ if (_index >= cursor_index && entry->preedit_length) {
+ if (_index >= cursor_index + entry->preedit_length)
+ _index -= entry->preedit_length;
+ else {
+ _index = cursor_index;
+ trailing = 0;
+ }
+ }
+
+ pos = g_utf8_pointer_to_offset (text, text + _index);
+ pos += trailing;
+
+ return pos;
+}
+
+static void
+egg_secure_entry_get_cursor_locations (EggSecureEntry *entry,
+ gint *strong_x, gint *weak_x)
+{
+ if (!entry->invisible_char) {
+ if (strong_x)
+ *strong_x = 0;
+ if (weak_x)
+ *weak_x = 0;
+ } else {
+ PangoLayout *layout = egg_secure_entry_ensure_layout (entry, TRUE);
+ const gchar *text = pango_layout_get_text (layout);
+ PangoRectangle strong_pos, weak_pos;
+ gint _index;
+
+ _index = g_utf8_offset_to_pointer (text, entry->current_pos +
+ entry->preedit_cursor) - text;
+
+ pango_layout_get_cursor_pos (layout, _index, &strong_pos, &weak_pos);
+
+ if (strong_x)
+ *strong_x = strong_pos.x / PANGO_SCALE;
+
+ if (weak_x)
+ *weak_x = weak_pos.x / PANGO_SCALE;
+ }
+}
+
+static void
+egg_secure_entry_adjust_scroll (EggSecureEntry *entry)
+{
+ gint min_offset, max_offset;
+ gint text_area_width, text_width;
+ gint strong_x, weak_x;
+ gint strong_xoffset, weak_xoffset;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+ PangoRectangle logical_rect;
+
+ if (!GTK_WIDGET_REALIZED(entry))
+ return;
+
+ gdk_drawable_get_size (entry->text_area, &text_area_width, NULL);
+ text_area_width -= 2 * INNER_BORDER;
+
+ layout = egg_secure_entry_ensure_layout (entry, TRUE);
+ line = pango_layout_get_lines (layout)->data;
+
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+
+ /* Display as much text as we can */
+
+ text_width = PANGO_PIXELS (logical_rect.width);
+
+ if (text_width > text_area_width) {
+ min_offset = 0;
+ max_offset = text_width - text_area_width;
+ } else {
+ min_offset = 0;
+ max_offset = min_offset;
+ }
+
+ entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
+
+ /*
+ * And make sure cursors are on screen. Note that the cursor is
+ * actually drawn one pixel into the INNER_BORDER space on
+ * the right, when the scroll is at the utmost right. This
+ * looks better to to me than confining the cursor inside the
+ * border entirely, though it means that the cursor gets one
+ * pixel closer to the the edge of the widget on the right than
+ * on the left. This might need changing if one changed
+ * INNER_BORDER from 2 to 1, as one would do on a
+ * small-screen-real-estate display.
+ *
+ * We always make sure that the strong cursor is on screen, and
+ * put the weak cursor on screen if possible.
+ */
+
+ egg_secure_entry_get_cursor_locations (entry, &strong_x, &weak_x);
+
+ strong_xoffset = strong_x - entry->scroll_offset;
+
+ if (strong_xoffset < 0) {
+ entry->scroll_offset += strong_xoffset;
+ strong_xoffset = 0;
+ } else if (strong_xoffset > text_area_width) {
+ entry->scroll_offset += strong_xoffset - text_area_width;
+ strong_xoffset = text_area_width;
+ }
+
+ weak_xoffset = weak_x - entry->scroll_offset;
+
+ if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width) {
+ entry->scroll_offset += weak_xoffset;
+ } else if (weak_xoffset > text_area_width &&
+ strong_xoffset - (weak_xoffset - text_area_width) >= 0) {
+ entry->scroll_offset += weak_xoffset - text_area_width;
+ }
+
+ g_object_notify (G_OBJECT (entry), "scroll_offset");
+}
+
+static gint
+egg_secure_entry_move_visually (EggSecureEntry * entry,
+ gint start, gint count)
+{
+ gint _index;
+ PangoLayout *layout = egg_secure_entry_ensure_layout (entry, FALSE);
+ const gchar *text;
+
+ text = pango_layout_get_text (layout);
+
+ _index = g_utf8_offset_to_pointer (text, start) - text;
+
+ while (count != 0) {
+ int new_index, new_trailing;
+ gboolean split_cursor;
+ gboolean strong;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
+ "gtk-split-cursor", &split_cursor, NULL);
+
+ if (split_cursor)
+ strong = TRUE;
+ else {
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry)));
+ PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
+
+ strong = keymap_direction == entry->resolved_dir;
+ }
+
+ if (count > 0) {
+ pango_layout_move_cursor_visually (layout, strong, _index, 0, 1,
+ &new_index, &new_trailing);
+ count--;
+ } else {
+ pango_layout_move_cursor_visually (layout, strong, _index, 0, -1,
+ &new_index, &new_trailing);
+ count++;
+ }
+
+ if (new_index < 0 || new_index == G_MAXINT)
+ break;
+
+ _index = new_index;
+
+ while (new_trailing--)
+ _index = g_utf8_next_char (text + new_index) - text;
+ }
+
+ return g_utf8_pointer_to_offset (text, text + _index);
+}
+
+static gint
+egg_secure_entry_move_logically (EggSecureEntry *entry,
+ gint start, gint count)
+{
+ gint new_pos = start;
+
+ /* Prevent any leak of information */
+ new_pos = CLAMP (start + count, 0, entry->text_length);
+ return new_pos;
+}
+
+/* Public API */
+
+GtkWidget *
+egg_secure_entry_new (void)
+{
+ return g_object_new (EGG_TYPE_SECURE_ENTRY, NULL);
+}
+
+void
+egg_secure_entry_set_text (EggSecureEntry *entry, const gchar *text)
+{
+ gint tmp_pos;
+
+ g_return_if_fail (EGG_IS_SECURE_ENTRY(entry));
+ g_return_if_fail (text != NULL);
+
+ /*
+ * Actually setting the text will affect the cursor and selection;
+ * if the contents don't actually change, this will look odd to the user.
+ */
+ if (strcmp (entry->text, text) == 0)
+ return;
+
+ gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
+
+ tmp_pos = 0;
+ gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
+}
+
+void
+egg_secure_entry_reset_changed (EggSecureEntry *entry)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+ entry->changed = FALSE;
+}
+
+gboolean
+egg_secure_entry_get_changed (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
+ return entry->changed;
+}
+
+void
+egg_secure_entry_set_visibility (EggSecureEntry *entry, gboolean setting)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+
+ if (setting == entry->visibility)
+ return;
+
+ entry->visibility = setting;
+ g_object_notify (G_OBJECT (entry), "visibility");
+ egg_secure_entry_recompute (entry);
+}
+
+gboolean
+egg_secure_entry_get_visibility (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
+ return entry->visibility;
+}
+
+
+/**
+ * egg_secure_entry_set_invisible_char:
+ * @entry: a #EggSecureEntry
+ * @ch: a Unicode character
+ *
+ * Sets the character to use in place of the actual text when
+ * egg_secure_entry_set_visibility() has been called to set text
+ * to %FALSE. i.e. this is the character used in "password mode" to
+ * show the user how many characters have been typed. The default
+ * invisible char is an asterisk ('*'). If you set the invisible char
+ * to 0, then the user will get no feedback at all; there will be
+ * no text on the screen as they type.
+ *
+ **/
+void
+egg_secure_entry_set_invisible_char (EggSecureEntry *entry, gunichar ch)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+
+ if (ch == entry->invisible_char)
+ return;
+
+ entry->invisible_char = ch;
+ g_object_notify (G_OBJECT (entry), "invisible_char");
+ egg_secure_entry_recompute (entry);
+}
+
+/**
+ * egg_secure_entry_get_invisible_char:
+ * @entry: a #EggSecureEntry
+ *
+ * Retrieves the character displayed in place of the real characters
+ * for entries with visisbility set to false. See egg_secure_entry_set_invisible_char().
+ *
+ * Return value: the current invisible char, or 0, if the entry does not
+ * show invisible text at all.
+ **/
+gunichar
+egg_secure_entry_get_invisible_char (EggSecureEntry * entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
+
+ return entry->invisible_char;
+}
+
+/**
+ * egg_secure_entry_get_text:
+ * @entry: a #EggSecureEntry
+ *
+ * Retrieves the contents of the entry widget.
+ * See also gtk_editable_get_chars().
+ *
+ * Return value: a pointer to the contents of the widget as a
+ * string. This string points to internally allocated
+ * storage in the widget and must not be freed, modified or
+ * stored.
+ **/
+G_CONST_RETURN gchar*
+egg_secure_entry_get_text (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), NULL);
+
+ return entry->text;
+}
+
+/**
+ * egg_secure_entry_set_max_length:
+ * @entry: a #EggSecureEntry.
+ * @max: the maximum length of the entry, or 0 for no maximum.
+ * (other than the maximum length of entries.) The value passed in will
+ * be clamped to the range 0-65536.
+ *
+ * Sets the maximum allowed length of the contents of the widget. If
+ * the current contents are longer than the given length, then they
+ * will be truncated to fit.
+ **/
+void
+egg_secure_entry_set_max_length(EggSecureEntry *entry, gint max)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+
+ max = CLAMP (max, 0, MAX_SIZE);
+
+ if (max > 0 && entry->text_length > max)
+ gtk_editable_delete_text (GTK_EDITABLE (entry), max, -1);
+
+ entry->text_max_length = max;
+ g_object_notify (G_OBJECT (entry), "max_length");
+}
+
+/**
+ * egg_secure_entry_get_max_length:
+ * @entry: a #EggSecureEntry
+ *
+ * Retrieves the maximum allowed length of the text in
+ * @entry. See egg_secure_entry_set_max_length().
+ *
+ * Return value: the maximum allowed number of characters
+ * in #EggSecureEntry, or 0 if there is no maximum.
+ **/
+gint
+egg_secure_entry_get_max_length (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
+ return entry->text_max_length;
+}
+
+/**
+ * egg_secure_entry_set_activates_default:
+ * @entry: a #EggSecureEntry
+ * @setting: %TRUE to activate window's default widget on Enter keypress
+ *
+ * If @setting is %TRUE, pressing Enter in the @entry will activate the default
+ * widget for the window containing the entry. This usually means that
+ * the dialog box containing the entry will be closed, since the default
+ * widget is usually one of the dialog buttons.
+ *
+ * (For experts: if @setting is %TRUE, the entry calls
+ * gtk_window_activate_default() on the window containing the entry, in
+ * the default handler for the "activate" signal.)
+ *
+ **/
+void
+egg_secure_entry_set_activates_default (EggSecureEntry *entry,
+ gboolean setting)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+ setting = setting != FALSE;
+
+ if (setting != entry->activates_default) {
+ entry->activates_default = setting;
+ g_object_notify (G_OBJECT (entry), "activates_default");
+ }
+}
+
+/**
+ * egg_secure_entry_get_activates_default:
+ * @entry: a #EggSecureEntry
+ *
+ * Retrieves the value set by egg_secure_entry_set_activates_default().
+ *
+ * Return value: %TRUE if the entry will activate the default widget
+ **/
+gboolean
+egg_secure_entry_get_activates_default (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
+ return entry->activates_default;
+}
+
+/**
+ * egg_secure_entry_set_width_chars:
+ * @entry: a #EggSecureEntry
+ * @n_chars: width in chars
+ *
+ * Changes the size request of the entry to be about the right size
+ * for @n_chars characters. Note that it changes the size
+ * <emphasis>request</emphasis>, the size can still be affected by
+ * how you pack the widget into containers. If @n_chars is -1, the
+ * size reverts to the default entry size.
+ *
+ **/
+void
+egg_secure_entry_set_width_chars (EggSecureEntry *entry, gint n_chars)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+
+ if (entry->width_chars != n_chars) {
+ entry->width_chars = n_chars;
+ g_object_notify (G_OBJECT (entry), "width_chars");
+ gtk_widget_queue_resize (GTK_WIDGET (entry));
+ }
+}
+
+/**
+ * egg_secure_entry_get_width_chars:
+ * @entry: a #EggSecureEntry
+ *
+ * Gets the value set by egg_secure_entry_set_width_chars().
+ *
+ * Return value: number of chars to request space for, or negative if unset
+ **/
+gint
+egg_secure_entry_get_width_chars (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), 0);
+ return entry->width_chars;
+}
+
+/**
+ * egg_secure_entry_set_has_frame:
+ * @entry: a #EggSecureEntry
+ * @setting: new value
+ *
+ * Sets whether the entry has a beveled frame around it.
+ **/
+void
+egg_secure_entry_set_has_frame (EggSecureEntry *entry, gboolean setting)
+{
+ g_return_if_fail (EGG_IS_SECURE_ENTRY (entry));
+
+ setting = (setting != FALSE);
+
+ if (entry->has_frame == setting)
+ return;
+
+ gtk_widget_queue_resize (GTK_WIDGET (entry));
+ entry->has_frame = setting;
+ g_object_notify (G_OBJECT (entry), "has_frame");
+}
+
+/**
+ * egg_secure_entry_get_has_frame:
+ * @entry: a #EggSecureEntry
+ *
+ * Gets the value set by egg_secure_entry_set_has_frame().
+ *
+ * Return value: whether the entry has a beveled frame
+ **/
+gboolean
+egg_secure_entry_get_has_frame (EggSecureEntry *entry)
+{
+ g_return_val_if_fail (EGG_IS_SECURE_ENTRY (entry), FALSE);
+ return entry->has_frame;
+}
+
+static gboolean
+egg_secure_entry_mnemonic_activate (GtkWidget *widget, gboolean group_cycling)
+{
+ gtk_widget_grab_focus (widget);
+ return TRUE;
+}
+
+/* We display the cursor when
+ *
+ * - the selection is empty, AND
+ * - the widget has focus
+ */
+
+#define CURSOR_ON_MULTIPLIER 0.66
+#define CURSOR_OFF_MULTIPLIER 0.34
+#define CURSOR_PEND_MULTIPLIER 1.0
+
+static gboolean
+cursor_blinks (EggSecureEntry *entry)
+{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
+ gboolean blink;
+
+ if (GTK_WIDGET_HAS_FOCUS (entry) &&
+ entry->selection_bound == entry->current_pos) {
+ g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
+ return blink;
+ } else
+ return FALSE;
+}
+
+static gint
+get_cursor_time (EggSecureEntry *entry)
+{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
+ gint time;
+
+ g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
+
+ return time;
+}
+
+static void
+show_cursor (EggSecureEntry *entry)
+{
+ if (!entry->cursor_visible) {
+ entry->cursor_visible = TRUE;
+
+ if (GTK_WIDGET_HAS_FOCUS (entry) &&
+ entry->selection_bound == entry->current_pos)
+ gtk_widget_queue_draw (GTK_WIDGET (entry));
+ }
+}
+
+static void
+hide_cursor(EggSecureEntry * entry)
+{
+ if (entry->cursor_visible) {
+ entry->cursor_visible = FALSE;
+
+ if (GTK_WIDGET_HAS_FOCUS (entry) &&
+ entry->selection_bound == entry->current_pos)
+ gtk_widget_queue_draw (GTK_WIDGET (entry));
+ }
+}
+
+/*
+ * Blink!
+ */
+static gint
+blink_cb (gpointer data)
+{
+ EggSecureEntry *entry;
+
+ GDK_THREADS_ENTER ();
+
+ entry = EGG_SECURE_ENTRY (data);
+
+ if (!GTK_WIDGET_HAS_FOCUS (entry)) {
+ g_warning ("EggSecureEntry - did not receive focus-out-event. If you\n"
+ "connect a handler to this signal, it must return\n"
+ "FALSE so the entry gets the event as well");
+ }
+
+ g_assert (GTK_WIDGET_HAS_FOCUS (entry));
+ g_assert (entry->selection_bound == entry->current_pos);
+
+ if (entry->cursor_visible) {
+ hide_cursor(entry);
+ entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
+ blink_cb, entry);
+ } else {
+ show_cursor(entry);
+ entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
+ blink_cb, entry);
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ /* Remove ourselves */
+ return FALSE;
+}
+
+static void
+egg_secure_entry_check_cursor_blink (EggSecureEntry *entry)
+{
+ if (cursor_blinks (entry)) {
+ if (!entry->blink_timeout) {
+ entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
+ blink_cb, entry);
+ show_cursor (entry);
+ }
+ } else {
+ if (entry->blink_timeout) {
+ g_source_remove (entry->blink_timeout);
+ entry->blink_timeout = 0;
+ }
+
+ entry->cursor_visible = TRUE;
+ }
+
+}
+
+static void
+egg_secure_entry_pend_cursor_blink (EggSecureEntry *entry)
+{
+ if (cursor_blinks (entry)) {
+ if (entry->blink_timeout != 0)
+ g_source_remove(entry->blink_timeout);
+
+ entry->blink_timeout = g_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
+ blink_cb, entry);
+ show_cursor (entry);
+ }
+}
+
+static inline gboolean
+keyval_is_cursor_move (guint keyval)
+{
+ if (keyval == GDK_Up || keyval == GDK_KP_Up)
+ return TRUE;
+
+ if (keyval == GDK_Down || keyval == GDK_KP_Down)
+ return TRUE;
+
+ if (keyval == GDK_Page_Up)
+ return TRUE;
+
+ if (keyval == GDK_Page_Down)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* stolen from gtkmarshalers.c */
+
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_int
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+
+/* VOID:ENUM,INT,BOOLEAN (gtkmarshalers.list:64) */
+static void
+_gtk_marshal_VOID__ENUM_INT_BOOLEAN(GClosure * closure, GValue * return_value,
+ guint n_param_values, const GValue * param_values,
+ gpointer invocation_hint, gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gboolean arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback;
+ register GCClosure *cc = (GCClosure *) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail(n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA(closure)) {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer(param_values + 0);
+ } else {
+ data1 = g_value_peek_pointer(param_values + 0);
+ data2 = closure->data;
+ }
+ callback =
+ (GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data
+ : cc->callback);
+
+ callback(data1,
+ g_marshal_value_peek_enum(param_values + 1),
+ g_marshal_value_peek_int(param_values + 2),
+ g_marshal_value_peek_boolean(param_values + 3), data2);
+}
+
+static void
+_gtk_marshal_VOID__ENUM_INT(GClosure * closure, GValue * return_value,
+ guint n_param_values, const GValue * param_values,
+ gpointer invocation_hint, gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__ENUM_INT callback;
+ register GCClosure *cc = (GCClosure *) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail(n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA(closure)) {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer(param_values + 0);
+ } else {
+ data1 = g_value_peek_pointer(param_values + 0);
+ data2 = closure->data;
+ }
+ callback =
+ (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->
+ callback);
+
+ callback(data1,
+ g_marshal_value_peek_enum(param_values + 1),
+ g_marshal_value_peek_int(param_values + 2), data2);
+}
diff --git a/egg/egg-secure-entry.h b/egg/egg-secure-entry.h
new file mode 100644
index 0000000..35bf8d4
--- /dev/null
+++ b/egg/egg-secure-entry.h
@@ -0,0 +1,187 @@
+/*
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Heavily stripped down for use in pinentry-gtk-2 by Albrecht Dreß
+ * <albrecht.dress@arcor.de> Feb. 2004.
+ *
+ * (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
+ * Public License.
+ *
+ * The entry is now always invisible, uses secure memory methods to
+ * allocate the text memory, and all potentially dangerous methods
+ * (copy & paste, popup, etc.) have been removed.
+ */
+
+/*
+ * Modified for inclusion into gnome-keyring by Stef Walter
+ */
+
+#ifndef __EGG_SECURE_ENTRY_H__
+#define __EGG_SECURE_ENTRY_H__
+
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EGG_TYPE_SECURE_ENTRY (egg_secure_entry_get_type ())
+#define EGG_SECURE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SECURE_ENTRY, EggSecureEntry))
+#define EGG_SECURE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SECURE_ENTRY, EggSecureEntryClass))
+#define EGG_IS_SECURE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SECURE_ENTRY))
+#define EGG_IS_SECURE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SECURE_ENTRY))
+#define EGG_SECURE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SECURE_ENTRY, EggSecureEntryClass))
+
+typedef struct _EggSecureEntry EggSecureEntry;
+typedef struct _EggSecureEntryClass EggSecureEntryClass;
+
+struct _EggSecureEntry {
+ GtkWidget widget;
+
+ gchar *text;
+
+ guint overwrite_mode : 1;
+
+ /* length in use, in chars */
+ guint16 text_length;
+ guint16 text_max_length;
+
+ /*< private > */
+ GdkWindow *text_area;
+ GtkIMContext *im_context;
+
+ gint current_pos;
+ gint selection_bound;
+
+ PangoLayout *cached_layout;
+ guint cache_includes_preedit : 1;
+
+ guint need_im_reset : 1;
+
+ guint has_frame : 1;
+
+ guint activates_default : 1;
+
+ guint cursor_visible : 1;
+
+ /* Flag so we don't select all when clicking in entry to focus in */
+ guint in_click : 1;
+
+ /* Only used by GtkCellRendererText */
+ guint is_cell_renderer : 1;
+ guint editing_canceled : 1;
+
+ guint mouse_cursor_obscured : 1;
+
+ /* PangoDirection */
+ guint resolved_dir : 4;
+
+ guint button;
+ guint blink_timeout;
+ guint recompute_idle;
+ gint scroll_offset;
+ gint ascent; /* font ascent, in pango units */
+ gint descent; /* font descent, in pango units */
+
+ guint16 text_size; /* allocated size, in bytes */
+ guint16 n_bytes; /* length in use, in bytes */
+
+ guint16 preedit_length; /* length of preedit string, in bytes */
+ guint16 preedit_cursor; /* offset of cursor within preedit string, in chars */
+
+ gunichar invisible_char;
+ gint width_chars;
+
+ gboolean visibility;
+
+ /* Keeps track of whether changed between resets */
+ gboolean changed;
+};
+
+struct _EggSecureEntryClass {
+ GtkWidgetClass parent_class;
+
+ /* Action signals */
+ void (*activate) (EggSecureEntry *entry);
+ void (*move_cursor) (EggSecureEntry *entry, GtkMovementStep step,
+ gint count, gboolean extend_selection);
+ void (*insert_at_cursor) (EggSecureEntry *entry, const gchar *str);
+ void (*delete_from_cursor) (EggSecureEntry *entry, GtkDeleteType type, gint count);
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+GType egg_secure_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget* egg_secure_entry_new (void);
+
+void egg_secure_entry_reset_changed (EggSecureEntry *entry);
+
+gboolean egg_secure_entry_get_changed (EggSecureEntry *entry);
+
+void egg_secure_entry_set_visibility (EggSecureEntry *entry, gboolean setting);
+
+gboolean egg_secure_entry_get_visibility (EggSecureEntry *entry);
+
+void egg_secure_entry_set_invisible_char (EggSecureEntry *entry, gunichar ch);
+
+gunichar egg_secure_entry_get_invisible_char (EggSecureEntry *entry);
+
+void egg_secure_entry_set_has_frame (EggSecureEntry *entry, gboolean setting);
+
+gboolean egg_secure_entry_get_has_frame (EggSecureEntry *entry);
+
+/* text is truncated if needed */
+void egg_secure_entry_set_max_length (EggSecureEntry *entry, gint max);
+
+gint egg_secure_entry_get_max_length (EggSecureEntry *entry);
+
+void egg_secure_entry_set_activates_default (EggSecureEntry *entry, gboolean setting);
+
+gboolean egg_secure_entry_get_activates_default (EggSecureEntry *entry);
+
+void egg_secure_entry_set_width_chars (EggSecureEntry *entry, gint n_chars);
+
+gint egg_secure_entry_get_width_chars (EggSecureEntry *entry);
+
+/* Somewhat more convenient than the GtkEditable generic functions */
+void egg_secure_entry_set_text (EggSecureEntry *entry, const gchar *text);
+
+/* returns a reference to the text */
+const gchar* egg_secure_entry_get_text (EggSecureEntry *entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__EGG_SECURE_ENTRY_H__ */
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index b1cef7d..0edd1de 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -1,12 +1,24 @@
-incdir = $(includedir)/gcr
+# ------------------------------------------------------------------
+# UI BUILDER
+#
-inc_HEADERS = \
- gcr-parser.h \
- gcr-types.h
+uidir = $(datadir)/gcr/ui/
+
+GLADE_FILES = \
+ gcr-import-dialog.glade
+
+.glade.ui:
+ gtk-builder-convert --skip-windows $< $@
+
+ui_DATA = $(GLADE_FILES:.glade=.ui)
+
+# ------------------------------------------------------------------
+# LIBRARY
INCLUDES = \
-I$(top_builddir) \
-I$(top_srcdir) \
+ $(GTK_CFLAGS) \
$(GOBJECT_CFLAGS) \
$(GLIB_CFLAGS)
@@ -16,17 +28,26 @@ BUILT_SOURCES = \
lib_LTLIBRARIES = libgcr.la
libgcr_la_SOURCES = \
- gcr-internal.c gcr-internal.h \
+ gcr-import-dialog.c gcr-import-dialog.h \
+ gcr-importer.c gcr-importer.h \
+ gcr-internal.h \
+ gcr-library.c \
gcr-parser.c gcr-parser.h \
gcr-types.h \
$(BUILT_SOURCES)
+libgcr_la_CFLAGS = \
+ -DPKCS11_MODULE_PATH=\""$(libdir)/gnome-keyring/gnome-keyring-pkcs11.so"\" \
+ -DGCR_API_SUBJECT_TO_CHANGE \
+ -DUIDIR=\""$(uidir)"\"
+
libgcr_la_LDFLAGS = \
-version-info $(GCR_LT_RELEASE) \
-no-undefined -export-symbols-regex 'gcr_*'
libgcr_la_LIBADD = \
$(top_builddir)/egg/libegg.la \
+ $(top_builddir)/egg/libegg-secure-entry.la \
$(top_builddir)/gp11/libgp11.la \
$(GOBJECT_LIBS) \
$(GLIB_LIBS)
@@ -41,16 +62,24 @@ gcr-marshal.c: gcr-marshal.list $(GLIB_GENMARSHAL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = gcr-$(GCR_MAJOR).pc
+gcr-$(GCR_MAJOR).pc: gcr.pc
+ cp gcr.pc gcr-$(GCR_MAJOR).pc
+
+# ----------------------------------------------------------------
+
EXTRA_DIST = \
gcr.pc.in \
gcr-marshal.list \
- gcr-import-dialog.glade
+ gcr-import-dialog.glade \
+ $(GLADE_FILES)
-DISTCLEANFILES = \
- gcr-$(GCR_MAJOR).pc
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ $(ui_DATA) \
+ $(pkgconfig_DATA)
-gcr-$(GCR_MAJOR).pc: gcr.pc
- cp gcr.pc gcr-$(GCR_MAJOR).pc
+DISTCLEANFILES = \
+ $(pkgconfig_DATA)
if WITH_TESTS
TESTS_DIR = tests
@@ -60,4 +89,3 @@ endif
SUBDIRS = . \
$(TESTS_DIR)
-
diff --git a/gcr/gcr-import-dialog.c b/gcr/gcr-import-dialog.c
new file mode 100644
index 0000000..b56b883
--- /dev/null
+++ b/gcr/gcr-import-dialog.c
@@ -0,0 +1,452 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-import-dialog.h"
+#include "gcr-internal.h"
+
+#include "egg/egg-secure-entry.h"
+
+enum {
+ PROP_0,
+ PROP_SELECTED_SLOT,
+ PROP_PASSWORD,
+ PROP_PRIMARY_TEXT,
+ PROP_SECONDARY_TEXT
+};
+
+enum {
+ COLUMN_SLOT,
+ COLUMN_ICON,
+ COLUMN_LABEL,
+ N_COLUMNS
+};
+
+struct _GcrImportDialogPrivate {
+ GtkBuilder *builder;
+ EggSecureEntry *entry;
+ GtkComboBox *combo;
+ GtkListStore *slots;
+};
+
+G_DEFINE_TYPE (GcrImportDialog, _gcr_import_dialog, GTK_TYPE_DIALOG);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+populate_slots (GcrImportDialog *self)
+{
+ GList *modules, *m;
+ GList *slots, *s;
+ GtkTreeIter iter;
+ GP11TokenInfo *info;
+ gboolean added;
+
+ g_assert (GCR_IS_IMPORT_DIALOG (self));
+
+ if (self->pv->slots)
+ return;
+
+ self->pv->slots = gtk_list_store_new (N_COLUMNS, GP11_TYPE_SLOT, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_combo_box_set_model (self->pv->combo, GTK_TREE_MODEL (self->pv->slots));
+
+ modules = _gcr_get_pkcs11_modules ();
+ g_return_if_fail (modules);
+
+ gtk_list_store_clear (self->pv->slots);
+
+ added = FALSE;
+ for (m = modules; m; m = g_list_next (m)) {
+
+ g_return_if_fail (GP11_IS_MODULE (m->data));
+ slots = gp11_module_get_slots (m->data, TRUE);
+
+ for (s = slots; s; s = g_list_next (s)) {
+ info = gp11_slot_get_token_info (s->data);
+ if (!(info->flags & CKF_WRITE_PROTECTED)) {
+ gtk_list_store_append (self->pv->slots, &iter);
+ gtk_list_store_set (self->pv->slots, &iter,
+ COLUMN_LABEL, info->label,
+ COLUMN_SLOT, s->data,
+ -1);
+ added = TRUE;
+ }
+ }
+
+ gp11_list_unref_free (slots);
+ }
+
+ if (added)
+ gtk_combo_box_set_active (self->pv->combo, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gcr_import_dialog_real_realize (GtkWidget *base)
+{
+ GcrImportDialog *self = GCR_IMPORT_DIALOG (base);
+ if (GTK_WIDGET_VISIBLE (self->pv->combo))
+ populate_slots (self);
+ GTK_WIDGET_CLASS (_gcr_import_dialog_parent_class)->realize (base);
+}
+
+static GObject*
+gcr_import_dialog_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GcrImportDialog *self = GCR_IMPORT_DIALOG (G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->constructor(type, n_props, props));
+ GtkCellRenderer *renderer;
+ GtkWidget *widget;
+
+ g_return_val_if_fail (self, NULL);
+
+ if (!gtk_builder_add_from_file (self->pv->builder, UIDIR "gcr-import-dialog.ui", NULL))
+ g_return_val_if_reached (NULL);
+
+ /* Fill in the dialog from builder */
+ widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "import-dialog"));
+ g_return_val_if_fail (widget, FALSE);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (self)->vbox), widget);
+
+ /* Add a secure entry */
+ self->pv->entry = EGG_SECURE_ENTRY (egg_secure_entry_new ());
+ widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area"));
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (self->pv->entry));
+ gtk_widget_show (GTK_WIDGET (self->pv->entry));
+
+ /* Initialize the combo box */
+ self->pv->combo = GTK_COMBO_BOX (gtk_builder_get_object (self->pv->builder, "slot-combo"));
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->pv->combo), renderer, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->pv->combo), renderer, "icon-name", COLUMN_ICON);
+ g_object_set (renderer, "xpad", 3, NULL);
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->pv->combo), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->pv->combo), renderer, "text", COLUMN_LABEL);
+
+ /* Add our various buttons */
+ gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+ return G_OBJECT (self);
+}
+
+static void
+_gcr_import_dialog_init (GcrImportDialog *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORT_DIALOG, GcrImportDialogPrivate);
+ self->pv->builder = gtk_builder_new ();
+}
+
+static void
+gcr_import_dialog_dispose (GObject *obj)
+{
+ G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->dispose (obj);
+}
+
+static void
+gcr_import_dialog_finalize (GObject *obj)
+{
+ GcrImportDialog *self = GCR_IMPORT_DIALOG (obj);
+
+ g_object_unref (self->pv->slots);
+ self->pv->slots = NULL;
+
+ g_object_unref (self->pv->builder);
+ self->pv->builder = NULL;
+
+ G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->finalize (obj);
+}
+
+static void
+gcr_import_dialog_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrImportDialog *self = GCR_IMPORT_DIALOG (obj);
+
+ switch (prop_id) {
+ case PROP_SELECTED_SLOT:
+ _gcr_import_dialog_set_selected_slot (self, g_value_get_object (value));
+ break;
+ case PROP_PASSWORD:
+ _gcr_import_dialog_set_password (self, g_value_get_pointer (value));
+ break;
+ case PROP_PRIMARY_TEXT:
+ _gcr_import_dialog_set_primary_text (self, g_value_get_string (value));
+ break;
+ case PROP_SECONDARY_TEXT:
+ _gcr_import_dialog_set_secondary_text (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_import_dialog_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrImportDialog *self = GCR_IMPORT_DIALOG (obj);
+
+ switch (prop_id) {
+ case PROP_SELECTED_SLOT:
+ g_value_set_object (value, _gcr_import_dialog_get_selected_slot (self));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_pointer (value, (gpointer)_gcr_import_dialog_get_password (self));
+ break;
+ case PROP_PRIMARY_TEXT:
+ g_value_set_string (value, _gcr_import_dialog_get_primary_text (self));
+ break;
+ case PROP_SECONDARY_TEXT:
+ g_value_set_string (value, _gcr_import_dialog_get_secondary_text (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+_gcr_import_dialog_class_init (GcrImportDialogClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->constructor = gcr_import_dialog_constructor;
+ gobject_class->dispose = gcr_import_dialog_dispose;
+ gobject_class->finalize = gcr_import_dialog_finalize;
+ gobject_class->set_property = gcr_import_dialog_set_property;
+ gobject_class->get_property = gcr_import_dialog_get_property;
+
+ widget_class->realize = gcr_import_dialog_real_realize;
+
+ g_type_class_add_private (gobject_class, sizeof (GcrImportDialogPrivate));
+
+ g_object_class_install_property (gobject_class, PROP_SELECTED_SLOT,
+ g_param_spec_object ("selected-slot", "Selected Slot", "Selected PKCS#11 slot",
+ GP11_TYPE_SLOT, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PASSWORD,
+ g_param_spec_pointer ("password", "Password", "Pointer to password",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PRIMARY_TEXT,
+ g_param_spec_string ("primary-text", "Primary Text", "Primary dialog text",
+ NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_SECONDARY_TEXT,
+ g_param_spec_string ("secondary-text", "Secondary Text", "Dialog secondary text",
+ NULL, G_PARAM_READWRITE));
+
+ _gcr_initialize ();
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrImportDialog*
+_gcr_import_dialog_new (void)
+{
+ GcrImportDialog *dialog = g_object_new (GCR_TYPE_IMPORT_DIALOG, NULL);
+ return g_object_ref_sink (dialog);
+}
+
+gboolean
+_gcr_import_dialog_run (GcrImportDialog *self, GtkWindow *parent)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), FALSE);
+
+ if (parent != NULL)
+ gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+
+ ret = (gtk_dialog_run (GTK_DIALOG (self)) == GTK_RESPONSE_OK);
+
+ if (parent != NULL)
+ gtk_window_set_transient_for (GTK_WINDOW (self), NULL);
+
+ gtk_widget_hide (GTK_WIDGET (self));
+ return ret;
+}
+
+GP11Slot*
+_gcr_import_dialog_get_selected_slot (GcrImportDialog *self)
+{
+ GtkTreeIter iter;
+ GP11Slot *slot;
+
+ g_return_val_if_fail (GCR_IMPORT_DIALOG (self), NULL);
+
+ if (GTK_WIDGET_VISIBLE (self->pv->combo))
+ populate_slots (self);
+ else
+ return NULL;
+
+ if (!gtk_combo_box_get_active_iter (self->pv->combo, &iter))
+ return NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (self->pv->slots), &iter, COLUMN_SLOT, &slot, -1);
+
+ /* We hold the reference to this */
+ if (slot != NULL)
+ g_object_unref (slot);
+
+ return slot;
+}
+
+void
+_gcr_import_dialog_set_selected_slot (GcrImportDialog *self, GP11Slot *slot)
+{
+ GtkTreeIter iter;
+ GP11Slot *it_slot;
+ gboolean matched;
+
+ g_return_if_fail (GCR_IMPORT_DIALOG (self));
+
+ if (GTK_WIDGET_VISIBLE (self->pv->combo))
+ populate_slots (self);
+ else
+ g_return_if_reached ();
+
+ if (slot == NULL) {
+ gtk_combo_box_set_active (self->pv->combo, -1);
+ return;
+ }
+
+ g_return_if_fail (GP11_IS_SLOT (slot));
+
+ matched = FALSE;
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->pv->slots), &iter)) {
+ do {
+ gtk_tree_model_get (GTK_TREE_MODEL (self->pv->slots), &iter, COLUMN_SLOT, &it_slot, -1);
+ if (gp11_slot_equal (it_slot, slot))
+ matched = TRUE;
+ g_object_unref (it_slot);
+ } while (!matched && gtk_tree_model_iter_next (GTK_TREE_MODEL (self->pv->slots), &iter));
+ }
+
+ if (matched) {
+ gtk_combo_box_set_active_iter (self->pv->combo, &iter);
+ } else {
+ gtk_combo_box_set_active (self->pv->combo, -1);
+ g_return_if_reached ();
+ }
+}
+
+void
+_gcr_import_dialog_show_selected_slot (GcrImportDialog *self)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+ gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-label")));
+ gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-area")));
+}
+
+void
+_gcr_import_dialog_hide_selected_slot (GcrImportDialog *self)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+ gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-label")));
+ gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-area")));
+}
+
+const gchar*
+_gcr_import_dialog_get_password (GcrImportDialog *self)
+{
+ g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL);
+ return egg_secure_entry_get_text (self->pv->entry);
+}
+
+void
+_gcr_import_dialog_set_password (GcrImportDialog *self, const gchar *password)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+ if (password == NULL)
+ password = "";
+ egg_secure_entry_set_text (self->pv->entry, password);
+}
+
+void
+_gcr_import_dialog_show_password (GcrImportDialog *self)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+ gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-label")));
+ gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area")));
+}
+
+void
+_gcr_import_dialog_hide_password (GcrImportDialog *self)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+ gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-label")));
+ gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area")));
+}
+
+const gchar*
+_gcr_import_dialog_get_primary_text (GcrImportDialog *self)
+{
+ g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL);
+ return gtk_label_get_text (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "primary-text")));
+}
+
+void
+_gcr_import_dialog_set_primary_text (GcrImportDialog *self, const gchar *text)
+{
+ gchar *label;
+
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+
+ if (text == NULL)
+ text = "";
+
+ label = g_markup_printf_escaped ("<span size='large' weight='bold'>%s</span>", text);
+ gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "primary-text")), label);
+ g_free (label);
+
+ g_object_notify (G_OBJECT (self), "primary-text");
+}
+
+const gchar*
+_gcr_import_dialog_get_secondary_text (GcrImportDialog *self)
+{
+ g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL);
+ return gtk_label_get_text (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text")));
+}
+
+void
+_gcr_import_dialog_set_secondary_text (GcrImportDialog *self, const gchar *text)
+{
+ g_return_if_fail (GCR_IS_IMPORT_DIALOG (self));
+
+ if (text == NULL)
+ text = "";
+
+ gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text")), text);
+ g_object_notify (G_OBJECT (self), "primary-text");
+}
diff --git a/gcr/gcr-import-dialog.glade b/gcr/gcr-import-dialog.glade
index 7da00a1..e91c662 100644
--- a/gcr/gcr-import-dialog.glade
+++ b/gcr/gcr-import-dialog.glade
@@ -1,164 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Sat Jan 17 14:53:28 2009 -->
+<!--Generated with glade3 3.4.5 on Wed Jan 21 16:01:29 2009 -->
<glade-interface>
- <widget class="GtkDialog" id="dialog1">
- <property name="border_width">5</property>
- <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="has_separator">False</property>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox1">
+ <widget class="GtkWindow" id="window1">
+ <child>
+ <widget class="GtkVBox" id="import-dialog">
<property name="visible">True</property>
- <property name="spacing">2</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
<child>
- <widget class="GtkVBox" id="vbox1">
+ <widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="spacing">12</property>
<child>
- <widget class="GtkHBox" id="hbox2">
+ <widget class="GtkImage" id="image1">
<property name="visible">True</property>
- <property name="spacing">12</property>
- <child>
- <widget class="GtkImage" id="image1">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="yalign">1</property>
- <property name="stock">gtk-dialog-authentication</property>
- <property name="icon_size">6</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVBox" id="vbox2">
- <property name="visible">True</property>
- <property name="spacing">6</property>
- <child>
- <widget class="GtkLabel" id="primary-text">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;span size='large' weight='bold'&gt;Import Certificates and Keys&lt;/span&gt;</property>
- <property name="use_markup">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="secondary-text">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Secondary prompt text</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">6</property>
</widget>
<packing>
<property name="expand">False</property>
+ <property name="fill">False</property>
</packing>
</child>
<child>
- <widget class="GtkTable" id="table1">
+ <widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
- <property name="n_rows">2</property>
- <property name="n_columns">2</property>
- <property name="column_spacing">12</property>
- <property name="row_spacing">6</property>
+ <property name="spacing">6</property>
<child>
- <widget class="GtkEntry" id="password-entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkComboBox" id="location-combo">
- <property name="visible">True</property>
- <property name="button_sensitivity">GTK_SENSITIVITY_ON</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label5">
+ <widget class="GtkLabel" id="primary-text">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Password:</property>
+ <property name="label" translatable="yes">&lt;span size='large' weight='bold'&gt;Import Certificates and Keys&lt;/span&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
</widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
</child>
<child>
- <widget class="GtkLabel" id="label4">
+ <widget class="GtkLabel" id="secondary-text">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Import Into:</property>
+ <property name="wrap">True</property>
</widget>
<packing>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="position">1</property>
</packing>
</child>
</widget>
<packing>
- <property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
- <property name="position">1</property>
+ <property name="expand">False</property>
</packing>
</child>
- <child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <child>
+ <widget class="GtkTable" id="table1">
<property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkAlignment" id="password-area">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAlignment" id="slot-area">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkComboBox" id="slot-combo">
+ <property name="visible">True</property>
+ <property name="button_sensitivity">GTK_SENSITIVITY_ON</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
<child>
- <widget class="GtkButton" id="button1">
+ <widget class="GtkLabel" id="password-label">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password:</property>
</widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
</child>
<child>
- <widget class="GtkButton" id="button2">
+ <widget class="GtkLabel" id="slot-label">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Import Into:</property>
</widget>
<packing>
- <property name="position">1</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
- <property name="pack_type">GTK_PACK_END</property>
+ <property name="position">1</property>
</packing>
</child>
</widget>
diff --git a/gcr/gcr-import-dialog.h b/gcr/gcr-import-dialog.h
new file mode 100644
index 0000000..2465f22
--- /dev/null
+++ b/gcr/gcr-import-dialog.h
@@ -0,0 +1,86 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_IMPORT_DIALOG_H__
+#define __GCR_IMPORT_DIALOG_H__
+
+#include "gcr.h"
+
+#include "gp11/gp11.h"
+
+#include <gtk/gtk.h>
+
+#define GCR_TYPE_IMPORT_DIALOG (_gcr_import_dialog_get_type ())
+#define GCR_IMPORT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORT_DIALOG, GcrImportDialog))
+#define GCR_IMPORT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORT_DIALOG, GcrImportDialogClass))
+#define GCR_IS_IMPORT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORT_DIALOG))
+#define GCR_IS_IMPORT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_IMPORT_DIALOG))
+#define GCR_IMPORT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_IMPORT_DIALOG, GcrImportDialogClass))
+
+typedef struct _GcrImportDialog GcrImportDialog;
+typedef struct _GcrImportDialogClass GcrImportDialogClass;
+typedef struct _GcrImportDialogPrivate GcrImportDialogPrivate;
+
+struct _GcrImportDialog {
+ GtkDialog parent;
+ GcrImportDialogPrivate *pv;
+};
+
+struct _GcrImportDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType _gcr_import_dialog_get_type (void);
+
+GcrImportDialog* _gcr_import_dialog_new (void);
+
+gboolean _gcr_import_dialog_run (GcrImportDialog *self,
+ GtkWindow *parent);
+
+GP11Slot* _gcr_import_dialog_get_selected_slot (GcrImportDialog *self);
+
+void _gcr_import_dialog_set_selected_slot (GcrImportDialog *self,
+ GP11Slot *slot);
+
+void _gcr_import_dialog_show_selected_slot (GcrImportDialog *self);
+
+void _gcr_import_dialog_hide_selected_slot (GcrImportDialog *self);
+
+const gchar* _gcr_import_dialog_get_password (GcrImportDialog *self);
+
+void _gcr_import_dialog_set_password (GcrImportDialog *self,
+ const gchar *password);
+
+void _gcr_import_dialog_show_password (GcrImportDialog *self);
+
+void _gcr_import_dialog_hide_password (GcrImportDialog *self);
+
+const gchar* _gcr_import_dialog_get_primary_text (GcrImportDialog *self);
+
+void _gcr_import_dialog_set_primary_text (GcrImportDialog *self,
+ const gchar *text);
+
+const gchar* _gcr_import_dialog_get_secondary_text (GcrImportDialog *self);
+
+void _gcr_import_dialog_set_secondary_text (GcrImportDialog *self,
+ const gchar *text);
+
+#endif /* __GCR_IMPORT_DIALOG_H__ */
diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c
index 35abba8..0fe0809 100644
--- a/gcr/gcr-importer.c
+++ b/gcr/gcr-importer.c
@@ -21,46 +21,555 @@
#include "config.h"
+#include "gcr-import-dialog.h"
#include "gcr-importer.h"
+#include "gcr-internal.h"
+#include "gcr-parser.h"
+
+#include <glib/gi18n-lib.h>
enum {
PROP_0,
- PROP_IMPORTER
+ PROP_SLOT,
+ PROP_PARSER,
+ PROP_PROMPT_BEHAVIOR
};
enum {
- SIGNAL,
+ IMPORTED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (GcrImporter, gcr_importer, G_TYPE_OBJECT);
+struct _GcrImporterPrivate {
+ GP11Slot *slot;
+ GcrParser *parser;
+ GcrImporterPromptBehavior behavior;
+
+ /* Information about last import */
+ GError *error;
+ gboolean succeeded;
+
+ /* State data during import */
+ gboolean processing;
+ GCancellable *cancel;
+ GInputStream *input;
+ gboolean prompted;
+ gboolean async;
+ GByteArray *buffer;
+ GP11Session *session;
+ GQueue queue;
+
+ /* Extra async stuff */
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+};
+
+/* State forward declarations */
+static void state_cancelled (GcrImporter *self, gboolean async);
+static void state_complete (GcrImporter *self, gboolean async);
+static void state_create_object (GcrImporter *self, gboolean async);
+static void state_open_session (GcrImporter *self, gboolean async);
+static void state_parse_buffer (GcrImporter *self, gboolean async);
+static void state_read_buffer (GcrImporter *self, gboolean async);
+
+static void gcr_importer_async_result (GAsyncResultIface *iface);
+G_DEFINE_TYPE_WITH_CODE (GcrImporter, gcr_importer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_importer_async_result));
+
+#define BLOCK 4096
/* -----------------------------------------------------------------------------
* INTERNAL
*/
+static void
+cleanup_state_data (GcrImporter *self)
+{
+ GP11Attributes *attrs;
+
+ if (self->pv->buffer)
+ g_byte_array_free (self->pv->buffer, TRUE);
+ self->pv->buffer = NULL;
+
+ if (self->pv->session)
+ g_object_unref (self->pv->session);
+ self->pv->session = NULL;
+
+ while ((attrs = g_queue_pop_head (&self->pv->queue)) != NULL)
+ gp11_attributes_unref (attrs);
+ g_assert (g_queue_is_empty (&self->pv->queue));
+
+ if (self->pv->input)
+ g_object_unref (self->pv->input);
+ self->pv->input = NULL;
+
+ if (self->pv->cancel)
+ g_object_unref (self->pv->cancel);
+ self->pv->cancel = NULL;
+}
+
+static void
+cleanup_import_data (GcrImporter *self)
+{
+ if (self->pv->error)
+ g_clear_error (&self->pv->error);
+ self->pv->succeeded = TRUE;
+}
+
+static void
+next_state (GcrImporter *self, void (*state) (GcrImporter*, gboolean))
+{
+ g_assert (GCR_IS_IMPORTER (self));
+ g_assert (self->pv->processing);
+ g_assert (state);
+
+ if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
+ state = state_cancelled;
+
+ (state) (self, self->pv->async);
+}
+
+/* ---------------------------------------------------------------------------------
+ * COMPLETE
+ */
+
+static void
+state_complete (GcrImporter *self, gboolean async)
+{
+ if (async && self->pv->callback != NULL)
+ (self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self), self->pv->user_data);
+
+ cleanup_state_data (self);
+ self->pv->processing = FALSE;
+}
+
+static void
+state_failure (GcrImporter *self, gboolean async)
+{
+ self->pv->succeeded = FALSE;
+ next_state (self, state_complete);
+}
+
+static void
+state_cancelled (GcrImporter *self, gboolean async)
+{
+ if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
+ g_cancellable_cancel (self->pv->cancel);
+ if (self->pv->error)
+ g_error_free (self->pv->error);
+ self->pv->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled"));
+ next_state (self, state_failure);
+}
+
+/* ---------------------------------------------------------------------------------
+ * CREATE OBJECTS
+ */
+
+static void
+complete_create_object (GcrImporter *self, GP11Object *object, GError *error)
+{
+ if (object == NULL) {
+ g_propagate_error (&self->pv->error, error);
+ next_state (self, state_failure);
+
+ } else {
+ g_signal_emit (self, signals[IMPORTED], 0, object);
+ g_object_unref (object);
+ next_state (self, state_create_object);
+ }
+}
+
+static void
+on_create_object (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GP11Object *object = gp11_session_create_object_finish (GP11_SESSION (obj), res, &error);
+ complete_create_object (GCR_IMPORTER (user_data), object, error);
+}
+
+static void
+state_create_object (GcrImporter *self, gboolean async)
+{
+ GP11Attributes *attrs;
+ GP11Object *object;
+ GError *error = NULL;
+
+ /* No more objects */
+ if (g_queue_is_empty (&self->pv->queue)) {
+ next_state (self, state_complete);
+
+ } else {
+
+ /* Pop first one off the list */
+ attrs = g_queue_pop_head (&self->pv->queue);
+ g_assert (attrs);
+
+ gp11_attributes_add_ulong (attrs, CKA_TOKEN, CK_TRUE);
+
+ if (async) {
+ gp11_session_create_object_async (self->pv->session, attrs, self->pv->cancel,
+ on_create_object, self);
+ } else {
+ object = gp11_session_create_object_full (self->pv->session, attrs, self->pv->cancel, &error);
+ complete_create_object (self, object, error);
+ }
+
+ gp11_attributes_unref (attrs);
+ }
+}
+
+/* ---------------------------------------------------------------------------------
+ * OPEN SESSION
+ */
+
+static void
+complete_open_session (GcrImporter *self, GP11Session *session, GError *error)
+{
+ if (!session) {
+ g_propagate_error (&self->pv->error, error);
+ next_state (self, state_failure);
+ } else {
+ self->pv->session = session;
+ next_state (self, state_create_object);
+ }
+}
+
+static void
+on_open_session (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GP11Session *session = gp11_slot_open_session_finish (GP11_SLOT (obj), res, &error);
+ complete_open_session (GCR_IMPORTER (user_data), session, error);
+}
+
+static void
+state_open_session (GcrImporter *self, gboolean async)
+{
+ GP11Session *session;
+ GError *error = NULL;
+
+ if (!self->pv->slot) {
+ g_set_error (&self->pv->error, GCR_DATA_ERROR, GCR_ERROR_FAILURE, _("No location available to import to"));
+ next_state (self, state_failure);
+
+ } else {
+
+ if (async) {
+ gp11_slot_open_session_async (self->pv->slot, CKF_RW_SESSION,
+ self->pv->cancel, on_open_session, self);
+ } else {
+ session = gp11_slot_open_session_full (self->pv->slot, CKF_RW_SESSION,
+ self->pv->cancel, &error);
+ complete_open_session (self, session, error);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------
+ * IMPORT PROMPT
+ */
+
+static void
+complete_import_prompt (GcrImporter *self, GcrImportDialog *dialog, gint response)
+{
+ GP11Slot *slot;
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ self->pv->prompted = TRUE;
+
+ /* No dialog or dialog completed */
+ if (response == GTK_RESPONSE_OK) {
+
+ slot = _gcr_import_dialog_get_selected_slot (dialog);
+ gcr_importer_set_slot (self, slot);
+ next_state (self, state_open_session);
+
+ /* The dialog was cancelled or closed */
+ } else {
+ next_state (self, state_cancelled);
+ }
+}
+
+static void
+on_prompt_response (GtkDialog *dialog, gint response, gpointer user_data)
+{
+ complete_import_prompt (GCR_IMPORTER (user_data), GCR_IMPORT_DIALOG (dialog), response);
+ g_object_unref (dialog);
+}
+
+static void
+state_import_prompt (GcrImporter *self, gboolean async)
+{
+ GcrImportDialog *dialog;
+ gboolean prompt;
+ gint response;
+
+ g_assert (GCR_IS_IMPORTER (self));
+
+ /* No need to prompt */
+ if (self->pv->prompted == TRUE)
+ prompt = FALSE;
+ else if (self->pv->behavior == GCR_IMPORTER_PROMPT_ALWAYS)
+ prompt = TRUE;
+ else if (self->pv->behavior == GCR_IMPORTER_PROMPT_NEVER)
+ prompt = FALSE;
+ else
+ prompt = self->pv->slot ? FALSE : TRUE;
+
+ if (prompt == FALSE) {
+ next_state (self, state_open_session);
+
+ } else {
+
+ dialog = _gcr_import_dialog_new ();
+
+ _gcr_import_dialog_set_primary_text (dialog, _("Import Certificates/Keys"));
+ _gcr_import_dialog_hide_password (dialog);
+
+ if (self->pv->slot) {
+ _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
+ _gcr_import_dialog_hide_selected_slot (dialog);
+ } else {
+ _gcr_import_dialog_set_secondary_text (dialog, _("Choose a location to store the imported certificates/keys."));
+ }
+
+ /* Prompt without blocking main loop */
+ if (async) {
+ g_signal_connect (dialog, "response", G_CALLBACK (on_prompt_response), self);
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ /* Block mainloop */
+ } else {
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ complete_import_prompt (self, dialog, response);
+ g_object_unref (dialog);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------
+ * PARSING
+ */
+
+static const gchar*
+prepare_auth_primary (CK_OBJECT_CLASS klass)
+{
+ if (klass == CKO_PRIVATE_KEY)
+ return _("Enter password to unlock the private key");
+ else if (klass == CKO_CERTIFICATE)
+ return _("Enter password to unlock the certificate");
+ else
+ return _("Enter password to unlock");
+}
+
+static gchar*
+prepare_auth_secondary (CK_OBJECT_CLASS klass, const gchar *label)
+{
+ if (label == NULL) {
+ if (klass == CKO_PRIVATE_KEY)
+ return g_strdup (_("In order to import the private key, it must be unlocked"));
+ else if (klass == CKO_CERTIFICATE)
+ return g_strdup (_("In order to import the certificate, it must be unlocked"));
+ else
+ return g_strdup (_("In order to import the data, it must be unlocked"));
+ } else {
+ if (klass == CKO_PRIVATE_KEY)
+ return g_strdup_printf (_("In order to import the private key '%s', it must be unlocked"), label);
+ else if (klass == CKO_CERTIFICATE)
+ return g_strdup_printf (_("In order to import the certificate '%s', it must be unlocked"), label);
+ else
+ return g_strdup_printf (_("In order to import '%s', it must be unlocked"), label);
+ }
+}
+
+static void
+on_parser_parsed (GcrParser *parser, GcrImporter *self)
+{
+ GP11Attributes *attrs;
+
+ g_return_if_fail (GCR_IS_PARSER (parser));
+ g_return_if_fail (GCR_IS_IMPORTER (self));
+
+ attrs = gcr_parser_get_parsed_attributes (parser);
+ g_return_if_fail (attrs);
+ g_queue_push_tail (&self->pv->queue, gp11_attributes_ref (attrs));
+}
+
+static gboolean
+on_parser_authenticate (GcrParser *parser, gint count, GcrImporter *self)
+{
+ GcrImportDialog *dialog;
+ GP11Attributes *attrs;
+ const gchar *password;
+ gchar *text, *label;
+ GP11Slot *slot;
+ gulong klass;
+
+ dialog = _gcr_import_dialog_new ();
+
+ if (self->pv->slot)
+ _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
+
+ /* Figure out the text for the dialog */
+ attrs = gcr_parser_get_parsed_attributes (parser);
+ g_return_val_if_fail (attrs, FALSE);
+
+ if (!gp11_attributes_find_ulong (attrs, CKA_CLASS, &klass))
+ klass = (gulong)-1;
+ if (!gp11_attributes_find_string (attrs, CKA_LABEL, &label))
+ label = NULL;
+
+ text = prepare_auth_secondary (klass, label);
+ _gcr_import_dialog_set_primary_text (dialog, prepare_auth_primary (klass));
+ _gcr_import_dialog_set_secondary_text (dialog, text);
+ g_free (label);
+ g_free (text);
+
+ if (!_gcr_import_dialog_run (dialog, NULL))
+ return FALSE;
+
+ slot = _gcr_import_dialog_get_selected_slot (dialog);
+ gcr_importer_set_slot (self, slot);
+
+ password = _gcr_import_dialog_get_password (dialog);
+ gcr_parser_add_password (parser, password);
+
+ g_object_unref (dialog);
+ self->pv->prompted = TRUE;
+ return TRUE;
+}
+
+static void
+state_parse_buffer (GcrImporter *self, gboolean async)
+{
+ GError *error = NULL;
+ GcrParser *parser;
+ gulong parsed_conn;
+ gulong auth_conn;
+ gboolean ret;
+
+ g_assert (GCR_IS_IMPORTER (self));
+ g_assert (self->pv->buffer);
+
+ parser = gcr_importer_get_parser (self);
+ g_object_ref (parser);
+
+ /* Listen in to the parser */
+ parsed_conn = g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), self);
+ auth_conn = g_signal_connect (parser, "authenticate", G_CALLBACK (on_parser_authenticate), self);
+
+ ret = gcr_parser_parse_data (parser, self->pv->buffer->data, self->pv->buffer->len, &error);
+
+ /* An optimization to free data early as possible */
+ g_byte_array_free (self->pv->buffer, TRUE);
+ self->pv->buffer = NULL;
+
+ g_signal_handler_disconnect (parser, parsed_conn);
+ g_signal_handler_disconnect (parser, auth_conn);
+ g_object_unref (parser);
+
+ if (ret == TRUE) {
+ next_state (self, state_import_prompt);
+ } else {
+ g_propagate_error (&self->pv->error, error);
+ next_state (self, state_failure);
+ }
+}
+
+/* ---------------------------------------------------------------------------------
+ * BUFFER READING
+ */
+
+static void
+complete_read_buffer (GcrImporter *self, gssize count, GError *error)
+{
+ g_assert (GCR_IS_IMPORTER (self));
+ g_assert (self->pv->buffer);
+
+ /* A failure */
+ if (count == -1) {
+ g_propagate_error (&self->pv->error, error);
+ next_state (self, state_failure);
+ } else {
+
+ g_return_if_fail (count >= 0 && count <= BLOCK);
+ g_byte_array_set_size (self->pv->buffer, self->pv->buffer->len - (BLOCK - count));
+
+ /* Finished reading */
+ if (count == 0) {
+
+ /* Optimization, unref input early */
+ g_object_unref (self->pv->input);
+ self->pv->input = NULL;
+
+ next_state (self, state_parse_buffer);
+
+ /* Read the next block */
+ } else {
+ next_state (self, state_read_buffer);
+ }
+ }
+
+}
+
+static void
+on_read_buffer (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ gssize count;
+
+ count = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error);
+ complete_read_buffer (user_data, count, error);
+}
+
+static void
+state_read_buffer (GcrImporter *self, gboolean async)
+{
+ GError *error = NULL;
+ gssize count;
+ gsize at;
+
+ g_assert (GCR_IS_IMPORTER (self));
+ g_assert (G_IS_INPUT_STREAM (self->pv->input));
+
+ if (!self->pv->buffer)
+ self->pv->buffer = g_byte_array_sized_new (BLOCK);
+
+ at = self->pv->buffer->len;
+ g_byte_array_set_size (self->pv->buffer, at + BLOCK);
+
+ if (async) {
+ g_input_stream_read_async (self->pv->input, self->pv->buffer->data + at,
+ BLOCK, G_PRIORITY_DEFAULT, self->pv->cancel,
+ on_read_buffer, self);
+ } else {
+ count = g_input_stream_read (self->pv->input, self->pv->buffer->data + at,
+ BLOCK, self->pv->cancel, &error);
+ complete_read_buffer (self, count, error);
+ }
+}
+
/* -----------------------------------------------------------------------------
* OBJECT
*/
-
static GObject*
gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props));
g_return_val_if_fail (self, NULL);
-
-
return G_OBJECT (self);
}
static void
gcr_importer_init (GcrImporter *self)
{
-
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORTER, GcrImporterPrivate);
+ self->pv->behavior = GCR_IMPORTER_PROMPT_NEEDED;
+ g_queue_init (&self->pv->queue);
}
static void
@@ -68,6 +577,17 @@ gcr_importer_dispose (GObject *obj)
{
GcrImporter *self = GCR_IMPORTER (obj);
+ cleanup_state_data (self);
+ cleanup_import_data (self);
+
+ if (self->pv->parser)
+ g_object_unref (self->pv->parser);
+ self->pv->parser = NULL;
+
+ if (self->pv->slot)
+ g_object_unref (self->pv->slot);
+ self->pv->slot = NULL;
+
G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj);
}
@@ -76,6 +596,9 @@ gcr_importer_finalize (GObject *obj)
{
GcrImporter *self = GCR_IMPORTER (obj);
+ g_assert (!self->pv->parser);
+ g_assert (!self->pv->slot);
+
G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj);
}
@@ -86,7 +609,14 @@ gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value,
GcrImporter *self = GCR_IMPORTER (obj);
switch (prop_id) {
- case PROP_IMPORTER:
+ case PROP_PARSER:
+ gcr_importer_set_parser (self, g_value_get_object (value));
+ break;
+ case PROP_SLOT:
+ gcr_importer_set_slot (self, g_value_get_object (value));
+ break;
+ case PROP_PROMPT_BEHAVIOR:
+ gcr_importer_set_prompt_behavior (self, (GcrImporterPromptBehavior)g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -101,7 +631,14 @@ gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value,
GcrImporter *self = GCR_IMPORTER (obj);
switch (prop_id) {
- case PROP_IMPORTER:
+ case PROP_PARSER:
+ g_value_set_object (value, gcr_importer_get_parser (self));
+ break;
+ case PROP_SLOT:
+ g_value_set_object (value, gcr_importer_get_slot (self));
+ break;
+ case PROP_PROMPT_BEHAVIOR:
+ g_value_set_int (value, gcr_importer_get_prompt_behavior (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -120,17 +657,49 @@ gcr_importer_class_init (GcrImporterClass *klass)
gobject_class->set_property = gcr_importer_set_property;
gobject_class->get_property = gcr_importer_get_property;
- g_object_class_install_property (gobject_class, PROP_IMPORTER,
- g_param_spec_pointer ("importer", "Importer", "Importer.", G_PARAM_READWRITE));
+ g_type_class_add_private (gobject_class, sizeof (GcrImporterPrivate));
+
+ g_object_class_install_property (gobject_class, PROP_PARSER,
+ g_param_spec_object ("parser", "Parser", "Parser used to parse imported data",
+ GCR_TYPE_PARSER, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PARSER,
+ g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into",
+ GP11_TYPE_SLOT, G_PARAM_READWRITE));
- signals[SIGNAL] = g_signal_new ("signal", GCR_TYPE_IMPORTER,
- G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, signal),
+ g_object_class_install_property (gobject_class, PROP_PROMPT_BEHAVIOR,
+ g_param_spec_int ("prompt-behavior", "Prompt Behavior", "Import Prompt Behavior",
+ 0, G_MAXINT, GCR_IMPORTER_PROMPT_NEEDED, G_PARAM_READWRITE));
+
+ signals[IMPORTED] = g_signal_new ("imported", GCR_TYPE_IMPORTER,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, imported),
NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 0);
+ G_TYPE_NONE, 1, GP11_TYPE_OBJECT);
_gcr_initialize ();
}
+static gpointer
+gcr_importer_real_get_user_data (GAsyncResult *base)
+{
+ g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
+ return GCR_IMPORTER (base)->pv->user_data;
+}
+
+static GObject*
+gcr_importer_real_get_source_object (GAsyncResult *base)
+{
+ g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
+ return G_OBJECT (base);
+}
+
+static void
+gcr_importer_async_result (GAsyncResultIface *iface)
+{
+ iface->get_source_object = gcr_importer_real_get_source_object;
+ iface->get_user_data = gcr_importer_real_get_user_data;
+}
+
/* -----------------------------------------------------------------------------
* PUBLIC
*/
@@ -141,73 +710,133 @@ gcr_importer_new (void)
return g_object_new (GCR_TYPE_IMPORTER, NULL);
}
-gboolean
-gcr_importer_import_data (GcrImporter *self, const guchar *data, gsize n_data,
- GError *err)
+GcrParser*
+gcr_importer_get_parser (GcrImporter *self)
{
- GckParser *parser;
- gulong parsed_conn;
- gulong auth_conn;
- gboolean ret;
-
- g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
- g_return_val_if_fail (data || !n_data, FALSE);
- g_return_val_if_fail (!error || !*error, FALSE);
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL);
+ if (!self->pv->parser)
+ self->pv->parser = gcr_parser_new ();
+ return self->pv->parser;
+}
+void
+gcr_importer_set_parser (GcrImporter *self, GcrParser *parser)
+{
+ g_return_if_fail (GCR_IS_IMPORTER (self));
+
+ if (parser)
+ g_object_ref (parser);
+ if (self->pv->parser)
+ g_object_unref (self->pv->parser);
+ self->pv->parser = parser;
+ g_object_notify (G_OBJECT (self), "parser");
+}
- xxxx;
+GP11Slot*
+gcr_importer_get_slot (GcrImporter *self)
+{
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL);
+ return self->pv->slot;
+}
+void
+gcr_importer_set_slot (GcrImporter *self, GP11Slot *slot)
+{
+ g_return_if_fail (GCR_IS_IMPORTER (self));
+
+ if (slot)
+ g_object_ref (slot);
+ if (self->pv->slot)
+ g_object_unref (self->pv->slot);
+ self->pv->slot = slot;
+ g_object_notify (G_OBJECT (self), "slot");
+}
- /*
- * Parse to see if it's something that needs a password
- * if we can't prompt,
- * return an error
- * Possibly prompt, if password needed, with all information necessary
- *
- */
+GcrImporterPromptBehavior
+gcr_importer_get_prompt_behavior (GcrImporter *self)
+{
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), GCR_IMPORTER_PROMPT_NEEDED);
+ return self->pv->behavior;
+}
+void
+gcr_importer_set_prompt_behavior (GcrImporter *self, GcrImporterPromptBehavior behavior)
+{
+ g_return_if_fail (GCR_IMPORTER (self));
+ self->pv->behavior = behavior;
+ g_object_notify (G_OBJECT (self), "prompt-behavior");
+}
- g_object_ref (self);
+gboolean
+gcr_importer_import (GcrImporter *self, GInputStream *input,
+ GCancellable *cancel, GError **error)
+{
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+ g_return_val_if_fail (!self->pv->processing, FALSE);
- parser = gcr_importer_get_parser (self);
+ cleanup_import_data (self);
- /* Listen in to the parser */
- g_object_ref (parser);
- parsed_conn = g_signal_connect (parser, "parsed-item", G_CALLBACK (parser_parsed_item), self);
- auth_conn = g_signal_connect (parser, "authenticate", G_CALLBACK (parser_authenticate), self);
+ self->pv->input = g_object_ref (input);
+ if (cancel)
+ self->pv->cancel = g_object_ref (cancel);
+ self->pv->processing = TRUE;
+ self->pv->async = FALSE;
- /* Feed the parser the data */
- ret = gcr_parser_parse_data (parser, data, n_data, err);
+ next_state (self, state_read_buffer);
- /* Now we should have all the data ready, check if we should prompt... */
- /* Import data one by one into module */
+ g_assert (!self->pv->processing);
+ g_assert (!self->pv->input);
+ g_assert (!self->pv->cancel);
- g_signal_handler_disconnect (parser, parsed_conn);
- g_signal_handler_disconnect (parser, auth_conn);
- g_object_unref (parser);
+ if (!self->pv->succeeded) {
+ g_propagate_error (error, self->pv->error);
+ self->pv->error = NULL;
+ return FALSE;
+ }
- g_object_unref (self);
+ return TRUE;
+}
- return ret;
+void
+gcr_importer_import_async (GcrImporter *self, GInputStream *input, GCancellable *cancel,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GCR_IS_IMPORTER (self));
+ g_return_if_fail (G_IS_INPUT_STREAM (input));
+ g_return_if_fail (!self->pv->processing);
+
+ cleanup_import_data (self);
+
+ self->pv->input = g_object_ref (input);
+ if (cancel)
+ self->pv->cancel = g_object_ref (cancel);
+ self->pv->processing = TRUE;
+ self->pv->async = TRUE;
+ self->pv->callback = callback;
+ self->pv->user_data = user_data;
+
+ next_state (self, state_read_buffer);
+ g_assert (self->pv->processing);
}
gboolean
-gcr_importer_import_file (GcrImporter *self, const gchar *filename,
- GError *err)
+gcr_importer_import_finish (GcrImporter *self, GAsyncResult *res, GError **error)
{
- gboolean ret;
- gchar *data;
- gsize n_data;
-
g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
- g_return_val_if_fail (filename, FALSE);
+ g_return_val_if_fail (GCR_IMPORTER (res) == self, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
+ g_return_val_if_fail (!self->pv->processing, FALSE);
- if (!g_file_get_contents (filename, &data, &n_data, err))
- return FALSE;
+ g_assert (!self->pv->input);
+ g_assert (!self->pv->cancel);
- ret = gcr_importer_import_data (self, (const guchar*)data, n_data, error);
- g_free (data);
+ if (!self->pv->succeeded) {
+ g_propagate_error (error, self->pv->error);
+ self->pv->error = NULL;
+ return FALSE;
+ }
- return ret;
+ return TRUE;
}
diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h
index dac1d51..ed366f3 100644
--- a/gcr/gcr-importer.h
+++ b/gcr/gcr-importer.h
@@ -22,8 +22,17 @@
#ifndef __GCR_IMPORTER_H__
#define __GCR_IMPORTER_H__
+#include "gcr.h"
+#include "gcr-parser.h"
+
#include <glib-object.h>
+typedef enum {
+ GCR_IMPORTER_PROMPT_NEEDED,
+ GCR_IMPORTER_PROMPT_ALWAYS,
+ GCR_IMPORTER_PROMPT_NEVER
+} GcrImporterPromptBehavior;
+
#define GCR_TYPE_IMPORTER (gcr_importer_get_type ())
#define GCR_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter))
#define GCR_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORTER, GcrImporterClass))
@@ -33,49 +42,53 @@
typedef struct _GcrImporter GcrImporter;
typedef struct _GcrImporterClass GcrImporterClass;
+typedef struct _GcrImporterPrivate GcrImporterPrivate;
struct _GcrImporter {
GObject parent;
+ GcrImporterPrivate *pv;
};
struct _GcrImporterClass {
GObjectClass parent_class;
- /* signals --------------------------------------------------------- */
+ /* signals */
- void (*signal) (GcrImporter *self, GkrImportedItem *item);
+ void (*imported) (GcrImporter *self, GP11Object *object);
};
-GType gcr_importer_get_type (void);
+GType gcr_importer_get_type (void);
-GcrImporter* gcr_importer_new (void);
+GcrImporter* gcr_importer_new (void);
-GcrImporter* gcr_importer_new_for_module (GP11Module *module);
+GcrParser* gcr_importer_get_parser (GcrImporter *self);
-GcrImporter* gcr_importer_new_for_module_funcs (gpointer pkcs11_funcs);
+void gcr_importer_set_parser (GcrImporter *self,
+ GcrParser *parser);
-void gcr_importer_set_slot (GcrImporter *self,
- GP11Slot *slot);
+struct _GP11Slot* gcr_importer_get_slot (GcrImporter *self);
-void gcr_importer_set_slot_id (GcrImporter *self,
- gulong slot_id);
+void gcr_importer_set_slot (GcrImporter *self,
+ struct _GP11Slot *slot);
-void gcr_importer_set_parser (GcrImporter *self,
- GcrParser *parser);
+GcrImporterPromptBehavior gcr_importer_get_prompt_behavior (GcrImporter *self);
-void gcr_importer_set_window (GcrImporter *self,
- GtkWindow *window);
+void gcr_importer_set_prompt_behavior (GcrImporter *self,
+ GcrImporterPromptBehavior behavior);
-void gcr_importer_set_prompt_behavior (GcrImporter *self,
- GcrImporterPromptBehavior behavior);
+gboolean gcr_importer_import (GcrImporter *self,
+ GInputStream *input,
+ GCancellable *cancel,
+ GError **error);
-gboolean gcr_importer_import_data (GcrImporter *self,
- const guchar *data,
- gsize n_data,
- GError *error);
+void gcr_importer_import_async (GcrImporter *self,
+ GInputStream *input,
+ GCancellable *cancel,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
-gboolean gcr_importer_import_file (GcrImporter *self,
- const gchar *filename,
- GError *error);
+gboolean gcr_importer_import_finish (GcrImporter *self,
+ GAsyncResult *res,
+ GError **error);
#endif /* __GCR_IMPORTER_H__ */
diff --git a/gcr/gcr-internal.h b/gcr/gcr-internal.h
index daad990..a8a4651 100644
--- a/gcr/gcr-internal.h
+++ b/gcr/gcr-internal.h
@@ -1,8 +1,14 @@
#ifndef GCR_INTERNAL_H_
#define GCR_INTERNAL_H_
+#include "gcr.h"
+
+#include "gp11/gp11.h"
+
#include <glib.h>
-void _gcr_initialize (void);
+void _gcr_initialize (void);
+
+GList* _gcr_get_pkcs11_modules (void);
#endif /* GCR_INTERNAL_H_ */
diff --git a/gcr/gcr-internal.c b/gcr/gcr-library.c
index 8daddee..41356b5 100644
--- a/gcr/gcr-internal.c
+++ b/gcr/gcr-library.c
@@ -21,12 +21,95 @@
#include "config.h"
+#include "gcr.h"
+#include "gcr-types.h"
#include "gcr-internal.h"
#include "egg/egg-secure-memory.h"
#include <gcrypt.h>
+static GList *all_modules = NULL;
+
+GQuark
+gcr_data_error_get_domain (void)
+{
+ static GQuark domain = 0;
+ if (domain == 0)
+ domain = g_quark_from_static_string ("gcr-parser-error");
+ return domain;
+}
+
+/* -----------------------------------------------------------------------------
+ * MEMORY
+ */
+
+static gboolean do_warning = TRUE;
+#define WARNING "couldn't allocate secure memory to keep passwords " \
+ "and or keys from being written to the disk"
+
+#define ABORTMSG "The GNOME_KEYRING_PARANOID environment variable was set. " \
+ "Exiting..."
+
+static G_LOCK_DEFINE (memory_lock);
+
+/*
+ * These are called from egg-secure-memory.c to provide appropriate
+ * locking for memory between threads
+ */
+
+void
+egg_memory_lock (void)
+{
+ G_LOCK (memory_lock);
+}
+
+void
+egg_memory_unlock (void)
+{
+ G_UNLOCK (memory_lock);
+}
+
+void*
+egg_memory_fallback (void *p, unsigned long sz)
+{
+ const gchar *env;
+
+ /* We were asked to free memory */
+ if (!sz) {
+ g_free (p);
+ return NULL;
+ }
+
+ /* We were asked to allocate */
+ if (!p) {
+ if (do_warning) {
+ g_message (WARNING);
+ do_warning = FALSE;
+ }
+
+ env = g_getenv ("GNOME_KEYRING_PARANOID");
+ if (env && *env)
+ g_error (ABORTMSG);
+
+ return g_malloc0 (sz);
+ }
+
+ /*
+ * Reallocation is a bit of a gray area, as we can be asked
+ * by external libraries (like libgcrypt) to reallocate a
+ * non-secure block into secure memory. We cannot satisfy
+ * this request (as we don't know the size of the original
+ * block) so we just try our best here.
+ */
+
+ return g_realloc (p, sz);
+}
+
+/* ------------------------------------------------------------------------------
+ * GCRYPT HOOKS
+ */
+
static void
log_handler (gpointer unused, int unknown, const gchar *msg, va_list va)
{
@@ -88,10 +171,12 @@ static struct gcry_thread_cbs glib_thread_cbs = {
void
_gcr_initialize (void)
{
- static gsize gcrypt_initialized = FALSE;
+ static volatile gsize gcr_initialized = 0;
+ GP11Module *module;
+ GError *error = NULL;
unsigned seed;
- if (g_once_init_enter (&gcrypt_initialized)) {
+ if (g_once_init_enter (&gcr_initialized)) {
/* Only initialize libgcrypt if it hasn't already been initialized */
if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
@@ -111,6 +196,20 @@ _gcr_initialize (void)
gcry_create_nonce (&seed, sizeof (seed));
srand (seed);
- g_once_init_leave (&gcrypt_initialized, 1);
+ /* TODO: This needs reworking for multiple modules */
+ module = gp11_module_initialize (PKCS11_MODULE_PATH, NULL, &error);
+ if (module)
+ all_modules = g_list_prepend (all_modules, module);
+ else
+ g_warning ("couldn't initialize PKCS#11 module: %s",
+ error && error->message ? error->message : "");
+
+ g_once_init_leave (&gcr_initialized, 1);
}
}
+
+GList*
+_gcr_get_pkcs11_modules (void)
+{
+ return all_modules;
+}
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 7348788..505f9f9 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -263,7 +263,7 @@ enum_next_password (GcrParser *self, PasswordState *state, const gchar **passwor
++state->ask_state;
if (!result)
- return GCR_PARSE_CANCELLED;
+ return GCR_ERROR_CANCELLED;
/* Return any passwords added */
if (state->seen < self->pv->passwords->len) {
@@ -273,7 +273,7 @@ enum_next_password (GcrParser *self, PasswordState *state, const gchar **passwor
return SUCCESS;
}
- return GCR_PARSE_LOCKED;
+ return GCR_ERROR_LOCKED;
}
static void
@@ -293,7 +293,7 @@ parsed_fire (GcrParser *self)
static gint
parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
{
- gint res = GCR_PARSE_UNRECOGNIZED;
+ gint res = GCR_ERROR_UNRECOGNIZED;
ASN1_TYPE asn = ASN1_TYPE_EMPTY;
guint version;
@@ -303,14 +303,14 @@ parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
parsed_clear (self, CKO_PRIVATE_KEY);
parsed_ulong (self, CKA_KEY_TYPE, CKK_RSA);
- res = GCR_PARSE_FAILURE;
+ res = GCR_ERROR_FAILURE;
if (!egg_asn1_read_uint (asn, "version", &version))
goto done;
/* We only support simple version */
if (version != 0) {
- res = GCR_PARSE_UNRECOGNIZED;
+ res = GCR_ERROR_UNRECOGNIZED;
g_message ("unsupported version of RSA key: %u", version);
goto done;
}
@@ -330,7 +330,7 @@ done:
if (asn)
asn1_delete_structure (&asn);
- if (res == GCR_PARSE_FAILURE)
+ if (res == GCR_ERROR_FAILURE)
g_message ("invalid RSA key");
return res;
@@ -343,7 +343,7 @@ done:
static gint
parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
{
- gint ret = GCR_PARSE_UNRECOGNIZED;
+ gint ret = GCR_ERROR_UNRECOGNIZED;
int res;
ASN1_TYPE asn;
@@ -353,7 +353,7 @@ parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
parsed_clear (self, CKO_PRIVATE_KEY);
parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA);
- res = GCR_PARSE_FAILURE;
+ res = GCR_ERROR_FAILURE;
if (!parsed_asn1_attribute (self, asn, data, n_data, "p", CKA_PRIME) ||
!parsed_asn1_attribute (self, asn, data, n_data, "q", CKA_SUBPRIME) ||
@@ -368,7 +368,7 @@ done:
if (asn)
asn1_delete_structure (&asn);
- if (ret == GCR_PARSE_FAILURE)
+ if (ret == GCR_ERROR_FAILURE)
g_message ("invalid DSA key");
return ret;
@@ -378,7 +378,7 @@ static gint
parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n_keydata,
const guchar *params, gsize n_params)
{
- gint ret = GCR_PARSE_UNRECOGNIZED;
+ gint ret = GCR_ERROR_UNRECOGNIZED;
int res;
ASN1_TYPE asn_params = ASN1_TYPE_EMPTY;
ASN1_TYPE asn_key = ASN1_TYPE_EMPTY;
@@ -390,7 +390,7 @@ parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n
parsed_clear (self, CKO_PRIVATE_KEY);
parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA);
- res = GCR_PARSE_FAILURE;
+ res = GCR_ERROR_FAILURE;
if (!parsed_asn1_attribute (self, asn_params, params, n_params, "p", CKA_PRIME) ||
!parsed_asn1_attribute (self, asn_params, params, n_params, "q", CKA_SUBPRIME) ||
@@ -407,7 +407,7 @@ done:
if (asn_params)
asn1_delete_structure (&asn_params);
- if (ret == GCR_PARSE_FAILURE)
+ if (ret == GCR_ERROR_FAILURE)
g_message ("invalid DSA key");
return ret;
@@ -423,7 +423,7 @@ parse_der_private_key (GcrParser *self, const guchar *data, gsize n_data)
gint res;
res = parse_der_private_key_rsa (self, data, n_data);
- if (res == GCR_PARSE_UNRECOGNIZED)
+ if (res == GCR_ERROR_UNRECOGNIZED)
res = parse_der_private_key_dsa (self, data, n_data);
return res;
@@ -445,13 +445,13 @@ parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
const guchar *params;
gsize n_params;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-8-PrivateKeyInfo", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
key_type = GP11_INVALID;
key_algo = egg_asn1_read_oid (asn, "privateKeyAlgorithm.algorithm");
@@ -463,7 +463,7 @@ parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
key_type = CKK_DSA;
if (key_type == GP11_INVALID) {
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
goto done;
}
@@ -487,17 +487,17 @@ done:
ret = parse_der_private_key_dsa (self, keydata, n_keydata);
/* Otherwise try the two part format that everyone seems to like */
- if (ret == GCR_PARSE_UNRECOGNIZED && params && n_params)
+ if (ret == GCR_ERROR_UNRECOGNIZED && params && n_params)
ret = parse_der_private_key_dsa_parts (self, keydata, n_keydata,
params, n_params);
break;
default:
g_message ("invalid or unsupported key type in PKCS#8 key");
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
break;
};
- } else if (ret == GCR_PARSE_FAILURE) {
+ } else if (ret == GCR_ERROR_FAILURE) {
g_message ("invalid PKCS#8 key");
}
@@ -521,13 +521,13 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
const gchar *password;
gint l;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-8-EncryptedPrivateKeyInfo", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
/* Figure out the type of encryption */
scheme = egg_asn1_read_oid (asn, "encryptionAlgorithm.algorithm");
@@ -576,7 +576,7 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
egg_secure_free (crypted);
crypted = NULL;
- if (r != GCR_PARSE_UNRECOGNIZED) {
+ if (r != GCR_ERROR_UNRECOGNIZED) {
ret = r;
break;
}
@@ -600,7 +600,7 @@ parse_der_pkcs8 (GcrParser *self, const guchar *data, gsize n_data)
gint ret;
ret = parse_der_pkcs8_plain (self, data, n_data);
- if (ret == GCR_PARSE_UNRECOGNIZED)
+ if (ret == GCR_ERROR_UNRECOGNIZED)
ret = parse_der_pkcs8_encrypted (self, data, n_data);
return ret;
@@ -618,7 +618,7 @@ parse_der_certificate (GcrParser *self, const guchar *data, gsize n_data)
asn = egg_asn1_decode ("PKIX1.Certificate", data, n_data);
if (asn == NULL)
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
parsed_clear (self, CKO_CERTIFICATE);
parsed_ulong (self, CKA_CERTIFICATE_TYPE, CKC_X_509);
@@ -651,13 +651,13 @@ handle_pkcs7_signed_data (GcrParser *self, const guchar *data, gsize n_data)
gsize n_certificate;
int i;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-7-SignedData", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
for (i = 0; TRUE; ++i) {
@@ -694,13 +694,13 @@ parse_der_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
gsize n_content;
GQuark oid;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-7-ContentInfo", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
oid = egg_asn1_read_oid (asn, "contentType");
if (!oid)
@@ -736,13 +736,13 @@ handle_pkcs12_cert_bag (GcrParser *self, const guchar *data, gsize n_data)
gsize n_certificate;
gint ret;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-12-CertBag", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
certificate = egg_asn1_read_content (asn, data, n_data, "certValue", &n_certificate);
if (!certificate)
@@ -775,13 +775,13 @@ handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data)
const guchar *element;
gsize n_element;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-12-SafeContents", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
/* Get the number of elements in this bag */
res = asn1_number_of_elements (asn, "", &count);
@@ -822,10 +822,10 @@ handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data)
/* TODO: OID_PKCS12_BAG_CRL */
} else {
- r = GCR_PARSE_UNRECOGNIZED;
+ r = GCR_ERROR_UNRECOGNIZED;
}
- if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) {
+ if (r == GCR_ERROR_FAILURE || r == GCR_ERROR_CANCELLED) {
ret = r;
goto done;
}
@@ -855,13 +855,13 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
gint ret, r;
gint l;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-7-EncryptedData", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
/* Check the encryption schema OID */
scheme = egg_asn1_read_oid (asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm");
@@ -887,7 +887,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
/* Parse the encryption stuff into a cipher. */
if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih)) {
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
goto done;
}
@@ -915,7 +915,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
egg_secure_free (crypted);
crypted = NULL;
- if (r != GCR_PARSE_UNRECOGNIZED) {
+ if (r != GCR_ERROR_UNRECOGNIZED) {
ret = r;
break;
}
@@ -944,13 +944,13 @@ handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data)
GQuark oid;
guint i;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-12-AuthenticatedSafe", data, n_data);
if (!asn)
goto done;
- ret = GCR_PARSE_FAILURE;
+ ret = GCR_ERROR_FAILURE;
/*
* Inside each PKCS12 safe there are multiple bags.
@@ -992,10 +992,10 @@ handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data)
/* Hmmmm, not sure what this is */
} else {
g_warning ("unrecognized type of safe content in pkcs12: %s", g_quark_to_string (oid));
- r = GCR_PARSE_UNRECOGNIZED;
+ r = GCR_ERROR_UNRECOGNIZED;
}
- if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) {
+ if (r == GCR_ERROR_FAILURE || r == GCR_ERROR_CANCELLED) {
ret = r;
goto done;
}
@@ -1019,7 +1019,7 @@ parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
gsize n_content;
GQuark oid;
- ret = GCR_PARSE_UNRECOGNIZED;
+ ret = GCR_ERROR_UNRECOGNIZED;
asn = egg_asn1_decode ("PKIX1.pkcs-12-PFX", data, n_data);
if (!asn)
@@ -1091,14 +1091,14 @@ handle_plain_pem (GcrParser *self, GQuark type, gint subformat,
format_id = GCR_FORMAT_DER_PKCS12;
else
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
if (subformat != 0 && subformat != format_id)
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
format = parser_format_lookup (format_id);
if (format == NULL)
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
return (format->function) (self, data, n_data);
}
@@ -1143,7 +1143,7 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat,
val = g_hash_table_lookup (headers, "DEK-Info");
if (!val) {
g_message ("missing encryption header");
- return GCR_PARSE_FAILURE;
+ return GCR_ERROR_FAILURE;
}
/* Fill in information necessary for prompting */
@@ -1162,7 +1162,7 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat,
ret = egg_openssl_decrypt_block (val, password, -1, data, n_data,
&decrypted, &n_decrypted);
if (!ret)
- return GCR_PARSE_FAILURE;
+ return GCR_ERROR_FAILURE;
g_assert (decrypted);
@@ -1176,11 +1176,11 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat,
egg_secure_free (decrypted);
/* Unrecognized is a bad password */
- if (res != GCR_PARSE_UNRECOGNIZED)
+ if (res != GCR_ERROR_UNRECOGNIZED)
return res;
}
- return GCR_PARSE_FAILURE;
+ return GCR_ERROR_FAILURE;
}
typedef struct {
@@ -1194,12 +1194,12 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data,
GHashTable *headers, gpointer user_data)
{
HandlePemArgs *args = (HandlePemArgs*)user_data;
- gint res = GCR_PARSE_FAILURE;
+ gint res = GCR_ERROR_FAILURE;
gboolean encrypted = FALSE;
const gchar *val;
/* Something already failed to parse */
- if (args->result == GCR_PARSE_FAILURE)
+ if (args->result == GCR_ERROR_FAILURE)
return;
/* See if it's encrypted PEM all openssl like*/
@@ -1216,8 +1216,8 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data,
res = handle_plain_pem (args->parser, type, args->subformat,
data, n_data);
- if (res != GCR_PARSE_UNRECOGNIZED) {
- if (args->result == GCR_PARSE_UNRECOGNIZED)
+ if (res != GCR_ERROR_UNRECOGNIZED) {
+ if (args->result == GCR_ERROR_UNRECOGNIZED)
args->result = res;
else if (res > args->result)
args->result = res;
@@ -1227,16 +1227,16 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data,
static gint
handle_pem_format (GcrParser *self, gint subformat, const guchar *data, gsize n_data)
{
- HandlePemArgs ctx = { self, GCR_PARSE_UNRECOGNIZED, subformat };
+ HandlePemArgs ctx = { self, GCR_ERROR_UNRECOGNIZED, subformat };
guint found;
if (n_data == 0)
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
found = egg_openssl_pem_parse (data, n_data, handle_pem_data, &ctx);
if (found == 0)
- return GCR_PARSE_UNRECOGNIZED;
+ return GCR_ERROR_UNRECOGNIZED;
return ctx.result;
}
@@ -1375,7 +1375,7 @@ parser_format_foreach (gpointer key, gpointer value, gpointer data)
g_assert (GCR_IS_PARSER (args->parser));
result = (format->function) (args->parser, args->data, args->n_data);
- if (result != GCR_PARSE_UNRECOGNIZED) {
+ if (result != GCR_ERROR_UNRECOGNIZED) {
args->result = result;
return TRUE;
}
@@ -1534,15 +1534,6 @@ gcr_parser_new (void)
return g_object_new (GCR_TYPE_PARSER, NULL);
}
-GQuark
-gcr_parser_get_error_domain (void)
-{
- static GQuark domain = 0;
- if (domain == 0)
- domain = g_quark_from_static_string ("gcr-parser-error");
- return domain;
-}
-
void
gcr_parser_add_password (GcrParser *self, const gchar *password)
{
@@ -1554,7 +1545,7 @@ gboolean
gcr_parser_parse_data (GcrParser *self, const guchar *data,
gsize n_data, GError **err)
{
- ForeachArgs args = { self, data, n_data, GCR_PARSE_UNRECOGNIZED };
+ ForeachArgs args = { self, data, n_data, GCR_ERROR_UNRECOGNIZED };
const gchar *message;
gint i;
@@ -1578,16 +1569,16 @@ gcr_parser_parse_data (GcrParser *self, const guchar *data,
switch (args.result) {
case SUCCESS:
return TRUE;
- case GCR_PARSE_CANCELLED:
+ case GCR_ERROR_CANCELLED:
message = _("The operation was cancelled");
break;
- case GCR_PARSE_UNRECOGNIZED:
+ case GCR_ERROR_UNRECOGNIZED:
message = _("Unrecognized or unsupported data.");
break;
- case GCR_PARSE_FAILURE:
+ case GCR_ERROR_FAILURE:
message = _("Could not parse invalid or corrupted data.");
break;
- case GCR_PARSE_LOCKED:
+ case GCR_ERROR_LOCKED:
message = _("The data is locked");
break;
default:
@@ -1595,37 +1586,11 @@ gcr_parser_parse_data (GcrParser *self, const guchar *data,
break;
};
- g_set_error_literal (err, GCR_PARSER_ERROR, args.result, message);
+ g_set_error_literal (err, GCR_DATA_ERROR, args.result, message);
return FALSE;
}
gboolean
-gcr_parser_parse_file (GcrParser *self, const gchar *filename, GError **err)
-{
- GMappedFile *mapped;
- gboolean ret;
- const guchar *data;
- gsize n_data;
-
- g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
- g_return_val_if_fail (filename, FALSE);
- g_return_val_if_fail (!err || !*err, FALSE);
-
- mapped = g_mapped_file_new (filename, FALSE, err);
- if (mapped == NULL)
- return FALSE;
-
- data = (const guchar*)g_mapped_file_get_contents (mapped);
- n_data = g_mapped_file_get_length (mapped);
-
- ret = gcr_parser_parse_data (self, data, n_data, err);
-
- g_mapped_file_free (mapped);
-
- return ret;
-}
-
-gboolean
gcr_parser_format_enable (GcrParser *self, gint format_id)
{
ParserFormat *format;
diff --git a/gcr/gcr-parser.h b/gcr/gcr-parser.h
index 4038cf0..2f5be35 100644
--- a/gcr/gcr-parser.h
+++ b/gcr/gcr-parser.h
@@ -22,12 +22,12 @@
#ifndef __GCR_PARSER_H__
#define __GCR_PARSER_H__
+#include "gcr.h"
+
#include <glib-object.h>
#include "gcr-types.h"
-#define GCR_PARSER_ERROR (gcr_parser_get_error_domain ())
-
#define GCR_TYPE_PARSER (gcr_parser_get_type ())
#define GCR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PARSER, GcrParser))
#define GCR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PARSER, GcrParserClass))
@@ -56,37 +56,31 @@ struct _GcrParserClass {
void (*parsed) (GcrParser *self);
};
-GType gcr_parser_get_type (void);
-
-GQuark gcr_parser_get_error_domain (void) G_GNUC_CONST;
-
-GcrParser* gcr_parser_new (void);
+GType gcr_parser_get_type (void);
-gboolean gcr_parser_format_enable (GcrParser *self,
- gint format);
+GcrParser* gcr_parser_new (void);
-gboolean gcr_parser_format_disable (GcrParser *self,
- gint format);
+gboolean gcr_parser_format_enable (GcrParser *self,
+ gint format);
-gboolean gcr_parser_format_supported (GcrParser *self,
- gint format);
+gboolean gcr_parser_format_disable (GcrParser *self,
+ gint format);
-gboolean gcr_parser_parse_data (GcrParser *self,
- const guchar *data,
- gsize n_data,
- GError **err);
+gboolean gcr_parser_format_supported (GcrParser *self,
+ gint format);
-gboolean gcr_parser_parse_file (GcrParser *self,
- const gchar *filename,
- GError **err);
+gboolean gcr_parser_parse_data (GcrParser *self,
+ const guchar *data,
+ gsize n_data,
+ GError **err);
-void gcr_parser_add_password (GcrParser *self,
- const gchar *password);
+void gcr_parser_add_password (GcrParser *self,
+ const gchar *password);
-const gchar* gcr_parser_get_parsed_label (GcrParser *self);
+const gchar* gcr_parser_get_parsed_label (GcrParser *self);
-const gchar* gcr_parser_get_parsed_description (GcrParser *self);
+const gchar* gcr_parser_get_parsed_description (GcrParser *self);
-GP11Attributes* gcr_parser_get_parsed_attributes (GcrParser *self);
+struct _GP11Attributes* gcr_parser_get_parsed_attributes (GcrParser *self);
#endif /* __GCR_PARSER_H__ */
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index 5f12892..ca2ed91 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -1,11 +1,15 @@
#ifndef GCRTYPES_H_
#define GCRTYPES_H_
+#define GCR_DATA_ERROR (gcr_data_error_get_domain ())
+
+GQuark gcr_data_error_get_domain (void) G_GNUC_CONST;
+
enum {
- GCR_PARSE_FAILURE = -1,
- GCR_PARSE_UNRECOGNIZED = 1,
- GCR_PARSE_CANCELLED = 2,
- GCR_PARSE_LOCKED = 3
+ GCR_ERROR_FAILURE = -1,
+ GCR_ERROR_UNRECOGNIZED = 1,
+ GCR_ERROR_CANCELLED = 2,
+ GCR_ERROR_LOCKED = 3
};
enum {
@@ -35,11 +39,8 @@ enum {
GCR_FORMAT_PEM_PKCS12
};
-#ifndef GP11_H
-
/* Forward declare some of the GP11 objects */
-typedef struct _GP11Attributes GP11Attributes;
-
-#endif /* GP11_H */
+struct _GP11Attributes;
+struct _GP11Slot;
#endif /* GCRTYPES_H_ */
diff --git a/gcr/gcr.h b/gcr/gcr.h
new file mode 100644
index 0000000..4cf2f23
--- /dev/null
+++ b/gcr/gcr.h
@@ -0,0 +1,46 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_H__
+#define __GCR_H__
+
+#include <glib.h>
+
+#ifndef GCR_API_SUBJECT_TO_CHANGE
+#error "This API has not yet reached stability."
+#endif
+
+struct _GP11Slot;
+
+#ifdef UNIMPLEMENTED
+enum {
+ GCR_INIT_NO_MODULES = 0x01,
+};
+
+void gcr_initialize (guint flags);
+
+void gcr_modules_register_loaded (gpointer funcs);
+
+gboolean gcr_modules_register_file (const gchar *module_path,
+ GError *error);
+#endif /* UNIMPLEMENTED */
+
+#endif /* __GCR_H__ */
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index 63b89d8..4306474 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -8,6 +8,10 @@ UNIT_PROMPT =
UNIT_LIBS = \
$(top_builddir)/gcr/libgcr.la \
$(top_builddir)/egg/libegg.la \
+ $(top_builddir)/egg/libegg-secure-entry.la \
$(top_builddir)/gp11/libgp11.la
+UNIT_FLAGS = \
+ -DGCR_API_SUBJECT_TO_CHANGE
+
include $(top_srcdir)/tests/gtest.make
diff --git a/gcr/tests/unit-test-parser.c b/gcr/tests/unit-test-parser.c
index 70c11bc..3a60489 100644
--- a/gcr/tests/unit-test-parser.c
+++ b/gcr/tests/unit-test-parser.c
@@ -29,6 +29,8 @@
#include "gcr/gcr-parser.h"
+#include "gp11/gp11.h"
+
#include <glib.h>
#include <gcrypt.h>
#include <libtasn1.h>