diff -u ../seahorse_orig/configure.in ./configure.in
--- ../seahorse_orig/configure.in       2004-09-29 18:35:43.421834652 -0000
+++ ./configure.in      2004-09-29 19:02:13.548553111 -0000
@@ -140,4 +140,5 @@
 src/Makefile
 plugins/Makefile
 plugins/nautilus/Makefile
+agent/Makefile
 ])
diff -u -N -r ../seahorse_orig/Makefile.am ./Makefile.am 
--- ../seahorse_orig/Makefile.am        2004-09-29 18:35:43.275863778 -0000
+++ ./Makefile.am       2004-09-29 19:01:01.397949483 -0000
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = libseahorse bonobo src po data help pixmaps plugins
+SUBDIRS = libseahorse bonobo src po data help pixmaps plugins agent
 
 EXTRA_DIST = \
 	AUTHORS \
diff -u -N -r ../seahorse_orig/agent/eggtrayicon.c ./agent/eggtrayicon.c
--- ../seahorse_orig/agent/eggtrayicon.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/eggtrayicon.c	2004-09-29 10:41:23.000000000 -0000
@@ -0,0 +1,471 @@
+/* From the gnome egg library */
+
+/* eggtrayicon.c
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * 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.
+ */
+
+/* From the gnome egg library */
+
+#include <config.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "eggtrayicon.h"
+
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#ifndef EGG_COMPILATION
+#ifndef _
+#define _(x) dgettext (GETTEXT_PACKAGE, x)
+#define N_(x) x
+#endif
+#else
+#define _(x) x
+#define N_(x) x
+#endif
+
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+#define SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define SYSTEM_TRAY_ORIENTATION_VERT 1
+
+enum {
+  PROP_0,
+  PROP_ORIENTATION
+};
+         
+static GtkPlugClass *parent_class = NULL;
+
+static void egg_tray_icon_init (EggTrayIcon *icon);
+static void egg_tray_icon_class_init (EggTrayIconClass *klass);
+
+static void egg_tray_icon_get_property (GObject    *object,
+					guint       prop_id,
+					GValue     *value,
+					GParamSpec *pspec);
+
+static void egg_tray_icon_realize   (GtkWidget *widget);
+static void egg_tray_icon_unrealize (GtkWidget *widget);
+
+static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
+
+GType
+egg_tray_icon_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (our_type == 0)
+    {
+      static const GTypeInfo our_info =
+      {
+	sizeof (EggTrayIconClass),
+	(GBaseInitFunc) NULL,
+	(GBaseFinalizeFunc) NULL,
+	(GClassInitFunc) egg_tray_icon_class_init,
+	NULL, /* class_finalize */
+	NULL, /* class_data */
+	sizeof (EggTrayIcon),
+	0,    /* n_preallocs */
+	(GInstanceInitFunc) egg_tray_icon_init
+      };
+
+      our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
+    }
+
+  return our_type;
+}
+
+static void
+egg_tray_icon_init (EggTrayIcon *icon)
+{
+  icon->stamp = 1;
+  icon->orientation = GTK_ORIENTATION_HORIZONTAL;
+  
+  gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+egg_tray_icon_class_init (EggTrayIconClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *)klass;
+  GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->get_property = egg_tray_icon_get_property;
+
+  widget_class->realize   = egg_tray_icon_realize;
+  widget_class->unrealize = egg_tray_icon_unrealize;
+
+  g_object_class_install_property (gobject_class,
+				   PROP_ORIENTATION,
+				   g_param_spec_enum ("orientation",
+						      _("Orientation"),
+						      _("The orientation of the tray."),
+						      GTK_TYPE_ORIENTATION,
+						      GTK_ORIENTATION_HORIZONTAL,
+						      G_PARAM_READABLE));
+}
+
+static void
+egg_tray_icon_get_property (GObject    *object,
+			    guint       prop_id,
+			    GValue     *value,
+			    GParamSpec *pspec)
+{
+  EggTrayIcon *icon = EGG_TRAY_ICON (object);
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, icon->orientation);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
+{
+  Display *xdisplay;
+  Atom type;
+  int format;
+  union {
+	gulong *prop;
+	guchar *prop_ch;
+  } prop = { NULL };
+  gulong nitems;
+  gulong bytes_after;
+  int error, result;
+
+  g_assert (icon->manager_window != None);
+  
+  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+
+  gdk_error_trap_push ();
+  type = None;
+  result = XGetWindowProperty (xdisplay,
+			       icon->manager_window,
+			       icon->orientation_atom,
+			       0, G_MAXLONG, FALSE,
+			       XA_CARDINAL,
+			       &type, &format, &nitems,
+			       &bytes_after, &(prop.prop_ch));
+  error = gdk_error_trap_pop ();
+
+  if (error || result != Success)
+    return;
+
+  if (type == XA_CARDINAL)
+    {
+      GtkOrientation orientation;
+
+      orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
+					GTK_ORIENTATION_HORIZONTAL :
+					GTK_ORIENTATION_VERTICAL;
+
+      if (icon->orientation != orientation)
+	{
+	  icon->orientation = orientation;
+
+	  g_object_notify (G_OBJECT (icon), "orientation");
+	}
+    }
+
+  if (prop.prop)
+    XFree (prop.prop);
+}
+
+static GdkFilterReturn
+egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
+{
+  EggTrayIcon *icon = user_data;
+  XEvent *xev = (XEvent *)xevent;
+
+  if (xev->xany.type == ClientMessage &&
+      xev->xclient.message_type == icon->manager_atom &&
+      xev->xclient.data.l[1] == icon->selection_atom)
+    {
+      egg_tray_icon_update_manager_window (icon);
+    }
+  else if (xev->xany.window == icon->manager_window)
+    {
+      if (xev->xany.type == PropertyNotify &&
+	  xev->xproperty.atom == icon->orientation_atom)
+	{
+	  egg_tray_icon_get_orientation_property (icon);
+	}
+      if (xev->xany.type == DestroyNotify)
+	{
+	  egg_tray_icon_update_manager_window (icon);
+	}
+    }
+  
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+egg_tray_icon_unrealize (GtkWidget *widget)
+{
+  EggTrayIcon *icon = EGG_TRAY_ICON (widget);
+  GdkWindow *root_window;
+
+  if (icon->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+                                              icon->manager_window);
+
+      gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+    }
+
+  root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+  gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
+
+  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+egg_tray_icon_send_manager_message (EggTrayIcon *icon,
+				    long         message,
+				    Window       window,
+				    long         data1,
+				    long         data2,
+				    long         data3)
+{
+  XClientMessageEvent ev;
+  Display *display;
+  
+  ev.type = ClientMessage;
+  ev.window = window;
+  ev.message_type = icon->system_tray_opcode_atom;
+  ev.format = 32;
+  ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+  ev.data.l[1] = message;
+  ev.data.l[2] = data1;
+  ev.data.l[3] = data2;
+  ev.data.l[4] = data3;
+
+  display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+  
+  gdk_error_trap_push ();
+  XSendEvent (display,
+	      icon->manager_window, False, NoEventMask, (XEvent *)&ev);
+  XSync (display, False);
+  gdk_error_trap_pop ();
+}
+
+static void
+egg_tray_icon_send_dock_request (EggTrayIcon *icon)
+{
+  egg_tray_icon_send_manager_message (icon,
+				      SYSTEM_TRAY_REQUEST_DOCK,
+				      icon->manager_window,
+				      gtk_plug_get_id (GTK_PLUG (icon)),
+				      0, 0);
+}
+
+static void
+egg_tray_icon_update_manager_window (EggTrayIcon *icon)
+{
+  Display *xdisplay;
+  
+  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+  
+  if (icon->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+					      icon->manager_window);
+      
+      gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+    }
+  
+  XGrabServer (xdisplay);
+  
+  icon->manager_window = XGetSelectionOwner (xdisplay,
+					     icon->selection_atom);
+
+  if (icon->manager_window != None)
+    XSelectInput (xdisplay,
+		  icon->manager_window, StructureNotifyMask|PropertyChangeMask);
+
+  XUngrabServer (xdisplay);
+  XFlush (xdisplay);
+  
+  if (icon->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+					      icon->manager_window);
+      
+      gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+
+      /* Send a request that we'd like to dock */
+      egg_tray_icon_send_dock_request (icon);
+
+      egg_tray_icon_get_orientation_property (icon);
+    }
+}
+
+static void
+egg_tray_icon_realize (GtkWidget *widget)
+{
+  EggTrayIcon *icon = EGG_TRAY_ICON (widget);
+  GdkScreen *screen;
+  GdkDisplay *display;
+  Display *xdisplay;
+  char buffer[256];
+  GdkWindow *root_window;
+
+  if (GTK_WIDGET_CLASS (parent_class)->realize)
+    GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+  screen = gtk_widget_get_screen (widget);
+  display = gdk_screen_get_display (screen);
+  xdisplay = gdk_x11_display_get_xdisplay (display);
+
+  /* Now see if there's a manager window around */
+  g_snprintf (buffer, sizeof (buffer),
+	      "_NET_SYSTEM_TRAY_S%d",
+	      gdk_screen_get_number (screen));
+
+  icon->selection_atom = XInternAtom (xdisplay, buffer, False);
+  
+  icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
+  
+  icon->system_tray_opcode_atom = XInternAtom (xdisplay,
+						   "_NET_SYSTEM_TRAY_OPCODE",
+						   False);
+
+  icon->orientation_atom = XInternAtom (xdisplay,
+					"_NET_SYSTEM_TRAY_ORIENTATION",
+					False);
+
+  egg_tray_icon_update_manager_window (icon);
+
+  root_window = gdk_screen_get_root_window (screen);
+  
+  /* Add a root window filter so that we get changes on MANAGER */
+  gdk_window_add_filter (root_window,
+			 egg_tray_icon_manager_filter, icon);
+}
+
+EggTrayIcon *
+egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
+{
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+  return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
+}
+
+EggTrayIcon*
+egg_tray_icon_new (const gchar *name)
+{
+  return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL);
+}
+
+guint
+egg_tray_icon_send_message (EggTrayIcon *icon,
+			    gint         timeout,
+			    const gchar *message,
+			    gint         len)
+{
+  guint stamp;
+  
+  g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
+  g_return_val_if_fail (timeout >= 0, 0);
+  g_return_val_if_fail (message != NULL, 0);
+		     
+  if (icon->manager_window == None)
+    return 0;
+
+  if (len < 0)
+    len = strlen (message);
+
+  stamp = icon->stamp++;
+  
+  /* Get ready to send the message */
+  egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+				      timeout, len, stamp);
+
+  /* Now to send the actual message */
+  gdk_error_trap_push ();
+  while (len > 0)
+    {
+      XClientMessageEvent ev;
+      Display *xdisplay;
+
+      xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+      
+      ev.type = ClientMessage;
+      ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
+      ev.format = 8;
+      ev.message_type = XInternAtom (xdisplay,
+				     "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+      if (len > 20)
+	{
+	  memcpy (&ev.data, message, 20);
+	  len -= 20;
+	  message += 20;
+	}
+      else
+	{
+	  memcpy (&ev.data, message, len);
+	  len = 0;
+	}
+
+      XSendEvent (xdisplay,
+		  icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
+      XSync (xdisplay, False);
+    }
+  gdk_error_trap_pop ();
+
+  return stamp;
+}
+
+void
+egg_tray_icon_cancel_message (EggTrayIcon *icon,
+			      guint        id)
+{
+  g_return_if_fail (EGG_IS_TRAY_ICON (icon));
+  g_return_if_fail (id > 0);
+  
+  egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+				      id, 0, 0);
+}
+
+GtkOrientation
+egg_tray_icon_get_orientation (EggTrayIcon *icon)
+{
+  g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
+
+  return icon->orientation;
+}
diff -u -N -r ../seahorse_orig/agent/eggtrayicon.h ./agent/eggtrayicon.h
--- ../seahorse_orig/agent/eggtrayicon.h	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/eggtrayicon.h	2004-09-29 10:41:18.000000000 -0000
@@ -0,0 +1,78 @@
+/* eggtrayicon.h
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * 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.
+ */
+
+/* From the gnome egg library */
+
+#ifndef __EGG_TRAY_ICON_H__
+#define __EGG_TRAY_ICON_H__
+
+#include <gtk/gtkplug.h>
+#include <gdk/gdkx.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TRAY_ICON		(egg_tray_icon_get_type ())
+#define EGG_TRAY_ICON(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon))
+#define EGG_TRAY_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
+#define EGG_IS_TRAY_ICON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON))
+#define EGG_IS_TRAY_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON))
+#define EGG_TRAY_ICON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
+	
+typedef struct _EggTrayIcon	  EggTrayIcon;
+typedef struct _EggTrayIconClass  EggTrayIconClass;
+
+struct _EggTrayIcon
+{
+  GtkPlug parent_instance;
+
+  guint stamp;
+  
+  Atom selection_atom;
+  Atom manager_atom;
+  Atom system_tray_opcode_atom;
+  Atom orientation_atom;
+  Window manager_window;
+
+  GtkOrientation orientation;
+};
+
+struct _EggTrayIconClass
+{
+  GtkPlugClass parent_class;
+};
+
+GType        egg_tray_icon_get_type       (void);
+
+EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen   *screen,
+					   const gchar *name);
+
+EggTrayIcon *egg_tray_icon_new            (const gchar *name);
+
+guint        egg_tray_icon_send_message   (EggTrayIcon *icon,
+					   gint         timeout,
+					   const char  *message,
+					   gint         len);
+void         egg_tray_icon_cancel_message (EggTrayIcon *icon,
+					   guint        id);
+
+GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon);
+					    
+G_END_DECLS
+
+#endif /* __EGG_TRAY_ICON_H__ */
diff -u -N -r ../seahorse_orig/agent/gtk-secure-entry.c ./agent/gtk-secure-entry.c
--- ../seahorse_orig/agent/gtk-secure-entry.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/gtk-secure-entry.c	2004-09-26 17:27:04.000000000 -0000
@@ -0,0 +1,3414 @@
+/* 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gtk-secure-entry.h"
+#include "seahorse-agent-secmem.h"
+
+#ifndef _
+#  include <libintl.h>
+#  define _(x) gettext(x)
+#endif
+
+#define MIN_SECURE_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
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* GObject, GtkObject methods
+ */
+static void gtk_secure_entry_class_init(GtkSecureEntryClass * klass);
+static void gtk_secure_entry_editable_init(GtkEditableClass * iface);
+static void gtk_secure_entry_cell_editable_init(GtkCellEditableIface *
+                      iface);
+static void gtk_secure_entry_init(GtkSecureEntry * entry);
+static void gtk_secure_entry_set_property(GObject * object,
+                    guint prop_id,
+                    const GValue * value,
+                     GParamSpec * pspec);
+static void gtk_secure_entry_get_property(GObject * object,
+                     guint prop_id,
+                    GValue * value,
+                   GParamSpec * pspec);
+static void gtk_secure_entry_finalize(GObject * object);
+
+/* GtkWidget methods
+ */
+static void gtk_secure_entry_realize(GtkWidget * widget);
+static void gtk_secure_entry_unrealize(GtkWidget * widget);
+static void gtk_secure_entry_size_request(GtkWidget * widget,
+                    GtkRequisition * requisition);
+static void gtk_secure_entry_size_allocate(GtkWidget * widget,
+                     GtkAllocation * allocation);
+static void gtk_secure_entry_draw_frame(GtkWidget * widget);
+static gint gtk_secure_entry_expose(GtkWidget * widget,
+                    GdkEventExpose * event);
+static gint gtk_secure_entry_button_press(GtkWidget * widget,
+                     GdkEventButton * event);
+static gint gtk_secure_entry_button_release(GtkWidget * widget,
+                       GdkEventButton * event);
+static gint gtk_secure_entry_motion_notify(GtkWidget * widget,
+                     GdkEventMotion * event);
+static gint gtk_secure_entry_key_press(GtkWidget * widget,
+                      GdkEventKey * event);
+static gint gtk_secure_entry_key_release(GtkWidget * widget,
+                     GdkEventKey * event);
+static gint gtk_secure_entry_focus_in(GtkWidget * widget,
+                   GdkEventFocus * event);
+static gint gtk_secure_entry_focus_out(GtkWidget * widget,
+                    GdkEventFocus * event);
+static void gtk_secure_entry_grab_focus(GtkWidget * widget);
+static void gtk_secure_entry_style_set(GtkWidget * widget,
+                     GtkStyle * previous_style);
+static void gtk_secure_entry_direction_changed(GtkWidget * widget,
+                           GtkTextDirection
+                          previous_dir);
+static void gtk_secure_entry_state_changed(GtkWidget * widget,
+                    GtkStateType previous_state);
+static void gtk_secure_entry_screen_changed(GtkWidget * widget,
+                     GdkScreen * old_screen);
+
+/* GtkEditable method implementations
+ */
+static void gtk_secure_entry_insert_text(GtkEditable * editable,
+                   const gchar * new_text,
+                   gint new_text_length,
+                     gint * position);
+static void gtk_secure_entry_delete_text(GtkEditable * editable,
+                   gint start_pos, gint end_pos);
+static void gtk_secure_entry_real_set_position(GtkEditable * editable,
+                          gint position);
+static gint gtk_secure_entry_get_position(GtkEditable * editable);
+static void gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
+                       gint start, gint end);
+static gboolean gtk_secure_entry_get_selection_bounds(GtkEditable *
+                           editable,
+                             gint * start,
+                             gint * end);
+
+/* GtkCellEditable method implementations
+ */
+static void gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
+                    GdkEvent * event);
+
+/* Default signal handlers
+ */
+static void gtk_secure_entry_real_insert_text(GtkEditable * editable,
+                         const gchar * new_text,
+                       gint new_text_length,
+                         gint * position);
+static void gtk_secure_entry_real_delete_text(GtkEditable * editable,
+                          gint start_pos,
+                       gint end_pos);
+static void gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
+                     GtkMovementStep step,
+                     gint count,
+                   gboolean extend_selection);
+static void gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
+                         const gchar * str);
+static void gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
+                        GtkDeleteType type,
+                       gint count);
+static void gtk_secure_entry_real_activate(GtkSecureEntry * entry);
+
+static void gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
+                              GtkSecureEntry *
+                              entry);
+/* IM Context Callbacks
+ */
+static void gtk_secure_entry_commit_cb(GtkIMContext * context,
+                      const gchar * str,
+                    GtkSecureEntry * entry);
+static void gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
+                      GtkSecureEntry * entry);
+static gboolean gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext *
+                          context,
+                          GtkSecureEntry *
+                          entry);
+static gboolean gtk_secure_entry_delete_surrounding_cb(GtkIMContext *
+                              context,
+                              gint offset,
+                              gint n_chars,
+                             GtkSecureEntry *
+                              entry);
+
+/* Internal routines
+ */
+static void gtk_secure_entry_enter_text(GtkSecureEntry * entry,
+                  const gchar * str);
+static void gtk_secure_entry_set_positions(GtkSecureEntry * entry,
+                      gint current_pos,
+                     gint selection_bound);
+static void gtk_secure_entry_draw_text(GtkSecureEntry * entry);
+static void gtk_secure_entry_draw_cursor(GtkSecureEntry * entry);
+static PangoLayout *gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
+                        gboolean
+                          include_preedit);
+static void gtk_secure_entry_reset_layout(GtkSecureEntry * entry);
+static void gtk_secure_entry_queue_draw(GtkSecureEntry * entry);
+static void gtk_secure_entry_reset_im_context(GtkSecureEntry * entry);
+static void gtk_secure_entry_recompute(GtkSecureEntry * entry);
+static gint gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x);
+static void gtk_secure_entry_get_cursor_locations(GtkSecureEntry * entry,
+                         gint * strong_x,
+                          gint * weak_x);
+static void gtk_secure_entry_adjust_scroll(GtkSecureEntry * entry);
+static gint gtk_secure_entry_move_visually(GtkSecureEntry * editable,
+                    gint start, gint count);
+static gint gtk_secure_entry_move_logically(GtkSecureEntry * entry,
+                      gint start, gint count);
+static gboolean gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
+                         gboolean group_cycling);
+static void gtk_secure_entry_state_changed(GtkWidget * widget,
+                      GtkStateType previous_state);
+static void gtk_secure_entry_check_cursor_blink(GtkSecureEntry * entry);
+static void gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * entry);
+static void get_text_area_size(GtkSecureEntry * entry,
+                  gint * x,
+                 gint * y, gint * width, gint * height);
+static void get_widget_window_size(GtkSecureEntry * 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;
+
+gboolean g_use_secure_mem = FALSE;
+
+#  define g_sec_new(type, count)    \
+      ((type *) g_sec_malloc ((unsigned) sizeof (type) * (count)))
+
+#define WITH_SECURE_MEM(EXP) do { \
+                    gboolean tmp = g_use_secure_mem; \
+                    g_use_secure_mem = TRUE; \
+                    EXP; \
+                    g_use_secure_mem = tmp; \
+             } while(0)
+
+
+gpointer
+g_malloc(gulong size)
+{
+    gpointer p;
+
+    if (size == 0)
+ return NULL;
+
+    if (g_use_secure_mem)
+ p = (gpointer) secmem_malloc(size);
+    else
+ p = (gpointer) malloc(size);
+    if (!p)
+ g_error("could not allocate %ld bytes", size);
+
+    return p;
+}
+
+gpointer
+g_malloc0(gulong size)
+{
+    gpointer p;
+
+    if (size == 0)
+  return NULL;
+
+    if (g_use_secure_mem) {
+   p = (gpointer) secmem_malloc(size);
+   if (p)
+        memset(p, 0, size);
+    } else
+   p = (gpointer) calloc(size, 1);
+    if (!p)
+  g_error("could not allocate %ld bytes", size);
+
+    return p;
+}
+
+gpointer
+g_realloc(gpointer mem, gulong size)
+{
+    gpointer p;
+
+    if (size == 0) {
+  g_free(mem);
+
+    return NULL;
+    }
+
+    if (!mem) {
+    if (g_use_secure_mem)
+     p = (gpointer) secmem_malloc(size);
+   else
+      p = (gpointer) malloc(size);
+    } else {
+    if (g_use_secure_mem) {
+       g_assert(m_is_secure(mem));
+       p = (gpointer) secmem_realloc(mem, size);
+ } else
+        p = (gpointer) realloc(mem, size);
+    }
+
+    if (!p)
+  g_error("could not reallocate %lu bytes", (gulong) size);
+
+    return p;
+}
+
+void
+g_free(gpointer mem)
+{
+    if (mem) {
+    if (m_is_secure(mem))
+     secmem_free(mem);
+ else
+      free(mem);
+    }
+}
+
+GType
+gtk_secure_entry_get_type(void)
+{
+    static GType entry_type = 0;
+
+    if (!entry_type) {
+ static const GTypeInfo entry_info = {
+     sizeof(GtkSecureEntryClass),
+      NULL,       /* base_init */
+       NULL,       /* base_finalize */
+       (GClassInitFunc) gtk_secure_entry_class_init,
+     NULL,       /* class_finalize */
+      NULL,       /* class_data */
+      sizeof(GtkSecureEntry),
+       0,          /* n_preallocs */
+     (GInstanceInitFunc) gtk_secure_entry_init,
+    };
+
+  static const GInterfaceInfo editable_info = {
+     (GInterfaceInitFunc) gtk_secure_entry_editable_init,    /* interface_init */
+      NULL,       /* interface_finalize */
+      NULL        /* interface_data */
+  };
+
+  static const GInterfaceInfo cell_editable_info = {
+        (GInterfaceInitFunc) gtk_secure_entry_cell_editable_init,   /* interface_init */
+      NULL,       /* interface_finalize */
+      NULL        /* interface_data */
+  };
+
+  entry_type =
+      g_type_register_static(GTK_TYPE_WIDGET, "GtkSecureEntry",
+                &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
+gtk_secure_entry_class_init(GtkSecureEntryClass * 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 = gtk_secure_entry_finalize;
+    gobject_class->set_property = gtk_secure_entry_set_property;
+    gobject_class->get_property = gtk_secure_entry_get_property;
+
+    widget_class->realize = gtk_secure_entry_realize;
+    widget_class->unrealize = gtk_secure_entry_unrealize;
+    widget_class->size_request = gtk_secure_entry_size_request;
+    widget_class->size_allocate = gtk_secure_entry_size_allocate;
+    widget_class->expose_event = gtk_secure_entry_expose;
+    widget_class->button_press_event = gtk_secure_entry_button_press;
+    widget_class->button_release_event = gtk_secure_entry_button_release;
+    widget_class->motion_notify_event = gtk_secure_entry_motion_notify;
+    widget_class->key_press_event = gtk_secure_entry_key_press;
+    widget_class->key_release_event = gtk_secure_entry_key_release;
+    widget_class->focus_in_event = gtk_secure_entry_focus_in;
+    widget_class->focus_out_event = gtk_secure_entry_focus_out;
+    widget_class->grab_focus = gtk_secure_entry_grab_focus;
+    widget_class->style_set = gtk_secure_entry_style_set;
+    widget_class->direction_changed = gtk_secure_entry_direction_changed;
+    widget_class->state_changed = gtk_secure_entry_state_changed;
+    widget_class->screen_changed = gtk_secure_entry_screen_changed;
+    widget_class->mnemonic_activate = gtk_secure_entry_mnemonic_activate;
+
+    class->move_cursor = gtk_secure_entry_move_cursor;
+    class->insert_at_cursor = gtk_secure_entry_insert_at_cursor;
+    class->delete_from_cursor = gtk_secure_entry_delete_from_cursor;
+    class->activate = gtk_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));
+
+    /* Action signals */
+
+    signals[ACTIVATE] =
+    g_signal_new("activate",
+           G_OBJECT_CLASS_TYPE(gobject_class),
+           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+          G_STRUCT_OFFSET(GtkSecureEntryClass, 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(GtkSecureEntryClass, 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(GtkSecureEntryClass,
+                  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(GtkSecureEntryClass,
+                  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);
+
+    gtk_settings_install_property(g_param_spec_boolean
+                 ("gtk-entry-select-on-focus",
+                  _("Select on focus"),
+                 _
+                 ("Whether to select the contents of an entry when it is focused"),
+                TRUE, G_PARAM_READWRITE));
+}
+
+static void
+gtk_secure_entry_editable_init(GtkEditableClass * iface)
+{
+    iface->do_insert_text = gtk_secure_entry_insert_text;
+    iface->do_delete_text = gtk_secure_entry_delete_text;
+    iface->insert_text = gtk_secure_entry_real_insert_text;
+    iface->delete_text = gtk_secure_entry_real_delete_text;
+    iface->set_selection_bounds = gtk_secure_entry_set_selection_bounds;
+    iface->get_selection_bounds = gtk_secure_entry_get_selection_bounds;
+    iface->set_position = gtk_secure_entry_real_set_position;
+    iface->get_position = gtk_secure_entry_get_position;
+}
+
+static void
+gtk_secure_entry_cell_editable_init(GtkCellEditableIface * iface)
+{
+    iface->start_editing = gtk_secure_entry_start_editing;
+}
+
+static void
+gtk_secure_entry_set_property(GObject * object,
+                guint prop_id,
+                const GValue * value, GParamSpec * pspec)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(object);
+
+    switch (prop_id) {
+    case PROP_MAX_LENGTH:
+    gtk_secure_entry_set_max_length(entry, g_value_get_int(value));
+   break;
+
+    case PROP_HAS_FRAME:
+    gtk_secure_entry_set_has_frame(entry, g_value_get_boolean(value));
+    break;
+
+    case PROP_INVISIBLE_CHAR:
+   gtk_secure_entry_set_invisible_char(entry,
+                        g_value_get_uint(value));
+ break;
+
+    case PROP_ACTIVATES_DEFAULT:
+    gtk_secure_entry_set_activates_default(entry,
+                        g_value_get_boolean(value));
+   break;
+
+    case PROP_WIDTH_CHARS:
+  gtk_secure_entry_set_width_chars(entry, g_value_get_int(value));
+  break;
+
+    case PROP_TEXT:
+ gtk_secure_entry_set_text(entry, g_value_get_string(value));
+  break;
+
+    case PROP_SCROLL_OFFSET:
+    case PROP_CURSOR_POSITION:
+    default:
+  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+    }
+}
+
+static void
+gtk_secure_entry_get_property(GObject * object,
+                guint prop_id,
+                GValue * value, GParamSpec * pspec)
+{
+    GtkSecureEntry *entry = GTK_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, gtk_secure_entry_get_text(entry));
+  break;
+
+    default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+    }
+}
+
+static void
+gtk_secure_entry_init(GtkSecureEntry * entry)
+{
+    GTK_WIDGET_SET_FLAGS(entry, GTK_CAN_FOCUS);
+
+    entry->text_size = MIN_SIZE;
+    WITH_SECURE_MEM(entry->text = g_malloc(entry->text_size));
+    entry->text[0] = '\0';
+
+    entry->invisible_char = '*';
+    entry->width_chars = -1;
+    entry->is_cell_renderer = FALSE;
+    entry->editing_canceled = FALSE;
+    entry->has_frame = TRUE;
+
+    /* 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(gtk_secure_entry_commit_cb), entry);
+    g_signal_connect(entry->im_context, "preedit_changed",
+           G_CALLBACK(gtk_secure_entry_preedit_changed_cb),
+          entry);
+    g_signal_connect(entry->im_context, "retrieve_surrounding",
+          G_CALLBACK(gtk_secure_entry_retrieve_surrounding_cb),
+             entry);
+    g_signal_connect(entry->im_context, "delete_surrounding",
+            G_CALLBACK(gtk_secure_entry_delete_surrounding_cb),
+           entry);
+}
+
+static void
+gtk_secure_entry_finalize(GObject * object)
+{
+    GtkSecureEntry *entry = GTK_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)
+  WITH_SECURE_MEM(g_free(entry->text));
+    entry->text = NULL;
+
+    G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+gtk_secure_entry_realize(GtkWidget * widget)
+{
+    GtkSecureEntry *entry;
+    GtkEditable *editable;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    entry = GTK_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);
+
+    gtk_secure_entry_adjust_scroll(entry);
+}
+
+static void
+gtk_secure_entry_unrealize(GtkWidget * widget)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_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(GtkSecureEntry * 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
+gtk_secure_entry_size_request(GtkWidget * widget,
+                GtkRequisition * requisition)
+{
+    GtkSecureEntry *entry = GTK_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_SECURE_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(GtkSecureEntry * 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(GtkSecureEntry * 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
+gtk_secure_entry_size_allocate(GtkWidget * widget,
+                   GtkAllocation * allocation)
+{
+    GtkSecureEntry *entry = GTK_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);
+
+  gtk_secure_entry_recompute(entry);
+    }
+}
+
+static void
+gtk_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
+gtk_secure_entry_expose(GtkWidget * widget, GdkEventExpose * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (widget->window == event->window)
+    gtk_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)
+     gtk_secure_entry_draw_cursor(GTK_SECURE_ENTRY(widget));
+
+ gtk_secure_entry_draw_text(GTK_SECURE_ENTRY(widget));
+    }
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_button_press(GtkWidget * widget, GdkEventButton * event)
+{
+    GtkSecureEntry *entry = GTK_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 =
+  gtk_secure_entry_find_position(entry,
+                    event->x + entry->scroll_offset);
+
+    if (event->button == 1) {
+ switch (event->type) {
+    case GDK_BUTTON_PRESS:
+        gtk_secure_entry_set_positions(entry, tmp_pos, tmp_pos);
+      break;
+
+  default:
+      break;
+    }
+
+   return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_button_release(GtkWidget * widget, GdkEventButton * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    if (event->window != entry->text_area
+    || entry->button != event->button)
+    return FALSE;
+
+    entry->button = 0;
+
+    return TRUE;
+}
+
+static gint
+gtk_secure_entry_motion_notify(GtkWidget * widget, GdkEventMotion * event)
+{
+    GtkSecureEntry *entry = GTK_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 =
+     gtk_secure_entry_find_position(entry,
+                        event->x +
+                        entry->scroll_offset);
+
+   gtk_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
+gtk_secure_entry_obscure_mouse_cursor(GtkSecureEntry * entry)
+{
+    if (entry->mouse_cursor_obscured)
+ return;
+
+    set_invisible_cursor(entry->text_area);
+
+    entry->mouse_cursor_obscured = TRUE;
+}
+
+static gint
+gtk_secure_entry_key_press(GtkWidget * widget, GdkEventKey * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+
+    if (gtk_im_context_filter_keypress(entry->im_context, event)) {
+      gtk_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
+gtk_secure_entry_key_release(GtkWidget * widget, GdkEventKey * event)
+{
+    GtkSecureEntry *entry = GTK_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
+gtk_secure_entry_focus_in(GtkWidget * widget, GdkEventFocus * event)
+{
+    GtkSecureEntry *entry = GTK_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(gtk_secure_entry_keymap_direction_changed),
+            entry);
+
+    gtk_secure_entry_check_cursor_blink(entry);
+
+    return FALSE;
+}
+
+static gint
+gtk_secure_entry_focus_out(GtkWidget * widget, GdkEventFocus * event)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_widget_queue_draw(widget);
+
+    entry->need_im_reset = TRUE;
+    gtk_im_context_focus_out(entry->im_context);
+
+    gtk_secure_entry_check_cursor_blink(entry);
+
+    g_signal_handlers_disconnect_by_func(gdk_keymap_get_for_display
+                  (gtk_widget_get_display(widget)),
+                     gtk_secure_entry_keymap_direction_changed,
+                    entry);
+
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_grab_focus(GtkWidget * widget)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+    gboolean select_on_focus;
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_DEFAULT);
+    GTK_WIDGET_CLASS(parent_class)->grab_focus(widget);
+
+    g_object_get(gtk_widget_get_settings(widget),
+      "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
+gtk_secure_entry_direction_changed(GtkWidget * widget,
+                   GtkTextDirection previous_dir)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_secure_entry_recompute(entry);
+
+    GTK_WIDGET_CLASS(parent_class)->direction_changed(widget,
+                              previous_dir);
+}
+
+static void
+gtk_secure_entry_state_changed(GtkWidget * widget,
+                   GtkStateType previous_state)
+{
+    GtkSecureEntry *entry = GTK_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
+gtk_secure_entry_screen_changed(GtkWidget * widget, GdkScreen * old_screen)
+{
+    gtk_secure_entry_recompute(GTK_SECURE_ENTRY(widget));
+}
+
+/* GtkEditable method implementations
+ */
+static void
+gtk_secure_entry_insert_text(GtkEditable * editable,
+              const gchar * new_text,
+               gint new_text_length, gint * position)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+    gchar *text;
+
+    if (*position < 0 || *position > entry->text_length)
+ *position = entry->text_length;
+
+    g_object_ref(editable);
+
+    WITH_SECURE_MEM(text = g_new(gchar, new_text_length + 1));
+
+    text[new_text_length] = '\0';
+    strncpy(text, new_text, new_text_length);
+
+    g_signal_emit_by_name(editable, "insert_text", text, new_text_length,
+           position);
+
+    WITH_SECURE_MEM(g_free(text));
+
+    g_object_unref(editable);
+}
+
+static void
+gtk_secure_entry_delete_text(GtkEditable * editable,
+                gint start_pos, gint end_pos)
+{
+    GtkSecureEntry *entry = GTK_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
+gtk_secure_entry_set_position_internal(GtkSecureEntry * 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)
+     gtk_secure_entry_reset_im_context(entry);
+ gtk_secure_entry_set_positions(entry, position, position);
+    }
+}
+
+static void
+gtk_secure_entry_real_set_position(GtkEditable * editable, gint position)
+{
+    gtk_secure_entry_set_position_internal(GTK_SECURE_ENTRY(editable),
+                    position, TRUE);
+}
+
+static gint
+gtk_secure_entry_get_position(GtkEditable * editable)
+{
+    return GTK_SECURE_ENTRY(editable)->current_pos;
+}
+
+static void
+gtk_secure_entry_set_selection_bounds(GtkEditable * editable,
+                   gint start, gint end)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    if (start < 0)
+ start = entry->text_length;
+    if (end < 0)
+ end = entry->text_length;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    gtk_secure_entry_set_positions(entry,
+                  MIN(end, entry->text_length),
+                 MIN(start, entry->text_length));
+}
+
+static gboolean
+gtk_secure_entry_get_selection_bounds(GtkEditable * editable,
+                    gint * start, gint * end)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(editable);
+
+    *start = entry->selection_bound;
+    *end = entry->current_pos;
+
+    return (entry->selection_bound != entry->current_pos);
+}
+
+static void
+gtk_secure_entry_style_set(GtkWidget * widget, GtkStyle * previous_style)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(widget);
+
+    gtk_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(GtkSecureEntry * 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(GtkSecureEntry * 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
+gtk_secure_entry_start_editing(GtkCellEditable * cell_editable,
+                  GdkEvent * event)
+{
+    GTK_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
+gtk_secure_entry_real_insert_text(GtkEditable * editable,
+                const gchar * new_text,
+               gint new_text_length, gint * position)
+{
+    gint _index;
+    gint n_chars;
+
+    GtkSecureEntry *entry = GTK_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;
+        }
+     }
+ }
+
+   WITH_SECURE_MEM(entry->text =
+         g_realloc(entry->text, entry->text_size));
+    }
+
+    _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;
+
+    gtk_secure_entry_recompute(entry);
+
+    g_signal_emit_by_name(editable, "changed");
+    g_object_notify(G_OBJECT(editable), "text");
+}
+
+static void
+gtk_secure_entry_real_delete_text(GtkEditable * editable,
+                 gint start_pos, gint end_pos)
+{
+    GtkSecureEntry *entry = GTK_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;
+
+   gtk_secure_entry_set_positions(entry, current_pos,
+                       selection_bound);
+
+    gtk_secure_entry_recompute(entry);
+
+  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(GtkSecureEntry * 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 = gtk_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
+gtk_secure_entry_move_cursor(GtkSecureEntry * entry,
+                GtkMovementStep step,
+                 gint count, gboolean extend_selection)
+{
+    gint new_pos = entry->current_pos;
+
+    gtk_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 =
+     gtk_secure_entry_move_logically(entry, new_pos, count);
+       break;
+    case GTK_MOVEMENT_VISUAL_POSITIONS:
+       new_pos =
+     gtk_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);
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+}
+
+static void
+gtk_secure_entry_insert_at_cursor(GtkSecureEntry * entry,
+                 const gchar * str)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint pos = entry->current_pos;
+
+    gtk_secure_entry_reset_im_context(entry);
+
+    gtk_editable_insert_text(editable, str, -1, &pos);
+    gtk_editable_set_position(editable, pos);
+}
+
+static void
+gtk_secure_entry_delete_from_cursor(GtkSecureEntry * entry,
+                    GtkDeleteType type, gint count)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint start_pos = entry->current_pos;
+    gint end_pos = entry->current_pos;
+
+    gtk_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 =
+     gtk_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;
+    }
+
+    gtk_secure_entry_pend_cursor_blink(entry);
+}
+
+static void
+gtk_secure_entry_delete_cb(GtkSecureEntry * entry)
+{
+    GtkEditable *editable = GTK_EDITABLE(entry);
+    gint start, end;
+
+    if (gtk_editable_get_selection_bounds(editable, &start, &end))
+      gtk_editable_delete_text(editable, start, end);
+}
+
+static void
+gtk_secure_entry_toggle_overwrite(GtkSecureEntry * entry)
+{
+    entry->overwrite_mode = !entry->overwrite_mode;
+}
+
+static void
+gtk_secure_entry_real_activate(GtkSecureEntry * 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
+gtk_secure_entry_keymap_direction_changed(GdkKeymap * keymap,
+                   GtkSecureEntry * entry)
+{
+    gtk_secure_entry_recompute(entry);
+}
+
+/* IM Context Callbacks
+ */
+
+static void
+gtk_secure_entry_commit_cb(GtkIMContext * context,
+               const gchar * str, GtkSecureEntry * entry)
+{
+    gtk_secure_entry_enter_text(entry, str);
+}
+
+static void
+gtk_secure_entry_preedit_changed_cb(GtkIMContext * context,
+                 GtkSecureEntry * 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);
+
+    gtk_secure_entry_recompute(entry);
+}
+
+static gboolean
+gtk_secure_entry_retrieve_surrounding_cb(GtkIMContext * context,
+                     GtkSecureEntry * 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
+gtk_secure_entry_delete_surrounding_cb(GtkIMContext * slave,
+                     gint offset,
+                      gint n_chars,
+                     GtkSecureEntry * 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
+gtk_secure_entry_enter_text(GtkSecureEntry * 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)
+        gtk_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);
+    gtk_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
+gtk_secure_entry_set_positions(GtkSecureEntry * 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)
+ gtk_secure_entry_recompute(entry);
+}
+
+static void
+gtk_secure_entry_reset_layout(GtkSecureEntry * entry)
+{
+    if (entry->cached_layout) {
+   g_object_unref(entry->cached_layout);
+ entry->cached_layout = NULL;
+    }
+}
+
+static void
+update_im_cursor_location(GtkSecureEntry * entry)
+{
+    GdkRectangle area;
+    gint strong_x;
+    gint strong_xoffset;
+    gint area_width, area_height;
+
+    gtk_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)
+{
+    GtkSecureEntry *entry;
+
+    GDK_THREADS_ENTER();
+
+    entry = GTK_SECURE_ENTRY(data);
+
+    entry->recompute_idle = 0;
+
+    if (gtk_widget_has_screen(GTK_WIDGET(entry))) {
+ gtk_secure_entry_adjust_scroll(entry);
+    gtk_secure_entry_queue_draw(entry);
+
+ update_im_cursor_location(entry);
+    }
+
+    GDK_THREADS_LEAVE();
+
+    return FALSE;
+}
+
+static void
+gtk_secure_entry_recompute(GtkSecureEntry * entry)
+{
+    gtk_secure_entry_reset_layout(entry);
+    gtk_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 void
+append_char(GString * str, gunichar ch, gint count)
+{
+    gint i;
+    gint char_len;
+    gchar buf[7];
+
+    char_len = g_unichar_to_utf8(ch, buf);
+
+    i = 0;
+    while (i < count) {
+  g_string_append_len(str, buf, char_len);
+  ++i;
+    }
+}
+
+static PangoLayout *
+gtk_secure_entry_create_layout(GtkSecureEntry * 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 ch_len;
+  gint preedit_len_chars;
+   gunichar invisible_char;
+
+    ch_len = g_utf8_strlen(entry->text, entry->n_bytes);
+  preedit_len_chars = g_utf8_strlen(preedit_string, -1);
+    ch_len += preedit_len_chars;
+
+    if (entry->invisible_char != 0)
+     invisible_char = entry->invisible_char;
+ else
+    invisible_char = ' '; /* just pick a char */
+
+  append_char(tmp_string, invisible_char, ch_len);
+
+    /* 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);
+        gunichar invisible_char;
+
+        if (entry->invisible_char != 0)
+       invisible_char = entry->invisible_char;
+       else
+      invisible_char = ' ';   /* just pick a char */
+
+      append_char(str, invisible_char, entry->text_length);
+     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 *
+gtk_secure_entry_ensure_layout(GtkSecureEntry * entry,
+                  gboolean include_preedit)
+{
+    if (entry->preedit_length > 0 &&
+ !include_preedit != !entry->cache_includes_preedit)
+   gtk_secure_entry_reset_layout(entry);
+
+    if (!entry->cached_layout) {
+ entry->cached_layout =
+        gtk_secure_entry_create_layout(entry, include_preedit);
+   entry->cache_includes_preedit = include_preedit;
+    }
+
+    return entry->cached_layout;
+}
+
+static void
+get_layout_position(GtkSecureEntry * entry, gint * x, gint * y)
+{
+    PangoLayout *layout;
+    PangoRectangle logical_rect;
+    gint area_width, area_height;
+    gint y_pos;
+    PangoLayoutLine *line;
+
+    layout = gtk_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
+gtk_secure_entry_draw_text(GtkSecureEntry * entry)
+{
+    GtkWidget *widget;
+    PangoLayoutLine *line;
+
+    if (entry->invisible_char == 0)
+   return;
+
+    if (GTK_WIDGET_DRAWABLE(entry)) {
+  PangoLayout *layout = gtk_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(GtkSecureEntry * 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
+gtk_secure_entry_draw_cursor(GtkSecureEntry * 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);
+
+   gtk_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
+gtk_secure_entry_queue_draw(GtkSecureEntry * entry)
+{
+    if (GTK_WIDGET_REALIZED(entry))
+   gdk_window_invalidate_rect(entry->text_area, NULL, FALSE);
+}
+
+static void
+gtk_secure_entry_reset_im_context(GtkSecureEntry * entry)
+{
+    if (entry->need_im_reset) {
+   entry->need_im_reset = 0;
+ gtk_im_context_reset(entry->im_context);
+    }
+}
+
+static gint
+gtk_secure_entry_find_position(GtkSecureEntry * entry, gint x)
+{
+    PangoLayout *layout;
+    PangoLayoutLine *line;
+    gint _index;
+    gint pos;
+    gboolean trailing;
+    const gchar *text;
+    gint cursor_index;
+
+    layout = gtk_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
+gtk_secure_entry_get_cursor_locations(GtkSecureEntry * 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 = gtk_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
+gtk_secure_entry_adjust_scroll(GtkSecureEntry * 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 = gtk_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.
+     */
+
+    gtk_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
+gtk_secure_entry_move_visually(GtkSecureEntry * entry,
+                   gint start, gint count)
+{
+    gint _index;
+    PangoLayout *layout = gtk_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
+gtk_secure_entry_move_logically(GtkSecureEntry * 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 *
+gtk_secure_entry_new(void)
+{
+    return g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
+}
+
+/**
+ * gtk_secure_entry_new_with_max_length:
+ * @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.
+ *
+ * Creates a new #GtkSecureEntry widget with the given maximum length.
+ * 
+ * Note: the existence of this function is inconsistent
+ * with the rest of the GTK+ API. The normal setup would
+ * be to just require the user to make an extra call
+ * to gtk_secure_entry_set_max_length() instead. It is not
+ * expected that this function will be removed, but
+ * it would be better practice not to use it.
+ * 
+ * Return value: a new #GtkSecureEntry.
+ **/
+GtkWidget *
+gtk_secure_entry_new_with_max_length(gint max)
+{
+    GtkSecureEntry *entry;
+
+    max = CLAMP(max, 0, MAX_SIZE);
+
+    entry = g_object_new(GTK_TYPE_SECURE_ENTRY, NULL);
+    entry->text_max_length = max;
+
+    return GTK_WIDGET(entry);
+}
+
+void
+gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_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
+gtk_secure_entry_append_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    g_return_if_fail(text != NULL);
+
+    tmp_pos = entry->text_length;
+    gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
+}
+
+void
+gtk_secure_entry_prepend_text(GtkSecureEntry * entry, const gchar * text)
+{
+    gint tmp_pos;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    g_return_if_fail(text != NULL);
+
+    tmp_pos = 0;
+    gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &tmp_pos);
+}
+
+void
+gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    gtk_editable_set_position(GTK_EDITABLE(entry), position);
+}
+
+/**
+ * gtk_secure_entry_set_invisible_char:
+ * @entry: a #GtkSecureEntry
+ * @ch: a Unicode character
+ * 
+ * Sets the character to use in place of the actual text when
+ * gtk_secure_entry_set_visibility() has been called to set text visibility
+ * 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
+gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    if (ch == entry->invisible_char)
+    return;
+
+    entry->invisible_char = ch;
+    g_object_notify(G_OBJECT(entry), "invisible_char");
+    gtk_secure_entry_recompute(entry);
+}
+
+/**
+ * gtk_secure_entry_get_invisible_char:
+ * @entry: a #GtkSecureEntry
+ *
+ * Retrieves the character displayed in place of the real characters
+ * for entries with visisbility set to false. See gtk_secure_entry_set_invisible_char().
+ *
+ * Return value: the current invisible char, or 0, if the entry does not
+ *               show invisible text at all. 
+ **/
+gunichar
+gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->invisible_char;
+}
+
+/**
+ * gtk_secure_entry_get_text:
+ * @entry: a #GtkSecureEntry
+ *
+ * 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 *
+gtk_secure_entry_get_text(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
+
+    return entry->text;
+}
+
+void
+gtk_secure_entry_select_region(GtkSecureEntry * entry,
+                 gint start, gint end)
+{
+    gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
+}
+
+/**
+ * gtk_secure_entry_set_max_length:
+ * @entry: a #GtkSecureEntry.
+ * @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
+gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max)
+{
+    g_return_if_fail(GTK_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");
+}
+
+/**
+ * gtk_secure_entry_get_max_length:
+ * @entry: a #GtkSecureEntry
+ *
+ * Retrieves the maximum allowed length of the text in
+ * @entry. See gtk_secure_entry_set_max_length().
+ *
+ * Return value: the maximum allowed number of characters
+ *               in #GtkSecureEntry, or 0 if there is no maximum.
+ **/
+gint
+gtk_secure_entry_get_max_length(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->text_max_length;
+}
+
+/**
+ * gtk_secure_entry_set_activates_default:
+ * @entry: a #GtkSecureEntry
+ * @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
+gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
+                      gboolean setting)
+{
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+    setting = setting != FALSE;
+
+    if (setting != entry->activates_default) {
+ entry->activates_default = setting;
+   g_object_notify(G_OBJECT(entry), "activates_default");
+    }
+}
+
+/**
+ * gtk_secure_entry_get_activates_default:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Retrieves the value set by gtk_secure_entry_set_activates_default().
+ * 
+ * Return value: %TRUE if the entry will activate the default widget
+ **/
+gboolean
+gtk_secure_entry_get_activates_default(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
+
+    return entry->activates_default;
+}
+
+/**
+ * gtk_secure_entry_set_width_chars:
+ * @entry: a #GtkSecureEntry
+ * @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
+gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars)
+{
+    g_return_if_fail(GTK_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));
+    }
+}
+
+/**
+ * gtk_secure_entry_get_width_chars:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the value set by gtk_secure_entry_set_width_chars().
+ * 
+ * Return value: number of chars to request space for, or negative if unset
+ **/
+gint
+gtk_secure_entry_get_width_chars(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    return entry->width_chars;
+}
+
+/**
+ * gtk_secure_entry_set_has_frame:
+ * @entry: a #GtkSecureEntry
+ * @setting: new value
+ * 
+ * Sets whether the entry has a beveled frame around it.
+ **/
+void
+gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting)
+{
+    g_return_if_fail(GTK_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");
+}
+
+/**
+ * gtk_secure_entry_get_has_frame:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the value set by gtk_secure_entry_set_has_frame().
+ * 
+ * Return value: whether the entry has a beveled frame
+ **/
+gboolean
+gtk_secure_entry_get_has_frame(GtkSecureEntry * entry)
+{
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), FALSE);
+
+    return entry->has_frame;
+}
+
+
+/**
+ * gtk_secure_entry_get_layout:
+ * @entry: a #GtkSecureEntry
+ * 
+ * Gets the #PangoLayout used to display the entry.
+ * The layout is useful to e.g. convert text positions to
+ * pixel positions, in combination with gtk_secure_entry_get_layout_offsets().
+ * The returned layout is owned by the entry so need not be
+ * freed by the caller.
+ *
+ * Keep in mind that the layout text may contain a preedit string, so
+ * gtk_secure_entry_layout_index_to_text_index() and
+ * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
+ * indices in the layout to byte indices in the entry contents.
+ * 
+ * Return value: the #PangoLayout for this entry
+ **/
+PangoLayout *
+gtk_secure_entry_get_layout(GtkSecureEntry * entry)
+{
+    PangoLayout *layout;
+
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), NULL);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+
+    return layout;
+}
+
+
+/**
+ * gtk_secure_entry_layout_index_to_text_index:
+ * @entry: a #GtkSecureEntry
+ * @layout_index: byte index into the entry layout text
+ * 
+ * Converts from a position in the entry contents (returned
+ * by gtk_secure_entry_get_text()) to a position in the
+ * entry's #PangoLayout (returned by gtk_secure_entry_get_layout(),
+ * with text retrieved via pango_layout_get_text()).
+ * 
+ * Return value: byte index into the entry contents
+ **/
+gint
+gtk_secure_entry_layout_index_to_text_index(GtkSecureEntry * entry,
+                      gint layout_index)
+{
+    PangoLayout *layout;
+    const gchar *text;
+    gint cursor_index;
+
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    text = pango_layout_get_text(layout);
+    cursor_index =
+   g_utf8_offset_to_pointer(text, entry->current_pos) - text;
+
+    if (layout_index >= cursor_index && entry->preedit_length) {
+    if (layout_index >= cursor_index + entry->preedit_length)
+     layout_index -= entry->preedit_length;
+    else
+      layout_index = cursor_index;
+    }
+
+    return layout_index;
+}
+
+/**
+ * gtk_secure_entry_text_index_to_layout_index:
+ * @entry: a #GtkSecureEntry
+ * @text_index: byte index into the entry contents
+ * 
+ * Converts from a position in the entry's #PangoLayout(returned by
+ * gtk_secure_entry_get_layout()) to a position in the entry contents
+ * (returned by gtk_secure_entry_get_text()).
+ * 
+ * Return value: byte index into the entry layout text
+ **/
+gint
+gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry,
+                        gint text_index)
+{
+    PangoLayout *layout;
+    const gchar *text;
+    gint cursor_index;
+    g_return_val_if_fail(GTK_IS_SECURE_ENTRY(entry), 0);
+
+    layout = gtk_secure_entry_ensure_layout(entry, TRUE);
+    text = pango_layout_get_text(layout);
+    cursor_index =
+   g_utf8_offset_to_pointer(text, entry->current_pos) - text;
+
+    if (text_index > cursor_index)
+  text_index += entry->preedit_length;
+
+    return text_index;
+}
+
+/**
+ * gtk_secure_entry_get_layout_offsets:
+ * @entry: a #GtkSecureEntry
+ * @x: location to store X offset of layout, or %NULL
+ * @y: location to store Y offset of layout, or %NULL
+ *
+ *
+ * Obtains the position of the #PangoLayout used to render text
+ * in the entry, in widget coordinates. Useful if you want to line
+ * up the text in an entry with some other text, e.g. when using the
+ * entry to implement editable cells in a sheet widget.
+ *
+ * Also useful to convert mouse events into coordinates inside the
+ * #PangoLayout, e.g. to take some action if some part of the entry text
+ * is clicked.
+ * 
+ * Note that as the user scrolls around in the entry the offsets will
+ * change; you'll need to connect to the "notify::scroll_offset"
+ * signal to track this. Remember when using the #PangoLayout
+ * functions you need to convert to and from pixels using
+ * PANGO_PIXELS() or #PANGO_SCALE.
+ *
+ * Keep in mind that the layout text may contain a preedit string, so
+ * gtk_secure_entry_layout_index_to_text_index() and
+ * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte
+ * indices in the layout to byte indices in the entry contents.
+ * 
+ **/
+void
+gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
+                  gint * x, gint * y)
+{
+    gint text_area_x, text_area_y;
+
+    g_return_if_fail(GTK_IS_SECURE_ENTRY(entry));
+
+    /* this gets coords relative to text area */
+    get_layout_position(entry, x, y);
+
+    /* convert to widget coords */
+    get_text_area_size(entry, &text_area_x, &text_area_y, NULL, NULL);
+
+    if (x)
+    *x += text_area_x;
+
+    if (y)
+  *y += text_area_y;
+}
+
+
+/* Quick hack of a popup menu
+ */
+static void
+activate_cb(GtkWidget * menuitem, GtkSecureEntry * entry)
+{
+    const gchar *signal =
+   g_object_get_data(G_OBJECT(menuitem), "gtk-signal");
+    g_signal_emit_by_name(entry, signal);
+}
+
+
+static gboolean
+gtk_secure_entry_mnemonic_activate(GtkWidget * widget,
+                  gboolean group_cycling)
+{
+    gtk_widget_grab_focus(widget);
+    return TRUE;
+}
+
+
+static void
+unichar_chosen_func(const char *text, gpointer data)
+{
+    GtkSecureEntry *entry = GTK_SECURE_ENTRY(data);
+
+    gtk_secure_entry_enter_text(entry, text);
+}
+
+/* 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(GtkSecureEntry * 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(GtkSecureEntry * 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(GtkSecureEntry * 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(GtkSecureEntry * 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)
+{
+    GtkSecureEntry *entry;
+
+    GDK_THREADS_ENTER();
+
+    entry = GTK_SECURE_ENTRY(data);
+
+    if (!GTK_WIDGET_HAS_FOCUS(entry)) {
+  g_warning
+     ("GtkSecureEntry - 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
+gtk_secure_entry_check_cursor_blink(GtkSecureEntry * 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
+gtk_secure_entry_pend_cursor_blink(GtkSecureEntry * 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 -u -N -r ../seahorse_orig/agent/gtk-secure-entry.h ./agent/gtk-secure-entry.h
--- ../seahorse_orig/agent/gtk-secure-entry.h	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/gtk-secure-entry.h	2004-09-25 14:21:18.000000000 -0000
@@ -0,0 +1,183 @@
+/* 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.
+ */
+
+#ifndef __GTK_SECURE_ENTRY_H__
+#define __GTK_SECURE_ENTRY_H__
+
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#ifdef MAKE_EMACS_HAPPY
+}
+#endif                /* MAKE_EMACS_HAPPY */
+#endif              /* __cplusplus */
+#define GTK_TYPE_SECURE_ENTRY                  (gtk_secure_entry_get_type ())
+#define GTK_SECURE_ENTRY(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SECURE_ENTRY, GtkSecureEntry))
+#define GTK_SECURE_ENTRY_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SECURE_ENTRY, GtkSecureEntryClass))
+#define GTK_IS_SECURE_ENTRY(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SECURE_ENTRY))
+#define GTK_IS_SECURE_ENTRY_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SECURE_ENTRY))
+#define GTK_SECURE_ENTRY_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SECURE_ENTRY, GtkSecureEntryClass))
+typedef struct _GtkSecureEntry GtkSecureEntry;
+typedef struct _GtkSecureEntryClass GtkSecureEntryClass;
+
+struct _GtkSecureEntry {
+    GtkWidget widget;
+
+    gchar *text;
+
+    guint overwrite_mode:1;
+
+    guint16 text_length; /* length in use, in chars */
+    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;
+
+    guint in_click:1;     /* Flag so we don't select all when clicking in entry to focus in */
+
+    guint is_cell_renderer:1;
+    guint editing_canceled:1;    /* Only used by GtkCellRendererText */
+
+    guint mouse_cursor_obscured:1;
+
+    guint resolved_dir : 4; /* PangoDirection */
+
+    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;
+};
+
+struct _GtkSecureEntryClass {
+    GtkWidgetClass parent_class;
+
+    /* Action signals
+     */
+    void (*activate) (GtkSecureEntry * entry);
+    void (*move_cursor) (GtkSecureEntry * entry,
+             GtkMovementStep step,
+             gint count, gboolean extend_selection);
+    void (*insert_at_cursor) (GtkSecureEntry * entry, const gchar * str);
+    void (*delete_from_cursor) (GtkSecureEntry * 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
+gtk_secure_entry_get_type(void)
+    G_GNUC_CONST;
+GtkWidget *
+gtk_secure_entry_new(void);
+void
+gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch);
+gunichar
+gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry);
+void
+gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting);
+gboolean
+gtk_secure_entry_get_has_frame(GtkSecureEntry * entry);
+/* text is truncated if needed */
+void
+gtk_secure_entry_set_max_length(GtkSecureEntry * entry, gint max);
+gint
+gtk_secure_entry_get_max_length(GtkSecureEntry * entry);
+void
+gtk_secure_entry_set_activates_default(GtkSecureEntry * entry,
+                    gboolean setting);
+gboolean
+gtk_secure_entry_get_activates_default(GtkSecureEntry * entry);
+
+void
+gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars);
+gint
+gtk_secure_entry_get_width_chars(GtkSecureEntry * entry);
+
+/* Somewhat more convenient than the GtkEditable generic functions
+ */
+void
+gtk_secure_entry_set_text(GtkSecureEntry * entry, const gchar * text);
+/* returns a reference to the text */
+G_CONST_RETURN gchar *
+gtk_secure_entry_get_text(GtkSecureEntry * entry);
+
+PangoLayout *
+gtk_secure_entry_get_layout(GtkSecureEntry * entry);
+void
+gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry,
+                  gint * x, gint * y);
+
+#ifdef __cplusplus
+}
+#endif               /* __cplusplus */
+
+
+#endif               /* __GTK_SECURE_ENTRY_H__ */
diff -u -N -r ../seahorse_orig/agent/Makefile.am ./agent/Makefile.am
--- ../seahorse_orig/agent/Makefile.am	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/Makefile.am	2004-09-29 12:44:38.000000000 -0000
@@ -0,0 +1,36 @@
+
+gladedir = $(datadir)/seahorse/glade/
+localedir = $(datadir)/locale
+pixmapsdir = $(datadir)/pixmaps/
+
+INCLUDES = -I$(top_builddir) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libseahorse \
+	$(SEAHORSE_CFLAGS) \
+	-DDATA_DIR=\""$(datadir)"\" \
+	-DSEAHORSE_GLADEDIR=\""$(gladedir)"\" \
+	-DLOCALEDIR=\""$(localedir)"\" \
+	-DPIXMAPSDIR=\""$(pixmapsdir)"\"
+
+bin_PROGRAMS = seahorse-agent
+
+seahorse_agent_SOURCES = seahorse-agent.c seahorse-agent.h seahorse-agent-actions.c \
+                         seahorse-agent-cache.c seahorse-agent-prompt.c seahorse-agent-io.c \
+                         gtk-secure-entry.c gtk-secure-entry.h seahorse-agent-secmem.h \
+                         seahorse-agent-secmem.c seahorse-agent-settings.c seahorse-agent-status.c \
+                         eggtrayicon.h eggtrayicon.c
+
+seahorse_agent_LDADD = \
+	$(top_builddir)/libseahorse/libseahorse.a \
+	$(SEAHORSE_LIBS)
+                         
+glade_DATA = \
+	seahorse-agent-cache.glade 
+
+EXTRA_DIST = \
+	$(glade_DATA) \
+	padlock-keyhole.xpm
+
+CLEANFILES = *.gladep* *.bak \
+	$(desktop_DATA)
+	
diff -u -N -r ../seahorse_orig/agent/padlock-keyhole.xpm ./agent/padlock-keyhole.xpm
--- ../seahorse_orig/agent/padlock-keyhole.xpm	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/padlock-keyhole.xpm	2004-09-25 12:28:06.000000000 -0000
@@ -0,0 +1,403 @@
+/* XPM */
+static const char * padlock_keyhole_xpm[] = {
+"48 48 352 2",
+"  	c None",
+". 	c #000000",
+"+ 	c #010101",
+"@ 	c #9B9B9B",
+"# 	c #D0D0D0",
+"$ 	c #E3E3E3",
+"% 	c #E9E9E9",
+"& 	c #DDDDDD",
+"* 	c #C6C6C6",
+"= 	c #919191",
+"- 	c #BEBEBE",
+"; 	c #EAEAEA",
+"> 	c #F1F1F1",
+", 	c #F3F3F3",
+"' 	c #EDEDED",
+") 	c #E6E6E6",
+"! 	c #E2E2E2",
+"~ 	c #D8D8D8",
+"{ 	c #9C9C9C",
+"] 	c #606060",
+"^ 	c #F0F0F0",
+"/ 	c #ECECEC",
+"( 	c #E4E4E4",
+"_ 	c #E1E1E1",
+": 	c #D6D6D6",
+"< 	c #B7B7B7",
+"[ 	c #454545",
+"} 	c #858585",
+"| 	c #D9D9D9",
+"1 	c #CECECE",
+"2 	c #C2C2C2",
+"3 	c #CCCCCC",
+"4 	c #D5D5D5",
+"5 	c #DCDCDC",
+"6 	c #E0E0E0",
+"7 	c #C8C8C8",
+"8 	c #B9B9B9",
+"9 	c #626262",
+"0 	c #5E5E5E",
+"a 	c #E8E8E8",
+"b 	c #BCBCBC",
+"c 	c #585858",
+"d 	c #141414",
+"e 	c #7B7B7B",
+"f 	c #DFDFDF",
+"g 	c #C3C3C3",
+"h 	c #BBBBBB",
+"i 	c #4C4C4C",
+"j 	c #979797",
+"k 	c #B6B6B6",
+"l 	c #EBEBEB",
+"m 	c #8A8A8A",
+"n 	c #929292",
+"o 	c #DBDBDB",
+"p 	c #B3B3B3",
+"q 	c #939393",
+"r 	c #C0C0C0",
+"s 	c #020202",
+"t 	c #A2A2A2",
+"u 	c #E5E5E5",
+"v 	c #A0A0A0",
+"w 	c #818181",
+"x 	c #D1D1D1",
+"y 	c #AEAEAE",
+"z 	c #767676",
+"A 	c #F2F2F2",
+"B 	c #2F2F2F",
+"C 	c #828282",
+"D 	c #EEEEEE",
+"E 	c #B5B5B5",
+"F 	c #9F9F9F",
+"G 	c #0F0F0F",
+"H 	c #1F1F1F",
+"I 	c #A8A8A8",
+"J 	c #EFEFEF",
+"K 	c #CBCBCB",
+"L 	c #C5C5C5",
+"M 	c #DADADA",
+"N 	c #CACACA",
+"O 	c #ABABAB",
+"P 	c #ADACAA",
+"Q 	c #371908",
+"R 	c #9C5217",
+"S 	c #B86E25",
+"T 	c #B96B26",
+"U 	c #A75A1C",
+"V 	c #A55E1F",
+"W 	c #BC732A",
+"X 	c #B86F27",
+"Y 	c #97551A",
+"Z 	c #C99C62",
+"` 	c #F4E1CB",
+" .	c #F4E0C9",
+"..	c #EFDBBE",
+"+.	c #ECD5B7",
+"@.	c #EBD2B4",
+"#.	c #EAD2B3",
+"$.	c #EBCFB0",
+"%.	c #E9CEAE",
+"&.	c #E8CCAB",
+"*.	c #E7CCA8",
+"=.	c #E8C7A1",
+"-.	c #E6C6A1",
+";.	c #E5C5A0",
+">.	c #E3C29A",
+",.	c #E0C09B",
+"'.	c #E0BD93",
+").	c #DFBA92",
+"!.	c #DFBA8E",
+"~.	c #DDB386",
+"{.	c #D5AC7C",
+"].	c #A2611B",
+"^.	c #F5E6D0",
+"/.	c #EAD3AF",
+"(.	c #EACFAF",
+"_.	c #DAB88B",
+":.	c #DAB687",
+"<.	c #D8B081",
+"[.	c #D6AE7F",
+"}.	c #D3AB7C",
+"|.	c #D1A974",
+"1.	c #CFA56E",
+"2.	c #CCA36D",
+"3.	c #CA9F67",
+"4.	c #C89E67",
+"5.	c #C69B63",
+"6.	c #C29861",
+"7.	c #C2935B",
+"8.	c #BF9052",
+"9.	c #BC884B",
+"0.	c #BD8440",
+"a.	c #BA7E3D",
+"b.	c #B37634",
+"c.	c #A76A22",
+"d.	c #8D5314",
+"e.	c #F5E4CE",
+"f.	c #E7CDA8",
+"g.	c #DFBD90",
+"h.	c #D8B183",
+"i.	c #D5AD7E",
+"j.	c #D2AA7B",
+"k.	c #D2A677",
+"l.	c #D0A771",
+"m.	c #CDA36C",
+"n.	c #C89F68",
+"o.	c #C59B64",
+"p.	c #C49961",
+"q.	c #C59A62",
+"r.	c #C2985D",
+"s.	c #C1945A",
+"t.	c #BF9054",
+"u.	c #BE8E4F",
+"v.	c #BC8749",
+"w.	c #BA823F",
+"x.	c #B17432",
+"y.	c #8B5214",
+"z.	c #E0BF97",
+"A.	c #DBB486",
+"B.	c #D7AF80",
+"C.	c #D2AC7B",
+"D.	c #D0A66F",
+"E.	c #B58E5C",
+"F.	c #A07E50",
+"G.	c #9A794D",
+"H.	c #B28C59",
+"I.	c #C79C64",
+"J.	c #C2975F",
+"K.	c #BC8441",
+"L.	c #B57C38",
+"M.	c #AF732C",
+"N.	c #A3661E",
+"O.	c #875014",
+"P.	c #E8CDA9",
+"Q.	c #DFBC92",
+"R.	c #D2AB7D",
+"S.	c #D0AB75",
+"T.	c #B38E5D",
+"U.	c #94744A",
+"V.	c #775E3C",
+"W.	c #201910",
+"X.	c #79603E",
+"Y.	c #C79D66",
+"Z.	c #BF9156",
+"`.	c #BE8645",
+" +	c #BC833F",
+".+	c #B67C37",
+"++	c #AA6C23",
+"@+	c #8D5514",
+"#+	c #E8CEA9",
+"$+	c #E1BC94",
+"%+	c #DAB487",
+"&+	c #D8B281",
+"*+	c #D0A873",
+"=+	c #92744D",
+"-+	c #7A603E",
+";+	c #79603F",
+">+	c #BE8F51",
+",+	c #BA813D",
+"'+	c #B77E3A",
+")+	c #B37934",
+"!+	c #A66921",
+"~+	c #E5C9A4",
+"{+	c #DDBA90",
+"]+	c #D2A575",
+"^+	c #D1A770",
+"/+	c #95754C",
+"(+	c #201A10",
+"_+	c #211A10",
+":+	c #D7B78E",
+"<+	c #BD8C4C",
+"[+	c #BC8845",
+"}+	c #BB823E",
+"|+	c #A96B22",
+"1+	c #E9CDAC",
+"2+	c #E7CBA6",
+"3+	c #D1A572",
+"4+	c #96774E",
+"5+	c #E1CBAD",
+"6+	c #C09359",
+"7+	c #BC8543",
+"8+	c #B37832",
+"9+	c #E8CCA7",
+"0+	c #E5CAA4",
+"a+	c #DCB98F",
+"b+	c #CAA16B",
+"c+	c #20190F",
+"d+	c #DEC6A7",
+"e+	c #C19256",
+"f+	c #BD8D4E",
+"g+	c #B57D3A",
+"h+	c #B57836",
+"i+	c #AD722C",
+"j+	c #A4661D",
+"k+	c #D2AB79",
+"l+	c #D1A872",
+"m+	c #755C38",
+"n+	c #755B37",
+"o+	c #DDC7A7",
+"p+	c #B77F3C",
+"q+	c #B0752F",
+"r+	c #A46821",
+"s+	c #895114",
+"t+	c #E9CEAA",
+"u+	c #DAB385",
+"v+	c #201A11",
+"w+	c #927E63",
+"x+	c #E3CFB3",
+"y+	c #D4B388",
+"z+	c #BB8444",
+"A+	c #B67A39",
+"B+	c #B17630",
+"C+	c #D2AC77",
+"D+	c #CDA46E",
+"E+	c #A57D4D",
+"F+	c #D9BF9E",
+"G+	c #CAA371",
+"H+	c #B77D38",
+"I+	c #AD6F26",
+"J+	c #BA935E",
+"K+	c #95744A",
+"L+	c #C5B39B",
+"M+	c #B57938",
+"N+	c #AD762E",
+"O+	c #AF722A",
+"P+	c #E1BD96",
+"Q+	c #CAA069",
+"R+	c #B58F5D",
+"S+	c #836641",
+"T+	c #8F7554",
+"U+	c #BC8947",
+"V+	c #BD8242",
+"W+	c #DBB88E",
+"X+	c #D1A776",
+"Y+	c #CCA26B",
+"Z+	c #B48D59",
+"`+	c #6C5433",
+" @	c #6D5536",
+".@	c #B47B37",
+"+@	c #C08D51",
+"@@	c #894E14",
+"#@	c #DCB387",
+"$@	c #D1AB76",
+"%@	c #B28B57",
+"&@	c #AC834E",
+"*@	c #3F301C",
+"=@	c #40321E",
+"-@	c #D2BD9F",
+";@	c #C8A271",
+">@	c #B8803D",
+",@	c #AF742E",
+"'@	c #864C13",
+")@	c #E6CAA5",
+"!@	c #B28753",
+"~@	c #9A7545",
+"{@	c #89683E",
+"]@	c #DAC1A0",
+"^@	c #BD8E50",
+"/@	c #B17732",
+"(@	c #AD712A",
+"_@	c #CA9D63",
+":@	c #874D14",
+"<@	c #E4C8A3",
+"[@	c #DAB88D",
+"}@	c #CBA26C",
+"|@	c #B78F5B",
+"1@	c #7F633F",
+"2@	c #725938",
+"3@	c #D3B389",
+"4@	c #CC9F69",
+"5@	c #B87F3B",
+"6@	c #BC823D",
+"7@	c #CEA16B",
+"8@	c #E8CBA5",
+"9@	c #C1975C",
+"0@	c #C09257",
+"a@	c #B9813E",
+"b@	c #BC8545",
+"c@	c #CDA06A",
+"d@	c #E6C9A3",
+"e@	c #D9B888",
+"f@	c #D3A676",
+"g@	c #BC8C4D",
+"h@	c #BE8A4D",
+"i@	c #BE8B4F",
+"j@	c #C79D60",
+"k@	c #E6C8A1",
+"l@	c #E1C39C",
+"m@	c #C2995F",
+"n@	c #D2AA77",
+"o@	c #D4A773",
+"p@	c #BF8A4C",
+"q@	c #B17330",
+"r@	c #331707",
+"s@	c #B2752D",
+"t@	c #C49A5D",
+"u@	c #B87E39",
+"v@	c #B37532",
+"w@	c #AF772E",
+"x@	c #AC742B",
+"y@	c #AA7229",
+"z@	c #AE7129",
+"A@	c #A96D26",
+"B@	c #A56922",
+"C@	c #A36720",
+"D@	c #A2651D",
+"E@	c #9E6119",
+"F@	c #9B5F18",
+"G@	c #995D16",
+"H@	c #925615",
+"I@	c #58300D",
+"J@	c #261105",
+"K@	c #311607",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                          . . . + . . .                                         ",
+"                                    . . + @ # $ % & * = + . .                                   ",
+"                                  . + - ; > , > ' ; ) ! ~ { + .                                 ",
+"                                . ] $ , ^ / ) ( $ ! ! ! _ : < [ .                               ",
+"                              . } / , ; $ | 1 2 2 3 4 5 6 & 7 8 9 .                             ",
+"                            . 0 / , a 5 b c d . . . e - 6 f 5 g h i .                           ",
+"                          . + ! , % ~ j . .           . { & 6 ~ h k +                           ",
+"                          . 8 , l ~ m .                 . n o f 1 p q .                         ",
+"                          + ; > & r s .                   . t u & r k +                         ",
+"                          v > a * w .                       s 5 ; x y z .                       ",
+"                        . # A | r B .                         C D 5 E F .                       ",
+"                        . _ > 3 r G                           H / ! r I .                       ",
+"                        . ) J K < .                           . % u L y .                       ",
+"                        . M l N O .                           . ( $ N P .                       ",
+"                    Q Q Q R S T U Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q V W X Y Q Q Q Q                 ",
+"                  Q Z `  ...+.@.#.$.%.&.*.=.-.-.-.-.-.=.=.-.;.;.>.,.'.).!.~.{.].Q               ",
+"                  Q ^./.(.>._.:.<.[.}.|.1.2.3.3.3.3.3.3.3.4.5.6.7.8.9.0.a.b.c.d.Q               ",
+"                  Q e.%.f.g.h.i.j.k.l.m.n.o.p.p.p.q.q.q.q.p.r.s.t.u.v.w.a.x.c.y.Q               ",
+"                  Q ^.$.%.z.A.<.B.C.k.D.2.E.F.G.G.H.I.I.I.q.J.7.t.u.K.w.L.M.N.O.Q               ",
+"                  Q e.(.P.Q.i.R.j.S.D.m.T.U.V.W.. W.X.n.3.Y.o.p.r.Z.u.`. +.+++@+Q               ",
+"                  Q e.%.#+$+%+&+B.j.*+1.=+-+. . . . . ;+3.o.q.J.s.>+v.,+'+)+!+y.Q               ",
+"                  Q ` P.~+{+i.j.j.]+^+2./+(+. . . . . _+:+4.5.p.J.Z.<+[+}+.+|+d.Q               ",
+"                  Q e.1+2+Q.<.[.R.j.3+m.4+. . . . . . . 5+Y.p.J.6+>+<+7+a.8+c.y.Q               ",
+"                  Q ` 9+0+a+[.}.j.]+1.b+/+W.. . . . . c+d+6.e+t.f+v.w.g+h+i+j+O.Q               ",
+"                  Q e.1+2+{+i.k+l+1.b+n.q.m+. . . . . n+o+7.e+8.u.9. +p+.+q+r+s+Q               ",
+"                  Q ^.(.t+$+A.u+u+[.k+*+1.2.-+_+. v+w+x+y+5.r.s.t.<+z+p+A+B+r+y.Q               ",
+"                  Q ` t+~+_.C+]+l+D+b+4.p.r.E+. . . F+G+p.6.7.Z.t.<+[+w.H+8+I+s+Q               ",
+"                  Q e.t+2+a+[.}.j.]+D.b+4.J+K+. . . L+p.p.6.e+Z.u.v. +'+M+N+O+O.Q               ",
+"                  Q ^.(.t+P+A.<.[.C.*+m.Q+R+S+. . . T+d+p.r.e+t.f+U+ +'+M+M.V+O.Q               ",
+"                  Q ` *.-.W+}.k+X+D.Y+3.q.Z+`+. . .  @d+p.6.7.e+8.9.z+a..@B++@@@Q               ",
+"                  Q  .2+-.#@$@3+D.m.n.p.%@&@*@. . . =@-@;@7.Z.t.f+U+>@L.h+,@+@'@Q               ",
+"                  Q ` )@-.A.$@l.1.b+4.p.!@~@*@. . . *@{@]@^@f+9.z+w.'+.@/@(@_@:@Q               ",
+"                  Q ` 9+<@[@}.k.S.D.}@n.|@1@. . . . . 2@3@6.e+Z.>+9.K.g+L.B+4@s+Q               ",
+"                  Q e.P.~+a+i.j.k+^+1.b+n.q.q.q.q.q.q.q.q.p.7.Z.8.v. +'+5@6@7@@@Q               ",
+"                  Q ` 8@~+_.j.]+^+D+3.I.p.9@6+0@0@0@0@0@0@Z.8.u.U+z+a@L.5@b@c@O.Q               ",
+"                  Q  .d@;.e@f@l.1.}@3.5.q.p.r.6.6.p.p.q.q.p.s.6+t.g@U+h@p.i@j@@@Q               ",
+"                  Q  .k@l@u+3+1.m.n.o.m@7.0@Z.Z.0@7.9@q.4.m.m.7@7@k+n@3+o@p@q@:@Q               ",
+"                  r@s@j@t@<+a.u@A+h+v@w@i+x@y@y@y@z@z@z@z@y@A@++B@C@D@E@F@G@H@I@J@              ",
+"                    K@Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q J@                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                "};
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-actions.c ./agent/seahorse-agent-actions.c
--- ../seahorse_orig/agent/seahorse-agent-actions.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-actions.c	2004-09-29 12:16:15.000000000 -0000
@@ -0,0 +1,202 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "seahorse-agent.h"
+
+/*
+ * Implements a queue of sa_passreq_t items. We can only show one dialog
+ * to the user at a time, due to keyboard grabbing issues, but mainly 
+ * for the users sanity. 
+ */
+
+static GQueue* g_queue = NULL;      /* The queue of sa_passreq_t items */
+static GMemChunk* g_memory = NULL;  /* Allocator for sa_passreq_t items */
+
+/* Whether to prompt authorize accesses to the cache or not */
+#define SETTING_AUTH "/apps/seahorse/agent/cache_authorize"
+
+/* -----------------------------------------------------------------------------
+ * IMPLEMENTATION
+ */
+ 
+void seahorse_agent_actions_init()
+{
+    /* The main memory queue */
+    g_memory = g_mem_chunk_create(sa_passreq_t, 128, G_ALLOC_AND_FREE);    
+    g_queue = g_queue_new();
+}
+
+void seahorse_agent_actions_uninit()
+{
+    if(g_queue);
+    {
+        /* All memory for elements freed below */
+        g_queue_free(g_queue);
+        g_queue = NULL;
+    }
+        
+    if(g_memory)
+    {
+        g_mem_chunk_destroy(g_memory);
+        g_memory = NULL;
+    }
+}
+
+void seahorse_agent_actions_getpass(sa_conn_t* rq, gchar* id,
+                                    gchar* errmsg, gchar* prompt, gchar* desc)
+{
+    sa_passreq_t* pr;
+    const gchar* pass;
+    
+    g_assert(rq != NULL);
+    g_assert(id != NULL && id[0]);
+
+    if(!seahorse_agent_settings_get_boolean(SETTING_AUTH, FALSE))
+    {   
+        /* 
+         * We don't need authorization, so if we have the password
+         * just reply now, without going to the queue.
+         */
+        if((pass = seahorse_agent_cache_get(id)) != NULL)
+        {
+            seahorse_agent_io_reply(rq, TRUE, pass);
+            return;
+        }
+    }
+
+    /* A new queue item */
+    pr = g_chunk_new(sa_passreq_t, g_memory);
+    memset(pr, 0, sizeof(*pr));
+    pr->id = g_strdup(id);
+    pr->errmsg = errmsg ? g_strdup(errmsg) : NULL;
+    pr->prompt = prompt ? g_strdup(prompt) : NULL;
+    pr->description = desc ? g_strdup(desc) : NULL;
+    pr->request = rq;
+    g_queue_push_head(g_queue, pr);
+
+    /* Process the queue */
+    seahorse_agent_actions_nextgui();
+}
+
+static void free_passreq(sa_passreq_t* pr)
+{
+    if(pr->id)
+        g_free((gpointer)pr->id);
+    if(pr->errmsg)
+        g_free((gpointer)pr->errmsg);
+    if(pr->prompt)
+        g_free((gpointer)pr->prompt);
+    if(pr->description)
+        g_free((gpointer)pr->description);
+        
+    /* Just in case, should already be popped */
+    g_queue_remove(g_queue, pr);
+    
+    g_chunk_free(pr, g_memory);
+}
+
+void seahorse_agent_actions_doneauth(sa_passreq_t* pr, gboolean authorized)
+{
+    const gchar* pass = NULL;
+    
+    g_assert(pr != NULL);
+    
+    if(authorized)
+    {
+        /* 
+         * The password will have been locked in the cache by the 
+         * time we arrive here. The code that checks that the password
+         * exists also locks it into the cache.
+         */
+        pass = seahorse_agent_cache_get(pr->id);
+        g_assert(pass != NULL);
+    }
+    
+    seahorse_agent_actions_donepass(pr, pass);
+}
+            
+void seahorse_agent_actions_donepass(sa_passreq_t* pr, const gchar* pass)
+{
+    if(pass == NULL)
+        seahorse_agent_io_reply(pr->request, FALSE, "111 cancelled");
+    else
+        seahorse_agent_io_reply(pr->request, TRUE, pass);
+        
+    free_passreq(pr);
+    seahorse_agent_actions_nextgui();
+}
+
+void seahorse_agent_actions_nextgui()
+{
+    sa_passreq_t* pr;
+    
+    g_assert(g_queue != NULL);
+    
+    /* If we already have some gui thing going on, then wait */
+    if(seahorse_agent_prompt_have())
+        return;
+        
+    if(g_queue_is_empty(g_queue))
+        return;
+   
+    pr = g_queue_pop_tail(g_queue);
+    if(pr != NULL)
+    {
+        /*
+         * Always prompt when we have an error message. If we already
+         * have something in the cache, however, we can just authorize
+         */
+        if(!pr->errmsg && seahorse_agent_cache_has(pr->id, TRUE))
+        {
+            /* Do we need to authorize with the user? */
+            if(seahorse_agent_settings_get_boolean(SETTING_AUTH, FALSE))
+                seahorse_agent_prompt_auth(pr);         
+                
+            /* Simple auto-authorize */   
+            else
+                seahorse_agent_actions_doneauth(pr, TRUE);
+        }
+        
+        /* Prompt for the password */
+        else
+        {
+            seahorse_agent_prompt_pass(pr);
+        }
+        
+        /* 
+         * Once either prompt is done it'll call the 
+         * seahorse_agent_actions_donexxxx functions above
+         */
+    }     
+    
+    seahorse_agent_actions_nextgui();
+}
+
+void seahorse_agent_actions_clrpass(sa_conn_t* rq, gchar* id)
+{
+    seahorse_agent_cache_clear(id);
+    seahorse_agent_io_reply(rq, TRUE, NULL);
+}
+
diff -u -N -r ../seahorse_orig/agent/seahorse-agent.c ./agent/seahorse-agent.c
--- ../seahorse_orig/agent/seahorse-agent.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent.c	2004-09-29 10:44:08.000000000 -0000
@@ -0,0 +1,168 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <unistd.h>
+
+#include <gtk/gtk.h>
+
+#include "config.h"
+#include "seahorse-agent.h"
+#include "seahorse-agent-secmem.h"
+
+/* -----------------------------------------------------------------------------
+ *  GLOBALS
+ */
+
+int g_daemonize = 1;
+
+/* -----------------------------------------------------------------------------
+ *  FORWARD DECLARATIONS
+ */
+ 
+static void daemonize(const char* sockname);
+static void usage();
+
+int main(int argc, char* argv[])
+{
+    int ch = 0;
+    const char* sockname;
+
+    secmem_init(65536);    
+
+    /* We need to drop privileges completely for security */
+    if(setresuid(getuid(), getuid(), getuid()) == -1 ||
+       setresgid(getgid(), getgid(), getgid()) == -1)
+        err(1, "couldn't drop privileges properly");
+
+    gtk_init(&argc, &argv);
+       
+    /* Parse the arguments nicely */
+    while((ch = getopt(argc, argv, "d")) != -1)
+    {    
+        switch(ch)
+        {
+        case 'd':
+            g_daemonize = 0;
+            break;
+            
+        case '?':
+        default:
+            usage();
+        };
+    }
+        
+    argc -= optind;
+    argv += optind;
+ 
+    if(argc > 0)
+        usage();
+
+    if(seahorse_agent_io_socket(&sockname) == -1)
+        return 1; /* message already printed */
+    
+    /* 
+     * All functions after this point have to print messages 
+     * nicely and not just called exit() 
+     */
+    daemonize(sockname);
+    
+    /* Initialize our sub systems */
+    seahorse_agent_actions_init();
+    seahorse_agent_cache_init();
+
+    if(seahorse_agent_io_init() == -1)
+        return 1; /* message already printed */
+
+    gtk_main();
+    
+    /* If any windows are open this closes them */
+    seahorse_agent_prompt_cleanup();
+    seahorse_agent_status_cleanup();
+
+    /* Uninitialize all subsystems */    
+    seahorse_agent_cache_uninit();
+    seahorse_agent_actions_uninit();
+    seahorse_agent_io_uninit();
+    seahorse_agent_settings_cleanup();
+    
+    return 0;
+}
+
+static void daemonize(const char* sockname)
+{
+    /* 
+     * We can't use the normal daemon call, because we have
+     * special things to do in the parent after forking 
+     */
+     
+    pid_t pid;
+    int i;
+
+    if(g_daemonize)
+    {    
+        switch((pid = fork()))
+        {
+        case -1:
+            err(1, "couldn't fork process");
+            break;
+        
+        /* The child */
+        case 0:
+            if(setsid() == -1)
+                err(1, "couldn't create new process group");
+
+            /* Close std descriptors */
+            for(i = 0; i <= 2; i++)
+                close(i); 
+
+            chdir("/");
+            return;
+        };
+    }
+    
+    /* Not daemonizing */
+    else
+    {
+        pid = getpid();
+    }
+        
+    /* The parent, or not daemonized */
+
+    /* Print out the socket name info: <name>:<pid>:<protocol_version> */
+    fprintf(stdout, "GPG_AGENT_INFO=%s:%lu:1; export GPG_AGENT_INFO", 
+            sockname, (long unsigned int)pid);
+    fflush(stdout);
+    
+    if(g_daemonize)                
+        exit(0);
+}
+
+static void usage()
+{
+    fprintf(stderr, "usage: seahorse-agent [-d]\n");
+    exit(2);
+}
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-cache.c ./agent/seahorse-agent-cache.c
--- ../seahorse_orig/agent/seahorse-agent-cache.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-cache.c	2004-09-29 11:52:13.000000000 -0000
@@ -0,0 +1,371 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "seahorse-agent.h"
+#include "seahorse-agent-secmem.h"
+
+/*
+ * Implementation of the password cache. Note that only passwords
+ * are stored in secure memory, all else is allocated normally.
+ * 
+ * Cache items can be locked which guarantees they'll stay around even
+ * if their TTL expires. 
+ */
+
+#define SETTING_CACHE       "/apps/seahorse/agent/cache_enabled"
+#define SETTING_TTL         "/apps/seahorse/agent/cache_ttl"
+
+#define UNPARSEABLE_KEY     "Unparseable Key ID"
+#define UNKNOWN_KEY         "Unknown/Invalid Key"
+
+typedef struct sa_cache_t
+{
+    gchar* id;          /* The password id */
+    gchar* pass;        /* The password itself (pointer to secure mem) */
+    gchar* desc;        /* A description of the key (parsed below) */
+    gboolean locked;    /* Whether this entry is locked in the cache */
+    time_t stamp;       /* The time which this password was last accessed */
+}
+sa_cache_t;
+
+static GHashTable* g_cache = NULL;
+static GMemChunk* g_memory = NULL;
+
+static void encode_password(gchar* dest, const gchar* src);
+static gchar* parse_description(const gchar* desc);
+static gboolean cache_check(gpointer data);
+
+static void destroy_cache_item(gpointer data)
+{
+    sa_cache_t* it = (sa_cache_t*)data;
+    if(it)
+    {
+        if(it->id)
+            g_free(it->id);
+            
+        if(it->desc)
+            g_free(it->desc);
+        
+        if(it->pass)
+            secmem_free(it->pass);
+            
+        g_chunk_free(it, g_memory);
+    } 
+}
+
+void seahorse_agent_cache_init()
+{
+    g_assert(g_cache == NULL);
+    g_assert(g_memory == NULL);
+    
+    g_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_cache_item);
+    g_memory = g_mem_chunk_create(sa_passreq_t, 128, G_ALLOC_AND_FREE);     
+    
+    /* Handle the cache timeouts */
+    g_timeout_add(1000, cache_check, g_cache);
+}
+
+void seahorse_agent_cache_uninit()
+{
+    if(g_cache);
+    {
+        g_idle_remove_by_data(g_cache);
+        g_hash_table_destroy(g_cache);
+        g_cache = NULL;
+    }
+        
+    if(g_memory)
+    {
+        g_mem_chunk_destroy(g_memory);
+        g_memory = NULL;
+    }     
+}
+
+const gchar* seahorse_agent_cache_get(const gchar* id)
+{
+    sa_cache_t* it;
+    
+    g_assert(id && id[0]);
+    g_assert(g_cache != NULL);
+    
+    it = g_hash_table_lookup(g_cache, id);
+    if(it)
+    {
+        /* Always updates the stamp when password retrieve */
+        it->stamp = time(NULL);
+        
+        /* Locks are always one off, so unset */
+        if(it->locked)
+            it->locked = FALSE;
+            
+        return it->pass;
+    }
+    
+    return NULL;
+}
+
+gboolean seahorse_agent_cache_has(const gchar* id, gboolean lock)
+{
+    sa_cache_t* it;
+    
+    g_assert(id && id[0]);
+    g_assert(g_cache != NULL);
+    
+    it = g_hash_table_lookup(g_cache, id);
+    if(it)
+    {
+        if(lock)
+            it->locked = TRUE;
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+void seahorse_agent_cache_clear(const gchar* id)
+{
+    g_assert(id && id[0]);
+    g_assert(g_cache != NULL);
+
+    /* Note that we ignore locks in this case, it was a specific request */
+    g_hash_table_remove(g_cache, id);
+    
+    /* UI hooks */
+    seahorse_agent_status_update();
+}
+
+static gboolean remove_cache_item(gpointer key, gpointer value, gpointer user_data)
+{
+    /* 
+     * This is a simple callback for removing all 
+     * items from a GHashTable. returning TRUE removes. 
+     */
+    return TRUE;
+}
+
+void seahorse_agent_cache_clearall()
+{
+    g_assert(g_cache != NULL);
+    
+    if(g_hash_table_foreach_remove(g_cache, remove_cache_item, NULL) > 0)
+        seahorse_agent_status_update();
+}
+
+void seahorse_agent_cache_set(const gchar* id, const gchar* pass,
+                              const gchar* desc, gboolean encode, gboolean lock)
+{
+    int len;
+    gboolean cache;
+    sa_cache_t* it;
+
+    g_assert(id && id[0]);
+    g_assert(pass && pass[0]);
+    
+    /* Whether to cache passwords or not */
+    cache = seahorse_agent_settings_get_boolean(SETTING_CACHE, TRUE);
+
+    /* No need to even bother the cache in this case */
+    if(!cache && !lock)
+        return;
+
+    g_assert(g_cache != NULL);
+    
+    it = g_hash_table_lookup(g_cache, id);
+    
+    if(!it)
+    {
+        /* Allocate and initialize a new cache item */
+        it = g_chunk_new(sa_cache_t, g_memory);
+        memset(it, 0, sizeof(*it));
+        it->id = g_strdup(id);
+    }
+    
+    g_assert(it->id != NULL);
+    
+    /* Put the description into memory */
+    if(desc)
+        it->desc = parse_description(desc);
+    
+    /* Work with the password */
+
+    if(it->pass)
+        secmem_free(it->pass);
+        
+    len = strlen(pass);
+    
+    if(encode)
+    {
+        it->pass = (gchar*)secmem_malloc(sizeof(gchar*) * ((len * 2) + 1));
+        if(!it->pass)
+        {
+            g_critical("out of secure memory");
+            return;
+        }
+        
+        encode_password(it->pass, pass);
+    }
+    
+    else
+    {
+        it->pass = (gchar*)secmem_malloc(sizeof(gchar) * (len + 1));
+        if(!it->pass)
+        {
+            g_critical("out of secure memory");
+            return;
+        }
+
+        strcpy(it->pass, pass);
+    }
+    
+    /* If not caching set to the epoch which should always expire */
+    it->stamp = cache ? time(NULL) : 0;
+    it->locked = lock ? TRUE : FALSE;
+    
+    g_hash_table_insert(g_cache, it->id, it);
+    
+    /* UI hooks */
+    seahorse_agent_status_update();    
+}
+
+guint seahorse_agent_cache_count()
+{
+    g_assert(g_cache != NULL);
+    return g_hash_table_size(g_cache);
+}
+
+static void encode_password(gchar* dest, const gchar* src)
+{
+    static const char HEXC[] = "0123456789abcdef";
+    int j;
+    
+    g_assert(dest && src);
+    
+    /* Simple hex encoding */
+    
+    while(*src)
+    {
+        j = *(src) >> 4 & 0xf;
+        *(dest++) = HEXC[j];
+    
+        j = *(src++) & 0xf;
+        *(dest++) = HEXC[j];
+    }
+}
+
+static gboolean cache_enumerator(gpointer key, gpointer value, gpointer user_data)
+{
+    sa_cache_t* it = (sa_cache_t*)value;
+    gint ttl = *((gint*)user_data);
+    
+    if(!it->locked)
+    {
+        /* Clear all items? */
+        if(ttl == -1)
+            return TRUE;
+            
+        /* Is expired? */
+        else if(time(NULL) > it->stamp + ttl)
+            return TRUE;
+    }
+    
+    return FALSE;
+}    
+
+
+static gboolean cache_check(gpointer data)
+{
+    gboolean cache;
+    gint ttl = -1;
+    
+    if(!g_cache)
+        return FALSE;
+        
+    if(g_hash_table_size(g_cache) > 0)
+    {
+        /* Whether to cache or not */
+        cache = seahorse_agent_settings_get_boolean(SETTING_CACHE, TRUE);
+        
+        if(cache)
+        {
+            /* How long to cache */
+            ttl = seahorse_agent_settings_get_integer(SETTING_TTL, 300);
+        }
+    
+        /* Zero means cache indefinitely */        
+        if(ttl != 0)
+        {
+            if(g_hash_table_foreach_remove(g_cache, cache_enumerator, &ttl) > 0)
+                seahorse_agent_status_update();
+        }
+    }
+            
+    return TRUE;
+}
+
+static gchar* parse_description(const gchar* desc)
+{
+    gchar* t;
+    gchar* x;
+    
+    g_assert(desc);
+    
+    /* 
+     * Basically we're looking for the first non-zero length 
+     * bit of text between quotes 
+     */
+     
+    if(desc == NULL || (t = strchr(desc, '"')) == NULL)
+        return g_strdup(UNPARSEABLE_KEY);
+
+    t++;
+    x = strchr(t, '"');
+        
+    if(x == NULL || t + 1 >= x)
+        return g_strdup(UNPARSEABLE_KEY);
+
+    return g_strndup(t, x - t);    
+}
+
+void seahorse_agent_cache_enum(GHFunc func, gpointer user_data)
+{
+    g_assert(g_cache != NULL);
+    g_assert(func != NULL);
+    g_hash_table_foreach(g_cache, func, user_data);
+}
+
+const gchar* seahorse_agent_cache_getname(const gchar* id)
+{
+    const gchar* ret;
+    sa_cache_t* it;
+    
+    g_assert(g_cache != NULL);
+    g_assert(id && id[0]);    
+    
+    it = g_hash_table_lookup(g_cache, id);
+    if(it != NULL)
+        ret = it->desc;
+    return ret == NULL ? UNKNOWN_KEY : ret;
+}
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-cache.glade ./agent/seahorse-agent-cache.glade
--- ../seahorse_orig/agent/seahorse-agent-cache.glade	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-cache.glade	2004-09-29 11:59:39.000000000 -0000
@@ -0,0 +1,176 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkDialog" id="agent-cache">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Cached GPG Passwords</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER</property>
+  <property name="modal">False</property>
+  <property name="default_width">380</property>
+  <property name="default_height">250</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">True</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="has_separator">False</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="clear_button">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-clear</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	      <signal name="clicked" handler="clear_clicked"/>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="close_button">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="has_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="has_focus">True</property>
+	      <property name="label">gtk-close</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	      <signal name="clicked" handler="close_clicked"/>
+	      <accelerator key="Escape" modifiers="0" signal="clicked"/>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox1">
+	  <property name="border_width">12</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	      <child>
+		<widget class="GtkTreeView" id="key_list">
+		  <property name="border_width">3</property>
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="headers_visible">True</property>
+		  <property name="rules_hint">False</property>
+		  <property name="reorderable">False</property>
+		  <property name="enable_search">True</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="insecure_label">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">WARNING: Your passwords are not being cached in secure memory.</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">6</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	      <property name="pack_type">GTK_PACK_END</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkMenu" id="context-menu">
+
+  <child>
+    <widget class="GtkImageMenuItem" id="clear_cache">
+      <property name="visible">True</property>
+      <property name="tooltip" translatable="yes">Clear GPG password cache</property>
+      <property name="label" translatable="yes">_Clear Cache</property>
+      <property name="use_underline">True</property>
+      <signal name="activate" handler="on_clear_cache_activate" last_modification_time="Wed, 29 Sep 2004 17:59:24 GMT"/>
+
+      <child internal-child="image">
+	<widget class="GtkImage" id="image1">
+	  <property name="visible">True</property>
+	  <property name="stock">gtk-clear</property>
+	  <property name="icon_size">1</property>
+	  <property name="xalign">0.5</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	</widget>
+      </child>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkMenuItem" id="show_window">
+      <property name="visible">True</property>
+      <property name="label" translatable="yes">_Show Window</property>
+      <property name="use_underline">True</property>
+      <signal name="activate" handler="on_show_window_activate" last_modification_time="Wed, 29 Sep 2004 17:59:24 GMT"/>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff -u -N -r ../seahorse_orig/agent/seahorse-agent.h ./agent/seahorse-agent.h
--- ../seahorse_orig/agent/seahorse-agent.h	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent.h	2004-09-29 11:51:18.000000000 -0000
@@ -0,0 +1,114 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 __SEAHORSE_AGENT_H__
+#define __SEAHORSE_AGENT_H__
+
+#include <glib.h>
+#include <gconf/gconf-client.h>
+#include <gconf/gconf.h>
+
+#ifndef KL
+#define KL(s)               ((sizeof(s) - 1) / sizeof(s[0]))
+#endif
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-io.c
+ */
+ 
+struct sa_conn;
+typedef struct sa_conn sa_conn_t;
+ 
+int seahorse_agent_io_socket(const char** socketname);
+int seahorse_agent_io_init();
+void seahorse_agent_io_uninit();
+void seahorse_agent_io_reply(sa_conn_t* rq, gboolean ok, const gchar* response);
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-actions.c
+ */
+
+
+typedef struct sa_passreq_t
+{
+    const gchar* id;
+    const gchar* errmsg;
+    const gchar* prompt;
+    const gchar* description;
+    sa_conn_t* request;
+}
+sa_passreq_t;
+
+void seahorse_agent_actions_init();
+void seahorse_agent_actions_uninit();
+void seahorse_agent_actions_getpass(sa_conn_t* rq, gchar* id,
+                                    gchar* errmsg, gchar* prompt, gchar* desc);
+void seahorse_agent_actions_clrpass(sa_conn_t* rq, gchar* id);
+void seahorse_agent_actions_doneauth(sa_passreq_t* pr, gboolean authorized);
+void seahorse_agent_actions_donepass(sa_passreq_t* pr, const gchar* pass);
+void seahorse_agent_actions_nextgui();
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-cache.c
+ */
+ 
+void seahorse_agent_cache_init();
+void seahorse_agent_cache_uninit();
+const gchar* seahorse_agent_cache_get(const gchar* id);
+void seahorse_agent_cache_set(const gchar* id, const gchar* pass, const gchar* desc,
+                              gboolean encode, gboolean lock);
+gboolean seahorse_agent_cache_has(const gchar* id, gboolean lock);
+void seahorse_agent_cache_clear(const gchar* id);
+void seahorse_agent_cache_clearall();
+guint seahorse_agent_cache_count();
+void seahorse_agent_cache_enum(GHFunc func, gpointer user_data);
+const gchar* seahorse_agent_cache_getname(const gchar* id);
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-prompt.c
+ */
+
+gboolean seahorse_agent_prompt_have();
+void seahorse_agent_prompt_pass(sa_passreq_t* pr);
+void seahorse_agent_prompt_auth(sa_passreq_t* pr);
+void seahorse_agent_prompt_cleanup();
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-settings.c
+ */
+ 
+#define SETTINGS_UNDEFINED  0
+
+void seahorse_agent_settings_cleanup();
+gboolean seahorse_agent_settings_get_boolean(const char* key, gboolean def);
+gboolean seahorse_agent_settings_get_integer(const char* key, gint def);
+guint seahorse_agent_settings_notify_add(const char *key, 
+                  GConfClientNotifyFunc callback, gpointer data);
+void seahorse_agent_settings_notify_remove(guint notification_id);
+
+/* -----------------------------------------------------------------------------
+ * seahorse-agent-status.c
+ */
+
+void seahorse_agent_status_cleanup();
+void seahorse_agent_status_update();
+
+#endif /* __SEAHORSE_AGENT_H__ */
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-io.c ./agent/seahorse-agent-io.c
--- ../seahorse_orig/agent/seahorse-agent-io.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-io.c	2004-09-29 12:10:10.000000000 -0000
@@ -0,0 +1,547 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <paths.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "seahorse-agent.h"
+
+/*
+ * Handles the server and open sockets. Parses received commands 
+ * and sends back responses when instructed to. Note that we don't
+ * use a seperate thread. Every uses glib event sources. This allows
+ * a clean and reliable implementation.
+ */
+ 
+static GList* g_connections = NULL;         /* All open connections */
+static GMemChunk* g_memory = NULL;          /* Memory for connections */
+
+static gint g_socket = -1;                  /* Socket we're listening on */
+static GIOChannel* g_iochannel = NULL;      /* IO channel for above */
+static gint g_iochannel_tag = 0;            /* Event source tag for above */
+static char g_socket_name[MAXPATHLEN];      /* Name of socket we're listening on */
+
+/* TODO: Do we need this anymore? */
+static guint g_unique_id = 0x00100000;
+ 
+struct sa_conn
+{
+    gint id;                    /* Unique id for connection */
+    gint stag;                  /* glib source tag */
+    gboolean input;             /* Whether in input mode or not */
+    GIOChannel* iochannel;      /* Io channel for connection */
+};
+
+/* -----------------------------------------------------------------------------
+ *  DEFINES and DEFAULTS
+ */
+
+#define SOCKET_DIR  "seahorse-XXXXXX"
+#define SOCKET_FILE "/S.gpg-agent"
+
+/* Commands */
+#define ASS_NOP     "NOP"
+#define ASS_BYE     "BYE"
+#define ASS_RESET   "RESET"
+#define ASS_OPTION  "OPTION"
+#define ASS_GETPASS "GET_PASSPHRASE"
+#define ASS_CLRPASS "CLEAR_PASSPHRASE"
+
+/* Responses */
+#define ASS_OK      "OK "
+#define ASS_ERR     "ERR "
+#define NL          "\n"
+
+/* -----------------------------------------------------------------------------
+ *  FORWARD DECLARATIONS
+ */
+ 
+static void free_request(sa_conn_t* cn);
+static gboolean is_valid_request(sa_conn_t* cn);
+static void process_line(sa_conn_t* cn, gchar* string);
+static int write_raw_data(int fd, const gchar* data, int len);
+static void decode_assuan_arg(gchar* arg);
+static gboolean is_null_argument(gchar* arg);
+static void split_arguments(gchar* line, ...);
+
+static gboolean io_handler(GIOChannel* source, GIOCondition condition, gpointer data);
+static gboolean connect_handler(GIOChannel* source, GIOCondition cond, gpointer data);
+
+int seahorse_agent_io_socket(const char** socketname)
+{
+    int len;
+    struct sockaddr_un addr;
+    
+    g_assert(g_socket == -1);
+    
+    /* Make the appropriate directory */
+    snprintf(g_socket_name, KL(g_socket_name), "%s/%s", _PATH_TMP, SOCKET_DIR);
+    
+    if(!mkdtemp(g_socket_name))
+    {
+        g_critical("can't create directory: %s: %s", g_socket_name, strerror(errno));
+        return -1;
+    }
+             
+    /* Build the socket name */
+    strncat(g_socket_name, SOCKET_FILE, KL(g_socket_name));
+    g_socket_name[KL(g_socket_name)] = 0;
+    
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, g_socket_name, KL(addr.sun_path));
+    addr.sun_path[KL(addr.sun_path)] = 0;
+
+    len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) + 1;
+
+    /* Now make the appropriate socket */
+    g_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+    if(g_socket == -1)
+    {
+        g_critical("can't create socket: %s", strerror(errno));
+        return -1;
+    }
+
+    /* Bind it to the address */
+    if(bind(g_socket, (struct sockaddr*)&addr, len) == -1)
+    {
+        g_critical("couldn't bind to socket: %s: %s", addr.sun_path, strerror(errno));
+        return -1;
+    }
+
+    if(socketname)
+        *socketname = g_socket_name;
+        
+    return 0;
+}
+        
+int seahorse_agent_io_init()
+{
+    g_assert(g_socket != -1);
+    g_assert(g_iochannel == NULL);
+    g_assert(g_iochannel_tag == 0);
+    
+    /* Listen for connections */
+    if(listen(g_socket, 5) == -1)
+    {
+        g_critical("couldn't listen for connections on socket: %s", strerror(errno));
+        return -1;
+    }
+    
+    /* Watch for connections on the socket */
+    g_iochannel = g_io_channel_unix_new(g_socket);    
+    g_iochannel_tag = g_io_add_watch(g_iochannel, G_IO_IN, connect_handler, NULL);
+
+    /* The main memory queue */
+    g_memory = g_mem_chunk_create(sa_conn_t, 128, G_ALLOC_AND_FREE);    
+                  
+    return 0; 
+}
+
+void seahorse_agent_io_uninit()
+{
+    if(g_connections);
+    {
+        g_list_free(g_connections);
+        g_connections = NULL;
+    }
+    
+    if(g_memory)
+    {
+        g_mem_chunk_destroy(g_memory);
+        g_memory = NULL;
+    }
+    
+    if(g_socket)
+    {
+        GError* err;
+        gchar* t;
+        
+        g_source_remove(g_iochannel_tag);
+        g_iochannel_tag = 0;
+        g_io_channel_shutdown(g_iochannel, FALSE, &err);
+        g_iochannel = NULL;
+        g_socket = -1;
+                
+        if(err)
+            g_error_free(err);
+
+        /* Remove the socket */
+        unlink(g_socket_name);
+        
+        /* Remove the directory */
+        t = strrchr(g_socket_name, '/');
+        if(t != NULL)
+        {
+            *t = 0;
+            rmdir(g_socket_name);
+        }
+        
+        g_socket_name[0] = 0;
+    }
+}
+
+static gboolean connect_handler(GIOChannel* source, GIOCondition cond, gpointer data)
+{
+    sa_conn_t* cn;
+    int fd;
+
+    g_assert(source);
+    g_assert(cond == G_IO_IN);
+    
+    fd = accept(g_io_channel_unix_get_fd(source), NULL, NULL);
+    if(fd < 0)
+    {
+        if(errno != EINTR && errno != EAGAIN)
+            g_critical("couldn't accept connection: %s", strerror(errno));
+        return TRUE; /* don't stop listening */
+    }
+
+    cn = g_chunk_new(sa_conn_t, g_memory);
+    memset(cn, 0, sizeof(*cn));
+
+    g_connections = g_list_append(g_connections, cn);
+    
+    cn->id = g_unique_id++;
+    
+    /* Setup io channel for new socket */
+    cn->iochannel = g_io_channel_unix_new(fd);
+    g_io_channel_set_encoding(cn->iochannel, NULL, NULL);        
+    cn->stag = g_io_add_watch(cn->iochannel, G_IO_IN | G_IO_HUP,  io_handler, cn);
+
+    /* 
+     * Initial response on the connection. This also enables
+     * listening on the connection
+     */    
+    seahorse_agent_io_reply(cn, TRUE, "your orders please");
+    
+    return TRUE;
+}
+
+static gboolean io_handler(GIOChannel* source, GIOCondition condition, gpointer data)
+{
+    sa_conn_t* cn = (sa_conn_t*)data;
+    gchar* string;
+    gsize length;
+    GError* err = NULL;
+    gboolean ret = TRUE;
+    
+    if(condition & G_IO_IN)
+    {    
+        /* Read 1 line from the io channel, including newline character */
+        g_io_channel_read_line(source, &string, &length, NULL, &err);
+        
+        if(err != NULL)
+        {
+            g_critical("couldn't read from socket: %s", err->message);
+            g_error_free(err);
+            free_request(cn);
+            ret = FALSE;
+        }
+        
+        else if(length > 0)
+        {        
+            /* Send it off for processing */
+            process_line(cn, string);
+        }
+        
+        if(string)
+            g_free(string);
+    }
+    
+    if(condition & G_IO_HUP)
+    {    
+        free_request(cn);
+        ret = FALSE; /* removes watch */
+    }
+   
+    return ret;     
+}
+
+static gboolean is_valid_request(sa_conn_t* cn)
+{
+    return cn && g_list_index(g_connections, cn) != -1;
+}
+
+static void free_request(sa_conn_t* cn)
+{
+    GError* err = NULL;
+    
+    if(cn->iochannel)
+    {
+        if(cn->stag)
+            g_source_remove(cn->stag);
+        
+        g_io_channel_shutdown(cn->iochannel, TRUE, &err);
+        
+        if(err)
+            g_error_free(err);
+            
+        cn->iochannel = 0;
+        cn->stag = 0;
+    }
+
+    g_connections = g_list_remove(g_connections, cn);
+    g_chunk_free(cn, g_memory);
+}
+
+static int write_raw_data(int fd, const gchar* data, int len)
+{
+    int r, x = 0;
+
+    /* 
+     * Guarantee that all data is written. We don't want to go
+     * through g_io_channel_xxx functions because they might cache
+     * their data. Passwords and the like shouldn't be cached anywhere
+     * but the secure memory.
+     */
+     
+    if(len == -1)
+        len = strlen(data);
+        
+    while(len > 0)
+    {
+        r = write(fd, data, len);
+        if(r == -1)
+        {
+            if(errno != EAGAIN && errno != EINTR)
+            {
+                g_critical("couldn't write data to socket: %s", strerror(errno));
+                return -1;
+            }
+        }
+        
+        else
+        {
+            data += r;
+            len -= r;
+            x += r;
+        }
+    }
+    
+    return x;
+}
+
+void seahorse_agent_io_reply(sa_conn_t* cn, gboolean ok, const gchar* response)
+{
+    int fd;
+    
+    /* The connection could have closed in the meantime */
+    if(!is_valid_request(cn))
+        return;
+        
+    fd = g_io_channel_unix_get_fd(cn->iochannel);
+        
+    if(write_raw_data(fd, ok ? ASS_OK : ASS_ERR, ok ? KL(ASS_OK) : KL(ASS_ERR)) == -1 ||
+       (response && write_raw_data(fd, response, -1)) == -1 ||
+       write_raw_data(fd, NL, KL(NL)) == -1)
+    {
+        /* error message already printed */
+        free_request(cn);
+    }
+    
+    /* After sending back a response we're ready for more */
+    cn->input = TRUE;
+    return;
+}
+
+static void process_line(sa_conn_t* cn, gchar* string)
+{
+    gchar* args;
+    
+    g_strstrip(string);
+    
+    if(strlen(string) == 0)
+        return;     /* don't worry about empty lines */
+        
+    /* Split the command off from the args */
+    args = strchr(string, ' ');
+    if(args)
+    {
+        *args = 0;
+        args++;
+    }
+    else
+    {
+        /* Pointer to the end, empty string */
+        args = string + strlen(string);
+    }
+    
+    /* We received a line, hold on until response is sent */
+    cn->input = FALSE;
+        
+    if(strcasecmp(string, ASS_OPTION) == 0)
+    {
+        /* We don't do anything with the options right now */
+        seahorse_agent_io_reply(cn, TRUE, NULL);
+    }
+
+    else if(strcasecmp(string, ASS_GETPASS) == 0)
+    {
+        gchar* id;
+        gchar* errmsg;
+        gchar* prompt;
+        gchar* description;
+
+        split_arguments(args, &id, &errmsg, &prompt, &description, NULL);        
+
+        if(!id || !errmsg || !prompt || !description)
+        {
+            seahorse_agent_io_reply(cn, FALSE, "105 parameter error");
+            g_warning("received invalid passphrase request");
+            return;
+        }
+
+        if(is_null_argument(errmsg))
+            errmsg = NULL;
+        if(is_null_argument(prompt))
+            prompt = NULL;
+        if(is_null_argument(description))
+            description = NULL;        
+        
+        seahorse_agent_actions_getpass(cn, id, errmsg, prompt, description);
+    }
+        
+    else if(strcasecmp(string, ASS_CLRPASS) == 0)
+    {
+        gchar* id;
+        split_arguments(args, &id, NULL);
+
+        if(!id)
+        {
+            seahorse_agent_io_reply(cn, FALSE, "105 parameter error");
+            g_warning("received invalid clear pass request: %s", string);
+        }
+        
+        seahorse_agent_actions_clrpass(cn, id);
+    }
+    
+    else if(strcasecmp(string, ASS_NOP) == 0)
+    {
+        seahorse_agent_io_reply(cn, TRUE, NULL);
+    }
+    
+    else if(strcasecmp(string, ASS_BYE) == 0)
+    {
+        seahorse_agent_io_reply(cn, TRUE, "closing connection");
+        free_request(cn);
+    }
+    
+    else if(strcasecmp(string, ASS_RESET) == 0)
+    {
+        /* We keep no state :) */
+        seahorse_agent_io_reply(cn, TRUE, NULL);
+    }            
+    
+    else
+    {
+        g_warning("unrecognized command: %s", string);
+        seahorse_agent_io_reply(cn, FALSE, "103 unknown command");
+    }   
+}
+
+static gboolean is_null_argument(gchar* arg)
+{
+    return (strcmp(arg, "X") == 0);
+}
+
+static const gchar HEX_CHARS[] = "0123456789ABCDEF";
+
+static void decode_assuan_arg(gchar* arg)
+{
+    gchar* t;
+    gint len;
+    
+    for(len = strlen(arg); len > 0; arg++, len--)
+    {
+        switch(*arg)
+        {
+        /* + becomes a space */
+        case '+':
+            *arg = ' ';
+            break;
+            
+        /* hex encoded as in URIs */
+        case '%':
+            *arg = '?';
+            t = strchr(HEX_CHARS, arg[1]);
+            if(t != NULL)
+            {
+                *arg = ((t - HEX_CHARS) & 0xf) << 4;
+                t = strchr(HEX_CHARS, arg[2]);
+                if(t != NULL)
+                    *arg |= (t - HEX_CHARS) & 0xf;
+            }
+            len -= 2;
+            if(len < 1)         /* last char, null terminate */
+                arg[1] = 0;
+            else                /* collapse rest */
+                memmove(arg + 1, arg + 3, len);
+            break;
+        };
+    }
+}
+
+static void split_arguments(gchar* line, ...)
+{
+    gchar** cur;
+    va_list ap;
+    
+    va_start(ap, line);
+    
+    while(*line && isspace(*line))
+        line++;
+        
+    while((cur = va_arg(ap, gchar**)) != NULL)
+    {
+        if(*line)
+        {
+            *cur = line;
+            
+            while(*line && !isspace(*line))
+                line++;
+                
+            while(*line && isspace(*line))
+            {
+                *line = 0;
+                line++;
+            }     
+            
+            decode_assuan_arg(*cur); 
+        }
+        else
+        {
+            *cur = NULL;
+        }
+    }
+    
+    va_end(ap);  
+}
+
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-prompt.c ./agent/seahorse-agent-prompt.c
--- ../seahorse_orig/agent/seahorse-agent-prompt.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-prompt.c	2004-09-29 11:54:04.000000000 -0000
@@ -0,0 +1,412 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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.
+ */
+
+/* 
+ * Much of the code is originally from pinentry-gtk2:
+ * (C) by Albrecht Dreß 2004 unter the terms of the GNU Lesser General
+ * Public License.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gnome.h>
+
+#include "seahorse-agent.h"
+#include "gtk-secure-entry.h"
+#include "padlock-keyhole.xpm"
+
+#define HIG_SMALL      6    /* gnome hig small space in pixels */
+#define HIG_LARGE     12    /* gnome hig large space in pixels */
+
+static GtkWidget* g_current_win = NULL;
+static GtkWidget* g_current_entry = NULL;
+
+gboolean seahorse_agent_prompt_have()
+{
+    /* Whether we currently have a prompt or not */
+    return g_current_win != NULL;
+}
+
+void seahorse_agent_prompt_cleanup()
+{
+    if(g_current_win)
+    {
+        gtk_widget_destroy(g_current_win);
+        g_current_win = NULL;
+        g_current_entry = NULL;
+    }
+}
+
+static gchar* utf8_validate(const gchar* text)
+{
+    gchar *result;
+
+    if(!text)
+        return NULL;
+
+    if(g_utf8_validate(text, -1, NULL))
+        return g_strdup(text);
+
+    result = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
+    if(!result) 
+    {
+        gchar *p;
+
+        result = p = g_strdup(text);
+        while(!g_utf8_validate(p, -1, (const gchar **)&p))
+            *p = '?';
+    }
+    return result;
+}
+
+
+/* 
+ * constrain_size - constrain size of the window the window should not
+ * shrink beyond the requisition, and should not grow vertically 
+ */
+static void constrain_size(GtkWidget* win, GtkRequisition* req, gpointer data)
+{
+    static gint width, height;
+    GdkGeometry geo;
+
+    if(req->width == width && req->height == height)
+        return;
+        
+    width = req->width;
+    height = req->height;
+    geo.min_width = width;
+    geo.max_width = 10000;  /* limit is arbitrary, INT_MAX breaks other things */
+    geo.min_height = geo.max_height = height;
+    gtk_window_set_geometry_hints(GTK_WINDOW(win), NULL, &geo,
+                                  GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+}
+
+/* -----------------------------------------------------------------------------
+ * Password Prompt
+ */
+
+static void prompt_done_dialog(sa_passreq_t* pr, gboolean ok)
+{
+    const gchar* pass = NULL;
+    
+    g_assert(g_current_win);
+    gtk_widget_destroy(g_current_win);
+    g_current_win = NULL;
+    
+    if(ok)
+    {
+        pass = seahorse_agent_cache_get(pr->id);
+        g_assert(pass != NULL);
+    }
+    
+    seahorse_agent_actions_donepass(pr, pass);
+}
+
+static void prompt_ok_button(GtkWidget* widget, gpointer data)
+{
+    sa_passreq_t* pr = (sa_passreq_t*)data;
+    const char *s;
+    
+    g_assert(g_current_entry != NULL);
+    s = gtk_secure_entry_get_text(GTK_SECURE_ENTRY(g_current_entry));
+    
+    /* This locks the passphrase in memory until the next get */
+    seahorse_agent_cache_set(pr->id, s != NULL ? s : "", pr->description, TRUE, TRUE);
+
+    prompt_done_dialog(pr, TRUE);    
+}
+
+static void prompt_cancel_button(GtkWidget* widget, gpointer data)
+{
+    sa_passreq_t* pr = (sa_passreq_t*)data;    
+    prompt_done_dialog(pr, FALSE);        
+}
+
+static void enter_callback(GtkWidget* widget, gpointer data)
+{
+    prompt_ok_button(widget, data);
+}
+
+/* grab_keyboard - grab the keyboard for maximum security */
+static void grab_keyboard(GtkWidget* win, GdkEvent* event, gpointer data)
+{
+    if(gdk_keyboard_grab(win->window, FALSE, gdk_event_get_time(event))) 
+        g_critical("could not grab keyboard");
+}
+
+/* ungrab_keyboard - remove grab */
+static void ungrab_keyboard(GtkWidget* win, GdkEvent* event, gpointer data)
+{
+    gdk_keyboard_ungrab(gdk_event_get_time(event));
+}
+
+static int prompt_delete_event(GtkWidget* widget, GdkEvent* event, gpointer data)
+{
+    prompt_cancel_button(widget, data);
+    return TRUE;
+}
+
+static GtkWidget* create_prompt_window(sa_passreq_t* pr)
+{
+    GtkWidget* w;
+    GtkWidget* win;
+    GtkWidget* box;
+    GtkWidget* ebox;
+    GtkWidget* wvbox;
+    GtkWidget* chbox;
+    GtkWidget* bbox;
+    GtkAccelGroup* acc;
+    GdkPixbuf* padlock_keyhole;
+    gchar* msg;
+    
+    gboolean grab = TRUE;
+
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    gtk_window_set_title(GTK_WINDOW(win), _("GPG Password"));
+    acc = gtk_accel_group_new();
+
+    g_signal_connect(G_OBJECT(win), "delete_event",
+                     G_CALLBACK(prompt_delete_event), pr);
+
+    g_signal_connect(G_OBJECT(win), "size-request",
+                     G_CALLBACK(constrain_size), NULL);
+                     
+    g_signal_connect(G_OBJECT(win), grab ? "map-event" : "focus-in-event",
+                     G_CALLBACK(grab_keyboard), NULL);
+    g_signal_connect(G_OBJECT(win), grab ? "unmap-event" : "focus-out-event",
+                     G_CALLBACK(ungrab_keyboard), NULL);
+    
+    gtk_window_add_accel_group(GTK_WINDOW(win), acc);
+
+    wvbox = gtk_vbox_new(FALSE, HIG_LARGE * 2);
+    gtk_container_add(GTK_CONTAINER(win), wvbox);
+    gtk_container_set_border_width(GTK_CONTAINER(wvbox), HIG_LARGE);
+
+    chbox = gtk_hbox_new(FALSE, HIG_LARGE);
+    gtk_box_pack_start(GTK_BOX(wvbox), chbox, FALSE, FALSE, 0);
+
+    padlock_keyhole = gdk_pixbuf_new_from_xpm_data(padlock_keyhole_xpm);
+    w = gtk_image_new_from_pixbuf(padlock_keyhole);
+    gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
+    gtk_box_pack_start(GTK_BOX(chbox), w, FALSE, FALSE, 0);
+
+    box = gtk_vbox_new(FALSE, HIG_SMALL);
+    gtk_box_pack_start(GTK_BOX(chbox), box, TRUE, TRUE, 0);
+
+    if(pr->description) 
+    {
+        msg = utf8_validate(pr->description);
+        w = gtk_label_new(msg);
+        g_free(msg);
+        
+        gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+        gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
+        gtk_box_pack_start(GTK_BOX(box), w, TRUE, FALSE, 0);
+    }
+
+    if(pr->errmsg) 
+    {
+        GdkColor color = { 0, 0xffff, 0, 0 };
+    
+        msg = utf8_validate(pr->errmsg);
+        w = gtk_label_new(msg);
+        g_free(msg);
+        
+        gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+        gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
+        gtk_box_pack_start(GTK_BOX(box), w, TRUE, FALSE, 0);
+        gtk_widget_modify_fg(w, GTK_STATE_NORMAL, &color);
+    }
+
+    ebox = gtk_hbox_new(FALSE, HIG_SMALL);
+    gtk_box_pack_start(GTK_BOX(box), ebox, FALSE, FALSE, 0);
+
+    if(pr->prompt) 
+    {
+        msg = utf8_validate(pr->prompt);
+        w = gtk_label_new(msg);
+        g_free(msg);
+
+        gtk_box_pack_start(GTK_BOX(ebox), w, FALSE, FALSE, 0);
+    }
+ 
+    g_current_entry = gtk_secure_entry_new();
+    gtk_widget_set_size_request(g_current_entry, 200, -1);
+    g_signal_connect(G_OBJECT(g_current_entry), "activate",
+                     G_CALLBACK(enter_callback), pr);
+                     
+    gtk_box_pack_start(GTK_BOX(ebox), g_current_entry, TRUE, TRUE, 0);
+    gtk_widget_grab_focus(g_current_entry);
+    gtk_widget_show(g_current_entry);
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_box_set_spacing(GTK_BOX(bbox), 6);
+    gtk_box_pack_start(GTK_BOX(wvbox), bbox, TRUE, FALSE, 0);
+
+    w = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    gtk_container_add(GTK_CONTAINER(bbox), w);
+    g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(prompt_cancel_button), pr);
+        
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+
+    w = gtk_button_new_from_stock(GTK_STOCK_OK);
+    gtk_container_add(GTK_CONTAINER(bbox), w);
+
+    g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(prompt_ok_button), pr);
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+    gtk_widget_grab_default(w);
+    g_signal_connect_object(G_OBJECT(g_current_entry), "focus_in_event", 
+                            G_CALLBACK(gtk_widget_grab_default), G_OBJECT(w), 0);
+
+    gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
+    gtk_widget_show_all(win);
+    return win;
+}
+
+void seahorse_agent_prompt_pass(sa_passreq_t* pr)
+{
+    g_assert(!seahorse_agent_prompt_have());
+    g_current_win = create_prompt_window(pr);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Authorize Prompt
+ */
+ 
+static void auth_done_dialog(sa_passreq_t* pr, gboolean ok)
+{
+    g_assert(g_current_win);
+    gtk_widget_destroy(g_current_win);
+    g_current_win = NULL;
+    
+    seahorse_agent_actions_doneauth(pr, ok);
+}
+
+static void auth_ok_button(GtkWidget* widget, gpointer data)
+{
+    sa_passreq_t* pr = (sa_passreq_t*)data;
+    auth_done_dialog(pr, TRUE);    
+}
+
+static void auth_cancel_button(GtkWidget* widget, gpointer data)
+{
+    sa_passreq_t* pr = (sa_passreq_t*)data;    
+    auth_done_dialog(pr, FALSE);        
+}
+
+static int auth_delete_event(GtkWidget* widget, GdkEvent* event, gpointer data)
+{
+    auth_cancel_button(widget, data);
+    return TRUE;
+}
+ 
+static GtkWidget* create_auth_window(sa_passreq_t* pr)
+{
+    GtkWidget* w;
+    GtkWidget* win;
+    GtkWidget* box;
+    GtkWidget* wvbox;
+    GtkWidget* chbox;
+    GtkWidget* bbox;
+    GtkAccelGroup* acc;
+    GdkPixbuf* padlock_keyhole;
+    gchar* msg;
+    
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(win), _("Authorize Password Access"));
+    acc = gtk_accel_group_new();
+
+    g_signal_connect(G_OBJECT(win), "delete_event",
+                     G_CALLBACK(auth_delete_event), pr);
+
+    g_signal_connect(G_OBJECT(win), "size-request",
+                     G_CALLBACK(constrain_size), NULL);
+                     
+    gtk_window_add_accel_group(GTK_WINDOW(win), acc);
+
+    wvbox = gtk_vbox_new(FALSE, HIG_LARGE * 2);
+    gtk_container_add(GTK_CONTAINER(win), wvbox);
+    gtk_container_set_border_width(GTK_CONTAINER(wvbox), HIG_LARGE);
+
+    chbox = gtk_hbox_new(FALSE, HIG_LARGE);
+    gtk_box_pack_start(GTK_BOX(wvbox), chbox, FALSE, FALSE, 0);
+
+    padlock_keyhole = gdk_pixbuf_new_from_xpm_data(padlock_keyhole_xpm);
+    w = gtk_image_new_from_pixbuf(padlock_keyhole);
+    gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
+    gtk_box_pack_start(GTK_BOX(chbox), w, FALSE, FALSE, 0);
+
+    box = gtk_vbox_new(FALSE, HIG_SMALL);
+    gtk_box_pack_start(GTK_BOX(chbox), box, TRUE, TRUE, 0);
+
+    if(pr->description) 
+    {
+        msg = utf8_validate(pr->description);
+        w = gtk_label_new(msg);
+        g_free(msg);
+        
+        gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+        gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
+        gtk_box_pack_start(GTK_BOX(box), w, TRUE, FALSE, 0);
+    }
+
+    w = gtk_label_new(_("The password is cached in memory."));
+    gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+    gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
+    gtk_box_pack_start(GTK_BOX(box), w, TRUE, FALSE, 0);
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_box_set_spacing(GTK_BOX(bbox), 6);
+    gtk_box_pack_start(GTK_BOX(wvbox), bbox, TRUE, FALSE, 0);
+
+    w = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    gtk_container_add(GTK_CONTAINER(bbox), w);
+    g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(auth_cancel_button), pr);
+        
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+
+    w = gtk_button_new_from_stock(GTK_STOCK_OK);
+    gtk_button_set_label(GTK_BUTTON(w), _("_Authorize"));
+    gtk_button_set_use_underline(GTK_BUTTON(w), TRUE);
+    gtk_container_add(GTK_CONTAINER(bbox), w);
+
+    g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(auth_ok_button), pr);
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+    gtk_widget_grab_default(w);
+
+    gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
+    gtk_widget_show_all(win);
+    return win;
+}
+
+void seahorse_agent_prompt_auth(sa_passreq_t* pr)
+{
+    g_assert(!seahorse_agent_prompt_have());
+    g_current_win = create_auth_window(pr);
+}
+
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-secmem.c ./agent/seahorse-agent-secmem.c
--- ../seahorse_orig/agent/seahorse-agent-secmem.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-secmem.c	2004-09-29 10:39:29.000000000 -0000
@@ -0,0 +1,464 @@
+/* secmem.c  -  memory allocation from a secure heap
+ * Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/* Originally from pinentry in gnupg */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
+# include <sys/mman.h>
+# include <sys/types.h>
+# include <fcntl.h>
+# ifdef USE_CAPABILITIES
+#  include <sys/capability.h>
+# endif
+#endif
+#include <string.h>
+
+#include "seahorse-agent-secmem.h"
+
+#ifdef ORIGINAL_GPG_VERSION
+#include "types.h"
+#include "util.h"
+#else /* ORIGINAL_GPG_VERSION */
+
+/* To avoid that a compiler optimizes certain memset calls away, these
+   macros may be used instead. */
+#define wipememory2(_ptr,_set,_len) do { \
+              volatile char *_vptr=(volatile char *)(_ptr); \
+              size_t _vlen=(_len); \
+              while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
+                  } while(0)
+#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len)
+#define wipe(_ptr,_len)       wipememory2(_ptr,0,_len)
+
+typedef union {
+    int a;
+    short b;
+    char c[1];
+    long d;
+#ifdef HAVE_U64_TYPEDEF
+    u64 e;
+#endif
+    float f;
+    double g;
+} PROPERLY_ALIGNED_TYPE;
+
+#define log_error log_info
+#define log_bug log_fatal
+
+void 
+log_info(char *template, ...)
+{
+  va_list args;
+  
+  va_start(args, template);
+  vfprintf(stderr, template, args);
+  va_end(args);
+}
+
+void 
+log_fatal(char *template, ...)
+{
+  va_list args;
+  
+  va_start(args, template);
+  vfprintf(stderr, template, args);
+  va_end(args);
+  exit(EXIT_FAILURE);
+}
+
+#endif /* ORIGINAL_GPG_VERSION */
+
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#  define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#define DEFAULT_POOLSIZE 16384
+
+typedef struct memblock_struct MEMBLOCK;
+struct memblock_struct {
+    unsigned size;
+    union {
+   MEMBLOCK *next;
+    PROPERLY_ALIGNED_TYPE aligned;
+    } u;
+};
+
+
+
+static void  *pool;
+static volatile int pool_okay; /* may be checked in an atexit function */
+static int   pool_is_mmapped;
+static size_t poolsize; /* allocated length */
+static size_t poollen; /* used length */
+static MEMBLOCK *unused_blocks;
+static unsigned max_alloced;
+static unsigned cur_alloced;
+static unsigned max_blocks;
+static unsigned cur_blocks;
+static int disable_secmem;
+static int show_warning;
+static int no_warning;
+static int suspend_warning;
+static int have_secure = 0;
+
+static void
+print_warn(void)
+{
+    if( !no_warning )
+  log_info("Warning: using insecure memory!\n");
+}
+
+
+static void
+lock_pool( void *p, size_t n )
+{
+#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
+    int err;
+
+    cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+    err = mlock( p, n );
+    if( err && errno )
+   err = errno;
+    cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+
+    if( err ) {
+ if( errno != EPERM
+   #ifdef EAGAIN  /* OpenBSD returns this */
+        && errno != EAGAIN
+   #endif
+     )
+        log_error("canŽt lock memory: %s\n", strerror(err));
+   show_warning = 1;
+    }
+
+#elif defined(HAVE_MLOCK)
+    uid_t uid;
+    int err;
+
+    uid = getuid();
+
+#ifdef HAVE_BROKEN_MLOCK
+    if( uid ) {
+  errno = EPERM;
+ err = errno;
+    }
+    else {
+  err = mlock( p, n );
+   if( err && errno )
+     err = errno;
+    }
+#else
+    err = mlock( p, n );
+    if( err && errno )
+   err = errno;
+#endif
+
+    if( uid && !geteuid() ) {
+ if( setuid( uid ) || getuid() != geteuid()  )
+      log_fatal("failed to reset uid: %s\n", strerror(errno));
+    }
+
+    if( err ) {
+    if( errno != EPERM
+#ifdef EAGAIN  /* OpenBSD returns this */
+       && errno != EAGAIN
+#endif
+    )
+        log_error("canŽt lock memory: %s\n", strerror(err));
+   show_warning = 1;
+    }
+    else
+    {
+        have_secure = 1;
+    }
+#else
+    log_info("Please note that you don't have secure memory on this system\n");
+#endif
+}
+
+
+static void
+init_pool( size_t n)
+{
+    size_t pgsize;
+
+    poolsize = n;
+
+    if( disable_secmem )
+   log_bug("secure memory is disabled");
+
+#ifdef HAVE_GETPAGESIZE
+    pgsize = getpagesize();
+#else
+    pgsize = 4096;
+#endif
+
+#if HAVE_MMAP
+    poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
+# ifdef MAP_ANONYMOUS
+       pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+                MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+# else /* map /dev/zero instead */
+    {    int fd;
+
+   fd = open("/dev/zero", O_RDWR);
+    if( fd == -1 ) {
+       log_error("can't open /dev/zero: %s\n", strerror(errno) );
+     pool = (void*)-1;
+  }
+  else {
+     pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+                      MAP_PRIVATE, fd, 0);
+ }
+    }
+# endif
+    if( pool == (void*)-1 )
+    log_info("can't mmap pool of %u bytes: %s - using malloc\n",
+               (unsigned)poolsize, strerror(errno));
+    else {
+   pool_is_mmapped = 1;
+   pool_okay = 1;
+    }
+
+#endif
+    if( !pool_okay ) {
+    pool = malloc( poolsize );
+ if( !pool )
+        log_fatal("can't allocate memory pool of %u bytes\n",
+                             (unsigned)poolsize);
+    else
+       pool_okay = 1;
+    }
+    lock_pool( pool, poolsize );
+    poollen = 0;
+}
+
+
+int secmem_have()
+{
+    return have_secure;
+}
+
+/* concatenate unused blocks */
+static void
+compress_pool(void)
+{
+    /* fixme: we really should do this */
+}
+
+void
+secmem_set_flags( unsigned flags )
+{
+    int was_susp = suspend_warning;
+
+    no_warning = flags & 1;
+    suspend_warning = flags & 2;
+
+    /* and now issue the warning if it is not longer suspended */
+    if( was_susp && !suspend_warning && show_warning ) {
+  show_warning = 0;
+  print_warn();
+    }
+}
+
+unsigned
+secmem_get_flags(void)
+{
+    unsigned flags;
+
+    flags  = no_warning      ? 1:0;
+    flags |= suspend_warning ? 2:0;
+    return flags;
+}
+
+void
+secmem_init( size_t n )
+{
+    if( !n ) {
+      #ifdef USE_CAPABILITIES
+ /* drop all capabilities */
+    cap_set_proc( cap_from_text("all-eip") );
+
+      #elif !defined(HAVE_DOSISH_SYSTEM)
+    uid_t uid;
+
+    disable_secmem=1;
+  uid = getuid();
+    if( uid != geteuid() ) {
+       if( setuid( uid ) || getuid() != geteuid() )
+       log_fatal("failed to drop setuid\n" );
+ }
+      #endif
+    }
+    else {
+    if( n < DEFAULT_POOLSIZE )
+     n = DEFAULT_POOLSIZE;
+  if( !pool_okay )
+       init_pool(n);
+  else
+       log_error("Oops, secure memory pool already initialized\n");
+    }
+}
+
+
+void *
+secmem_malloc( size_t size )
+{
+    MEMBLOCK *mb, *mb2;
+    int compressed=0;
+
+    if( !pool_okay ) {
+ log_info(
+  "operation is not possible without initialized secure memory\n");
+  log_info("(you may have used the wrong program for this task)\n");
+    }
+    if( show_warning && !suspend_warning ) {
+    show_warning = 0;
+  print_warn();
+    }
+
+    /* blocks are always a multiple of 32 */
+    size += sizeof(MEMBLOCK);
+    size = ((size + 31) / 32) * 32;
+
+  retry:
+    /* try to get it from the used blocks */
+    for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
+  if( mb->size >= size ) {
+       if( mb2 )
+      mb2->u.next = mb->u.next;
+      else
+       unused_blocks = mb->u.next;
+        goto leave;
+    }
+    /* allocate a new block */
+    if( (poollen + size <= poolsize) ) {
+  mb = (void*)((char*)pool + poollen);
+   poollen += size;
+   mb->size = size;
+    }
+    else if( !compressed ) {
+    compressed=1;
+  compress_pool();
+   goto retry;
+    }
+    else
+ return NULL;
+
+  leave:
+    cur_alloced += mb->size;
+    cur_blocks++;
+    if( cur_alloced > max_alloced )
+  max_alloced = cur_alloced;
+    if( cur_blocks > max_blocks )
+   max_blocks = cur_blocks;
+
+    return &mb->u.aligned.c;
+}
+
+
+void *
+secmem_realloc( void *p, size_t newsize )
+{
+    MEMBLOCK *mb;
+    size_t size;
+    void *a;
+
+    mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+    size = mb->size;
+    if( newsize < size )
+ return p; /* it is easier not to shrink the memory */
+    a = secmem_malloc( newsize );
+    memcpy(a, p, size);
+    memset((char*)a+size, 0, newsize-size);
+    secmem_free(p);
+    return a;
+}
+
+
+void
+secmem_free( void *a )
+{
+    MEMBLOCK *mb;
+    size_t size;
+
+    if( !a )
+   return;
+
+    mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+    size = mb->size;
+    /* This does not make much sense: probably this memory is held in the
+     * cache. We do it anyway: */
+    wipememory2(mb, 0xff, size );
+    wipememory2(mb, 0xaa, size );
+    wipememory2(mb, 0x55, size );
+    wipememory2(mb, 0x00, size );
+    mb->size = size;
+    mb->u.next = unused_blocks;
+    unused_blocks = mb;
+    cur_blocks--;
+    cur_alloced -= size;
+}
+
+int
+m_is_secure( const void *p )
+{
+    return p >= pool && p < (void*)((char*)pool+poolsize);
+}
+
+void
+secmem_term()
+{
+    if( !pool_okay )
+  return;
+
+    wipememory2( pool, 0xff, poolsize);
+    wipememory2( pool, 0xaa, poolsize);
+    wipememory2( pool, 0x55, poolsize);
+    wipememory2( pool, 0x00, poolsize);
+#if HAVE_MMAP
+    if( pool_is_mmapped )
+   munmap( pool, poolsize );
+#endif
+    pool = NULL;
+    pool_okay = 0;
+    poolsize=0;
+    poollen=0;
+    unused_blocks=NULL;
+}
+
+
+void
+secmem_dump_stats()
+{
+    if( disable_secmem )
+    return;
+    fprintf(stderr,
+        "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
+     cur_alloced, max_alloced, cur_blocks, max_blocks,
+      (unsigned long)poollen, (unsigned long)poolsize );
+}
+
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-secmem.h ./agent/seahorse-agent-secmem.h
--- ../seahorse_orig/agent/seahorse-agent-secmem.h	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-secmem.h	2004-09-29 10:41:04.000000000 -0000
@@ -0,0 +1,41 @@
+/* Quintuple Agent secure memory allocation
+ * Copyright (C) 1998,1999 Free Software Foundation, Inc.
+ * Copyright (C) 1999,2000 Robert Bihlmeyer <robbe@orcus.priv.at>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SEAHORSE_SECMEM_H
+#define _SEAHORSE_SECMEM_H
+
+#include <sys/types.h>
+
+/* values for flags, hardcoded in secmem.c */
+#define SECMEM_WARN      0
+#define SECMEM_DONT_WARN  1
+#define SECMEM_SUSPEND_WARN   2
+
+void secmem_init( size_t npool );
+void secmem_term( void );
+int  secmem_have();
+void *secmem_malloc( size_t size );
+void *secmem_realloc( void *a, size_t newsize );
+void secmem_free( void *a );
+int  m_is_secure( const void *p );
+void secmem_dump_stats(void);
+void secmem_set_flags( unsigned flags );
+unsigned secmem_get_flags(void);
+
+#endif /* _SEAHORSE_SECMEM_H */
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-settings.c ./agent/seahorse-agent-settings.c
--- ../seahorse_orig/agent/seahorse-agent-settings.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-settings.c	2004-09-29 10:39:09.000000000 -0000
@@ -0,0 +1,131 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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.
+ */
+ 
+/*
+ * Simple helper functions for use of gconf. Some code ideas
+ * from gnome's libeel 
+ */
+
+#include <stdlib.h>
+#include "seahorse-agent.h"
+
+static GConfClient* g_gconf = NULL;
+
+gboolean initialize_gconf()
+{
+    if(!g_gconf)
+    {
+        g_gconf = gconf_client_get_default();
+        if(!g_gconf)
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+void seahorse_agent_settings_cleanup()
+{
+    if(g_gconf)
+    {
+        g_object_unref(g_gconf);
+        g_gconf = NULL;
+    }
+}
+
+static void handle_gconf_error(GError* error)
+{
+    if(error != NULL) 
+    {
+        g_warning("GConf error:\n  %s", error->message);
+        g_error_free(error);
+    }
+}
+
+gboolean seahorse_agent_settings_get_boolean(const char* key, gboolean def)
+{
+    gboolean result;
+    GError *error = NULL;
+    
+    if(!initialize_gconf())
+        return def;
+  
+    result = gconf_client_get_bool(g_gconf, key, &error);
+    if(error != NULL) 
+    {
+        handle_gconf_error(error);
+        return def;
+    }
+    
+    return result;
+}
+
+gboolean seahorse_agent_settings_get_integer(const char* key, gint def)
+{
+    gint result;
+    GError* error = NULL;
+
+    if(!initialize_gconf())
+        return def;
+  
+    result = gconf_client_get_int(g_gconf, key, &error);
+    if(error != NULL) 
+    {
+        handle_gconf_error(error);
+        return def;
+    }
+    
+    return result;
+}
+
+guint seahorse_agent_settings_notify_add(const char *key, 
+                        GConfClientNotifyFunc callback, gpointer data)
+{
+    guint id;
+    GError* error = NULL;
+  
+    if(!initialize_gconf())
+        return SETTINGS_UNDEFINED;
+  
+    id = gconf_client_notify_add(g_gconf, key, callback, data, NULL, &error);
+    if(error != NULL)
+    {
+        handle_gconf_error(error);
+        if(id != SETTINGS_UNDEFINED) 
+        {
+            gconf_client_notify_remove(g_gconf, id);
+            id = SETTINGS_UNDEFINED;
+        }
+    }
+  
+    return id;
+}
+
+void seahorse_agent_settings_notify_remove(guint notification_id)
+{
+    if(notification_id == SETTINGS_UNDEFINED) 
+       return;
+
+    if(!initialize_gconf())
+        return;
+  
+    gconf_client_notify_remove(g_gconf, notification_id);
+}
+
diff -u -N -r ../seahorse_orig/agent/seahorse-agent-status.c ./agent/seahorse-agent-status.c
--- ../seahorse_orig/agent/seahorse-agent-status.c	1969-12-31 17:00:00.000000000 -0000
+++ ./agent/seahorse-agent-status.c	2004-09-29 14:08:45.000000000 -0000
@@ -0,0 +1,299 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004 Nate Nielsen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ * You should have received a copy of the GNU 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 <sys/types.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <gnome.h>
+#include <glade/glade-xml.h>
+
+#include "eggtrayicon.h"
+#include "seahorse-agent.h"
+#include "seahorse-context.h"
+#include "seahorse-widget.h"
+#include "seahorse-agent-secmem.h"
+
+/* 
+ * Implements tray icon, menu and status window. Tray icon ideas 
+ * came from gaim
+ */
+
+/* For the docklet icon */
+static EggTrayIcon* g_docklet = NULL;
+static GtkWidget* g_image = NULL;
+
+/* For the popup window */
+static SeahorseWidget* g_window = NULL;
+static SeahorseContext* g_sctx = NULL; 
+
+/* -----------------------------------------------------------------------------
+ *  Popup Window
+ */
+
+enum 
+{
+    KEY_COLUMN,
+    N_COLUMNS
+};
+
+void window_destroy()
+{
+    if(g_window)
+    {
+        seahorse_widget_destroy(g_window);
+        g_window = NULL; 
+    }
+}
+
+static int delete_event(GtkWidget* widget, GdkEvent* event, gpointer data)
+{
+    window_destroy();
+    return FALSE;
+}
+
+static void close_clicked(GtkButton* button, SeahorseWidget* swidget)
+{
+    window_destroy();
+}
+
+static void clear_clicked(GtkButton* button, SeahorseWidget* swidget)
+{
+    seahorse_agent_cache_clearall();
+    window_destroy();
+}
+
+static void add_row_for_key(gpointer id, gpointer reserved, gpointer user_data)
+{
+    GtkTreeStore* store = GTK_TREE_STORE(user_data);
+    GtkTreeIter iter;
+    
+    /* Add a new row to the model */
+    gtk_tree_store_append(store, &iter, NULL);        
+    gtk_tree_store_set(store, &iter, KEY_COLUMN, 
+                       seahorse_agent_cache_getname((const gchar*)id), -1);
+}
+
+static void window_update_keys()
+{
+    GtkTreeViewColumn* column;
+    GtkTreeStore* store;
+    GtkTreeView* tree;
+
+    g_assert(g_window != NULL);
+    tree = GTK_TREE_VIEW(glade_xml_get_widget(g_window->xml, "key_list"));
+    g_assert(tree != NULL);
+        
+    store = GTK_TREE_STORE(gtk_tree_view_get_model(tree));
+    if(!store)
+    {
+        store = gtk_tree_store_new(N_COLUMNS,
+                                   G_TYPE_STRING);
+        gtk_tree_view_set_model(tree, GTK_TREE_MODEL(store));
+
+        /* Make the column */
+        column = gtk_tree_view_column_new_with_attributes(_("Cached GPG Keys"), 
+                                gtk_cell_renderer_text_new(), "text", KEY_COLUMN, NULL);
+        gtk_tree_view_append_column(tree, column);
+    }
+    else
+    {
+        /* We refill below */
+        gtk_tree_store_clear(store);
+    }
+
+    seahorse_agent_cache_enum(add_row_for_key, store);
+    g_object_unref(G_OBJECT(store));
+}   
+
+static void window_show()
+{
+    GtkWidget* w;
+    
+    if(g_window)
+    {
+        w = glade_xml_get_widget(g_window->xml, g_window->name);
+        gtk_window_present(GTK_WINDOW(w));
+        return;
+    }
+    
+    if(!g_sctx)
+        g_sctx = seahorse_context_new();
+        
+    g_window = seahorse_widget_new("agent-cache", g_sctx);
+
+    w = glade_xml_get_widget(g_window->xml, g_window->name);
+        
+    gtk_window_set_icon(GTK_WINDOW(w),
+        gdk_pixbuf_new_from_file(PIXMAPSDIR "/seahorse/lock.png", NULL));
+
+    g_signal_connect(G_OBJECT(w), "delete_event", 
+                     G_CALLBACK(delete_event), NULL);
+
+    glade_xml_signal_connect_data(g_window->xml, "close_clicked",
+        G_CALLBACK(close_clicked), g_window);
+    glade_xml_signal_connect_data(g_window->xml, "clear_clicked",
+        G_CALLBACK(clear_clicked), g_window);
+        
+    if(!secmem_have())
+    {
+        GdkColor color = { 0, 0xffff, 0, 0 };
+        w = glade_xml_get_widget(g_window->xml, "insecure_label");
+        gtk_widget_modify_fg(w, GTK_STATE_NORMAL, &color);        
+        gtk_widget_show(w);
+    }
+        
+    window_update_keys();
+}
+
+/* -----------------------------------------------------------------------------
+ *  Tray Icon Code
+ */
+
+static void on_clear_cache_activate(GtkWidget* item, gpointer data)
+{
+    seahorse_agent_cache_clearall();
+}
+
+static void on_show_window_activate(GtkWidget* item, gpointer data)
+{
+    window_show();
+}
+
+static void tray_embedded(GtkWidget* widget, void* data)
+{
+}
+
+static void tray_destroyed(GtkWidget* widget, void* data)
+{
+    g_object_unref(G_OBJECT(g_docklet));
+    g_docklet = NULL;
+}
+
+static void tray_clicked(GtkWidget* button, GdkEventButton* event, void* data)
+{ 
+    if(event->type != GDK_BUTTON_PRESS)
+       return;
+
+    if(event->button == 3)
+    {
+        GtkWidget* menu;
+        GladeXML* xml;
+        xml = glade_xml_new(SEAHORSE_GLADEDIR "seahorse-agent-cache.glade", "context-menu", NULL);
+        menu = glade_xml_get_widget (xml, "context-menu");
+        glade_xml_signal_connect_data(xml, "on_clear_cache_activate",
+                    G_CALLBACK(on_clear_cache_activate), NULL);
+        glade_xml_signal_connect_data(xml, "on_show_window_activate",
+                    G_CALLBACK(on_show_window_activate), NULL);
+                
+        gtk_menu_popup(GTK_MENU (menu), NULL, NULL, NULL, NULL, 
+                        event->button, gtk_get_current_event_time());
+        gtk_widget_show(menu);
+        g_object_unref(xml);
+    }
+    
+    else if(event->button == 1)
+    {
+        window_show();
+    }
+}
+
+void docklet_destroy()
+{
+    if(g_docklet)
+    {
+        g_signal_handlers_disconnect_by_func(G_OBJECT(g_docklet), 
+                                             G_CALLBACK(tray_destroyed), NULL);
+        gtk_widget_destroy(GTK_WIDGET(g_docklet));
+
+        g_object_unref(G_OBJECT(g_docklet));
+        g_docklet = NULL;
+    }
+}
+
+void docklet_create()
+{
+    GtkWidget* box;
+    GdkPixbuf* pb;
+
+    if(g_docklet) 
+    {
+        /* 
+         * If this is being called when a tray icon exists, it's 
+         * because something messed up. try destroying it before 
+         * we proceed, although docklet_refcount may be all hosed. 
+         * hopefully won't happen. 
+         */
+         
+        g_warning("trying to create icon but it already exists");
+        docklet_destroy();
+    }       
+    
+    g_docklet = egg_tray_icon_new("seahorse-agent");
+    box = gtk_event_box_new();
+    
+    /* 
+     * TODO: Is loading an external file a security risk? It may well be
+     * with all the image vulnerabilities going around. We may want to 
+     * encode this image and include in the code.
+     */
+    
+    g_image = gtk_image_new();
+    pb = gdk_pixbuf_new_from_file(PIXMAPSDIR "/seahorse/lock.png", NULL);
+    if(pb != NULL)
+        gtk_image_set_from_pixbuf(GTK_IMAGE(g_image), pb);
+    
+    g_signal_connect(G_OBJECT(g_docklet), "embedded", G_CALLBACK(tray_embedded), NULL);
+    g_signal_connect(G_OBJECT(g_docklet), "destroy", G_CALLBACK(tray_destroyed), NULL);
+    g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(tray_clicked), NULL);    
+    
+    gtk_container_add(GTK_CONTAINER(box), g_image);
+    gtk_container_add(GTK_CONTAINER(g_docklet), box);    
+    
+    if(!gtk_check_version(2,4,0))
+        g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);    
+
+    gtk_widget_show_all(GTK_WIDGET(g_docklet));
+
+    /* ref the docklet before we bandy it about the place */
+    g_object_ref(G_OBJECT(g_docklet));      
+}    
+
+
+void seahorse_agent_status_cleanup()
+{
+    docklet_destroy();
+}
+
+void seahorse_agent_status_update()
+{
+    gboolean have = (seahorse_agent_cache_count() > 0);
+    
+    if(have && !g_docklet)
+        docklet_create();
+        
+    else if(!have && g_docklet)
+        docklet_destroy();    
+        
+    if(g_window)
+        window_update_keys();        
+}
+
diff -u -N -r ../seahorse_orig/data/Makefile.am ./data/Makefile.am
--- ../seahorse_orig/data/Makefile.am	2003-04-29 18:47:16.000000000 -0000
+++ ./data/Makefile.am	2004-09-29 12:36:25.000000000 -0000
@@ -1,6 +1,9 @@
 
 @INTLTOOL_SCHEMAS_RULE@
 
