[evolution-data-server] Don't use gnome-keyring to store credentials on Windows



commit 827688ec6e3cab595e11511f0ee1f68539796ca1
Author: Fridrich Å trba <fridrich strba bluewin ch>
Date:   Mon Mar 7 10:48:14 2011 +0100

    Don't use gnome-keyring to store credentials on Windows

 .gitignore                           |    2 +-
 configure.ac                         |   15 +-
 libedataserverui/Makefile.am         |   15 +-
 libedataserverui/e-passwords-win32.c | 1043 ++++++++++++++++++++++++++++++++++
 4 files changed, 1070 insertions(+), 5 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 047b141..2467253 100644
--- a/.gitignore
+++ b/.gitignore
@@ -101,7 +101,7 @@
 /po/boldquot.sed
 /po/en boldquot header
 /po/en quot header
-/po/evolution-data-server-2.92.pot
+/po/evolution-data-server-3.0.pot
 /po/insert-header.sin
 /po/quot.sed
 /po/remove-potcdate.sin
diff --git a/configure.ac b/configure.ac
index 6758ae3..78f71ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -317,8 +317,17 @@ PKG_CHECK_MODULES(GNOME_PLATFORM,
 	gconf-2.0 >= gconf_minimum_version
 	libxml-2.0 >= libxml_minimum_version
 	libsoup-2.4 >= libsoup_minimum_version
-	libgdata >= libgdata_minimum_version
-	gnome-keyring-1 >= gnome_keyring_minimum_version])
+	libgdata >= libgdata_minimum_version])
+
+
+if test x$os_win32 = xno; then
+  dnl ***********************************
+  dnl Check for GNOME Keyring.
+  dnl ***********************************
+  PKG_CHECK_MODULES(GNOME_KEYRING,
+	[gnome-keyring-1 >= gnome_keyring_minimum_version])
+fi
+
 
 LIBICAL_REQUIRED=libical_minimum_version
 AC_SUBST(LIBICAL_REQUIRED)
@@ -1188,7 +1197,7 @@ AC_SUBST(E_DATA_SERVER_LIBS)
 dnl ******************************
 dnl libedataserverui
 dnl ******************************
-E_DATA_SERVER_UI_DEPS="gtk+-3.0 libxml-2.0 gconf-2.0 gnome-keyring-1"
+E_DATA_SERVER_UI_DEPS="gtk+-3.0 libxml-2.0 gconf-2.0"
 
 EVO_SET_COMPILE_FLAGS(E_DATA_SERVER_UI, $E_DATA_SERVER_UI_DEPS, , )
 AC_SUBST(E_DATA_SERVER_UI_CFLAGS)
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
index b3db0c0..01dc99d 100644
--- a/libedataserverui/Makefile.am
+++ b/libedataserverui/Makefile.am
@@ -17,6 +17,7 @@ CPPFLAGS = \
 	-DG_LOG_DOMAIN=\"e-data-server-ui\"	\
 	-DE_DATA_SERVER_UI_UIDIR=\""$(uidir)"\"	\
 	$(E_DATA_SERVER_UI_CFLAGS)		\
+	$(GNOME_KEYRING_CFLAGS) \
 	$(CAMEL_CFLAGS)
 
 lib_LTLIBRARIES = libedataserverui-3.0.la
@@ -39,6 +40,14 @@ libedataserveruiinclude_HEADERS =	\
 	e-tree-model-generator.h	\
 	e-cell-renderer-color.h
 
+if OS_WIN32
+e_passwords_build = e-passwords-win32.c
+e_passwords_dist = e-passwords.c
+else
+e_passwords_build = e-passwords.c
+e_passwords_dist = e-passwords-win32.c
+endif
+
 libedataserverui_3_0_la_SOURCES =	\
 	$(MARSHAL_GENERATED)		\
 	e-categories-dialog.c		\
@@ -51,7 +60,7 @@ libedataserverui_3_0_la_SOURCES =	\
 	e-name-selector-entry.c		\
 	e-name-selector-model.c		\
 	e-name-selector-list.c		\
-	e-passwords.c			\
+	$(e_passwords_build)		\
 	e-source-selector.c		\
 	e-source-selector-dialog.c	\
 	e-source-combo-box.c		\
