krb5-auth-dialog r101 - in trunk: . gtksecentry secmem src



Author: guidog
Date: Sat Jan 10 13:25:23 2009
New Revision: 101
URL: http://svn.gnome.org/viewvc/krb5-auth-dialog?rev=101&view=rev

Log:
add gtksecentry/secmem from gpg pinentry

code is licensed GPLv2

Added:
   trunk/acinclude.m4
   trunk/gtksecentry/
   trunk/gtksecentry/Makefile.am
   trunk/gtksecentry/gtksecentry.c
   trunk/gtksecentry/gtksecentry.h
   trunk/secmem/
   trunk/secmem/Makefile.am
   trunk/secmem/Manifest
   trunk/secmem/memory.h
   trunk/secmem/secmem-util.h
   trunk/secmem/secmem.c
   trunk/secmem/util.c
   trunk/secmem/util.h
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.ac
   trunk/src/Makefile.am

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Sat Jan 10 13:25:23 2009
@@ -1,4 +1,4 @@
-SUBDIRS = src po etpo icons
+SUBDIRS = secmem gtksecentry src po etpo icons
 
 EXTRA_DIST =                            \
 	intltool-extract.in             \

Added: trunk/acinclude.m4
==============================================================================
--- (empty file)
+++ trunk/acinclude.m4	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,117 @@
+dnl Autoconf macros used by PINENTRY
+dnl
+dnl Copyright (C) 2002 g10 Code GmbH
+dnl
+dnl
+dnl GNUPG_CHECK_TYPEDEF(TYPE, HAVE_NAME)
+dnl Check whether a typedef exists and create a #define $2 if it exists
+dnl
+AC_DEFUN([GNUPG_CHECK_TYPEDEF],
+  [ AC_MSG_CHECKING(for $1 typedef)
+    AC_CACHE_VAL(gnupg_cv_typedef_$1,
+    [AC_TRY_COMPILE([#define _GNU_SOURCE 1
+    #include <stdlib.h>
+    #include <sys/types.h>], [
+    #undef $1
+    int a = sizeof($1);
+    ], gnupg_cv_typedef_$1=yes, gnupg_cv_typedef_$1=no )])
+    AC_MSG_RESULT($gnupg_cv_typedef_$1)
+    if test "$gnupg_cv_typedef_$1" = yes; then
+        AC_DEFINE($2,1,[Defined if a `]$1[' is typedef'd])
+    fi
+  ])
+
+
+######################################################################
+# Check whether mlock is broken (hpux 10.20 raises a SIGBUS if mlock
+# is not called from uid 0 (not tested whether uid 0 works)
+# For DECs Tru64 we have also to check whether mlock is in librt
+# mlock is there a macro using memlk()
+######################################################################
+dnl GNUPG_CHECK_MLOCK
+dnl
+define([GNUPG_CHECK_MLOCK],
+  [ AC_CHECK_FUNCS(mlock)
+    if test "$ac_cv_func_mlock" = "no"; then
+        AC_CHECK_HEADERS(sys/mman.h)
+        if test "$ac_cv_header_sys_mman_h" = "yes"; then
+            # Add librt to LIBS:
+            AC_CHECK_LIB(rt, memlk)
+            AC_CACHE_CHECK([whether mlock is in sys/mman.h],
+                            gnupg_cv_mlock_is_in_sys_mman,
+                [AC_TRY_LINK([
+                    #include <assert.h>
+                    #ifdef HAVE_SYS_MMAN_H
+                    #include <sys/mman.h>
+                    #endif
+                ], [
+                    int i;
+
+                    /* glibc defines this for functions which it implements
+                     * to always fail with ENOSYS.  Some functions are actually
+                     * named something starting with __ and the normal name
+                     * is an alias.  */
+                    #if defined (__stub_mlock) || defined (__stub___mlock)
+                    choke me
+                    #else
+                    mlock(&i, 4);
+                    #endif
+                    ; return 0;
+                ],
+                gnupg_cv_mlock_is_in_sys_mman=yes,
+                gnupg_cv_mlock_is_in_sys_mman=no)])
+            if test "$gnupg_cv_mlock_is_in_sys_mman" = "yes"; then
+                AC_DEFINE(HAVE_MLOCK,1,
+                          [Defined if the system supports an mlock() call])
+            fi
+        fi
+    fi
+    if test "$ac_cv_func_mlock" = "yes"; then
+        AC_MSG_CHECKING(whether mlock is broken)
+          AC_CACHE_VAL(gnupg_cv_have_broken_mlock,
+             AC_TRY_RUN([
+                #include <stdlib.h>
+                #include <unistd.h>
+                #include <errno.h>
+                #include <sys/mman.h>
+                #include <sys/types.h>
+                #include <fcntl.h>
+
+                int main()
+                {
+                    char *pool;
+                    int err;
+                    long int pgsize = getpagesize();
+
+                    pool = malloc( 4096 + pgsize );
+                    if( !pool )
+                        return 2;
+                    pool += (pgsize - ((long int)pool % pgsize));
+
+                    err = mlock( pool, 4096 );
+                    if( !err || errno == EPERM )
+                        return 0; /* okay */
+
+                    return 1;  /* hmmm */
+                }
+
+            ],
+            gnupg_cv_have_broken_mlock="no",
+            gnupg_cv_have_broken_mlock="yes",
+            gnupg_cv_have_broken_mlock="assume-no"
+           )
+         )
+         if test "$gnupg_cv_have_broken_mlock" = "yes"; then
+             AC_DEFINE(HAVE_BROKEN_MLOCK,1,
+                       [Defined if the mlock() call does not work])
+             AC_MSG_RESULT(yes)
+             AC_CHECK_FUNCS(plock)
+         else
+            if test "$gnupg_cv_have_broken_mlock" = "no"; then
+                AC_MSG_RESULT(no)
+            else
+                AC_MSG_RESULT(assuming no)
+            fi
+         fi
+    fi
+  ])

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Sat Jan 10 13:25:23 2009
@@ -137,6 +137,26 @@
 AC_SUBST(LIBNOTIFY_CFLAGS)
 AC_SUBST(LIBNOTIFY_LIBS)
 
+
+dnl secmem
+dnl Checks for library functions.
+AC_CHECK_FUNCS(seteuid stpcpy mmap)
+GNUPG_CHECK_MLOCK
+GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF)
+GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF)
+dnl Check for libcap
+AC_ARG_WITH([libcap], AC_HELP_STRING([--without-libcap],
+            [Disable support for capabilities library]))
+if test "x$with_libcap" != "xno"; then
+  AC_PATH_PROG(SETCAP, setcap, :, "$PATH:/sbin:/usr/sbin")
+  AC_CHECK_LIB(cap, cap_set_proc, [
+    AC_DEFINE(USE_CAPABILITIES,1,[The capabilities support library is installed])
+    LIBCAP=-lcap
+  ])
+fi
+AC_SUBST(LIBCAP)
+
+
 check_interval=30
 AC_DEFINE_UNQUOTED(CREDENTIAL_CHECK_INTERVAL,[$check_interval],
 		   [Define the to number of seconds to wait between checks of
@@ -165,6 +185,8 @@
 krb5-auth-dialog.spec
 src/Makefile
 src/krb5-auth-dialog.1
+secmem/Makefile
+gtksecentry/Makefile
 icons/Makefile
 etpo/Makefile
 po/Makefile.in

Added: trunk/gtksecentry/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/gtksecentry/Makefile.am	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = $(GTK_CFLAGS) \
+	-I$(top_srcdir)/secmem -I$(top_srcdir)/gtksecentry
+
+noinst_LIBRARIES = libgtksecentry.a
+
+libgtksecentry_a_SOURCES = gtksecentry.c gtksecentry.h
+

Added: trunk/gtksecentry/gtksecentry.c
==============================================================================
--- (empty file)
+++ trunk/gtksecentry/gtksecentry.c	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,3412 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ * Copyright (C) 2004 Albrecht Dreß+ *
+ * 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:
+ *
+ * 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 "gtksecentry.h"
+#include "memory.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(gsize 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(gsize 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, gsize 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 blinktime;
+
+    g_object_get(settings, "gtk-cursor-blink-time", &blinktime, NULL);
+
+    return blinktime;
+}
+
+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);
+}

Added: trunk/gtksecentry/gtksecentry.h
==============================================================================
--- (empty file)
+++ trunk/gtksecentry/gtksecentry.h	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,181 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ * Copyright (C) 2004 Albrecht Dreß+ *
+ * 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:
+ *
+ * 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__ */

Added: trunk/secmem/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/secmem/Makefile.am	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,31 @@
+# Secure Memory Makefile
+# Copyright (C) 2002 g10 Code GmbH
+#
+# This file is part of PINENTRY.
+#
+# PINENTRY 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.
+#
+# PINENTRY 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
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = Manifest
+
+noinst_LIBRARIES = libsecmem.a
+
+libsecmem_a_SOURCES = \
+	memory.h \
+	secmem-util.h \
+	util.h \
+	secmem.c \
+	util.c

Added: trunk/secmem/Manifest
==============================================================================
--- (empty file)
+++ trunk/secmem/Manifest	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,7 @@
+Makefile.am
+memory.h
+secmem-util.h
+secmem.c
+util.c
+util.h
+$names$ iQCVAwUAP+f/RDEAnp832S/7AQIbRQQAzR7UvGOTMl8AWyVgHGQjW5A5fGzRlaEABl+5UpGmzoFGFdP9upHv3Tj0MKETHNRkdOAA5k5QzamDypAr5RINz9rdZPkNPIAtg4csN7Yb6ITJZaH7yLDJcBmhM49a8ZNpDpQeImzpE05cM6TuGVO6NSIrlt9OBhaHfbkpzgr1tI0==6Tyw

Added: trunk/secmem/memory.h
==============================================================================
--- (empty file)
+++ trunk/secmem/memory.h	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,40 @@
+/* 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 _MEMORY_H
+#define _MEMORY_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 );
+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 /* _MEMORY_H */

Added: trunk/secmem/secmem-util.h
==============================================================================
--- (empty file)
+++ trunk/secmem/secmem-util.h	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,3 @@
+/* This file exists because "util.h" is such a generic name that it is
+   likely to clash with other such files.  */
+#include "util.h"

Added: trunk/secmem/secmem.c
==============================================================================
--- (empty file)
+++ trunk/secmem/secmem.c	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,448 @@
+/* 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
+ */
+
+#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 "memory.h"
+
+#ifdef ORIGINAL_GPG_VERSION
+#include "types.h"
+#include "util.h"
+#else /* ORIGINAL_GPG_VERSION */
+
+#include "util.h"
+
+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 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
+    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);
+	    close (fd);
+	}
+    }
+# 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;
+}
+
+
+/* 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");
+	exit(2);
+    }
+    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,
+		(ulong)poollen, (ulong)poolsize );
+}
+

Added: trunk/secmem/util.c
==============================================================================
--- (empty file)
+++ trunk/secmem/util.c	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,139 @@
+/* Quintuple Agent
+ * Copyright (C) 1999 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.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util.h"
+
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(expression) \
+  (__extension__							      \
+    ({ long int __result;						      \
+       do __result = (long int) (expression);				      \
+       while (__result == -1L && errno == EINTR);			      \
+       __result; }))
+#endif
+
+#ifndef HAVE_DOSISH_SYSTEM
+static int uid_set = 0;
+static uid_t real_uid, file_uid;
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+/* write DATA of size BYTES to FD, until all is written or an error occurs */
+ssize_t xwrite(int fd, const void *data, size_t bytes)
+{
+  char *ptr;
+  size_t todo;
+  ssize_t written = 0;
+
+  for (ptr = (char *)data, todo = bytes; todo; ptr += written, todo -= written)
+    if ((written = TEMP_FAILURE_RETRY(write(fd, ptr, todo))) < 0)
+      break;
+  return written;
+}
+
+#if 0
+extern int debug;
+
+int debugmsg(const char *fmt, ...)
+{
+  va_list va;
+  int ret;
+
+  if (debug) {
+    va_start(va, fmt);
+    fprintf(stderr, "\e[4m");
+    ret = vfprintf(stderr, fmt, va);
+    fprintf(stderr, "\e[24m");
+    va_end(va);
+    return ret;
+  } else
+    return 0;
+}
+#endif
+
+/* initialize uid variables */
+#ifndef HAVE_DOSISH_SYSTEM
+static void init_uids(void)
+{
+  real_uid = getuid();
+  file_uid = geteuid();
+  uid_set = 1;
+}
+#endif
+
+
+#if 0 /* Not used. */
+/* lower privileges to the real user's */
+void lower_privs()
+{
+  if (!uid_set)
+    init_uids();
+  if (real_uid != file_uid) {
+#ifdef HAVE_SETEUID
+    if (seteuid(real_uid) < 0) {
+      perror("lowering privileges failed");
+      exit(EXIT_FAILURE);
+    }
+#else
+    fprintf(stderr, _("Warning: running q-agent setuid on this system is dangerous\n"));
+#endif /* HAVE_SETEUID */
+  }
+}
+#endif /* if 0 */
+
+#if 0 /* Not used. */
+/* raise privileges to the effective user's */
+void raise_privs()
+{
+  assert(real_uid >= 0);	/* lower_privs() must be called before this */
+#ifdef HAVE_SETEUID
+  if (real_uid != file_uid && seteuid(file_uid) < 0) {
+   perror("Warning: raising privileges failed");
+  }
+#endif /* HAVE_SETEUID */
+}
+#endif /* if 0 */
+
+/* drop all additional privileges */
+void drop_privs()
+{
+#ifndef HAVE_DOSISH_SYSTEM
+  if (!uid_set)
+    init_uids();
+  if (real_uid != file_uid) {
+    if (setuid(real_uid) < 0) {
+      perror("dropping privileges failed");
+      exit(EXIT_FAILURE);
+    }
+    file_uid = real_uid;
+  }
+#endif
+}

Added: trunk/secmem/util.h
==============================================================================
--- (empty file)
+++ trunk/secmem/util.h	Sat Jan 10 13:25:23 2009
@@ -0,0 +1,66 @@
+/* Quintuple Agent utilities
+ * Copyright (C) 1999 Robert Bihlmeyer <robbe orcus priv at>
+ * Copyright (C) 2003 g10 Code GmbH
+ *
+ *  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 _UTIL_H
+#define _UTIL_H
+
+#include <sys/types.h>
+
+#ifndef HAVE_BYTE_TYPEDEF
+# undef byte
+# ifdef __riscos__
+    /* Norcroft treats char == unsigned char but char* != unsigned char*  */
+    typedef char byte;
+# else
+    typedef unsigned char byte;
+# endif
+# define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_ULONG_TYPEDEF
+# undef ulong
+  typedef unsigned long ulong;
+# define HAVE_ULONG_TYPEDEF
+#endif
+
+
+ssize_t xwrite(int, const void *, size_t); /* write until finished */
+int debugmsg(const char *, ...); /* output a debug message if debugging==on */
+void drop_privs(void);		/* finally drop privileges */
+
+
+/* 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)
+
+
+
+
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+#endif

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Sat Jan 10 13:25:23 2009
@@ -22,6 +22,8 @@
 	dummy-strings.c
 
 krb5_auth_dialog_LDADD = \
+			../gtksecentry/libgtksecentry.a \
+			../secmem/libsecmem.a \
 			@NETWORK_MANAGER_LIBS@ \
 			@KRB5_LIBS@ \
 			@LIBNOTIFY_LIBS@ \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]