+distpixmapdir = $(datadir)/pixmaps/seahorse
+distpixmap_DATA = lock.png
+
 schemasdir = $(GCONF_SCHEMA_FILE_DIR)
 schemas_in_files = seahorse.schemas.in
 schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)
@@ -18,6 +21,6 @@
 install-data-local:
 endif
 
-EXTRA_DIST = $(schemas_in_files)
+EXTRA_DIST = $(schemas_in_files) $(distpixmap_DATA)
 
 CLEANFILES = $(schemas_DATA)
diff -u -N -r ../seahorse_orig/data/seahorse.schemas.in ./data/seahorse.schemas.in
--- ../seahorse_orig/data/seahorse.schemas.in	2003-04-14 08:22:30.000000000 -0000
+++ ./data/seahorse.schemas.in	2004-09-27 21:24:41.000000000 -0000
@@ -155,4 +155,43 @@
 			</locale>
 		</schema>
 	</schemalist>
+	<schemalist>
+		<schema>
+			<key>/schemas/apps/seahorse/agent/cache_enabled</key>
+			<applyto>/apps/seahorse/agent/cache_enabled</applyto>
+			<owner>seahorse</owner>
+			<type>bool</type>
+			<default>TRUE</default>
+			<locale name="C">
+				<short>Whether the GPG password cache is enabled</short>
+				<long>This option enables the GPG password cache in the 
+				seahorse-agent daemon.</long>
+			</locale>
+		</schema>
+		<schema>
+			<key>/schemas/apps/seahorse/agent/cache_ttl</key>
+			<applyto>/apps/seahorse/agent/cache_ttl</applyto>
+			<owner>seahorse</owner>
+			<type>int</type>
+			<default>300</default>
+			<locale name="C">
+				<short>The time (in seconds) to cache GPG passwords</short>
+				<long>This is the amount of time, specified in seconds,
+				to cache GPG passwords in seahorse-agent. A setting of 0
+				means cache forever.</long>
+			</locale>
+		</schema>
+		<schema>
+			<key>/schemas/apps/seahorse/agent/cache_authorize</key>
+			<applyto>/apps/seahorse/agent/cache_authorize</applyto>
+			<owner>seahorse</owner>
+			<type>bool</type>
+			<default>TRUE</default>
+			<locale name="C">
+				<short>Prompt before using GPG passwords in cache</short>
+				<long>Set to 'true' to have seahorse-agent prompt before
+				giving out passwords it has cached.</long>
+			</locale>
+		</schema>
+	</schemalist>
 </gconfschemafile>