@@ -65,6 +74,7 @@ libedataserverui_3_0_la_LIBADD = 				\
 	$(top_builddir)/libedataserver/libedataserver-1.2.la	\
 	$(top_builddir)/camel/libcamel-1.2.la			\
 	$(E_DATA_SERVER_UI_LIBS)				\
+	$(GNOME_KEYRING_LIBS) \
 	$(CAMEL_LIBS)
 
 libedataserverui_3_0_la_LDFLAGS = \
@@ -79,6 +89,7 @@ TEST_EDATASERVERUI_CPPFLAGS= \
 	-I$(top_builddir)/addressbook		\
 	-DG_LOG_DOMAIN=\"e-data-server-ui\"	\
 	$(CAMEL_CFLAGS)				\
+	$(GNOME_KEYRING_CFLAGS) \
 	$(E_DATA_SERVER_UI_CFLAGS)
 
 TEST_EDATASERVERUI_LDFLAGS = \
@@ -86,6 +97,7 @@ TEST_EDATASERVERUI_LDFLAGS = \
 	$(top_builddir)/camel/libcamel-1.2.la			\
 	$(top_builddir)/addressbook/libebook/libebook-1.2.la	\
 	$(top_builddir)/libedataserver/libedataserver-1.2.la	\
+	$(GNOME_KEYRING_LIBS) \
 	$(E_DATA_SERVER_UI_LIBS)
 
 # Test programs
@@ -118,6 +130,7 @@ pkgconfig_DATA = libedataserverui-3.0.pc
 BUILT_SOURCES = $(MARSHAL_GENERATED)
 
 EXTRA_DIST = 						\
+	e_passwords_dist \
 	e-data-server-ui-marshal.list			\
 	$(pkgconfig_DATA:-3.0.pc=.pc.in)
 
diff --git a/libedataserverui/e-passwords-win32.c b/libedataserverui/e-passwords-win32.c
new file mode 100644
index 0000000..ef9d1dc
--- /dev/null
+++ b/libedataserverui/e-passwords-win32.c
@@ -0,0 +1,1043 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * e-passwords.c
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA.
+ */
+
+/*
+ * This looks a lot more complicated than it is, and than you'd think
+ * it would need to be.  There is however, method to the madness.
+ *
+ * The code most cope with being called from any thread at any time,
+ * recursively from the main thread, and then serialising every
+ * request so that sane and correct values are always returned, and
+ * duplicate requests are never made.
+ *
+ * To this end, every call is marshalled and queued and a dispatch
+ * method invoked until that request is satisfied.  If mainloop
+ * recursion occurs, then the sub-call will necessarily return out of
+ * order, but will not be processed out of order.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-passwords.h"
+#include "libedataserver/e-data-server-util.h"
+#include "libedataserver/e-flag.h"
+#include "libedataserver/e-url.h"
+
+#define d(x)
+
+typedef struct _EPassMsg EPassMsg;
+
+struct _EPassMsg {
+	void (*dispatch) (EPassMsg *);
+	EFlag *done;
+
+	/* input */
+	GtkWindow *parent;
+	const gchar *component;
+	const gchar *key;
+	const gchar *title;
+	const gchar *prompt;
+	const gchar *oldpass;
+	guint32 flags;
+
+	/* output */
+	gboolean *remember;
+	gchar *password;
+	GError *error;
+
+	/* work variables */
+	GtkWidget *entry;
+	GtkWidget *check;
+	guint ismain:1;
+	guint noreply:1;	/* supress replies; when calling
+				 * dispatch functions from others */
+};
+
+G_LOCK_DEFINE_STATIC (passwords);
+static GThread *main_thread = NULL;
+static GHashTable *password_cache = NULL;
+static GtkDialog *password_dialog = NULL;
+static GQueue message_queue = G_QUEUE_INIT;
+static gint idle_id;
+static gint ep_online_state = TRUE;
+
+#define KEY_FILE_GROUP_PREFIX "Passwords-"
+static GKeyFile *key_file = NULL;
+
+static gboolean
+check_key_file (const gchar *funcname)
+{
+	if (!key_file)
+		g_message ("%s: Failed to create key file!", funcname);
+
+	return key_file != NULL;
+}
+
+static gchar *
+ep_key_file_get_filename (void)
+{
+	return g_build_filename (
+		e_get_user_config_dir (), "credentials", "Passwords", NULL);
+}
+
+static gchar *
+ep_key_file_get_group (const gchar *component)
+{
+	return g_strconcat (KEY_FILE_GROUP_PREFIX, component, NULL);
+}
+
+static gchar *
+ep_key_file_normalize_key (const gchar *key)
+{
+	/* XXX Previous code converted all slashes and equal signs in the
+	 * key to underscores for use with "gnome-config" functions.  While
+	 * it may not be necessary to convert slashes for use with GKeyFile,
+	 * we continue to do the same for backward-compatibility. */
+
+	gchar *normalized_key, *cp;
+
+	normalized_key = g_strdup (key);
+	for (cp = normalized_key; *cp != '\0'; cp++)
+		if (*cp == '/' || *cp == '=')
+			*cp = '_';
+
+	return normalized_key;
+}
+
+static void
+ep_key_file_load (void)
+{
+	gchar *filename;
+	GError *error = NULL;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	filename = ep_key_file_get_filename ();
+
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+		goto exit;
+
+	g_key_file_load_from_file (
+		key_file, filename, G_KEY_FILE_KEEP_COMMENTS |
+		G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+
+	if (error != NULL) {
+		g_warning ("%s: %s", filename, error->message);
+		g_error_free (error);
+	}
+
+exit:
+	g_free (filename);
+}
+
+static void
+ep_key_file_save (void)
+{
+	gchar *contents;
+	gchar *filename;
+	gchar *pathname;
+	gsize length = 0;
+	GError *error = NULL;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	filename = ep_key_file_get_filename ();
+	contents = g_key_file_to_data (key_file, &length, &error);
+	pathname = g_path_get_dirname (filename);
+
+	if (!error) {
+		g_mkdir_with_parents (pathname, 0700);
+		g_file_set_contents (filename, contents, length, &error);
+	}
+
+	g_free (pathname);
+
+	if (error != NULL) {
+		g_warning ("%s: %s", filename, error->message);
+		g_error_free (error);
+	}
+
+	g_free (contents);
+	g_free (filename);
+}
+
+static gchar *
+ep_password_encode (const gchar *password)
+{
+	/* XXX The previous Base64 encoding function did not encode the
+	 * password's trailing nul byte.  This makes decoding the Base64
+	 * string into a nul-terminated password more difficult, but we
+	 * continue to do it this way for backward-compatibility. */
+
+	gsize length = strlen (password);
+	return g_base64_encode ((const guchar *) password, length);
+}
+
+static gchar *
+ep_password_decode (const gchar *encoded_password)
+{
+	/* XXX The previous Base64 encoding function did not encode the
+	 * password's trailing nul byte, so we have to append a nul byte
+	 * to the decoded data to make it a nul-terminated string. */
+
+	gchar *password;
+	gsize length = 0;
+
+	password = (gchar *) g_base64_decode (encoded_password, &length);
+	password = g_realloc (password, length + 1);
+	password[length] = '\0';
+
+	return password;
+}
+
+static gboolean
+ep_idle_dispatch (gpointer data)
+{
+	EPassMsg *msg;
+
+	/* As soon as a password window is up we stop; it will
+	   re-invoke us when it has been closed down */
+	G_LOCK (passwords);
+	while (password_dialog == NULL && (msg = g_queue_pop_head (&message_queue)) != NULL) {
+		G_UNLOCK (passwords);
+
+		msg->dispatch (msg);
+
+		G_LOCK (passwords);
+	}
+
+	idle_id = 0;
+	G_UNLOCK (passwords);
+
+	return FALSE;
+}
+
+static EPassMsg *
+ep_msg_new (void (*dispatch) (EPassMsg *))
+{
+	EPassMsg *msg;
+
+	e_passwords_init ();
+
+	msg = g_malloc0 (sizeof (*msg));
+	msg->dispatch = dispatch;
+	msg->done = e_flag_new ();
+	msg->ismain = (g_thread_self () == main_thread);
+
+	return msg;
+}
+
+static void
+ep_msg_free (EPassMsg *msg)
+{
+	/* XXX We really should be passing this back to the caller, but
+	 *     doing so will require breaking the password API. */
+	if (msg->error != NULL) {
+		g_warning ("%s", msg->error->message);
+		g_error_free (msg->error);
+	}
+
+	e_flag_free (msg->done);
+	g_free (msg->password);
+	g_free (msg);
+}
+
+static void
+ep_msg_send (EPassMsg *msg)
+{
+	gint needidle = 0;
+
+	G_LOCK (passwords);
+	g_queue_push_tail (&message_queue, msg);
+	if (!idle_id) {
+		if (!msg->ismain)
+			idle_id = g_idle_add (ep_idle_dispatch, NULL);
+		else
+			needidle = 1;
+	}
+	G_UNLOCK (passwords);
+
+	if (msg->ismain) {
+		if (needidle)
+			ep_idle_dispatch (NULL);
+		while (!e_flag_is_set (msg->done))
+			g_main_context_iteration (NULL, TRUE);
+	} else
+		e_flag_wait (msg->done);
+}
+
+/* the functions that actually do the work */
+
+static void
+ep_clear_passwords_keyfile (EPassMsg *msg)
+{
+	gchar *group;
+	GError *error = NULL;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	group = ep_key_file_get_group (msg->component);
+
+	if (g_key_file_remove_group (key_file, group, &error))
+		ep_key_file_save ();
+
+	/* Not finding the requested group is acceptable, but we still
+	 * want to leave an informational message on the terminal. */
+	else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+		g_message ("%s", error->message);
+		g_error_free (error);
+
+	} else if (error != NULL)
+		g_propagate_error (&msg->error, error);
+
+	g_free (group);
+}
+
+static void
+ep_clear_passwords (EPassMsg *msg)
+{
+	ep_clear_passwords_keyfile (msg);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void
+ep_forget_passwords_keyfile (EPassMsg *msg)
+{
+	gchar **groups;
+	gsize length = 0, ii;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	groups = g_key_file_get_groups (key_file, &length);
+
+	if (!groups)
+		return;
+
+	for (ii = 0; ii < length; ii++) {
+		GError *error = NULL;
+
+		if (!g_str_has_prefix (groups[ii], KEY_FILE_GROUP_PREFIX))
+			continue;
+
+		g_key_file_remove_group (key_file, groups[ii], &error);
+
+		/* Not finding the requested group is acceptable, but we still
+		 * want to leave an informational message on the terminal. */
+		if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+			g_message ("%s", error->message);
+			g_error_free (error);
+
+		/* Issue a warning if anything else goes wrong. */
+		} else if (error != NULL) {
+			g_warning ("%s", error->message);
+			g_error_free (error);
+		}
+	}
+	ep_key_file_save ();
+	g_strfreev (groups);
+}
+
+static void
+ep_forget_passwords (EPassMsg *msg)
+{
+	g_hash_table_remove_all (password_cache);
+
+	ep_forget_passwords_keyfile (msg);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void
+ep_remember_password_keyfile (EPassMsg *msg)
+{
+	gchar *group, *key, *password;
+
+	password = g_hash_table_lookup (password_cache, msg->key);
+	if (password == NULL) {
+		g_warning ("Password for key \"%s\" not found", msg->key);
+		return;
+	}
+
+	group = ep_key_file_get_group (msg->component);
+	key = ep_key_file_normalize_key (msg->key);
+	password = ep_password_encode (password);
+
+	g_hash_table_remove (password_cache, msg->key);
+	if (check_key_file (G_STRFUNC)) {
+		g_key_file_set_string (key_file, group, key, password);
+		ep_key_file_save ();
+	}
+
+	g_free (group);
+	g_free (key);
+	g_free (password);
+}
+
+static void
+ep_remember_password (EPassMsg *msg)
+{
+	ep_remember_password_keyfile (msg);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void
+ep_forget_password_keyfile (EPassMsg *msg)
+{
+	gchar *group, *key;
+	GError *error = NULL;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	group = ep_key_file_get_group (msg->component);
+	key = ep_key_file_normalize_key (msg->key);
+
+	if (g_key_file_remove_key (key_file, group, key, &error))
+		ep_key_file_save ();
+
+	/* Not finding the requested key is acceptable, but we still
+	 * want to leave an informational message on the terminal. */
+	else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+		g_message ("%s", error->message);
+		g_error_free (error);
+
+	/* Not finding the requested group is also acceptable. */
+	} else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+		g_message ("%s", error->message);
+		g_error_free (error);
+
+	} else if (error != NULL)
+		g_propagate_error (&msg->error, error);
+
+	g_free (group);
+	g_free (key);
+}
+
+static void
+ep_forget_password (EPassMsg *msg)
+{
+	g_hash_table_remove (password_cache, msg->key);
+
+	ep_forget_password_keyfile (msg);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void
+ep_get_password_keyfile (EPassMsg *msg)
+{
+	gchar *group, *key, *password;
+	GError *error = NULL;
+
+	if (!check_key_file (G_STRFUNC))
+		return;
+
+	group = ep_key_file_get_group (msg->component);
+	key = ep_key_file_normalize_key (msg->key);
+
+	password = g_key_file_get_string (key_file, group, key, &error);
+	if (password != NULL) {
+		msg->password = ep_password_decode (password);
+		g_free (password);
+
+	/* Not finding the requested key is acceptable, but we still
+	 * want to leave an informational message on the terminal. */
+	} else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+		g_message ("%s", error->message);
+		g_error_free (error);
+
+	/* Not finding the requested group is also acceptable. */
+	} else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+		g_message ("%s", error->message);
+		g_error_free (error);
+
+	} else if (error != NULL)
+		g_propagate_error (&msg->error, error);
+
+	g_free (group);
+	g_free (key);
+}
+
+static void
+ep_get_password (EPassMsg *msg)
+{
+	gchar *password;
+
+	/* Check the in-memory cache first. */
+	password = g_hash_table_lookup (password_cache, msg->key);
+	if (password != NULL) {
+		msg->password = g_strdup (password);
+
+	} else
+		ep_get_password_keyfile (msg);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void
+ep_add_password (EPassMsg *msg)
+{
+	g_hash_table_insert (
+		password_cache, g_strdup (msg->key),
+		g_strdup (msg->oldpass));
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+}
+
+static void ep_ask_password (EPassMsg *msg);
+
+static void
+pass_response (GtkDialog *dialog, gint response, gpointer data)
+{
+	EPassMsg *msg = data;
+	gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
+	GList *iter, *trash = NULL;
+
+	if (response == GTK_RESPONSE_OK) {
+		msg->password = g_strdup (gtk_entry_get_text ((GtkEntry *)msg->entry));
+
+		if (type != E_PASSWORDS_REMEMBER_NEVER) {
+			gint noreply = msg->noreply;
+
+			*msg->remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (msg->check));
+
+			msg->noreply = 1;
+
+			if (*msg->remember || type == E_PASSWORDS_REMEMBER_FOREVER) {
+				msg->oldpass = msg->password;
+				ep_add_password (msg);
+			}
+			if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER)
+				ep_remember_password (msg);
+
+			msg->noreply = noreply;
+		}
+	}
+
+	gtk_widget_destroy ((GtkWidget *)dialog);
+	password_dialog = NULL;
+
+	/* ok, here things get interesting, we suck up any pending
+	 * operations on this specific password, and return the same
+	 * result or ignore other operations */
+
+	G_LOCK (passwords);
+	for (iter = g_queue_peek_head_link (&message_queue); iter != NULL; iter = iter->next) {
+		EPassMsg *pending = iter->data;
+
+		if ((pending->dispatch == ep_forget_password
+		     || pending->dispatch == ep_get_password
+		     || pending->dispatch == ep_ask_password)
+		    && (strcmp (pending->component, msg->component) == 0
+			&& strcmp (pending->key, msg->key) == 0)) {
+
+			/* Satisfy the pending operation. */
+			pending->password = g_strdup (msg->password);
+			e_flag_set (pending->done);
+
+			/* Mark the queue node for deletion. */
+			trash = g_list_prepend (trash, iter);
+		}
+	}
+
+	/* Expunge the message queue. */
+	for (iter = trash; iter != NULL; iter = iter->next)
+		g_queue_delete_link (&message_queue, iter->data);
+	g_list_free (trash);
+
+	G_UNLOCK (passwords);
+
+	if (!msg->noreply)
+		e_flag_set (msg->done);
+
+	ep_idle_dispatch (NULL);
+}
+
+static gboolean
+update_capslock_state (GtkDialog *dialog,
+                       GdkEvent *event,
+                       GtkWidget *label)
+{
+	GdkModifierType mask = 0;
+	GdkWindow *window;
+	gchar *markup = NULL;
+
+	window = gtk_widget_get_window (GTK_WIDGET (dialog));
+	gdk_window_get_pointer (window, NULL, NULL, &mask);
+
+	/* The space acts as a vertical placeholder. */
+	markup = g_markup_printf_escaped (
+		"<small>%s</small>", (mask & GDK_LOCK_MASK) ?
+		 _("You have the Caps Lock key on.") : " ");
+	gtk_label_set_markup (GTK_LABEL (label), markup);
+	g_free (markup);
+
+	return FALSE;
+}
+
+static void
+ep_ask_password (EPassMsg *msg)
+{
+	GtkWidget *widget;
+	GtkWidget *container;
+	GtkWidget *action_area;
+	GtkWidget *content_area;
+	gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
+	guint noreply = msg->noreply;
+	gboolean visible;
+	AtkObject *a11y;
+
+	msg->noreply = 1;
+
+	widget = gtk_dialog_new_with_buttons (
+		msg->title, msg->parent, 0,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_OK, GTK_RESPONSE_OK,
+		NULL);
+#if !GTK_CHECK_VERSION(2,90,7)
+	g_object_set (widget, "has-separator", FALSE, NULL);
+#endif
+	gtk_dialog_set_default_response (
+		GTK_DIALOG (widget), GTK_RESPONSE_OK);
+	gtk_window_set_resizable (GTK_WINDOW (widget), FALSE);
+	gtk_window_set_transient_for (GTK_WINDOW (widget), msg->parent);
+	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ON_PARENT);
+	gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
+	password_dialog = GTK_DIALOG (widget);
+
+	action_area = gtk_dialog_get_action_area (password_dialog);
+	content_area = gtk_dialog_get_content_area (password_dialog);
+
+	/* Override GtkDialog defaults */
+	gtk_box_set_spacing (GTK_BOX (action_area), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+	gtk_box_set_spacing (GTK_BOX (content_area), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+	/* Table */
+	container = gtk_table_new (2, 3, FALSE);
+	gtk_table_set_col_spacings (GTK_TABLE (container), 12);
+	gtk_table_set_row_spacings (GTK_TABLE (container), 6);
+	gtk_table_set_row_spacing (GTK_TABLE (container), 0, 12);
+	gtk_table_set_row_spacing (GTK_TABLE (container), 1, 0);
+	gtk_widget_show (container);
+
+	gtk_box_pack_start (
+		GTK_BOX (content_area), container, FALSE, TRUE, 0);
+
+	/* Password Image */
+	widget = gtk_image_new_from_icon_name (
+		"dialog-password", GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+	gtk_widget_show (widget);
+
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 0, 3, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+	/* Password Label */
+	widget = gtk_label_new (NULL);
+	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+	gtk_label_set_markup (GTK_LABEL (widget), msg->prompt);
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_widget_show (widget);
+
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+	/* Password Entry */
+	widget = gtk_entry_new ();
+	a11y = gtk_widget_get_accessible (widget);
+	visible = !(msg->flags & E_PASSWORDS_SECRET);
+	atk_object_set_description (a11y, msg->prompt);
+	gtk_entry_set_visibility (GTK_ENTRY (widget), visible);
+	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+	gtk_widget_grab_focus (widget);
+	gtk_widget_show (widget);
+	msg->entry = widget;
+
+	if ((msg->flags & E_PASSWORDS_REPROMPT)) {
+		ep_get_password (msg);
+		if (msg->password != NULL) {
+			gtk_entry_set_text (GTK_ENTRY (widget), msg->password);
+			g_free (msg->password);
+			msg->password = NULL;
+		}
+	}
+
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+	/* Caps Lock Label */
+	widget = gtk_label_new (NULL);
+	gtk_widget_show (widget);
+
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+	g_signal_connect (
+		password_dialog, "key-release-event",
+		G_CALLBACK (update_capslock_state), widget);
+	g_signal_connect (
+		password_dialog, "focus-in-event",
+		G_CALLBACK (update_capslock_state), widget);
+
+	/* static password, shouldn't be remembered between sessions,
+	   but will be remembered within the session beyond our control */
+	if (type != E_PASSWORDS_REMEMBER_NEVER) {
+		if (msg->flags & E_PASSWORDS_PASSPHRASE) {
+			widget = gtk_check_button_new_with_mnemonic (
+				(type == E_PASSWORDS_REMEMBER_FOREVER)
+				? _("_Remember this passphrase")
+				: _("_Remember this passphrase for"
+				    " the remainder of this session"));
+		} else {
+			widget = gtk_check_button_new_with_mnemonic (
+				(type == E_PASSWORDS_REMEMBER_FOREVER)
+				? _("_Remember this password")
+				: _("_Remember this password for"
+				    " the remainder of this session"));
+		}
+
+		gtk_toggle_button_set_active (
+			GTK_TOGGLE_BUTTON (widget), *msg->remember);
+		if (msg->flags & E_PASSWORDS_DISABLE_REMEMBER)
+			gtk_widget_set_sensitive (widget, FALSE);
+		gtk_widget_show (widget);
+		msg->check = widget;
+
+		gtk_table_attach (
+			GTK_TABLE (container), widget,
+			1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+	}
+
+	msg->noreply = noreply;
+
+	g_signal_connect (
+		password_dialog, "response",
+		G_CALLBACK (pass_response), msg);
+
+	if (msg->parent) {
+		gtk_dialog_run (GTK_DIALOG (password_dialog));
+	} else {
+		gtk_window_present (GTK_WINDOW (password_dialog));
+		/* workaround GTK+ bug (see Gnome's bugzilla bug #624229) */
+		gtk_grab_add (GTK_WIDGET (password_dialog));
+	}
+}
+
+/**
+ * e_passwords_init:
+ *
+ * Initializes the e_passwords routines. Must be called before any other
+ * e_passwords_* function.
+ **/
+void
+e_passwords_init (void)
+{
+	G_LOCK (passwords);
+
+	if (password_cache == NULL) {
+		password_cache = g_hash_table_new_full (
+			g_str_hash, g_str_equal,
+			(GDestroyNotify) g_free,
+			(GDestroyNotify) g_free);
+		main_thread = g_thread_self ();
+
+		/* Load the keyfile even if we're using the keyring.
+		 * We might be able to extract passwords from it. */
+		key_file = g_key_file_new ();
+		ep_key_file_load ();
+
+	}
+
+	G_UNLOCK (passwords);
+}
+
+/**
+ * e_passwords_cancel:
+ *
+ * Cancel any outstanding password operations and close any dialogues
+ * currently being shown.
+ **/
+void
+e_passwords_cancel (void)
+{
+	EPassMsg *msg;
+
+	G_LOCK (passwords);
+	while ((msg = g_queue_pop_head (&message_queue)) != NULL)
+		e_flag_set (msg->done);
+	G_UNLOCK (passwords);
+
+	if (password_dialog)
+		gtk_dialog_response (password_dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * e_passwords_shutdown:
+ *
+ * Cleanup routine to call before exiting.
+ **/
+void
+e_passwords_shutdown (void)
+{
+	EPassMsg *msg;
+
+	G_LOCK (passwords);
+
+	while ((msg = g_queue_pop_head (&message_queue)) != NULL)
+		e_flag_set (msg->done);
+
+	if (password_cache != NULL) {
+		g_hash_table_destroy (password_cache);
+		password_cache = NULL;
+	}
+
+
+	G_UNLOCK (passwords);
+
+	if (password_dialog != NULL)
+		gtk_dialog_response (password_dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * e_passwords_set_online:
+ * @state:
+ *
+ * Set the offline-state of the application.  This is a work-around
+ * for having the backends fully offline aware, and returns a
+ * cancellation response instead of prompting for passwords.
+ *
+ * FIXME: This is not a permanent api, review post 2.0.
+ **/
+void
+e_passwords_set_online (gint state)
+{
+	ep_online_state = state;
+	/* TODO: we could check that a request is open and close it, or maybe who cares */
+}
+
+/**
+ * e_passwords_forget_passwords:
+ *
+ * Forgets all cached passwords, in memory and on disk.
+ **/
+void
+e_passwords_forget_passwords (void)
+{
+	EPassMsg *msg = ep_msg_new (ep_forget_passwords);
+
+	ep_msg_send (msg);
+	ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_clear_passwords:
+ *
+ * Forgets all disk cached passwords for the component.
+ **/
+void
+e_passwords_clear_passwords (const gchar *component_name)
+{
+	EPassMsg *msg = ep_msg_new (ep_clear_passwords);
+
+	msg->component = component_name;
+	ep_msg_send (msg);
+	ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_remember_password:
+ * @key: the key
+ *
+ * Saves the password associated with @key to disk.
+ **/
+void
+e_passwords_remember_password (const gchar *component_name, const gchar *key)
+{
+	EPassMsg *msg;
+
+	g_return_if_fail (component_name != NULL);
+	g_return_if_fail (key != NULL);
+
+	msg = ep_msg_new (ep_remember_password);
+	msg->component = component_name;
+	msg->key = key;
+
+	ep_msg_send (msg);
+	ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_forget_password:
+ * @key: the key
+ *
+ * Forgets the password associated with @key, in memory and on disk.
+ **/
+void
+e_passwords_forget_password (const gchar *component_name, const gchar *key)
+{
+	EPassMsg *msg;
+
+	g_return_if_fail (component_name != NULL);
+	g_return_if_fail (key != NULL);
+
+	msg = ep_msg_new (ep_forget_password);
+	msg->component = component_name;
+	msg->key = key;
+
+	ep_msg_send (msg);
+	ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_get_password:
+ * @key: the key
+ *
+ * Returns: the password associated with @key, or %NULL.  Caller
+ * must free the returned password.
+ **/
+gchar *
+e_passwords_get_password (const gchar *component_name, const gchar *key)
+{
+	EPassMsg *msg;
+	gchar *passwd;
+
+	g_return_val_if_fail (component_name != NULL, NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	msg = ep_msg_new (ep_get_password);
+	msg->component = component_name;
+	msg->key = key;
+
+	ep_msg_send (msg);
+
+	passwd = msg->password;
+	msg->password = NULL;
+	ep_msg_free (msg);
+
+	return passwd;
+}
+
+/**
+ * e_passwords_add_password:
+ * @key: a key
+ * @passwd: the password for @key
+ *
+ * This stores the @key/@passwd pair in the current session's password
+ * hash.
+ **/
+void
+e_passwords_add_password (const gchar *key, const gchar *passwd)
+{
+	EPassMsg *msg;
+
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (passwd != NULL);
+
+	msg = ep_msg_new (ep_add_password);
+	msg->key = key;
+	msg->oldpass = passwd;
+
+	ep_msg_send (msg);
+	ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_ask_password:
+ * @title: title for the password dialog
+ * @component_name: the name of the component for which we're storing
+ * the password (e.g. Mail, Addressbook, etc.)
+ * @key: key to store the password under
+ * @prompt: prompt string
+ * @type: whether or not to offer to remember the password,
+ * and for how long.
+ * @remember: on input, the default state of the remember checkbox.
+ * on output, the state of the checkbox when the dialog was closed.
+ * @parent: parent window of the dialog, or %NULL
+ *
+ * Asks the user for a password.
+ *
+ * Returns: the password, which the caller must free, or %NULL if
+ * the user cancelled the operation. * remember will be set if the
+ * return value is non-%NULL and @remember_type is not
+ * E_PASSWORDS_DO_NOT_REMEMBER.
+ **/
+gchar *
+e_passwords_ask_password (const gchar *title, const gchar *component_name,
+			  const gchar *key,
+			  const gchar *prompt,
+			  EPasswordsRememberType type,
+			  gboolean *remember,
+			  GtkWindow *parent)
+{
+	gchar *passwd;
+	EPassMsg *msg;
+
+	g_return_val_if_fail (component_name != NULL, NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	if ((type & E_PASSWORDS_ONLINE) && !ep_online_state)
+		return NULL;
+
+	msg = ep_msg_new (ep_ask_password);
+	msg->title = title;
+	msg->component = component_name;
+	msg->key = key;
+	msg->prompt = prompt;
+	msg->flags = type;
+	msg->remember = remember;
+	msg->parent = parent;
+
+	ep_msg_send (msg);
+	passwd = msg->password;
+	msg->password = NULL;
+	ep_msg_free (msg);
+
+	return passwd;
+}



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