[evolution/email-factory-3-4: 3/47] Split mail to independent library to be used by a mail daemon.



commit aa8e0ce687bd81f4501dbdab79469e30337629d3
Author: Srinivasa Ragavan <sragavan gnome org>
Date:   Mon Oct 17 23:37:27 2011 +0530

    Split mail to independent library to be used by a mail daemon.

 Makefile.am                            |    2 +
 composer/e-composer-actions.c          |    5 +
 configure.ac                           |    8 +-
 e-util/Makefile.am                     |    1 +
 libemail-engine/Makefile.am            |   65 +
 libemail-engine/e-mail-enums.h         |   74 +
 libemail-engine/e-mail-folder-utils.c  | 1680 +++++++++++++++++++++
 libemail-engine/e-mail-folder-utils.h  |  164 +++
 libemail-engine/e-mail-junk-filter.c   |   82 ++
 libemail-engine/e-mail-junk-filter.h   |   74 +
 libemail-engine/e-mail-local.c         |  157 ++
 libemail-engine/e-mail-local.h         |   39 +
 libemail-engine/e-mail-session-utils.c |  942 ++++++++++++
 libemail-engine/e-mail-session-utils.h |   97 ++
 libemail-engine/e-mail-session.c       | 1517 +++++++++++++++++++
 libemail-engine/e-mail-session.h       |  124 ++
 libemail-engine/e-mail-store-utils.c   |  393 +++++
 libemail-engine/e-mail-store-utils.h   |   74 +
 libemail-engine/e-mail-store.c         |  464 ++++++
 libemail-engine/e-mail-store.h         |   47 +
 libemail-engine/e-mail-utils.c         | 1109 ++++++++++++++
 libemail-engine/e-mail-utils.h         |   50 +
 libemail-engine/libemail-engine.pc.in  |   15 +
 libemail-engine/mail-config.c          |  367 +++++
 libemail-engine/mail-config.h          |   49 +
 libemail-engine/mail-folder-cache.c    | 1424 ++++++++++++++++++
 libemail-engine/mail-folder-cache.h    |  112 ++
 libemail-engine/mail-ops.c             | 1680 +++++++++++++++++++++
 libemail-engine/mail-ops.h             |  102 ++
 libemail-engine/mail-tools.c           |  234 +++
 libemail-engine/mail-tools.h           |   41 +
 libemail-utils/Makefile.am             |   57 +
 libemail-utils/e-account-utils.c       |  252 ++++
 libemail-utils/e-account-utils.h       |   37 +
 libemail-utils/e-marshal.c             | 2492 ++++++++++++++++++++++++++++++++
 libemail-utils/e-marshal.h             |  530 +++++++
 libemail-utils/e-marshal.list          |   60 +
 libemail-utils/e-signature-list.c      |  494 +++++++
 libemail-utils/e-signature-list.h      |   92 ++
 libemail-utils/e-signature-utils.c     |  342 +++++
 libemail-utils/e-signature-utils.h     |   40 +
 libemail-utils/e-signature.c           |  747 ++++++++++
 libemail-utils/e-signature.h           |   90 ++
 libemail-utils/gconf-bridge.c          | 1364 +++++++++++++++++
 libemail-utils/gconf-bridge.h          |  134 ++
 libemail-utils/libemail-utils.pc.in    |   15 +
 libemail-utils/mail-mt.c               |  639 ++++++++
 libemail-utils/mail-mt.h               |  116 ++
 mail/Makefile.am                       |    2 +
 modules/mail/Makefile.am               |    2 +
 shell/Makefile.am                      |    5 +-
 51 files changed, 18697 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a5e0d9f..d1f0d19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,8 @@ SUBDIRS = 			\
 	data			\
 	smclient		\
 	libgnomecanvas		\
+	libemail-utils		\
+	libemail-engine		\
 	e-util			\
 	a11y			\
 	filter			\
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index cd50f7a..f8ed9b5 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -196,6 +196,11 @@ action_save_cb (GtkAction *action,
 	gtkhtml_editor_run_command (GTKHTML_EDITOR (composer), "saved");
 }
 
+int gtkhtml_editor_file_chooser_dialog_run (GtkhtmlEditor *e, GtkWidget *w)
+{
+	return 0;
+}
+
 static void
 action_save_as_cb (GtkAction *action,
                    EMsgComposer *composer)
diff --git a/configure.ac b/configure.ac
index ee9e002..ee46cd2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1225,14 +1225,14 @@ AC_SUBST(CERT_UI_LIBS)
 dnl ************
 dnl EMAILS_UTILS Flags
 dnl ************
-EVO_SET_COMPILE_FLAGS(EMAIL_UTILS, libemail-utils)
+EVO_SET_COMPILE_FLAGS(EMAIL_UTILS, camel-1.2  libedataserver-1.2)
 AC_SUBST(EMAIL_UTILS_CFLAGS)
 AC_SUBST(EMAIL_UTILS_LIBS)
 
 dnl ************
 dnl EMAIL_ENGINE Flags
 dnl ************
-EVO_SET_COMPILE_FLAGS(EMAIL_ENGINE, libemail-engine)
+EVO_SET_COMPILE_FLAGS(EMAIL_ENGINE, camel-1.2  libedataserverui-3.0 libebackend-1.2 libedataserver-1.2)
 AC_SUBST(EMAIL_ENGINE_CFLAGS)
 AC_SUBST(EMAIL_ENGINE_LIBS)
 
@@ -1632,6 +1632,10 @@ help/quickref/pl/Makefile
 help/quickref/pt/Makefile
 help/quickref/sv/Makefile
 help/quickref/sq/Makefile
+libemail-utils/Makefile
+libemail-utils/libemail-utils.pc
+libemail-engine/Makefile
+libemail-engine/libemail-engine.pc
 libgnomecanvas/Makefile
 shell/Makefile
 shell/evolution-nognome
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 6510cf9..b8cb56f 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -122,6 +122,7 @@ libeutil_la_SOURCES =				\
 libeutil_la_LDFLAGS = $(NO_UNDEFINED)
 
 libeutil_la_LIBADD = 			\
+	$(top_builddir)/libemail-utils/libemail-utils.la		\
 	$(ICONV_LIBS)			\
 	$(EVOLUTION_DATA_SERVER_LIBS)	\
 	$(EMAIL_UTILS_LIBS)		\
diff --git a/libemail-engine/Makefile.am b/libemail-engine/Makefile.am
new file mode 100644
index 0000000..fd62daf
--- /dev/null
+++ b/libemail-engine/Makefile.am
@@ -0,0 +1,65 @@
+
+lib_LTLIBRARIES = libemail-engine.la
+
+AM_CFLAGS = -I$(top_builddir) @GNOME_PLATFORM_CFLAGS@ -Wall -Werror -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\""
+AM_LDFLAGS = @GNOME_PLATFORM_LIBS@ 
+
+
+libemail_engine_la_CPPFLAGS = \
+	$(AM_CPPFLAGS)	\
+	$(EMAIL_ENGINE_CFLAGS)	\
+	$(CAMEL_CFLAGS)		\
+	$(GNOME_PLATFORM_CFLAGS)
+
+libemail_engine_la_LIBADD = 	\
+		$(CAMEL_LIBS)		\
+		$(EMAIL_ENGINE_LIBS)	\
+		$(GNOME_PLATFORM_LIBS)	\
+		$(top_builddir)/libemail-utils/libemail-utils.la
+		
+
+libemail_engine_la_LDFLAGS = 
+
+libemail_engine_la_SOURCES = 	\
+		e-mail-local.c		\
+		e-mail-folder-utils.c	\
+		e-mail-store-utils.c	\
+		mail-tools.c		\
+		e-mail-utils.c		\
+		mail-config.c		\
+		mail-folder-cache.c	\
+		e-mail-session-utils.c	\
+		mail-ops.c		\
+		e-mail-junk-filter.c	\
+		e-mail-session.c	\
+		e-mail-store.c
+
+libmailengineincludedir = $(privincludedir)/libemail-engine
+libmailengineinclude_HEADERS = 	\
+		e-mail-session.h	\
+		e-mail-enums.h		\
+		mail-folder-cache.h	\
+		e-mail-local.h		\
+		e-mail-folder-utils.h	\
+		e-mail-store-utils.h	\
+		mail-tools.h		\
+		e-mail-utils.h		\
+		mail-config.h		\
+		e-mail-session-utils.h	\
+		mail-ops.h		\
+		e-mail-junk-filter.h	\
+		e-mail-store.h
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libemail-engine.pc
+
+BUILT_SOURCES = 
+
+EXTRA_DIST = 
+CLEANFILES    = $(BUILT_SOURCES)
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+dist-hook:
+	cd $(distdir); rm -f $(BUILT_SOURCES)
+
diff --git a/libemail-engine/e-mail-enums.h b/libemail-engine/e-mail-enums.h
new file mode 100644
index 0000000..e0ad3ad
--- /dev/null
+++ b/libemail-engine/e-mail-enums.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-enums.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_ENUMS_H
+#define E_MAIL_ENUMS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	E_MAIL_DISPLAY_STYLE_NORMAL,
+	E_MAIL_DISPLAY_STYLE_FULL_HEADERS,
+	E_MAIL_DISPLAY_STYLE_SOURCE
+} EMailDisplayStyle;
+
+typedef enum {
+	E_MAIL_FORWARD_STYLE_ATTACHED,
+	E_MAIL_FORWARD_STYLE_INLINE,
+	E_MAIL_FORWARD_STYLE_QUOTED
+} EMailForwardStyle;
+
+typedef enum {
+	E_MAIL_IMAGE_LOADING_POLICY_NEVER,
+	E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES,
+	E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
+} EMailImageLoadingPolicy;
+
+/* XXX E_MAIL_FOLDER_TEMPLATES is a prime example of why templates
+ *     should be a core feature: the mailer now has to know about
+ *     this specific plugin, which defeats the purpose of plugins. */
+typedef enum {
+	E_MAIL_LOCAL_FOLDER_INBOX,
+	E_MAIL_LOCAL_FOLDER_DRAFTS,
+	E_MAIL_LOCAL_FOLDER_OUTBOX,
+	E_MAIL_LOCAL_FOLDER_SENT,
+	E_MAIL_LOCAL_FOLDER_TEMPLATES,
+	E_MAIL_LOCAL_FOLDER_LOCAL_INBOX,
+	E_MAIL_NUM_LOCAL_FOLDERS
+} EMailLocalFolder;
+
+typedef enum {
+	E_MAIL_REPLY_STYLE_QUOTED,
+	E_MAIL_REPLY_STYLE_DO_NOT_QUOTE,
+	E_MAIL_REPLY_STYLE_ATTACH,
+	E_MAIL_REPLY_STYLE_OUTLOOK
+} EMailReplyStyle;
+
+typedef enum {
+	E_MAIL_REPLY_TO_SENDER,
+	E_MAIL_REPLY_TO_RECIPIENT,
+	E_MAIL_REPLY_TO_FROM,
+	E_MAIL_REPLY_TO_ALL,
+	E_MAIL_REPLY_TO_LIST
+} EMailReplyType;
+
+G_END_DECLS
+
+#endif /* E_MAIL_ENUMS_H */
diff --git a/libemail-engine/e-mail-folder-utils.c b/libemail-engine/e-mail-folder-utils.c
new file mode 100644
index 0000000..7f1e86e
--- /dev/null
+++ b/libemail-engine/e-mail-folder-utils.c
@@ -0,0 +1,1680 @@
+/*
+ * e-mail-folder-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-folder-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "mail-tools.h"
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution Mail Data Server" PACKAGE_VERSION)
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	CamelMimeMessage *message;
+	CamelMessageInfo *info;
+	CamelMimePart *part;
+	GHashTable *hash_table;
+	GPtrArray *ptr_array;
+	GFile *destination;
+	gchar *fwd_subject;
+	gchar *message_uid;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+	if (context->message != NULL)
+		g_object_unref (context->message);
+
+	if (context->info != NULL)
+		camel_message_info_free (context->info);
+
+	if (context->part != NULL)
+		g_object_unref (context->part);
+
+	if (context->hash_table != NULL)
+		g_hash_table_unref (context->hash_table);
+
+	if (context->ptr_array != NULL)
+		g_ptr_array_unref (context->ptr_array);
+
+	if (context->destination != NULL)
+		g_object_unref (context->destination);
+
+	g_free (context->fwd_subject);
+	g_free (context->message_uid);
+
+	g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_folder_append_message_thread (GSimpleAsyncResult *simple,
+                                   GObject *object,
+                                   GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_folder_append_message_sync (
+		CAMEL_FOLDER (object), context->message,
+		context->info, &context->message_uid,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+gboolean
+e_mail_folder_append_message_sync (CamelFolder *folder,
+                                   CamelMimeMessage *message,
+                                   CamelMessageInfo *info,
+                                   gchar **appended_uid,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+	CamelMedium *medium;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+	medium = CAMEL_MEDIUM (message);
+
+	camel_operation_push_message (
+		cancellable,
+		_("Saving message to folder '%s'"),
+		camel_folder_get_full_name (folder));
+
+	if (camel_medium_get_header (medium, "X-Mailer") == NULL)
+		camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+	camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+
+	success = camel_folder_append_message_sync (
+		folder, message, info, appended_uid, cancellable, error);
+
+	camel_operation_pop_message (cancellable);
+
+	return success;
+}
+
+void
+e_mail_folder_append_message (CamelFolder *folder,
+                              CamelMimeMessage *message,
+                              CamelMessageInfo *info,
+                              gint io_priority,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+	context = g_slice_new0 (AsyncContext);
+	context->message = g_object_ref (message);
+
+	if (info != NULL)
+		context->info = camel_message_info_ref (info);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_append_message);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_append_message_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_append_message_finish (CamelFolder *folder,
+                                     GAsyncResult *result,
+                                     gchar **appended_uid,
+                                     GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_append_message), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (appended_uid != NULL) {
+		*appended_uid = context->message_uid;
+		context->message_uid = NULL;
+	}
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_build_attachment_thread (GSimpleAsyncResult *simple,
+                                     GObject *object,
+                                     GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->part = e_mail_folder_build_attachment_sync (
+		CAMEL_FOLDER (object), context->ptr_array,
+		&context->fwd_subject, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_sync (CamelFolder *folder,
+                                     GPtrArray *message_uids,
+                                     gchar **fwd_subject,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+	GHashTable *hash_table;
+	CamelMimeMessage *message;
+	CamelMimePart *part;
+	const gchar *uid;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+	g_return_val_if_fail (message_uids != NULL, NULL);
+
+	/* Need at least one message UID to make an attachment. */
+	g_return_val_if_fail (message_uids->len > 0, NULL);
+
+	hash_table = e_mail_folder_get_multiple_messages_sync (
+		folder, message_uids, cancellable, error);
+
+	if (hash_table == NULL)
+		return NULL;
+
+	/* Create the forward subject from the first message. */
+
+	uid = g_ptr_array_index (message_uids, 0);
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	message = g_hash_table_lookup (hash_table, uid);
+	g_return_val_if_fail (message != NULL, NULL);
+
+	if (fwd_subject != NULL)
+		*fwd_subject = mail_tool_generate_forward_subject (message);
+
+	if (message_uids->len == 1) {
+		part = mail_tool_make_message_attachment (message);
+
+	} else {
+		CamelMultipart *multipart;
+		guint ii;
+
+		multipart = camel_multipart_new ();
+		camel_data_wrapper_set_mime_type (
+			CAMEL_DATA_WRAPPER (multipart), "multipart/digest");
+		camel_multipart_set_boundary (multipart, NULL);
+
+		for (ii = 0; ii < message_uids->len; ii++) {
+			uid = g_ptr_array_index (message_uids, ii);
+			g_return_val_if_fail (uid != NULL, NULL);
+
+			message = g_hash_table_lookup (hash_table, uid);
+			g_return_val_if_fail (message != NULL, NULL);
+
+			part = mail_tool_make_message_attachment (message);
+			camel_multipart_add_part (multipart, part);
+			g_object_unref (part);
+		}
+
+		part = camel_mime_part_new ();
+
+		camel_medium_set_content (
+			CAMEL_MEDIUM (part),
+			CAMEL_DATA_WRAPPER (multipart));
+
+		camel_mime_part_set_description (
+			part, _("Forwarded messages"));
+
+		g_object_unref (multipart);
+	}
+
+	g_hash_table_unref (hash_table);
+
+	return part;
+}
+
+void
+e_mail_folder_build_attachment (CamelFolder *folder,
+                                GPtrArray *message_uids,
+                                gint io_priority,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (message_uids != NULL);
+
+	/* Need at least one message UID to make an attachment. */
+	g_return_if_fail (message_uids->len > 0);
+
+	context = g_slice_new0 (AsyncContext);
+	context->ptr_array = g_ptr_array_ref (message_uids);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_build_attachment);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_build_attachment_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_finish (CamelFolder *folder,
+                                       GAsyncResult *result,
+                                       gchar **fwd_subject,
+                                       GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_build_attachment), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	if (fwd_subject != NULL) {
+		*fwd_subject = context->fwd_subject;
+		context->fwd_subject = NULL;
+	}
+
+	g_return_val_if_fail (CAMEL_IS_MIME_PART (context->part), NULL);
+
+	return g_object_ref (context->part);
+}
+
+static void
+mail_folder_find_duplicate_messages_thread (GSimpleAsyncResult *simple,
+                                            GObject *object,
+                                            GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->hash_table = e_mail_folder_find_duplicate_messages_sync (
+		CAMEL_FOLDER (object), context->ptr_array,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_sync (CamelFolder *folder,
+                                            GPtrArray *message_uids,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+	GQueue trash = G_QUEUE_INIT;
+	GHashTable *hash_table;
+	GHashTable *unique_ids;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+	g_return_val_if_fail (message_uids != NULL, NULL);
+
+	/* hash_table = { MessageUID : CamelMessage } */
+	hash_table = e_mail_folder_get_multiple_messages_sync (
+		folder, message_uids, cancellable, error);
+
+	if (hash_table == NULL)
+		return NULL;
+
+	camel_operation_push_message (
+		cancellable, _("Scanning messages for duplicates"));
+
+	unique_ids = g_hash_table_new_full (
+		(GHashFunc) g_int64_hash,
+		(GEqualFunc) g_int64_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_free);
+
+	g_hash_table_iter_init (&iter, hash_table);
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		const CamelSummaryMessageID *message_id;
+		CamelDataWrapper *content;
+		CamelMessageFlags flags;
+		CamelMessageInfo *info;
+		CamelStream *stream;
+		GByteArray *buffer;
+		gboolean duplicate;
+		gssize n_bytes;
+		gchar *digest;
+
+		info = camel_folder_get_message_info (folder, key);
+		message_id = camel_message_info_message_id (info);
+		flags = camel_message_info_flags (info);
+
+		/* Skip messages marked for deletion. */
+		if (flags & CAMEL_MESSAGE_DELETED) {
+			g_queue_push_tail (&trash, key);
+			camel_message_info_free (info);
+			continue;
+		}
+
+		/* Generate a digest string from the message's content. */
+
+		content = camel_medium_get_content (CAMEL_MEDIUM (value));
+
+		if (content == NULL) {
+			g_queue_push_tail (&trash, key);
+			camel_message_info_free (info);
+			continue;
+		}
+
+		stream = camel_stream_mem_new ();
+
+		n_bytes = camel_data_wrapper_decode_to_stream_sync (
+			content, stream, cancellable, error);
+
+		if (n_bytes < 0) {
+			camel_message_info_free (info);
+			g_object_unref (stream);
+			goto fail;
+		}
+
+		/* The CamelStreamMem owns the buffer. */
+		buffer = camel_stream_mem_get_byte_array (
+			CAMEL_STREAM_MEM (stream));
+		g_return_val_if_fail (buffer != NULL, NULL);
+
+		digest = g_compute_checksum_for_data (
+			G_CHECKSUM_SHA256, buffer->data, buffer->len);
+
+		g_object_unref (stream);
+
+		/* Determine if the message a duplicate. */
+
+		value = g_hash_table_lookup (unique_ids, &message_id->id.id);
+		duplicate = (value != NULL) && g_str_equal (digest, value);
+
+		if (duplicate)
+			g_free (digest);
+		else {
+			gint64 *v_int64;
+
+			/* XXX Might be better to create a GArray
+			 *     of 64-bit integers and have the hash
+			 *     table keys point to array elements. */
+			v_int64 = g_new0 (gint64, 1);
+			*v_int64 = (gint64) message_id->id.id;
+
+			g_hash_table_insert (unique_ids, v_int64, digest);
+			g_queue_push_tail (&trash, key);
+		}
+
+		camel_message_info_free (info);
+	}
+
+	/* Delete all non-duplicate messages from the hash table. */
+	while ((key = g_queue_pop_head (&trash)) != NULL)
+		g_hash_table_remove (hash_table, key);
+
+	goto exit;
+
+fail:
+	g_hash_table_destroy (hash_table);
+	hash_table = NULL;
+
+exit:
+	camel_operation_pop_message (cancellable);
+
+	g_hash_table_destroy (unique_ids);
+
+	return hash_table;
+}
+
+void
+e_mail_folder_find_duplicate_messages (CamelFolder *folder,
+                                       GPtrArray *message_uids,
+                                       gint io_priority,
+                                       GCancellable *cancellable,
+                                       GAsyncReadyCallback callback,
+                                       gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (message_uids != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->ptr_array = g_ptr_array_ref (message_uids);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_find_duplicate_messages);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_find_duplicate_messages_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_finish (CamelFolder *folder,
+                                              GAsyncResult *result,
+                                              GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_find_duplicate_messages), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_get_multiple_messages_thread (GSimpleAsyncResult *simple,
+                                          GObject *object,
+                                          GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->hash_table = e_mail_folder_get_multiple_messages_sync (
+		CAMEL_FOLDER (object), context->ptr_array,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_sync (CamelFolder *folder,
+                                          GPtrArray *message_uids,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+	GHashTable *hash_table;
+	CamelMimeMessage *message;
+	guint ii;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+	g_return_val_if_fail (message_uids != NULL, NULL);
+
+	camel_operation_push_message (
+		cancellable,
+		ngettext (
+			"Retrieving %d message",
+			"Retrieving %d messages",
+			message_uids->len),
+		message_uids->len);
+
+	hash_table = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_object_unref);
+
+	/* This is an all or nothing operation.  Destroy the
+	 * hash table if we fail to retrieve any message. */
+
+	for (ii = 0; ii < message_uids->len; ii++) {
+		const gchar *uid;
+		gint percent;
+
+		uid = g_ptr_array_index (message_uids, ii);
+		percent = ((ii + 1) * 100) / message_uids->len;
+
+		message = camel_folder_get_message_sync (
+			folder, uid, cancellable, error);
+
+		camel_operation_progress (cancellable, percent);
+
+		if (CAMEL_IS_MIME_MESSAGE (message)) {
+			g_hash_table_insert (
+				hash_table, g_strdup (uid), message);
+		} else {
+			g_hash_table_destroy (hash_table);
+			hash_table = NULL;
+			break;
+		}
+	}
+
+	camel_operation_pop_message (cancellable);
+
+	return hash_table;
+}
+
+void
+e_mail_folder_get_multiple_messages (CamelFolder *folder,
+                                     GPtrArray *message_uids,
+                                     gint io_priority,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (message_uids != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->ptr_array = g_ptr_array_ref (message_uids);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_get_multiple_messages);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_get_multiple_messages_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_finish (CamelFolder *folder,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_get_multiple_messages), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_remove_thread (GSimpleAsyncResult *simple,
+                           GObject *object,
+                           GCancellable *cancellable)
+{
+	GError *error = NULL;
+
+	e_mail_folder_remove_sync (
+		CAMEL_FOLDER (object), cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+static gboolean
+mail_folder_remove_recursive (CamelStore *store,
+                              CamelFolderInfo *folder_info,
+                              GCancellable *cancellable,
+                              GError **error)
+{
+	gboolean success = TRUE;
+
+	while (folder_info != NULL) {
+		CamelFolder *folder;
+
+		if (folder_info->child != NULL) {
+			success = mail_folder_remove_recursive (
+				store, folder_info->child, cancellable, error);
+			if (!success)
+				break;
+		}
+
+		folder = camel_store_get_folder_sync (
+			store, folder_info->full_name, 0, cancellable, error);
+		if (folder == NULL) {
+			success = FALSE;
+			break;
+		}
+
+		if (!CAMEL_IS_VEE_FOLDER (folder)) {
+			GPtrArray *uids;
+			guint ii;
+
+			/* Delete every message in this folder,
+			 * then expunge it. */
+
+			camel_folder_freeze (folder);
+
+			uids = camel_folder_get_uids (folder);
+
+			for (ii = 0; ii < uids->len; ii++)
+				camel_folder_delete_message (
+					folder, uids->pdata[ii]);
+
+			camel_folder_free_uids (folder, uids);
+
+			success = camel_folder_synchronize_sync (
+				folder, TRUE, cancellable, error);
+
+			camel_folder_thaw (folder);
+		}
+
+		g_object_unref (folder);
+
+		if (!success)
+			break;
+
+		/* If the store supports subscriptions,
+		 * then unsubscribe from this folder. */
+		if (CAMEL_IS_SUBSCRIBABLE (store)) {
+			success = camel_subscribable_unsubscribe_folder_sync (
+				CAMEL_SUBSCRIBABLE (store),
+				folder_info->full_name,
+				cancellable, error);
+			if (!success)
+				break;
+		}
+
+		success = camel_store_delete_folder_sync (
+			store, folder_info->full_name, cancellable, error);
+		if (!success)
+			break;
+
+		folder_info = folder_info->next;
+	}
+
+	return success;
+}
+
+static void
+follow_cancel_cb (GCancellable *cancellable,
+                  GCancellable *transparent_cancellable)
+{
+	g_cancellable_cancel (transparent_cancellable);
+}
+
+gboolean
+e_mail_folder_remove_sync (CamelFolder *folder,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	CamelFolderInfo *folder_info;
+	CamelFolderInfo *to_remove;
+	CamelFolderInfo *next = NULL;
+	CamelStore *parent_store;
+	const gchar *full_name;
+	gboolean success = TRUE;
+	GCancellable *transparent_cancellable = NULL;
+	gulong cbid = 0;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+	full_name = camel_folder_get_full_name (folder);
+	parent_store = camel_folder_get_parent_store (folder);
+
+	folder_info = camel_store_get_folder_info_sync (
+		parent_store, full_name,
+		CAMEL_STORE_FOLDER_INFO_FAST |
+		CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+		CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+		cancellable, error);
+
+	if (folder_info == NULL)
+		return FALSE;
+
+	to_remove = folder_info;
+
+	/* For cases when the top-level folder_info contains siblings,
+	 * such as when full_name contains a wildcard letter, compare
+	 * the folder name against folder_info->full_name to avoid
+	 * removing more folders than requested. */
+	if (folder_info->next != NULL) {
+		while (to_remove != NULL) {
+			if (g_strcmp0 (to_remove->full_name, full_name) == 0)
+				break;
+			to_remove = to_remove->next;
+		}
+
+		/* XXX Should we set a GError and return FALSE here? */
+		if (to_remove == NULL) {
+			g_warning (
+				"%s: Failed to find folder '%s'",
+				G_STRFUNC, full_name);
+			camel_store_free_folder_info (
+				parent_store, folder_info);
+			return TRUE;
+		}
+
+		/* Prevent iterating over siblings. */
+		next = to_remove->next;
+		to_remove->next = NULL;
+	}
+
+	camel_operation_push_message (
+		cancellable, _("Removing folder '%s'"),
+		camel_folder_get_full_name (folder));
+
+	if (cancellable) {
+		transparent_cancellable = g_cancellable_new ();
+		cbid = g_cancellable_connect (cancellable, G_CALLBACK (follow_cancel_cb), transparent_cancellable, NULL);
+	}
+
+	success = mail_folder_remove_recursive (
+		parent_store, to_remove, transparent_cancellable, error);
+
+	if (transparent_cancellable) {
+		g_cancellable_disconnect (cancellable, cbid);
+		g_object_unref (transparent_cancellable);
+	}
+
+	camel_operation_pop_message (cancellable);
+
+	/* Restore the folder_info tree to its original
+	 * state so we don't leak folder_info nodes. */
+	to_remove->next = next;
+
+	camel_store_free_folder_info (parent_store, folder_info);
+
+	return success;
+}
+
+void
+e_mail_folder_remove (CamelFolder *folder,
+                      gint io_priority,
+                      GCancellable *cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback,
+		user_data, e_mail_folder_remove);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_remove_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_finish (CamelFolder *folder,
+                             GAsyncResult *result,
+                             GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_remove), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_remove_attachments_thread (GSimpleAsyncResult *simple,
+                                       GObject *object,
+                                       GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_folder_remove_attachments_sync (
+		CAMEL_FOLDER (object), context->ptr_array,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+/* Helper for e_mail_folder_remove_attachments_sync() */
+static gboolean
+mail_folder_strip_message (CamelFolder *folder,
+                           CamelMimeMessage *message,
+                           const gchar *message_uid,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	CamelDataWrapper *content;
+	CamelMultipart *multipart;
+	gboolean modified = FALSE;
+	gboolean success = TRUE;
+	guint ii, n_parts;
+
+	content = camel_medium_get_content (CAMEL_MEDIUM (message));
+
+	if (!CAMEL_IS_MULTIPART (content))
+		return TRUE;
+
+	multipart = CAMEL_MULTIPART (content);
+	n_parts = camel_multipart_get_number (multipart);
+
+	/* Replace MIME parts with "attachment" or "inline" dispositions
+	 * with a small "text/plain" part saying the file was removed. */
+	for (ii = 0; ii < n_parts; ii++) {
+		CamelMimePart *mime_part;
+		const gchar *disposition;
+		gboolean is_attachment;
+
+		mime_part = camel_multipart_get_part (multipart, ii);
+		disposition = camel_mime_part_get_disposition (mime_part);
+
+		is_attachment =
+			(g_strcmp0 (disposition, "attachment") == 0) ||
+			(g_strcmp0 (disposition, "inline") == 0);
+
+		if (is_attachment) {
+			const gchar *filename;
+			const gchar *content_type;
+			gchar *content;
+
+			disposition = "inline";
+			content_type = "text/plain";
+			filename = camel_mime_part_get_filename (mime_part);
+
+			if (filename != NULL && *filename != '\0')
+				content = g_strdup_printf (
+					_("File \"%s\" has been removed."),
+					filename);
+			else
+				content = g_strdup (
+					_("File has been removed."));
+
+			camel_mime_part_set_content (
+				mime_part, content,
+				strlen (content), content_type);
+			camel_mime_part_set_content_type (
+				mime_part, content_type);
+			camel_mime_part_set_disposition (
+				mime_part, disposition);
+
+			modified = TRUE;
+		}
+	}
+
+	/* Append the modified message with removed attachments to
+	 * the folder and mark the original message for deletion. */
+	if (modified) {
+		CamelMessageInfo *orig_info;
+		CamelMessageInfo *copy_info;
+		CamelMessageFlags flags;
+
+		orig_info =
+			camel_folder_get_message_info (folder, message_uid);
+		copy_info =
+			camel_message_info_new_from_header (
+			NULL, CAMEL_MIME_PART (message)->headers);
+
+		flags = camel_folder_get_message_flags (folder, message_uid);
+		camel_message_info_set_flags (copy_info, flags, flags);
+
+		success = camel_folder_append_message_sync (
+			folder, message, copy_info, NULL, cancellable, error);
+		if (success)
+			camel_message_info_set_flags (
+				orig_info,
+				CAMEL_MESSAGE_DELETED,
+				CAMEL_MESSAGE_DELETED);
+
+		camel_folder_free_message_info (folder, orig_info);
+		camel_message_info_free (copy_info);
+	}
+
+	return success;
+}
+
+gboolean
+e_mail_folder_remove_attachments_sync (CamelFolder *folder,
+                                       GPtrArray *message_uids,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+	gboolean success = TRUE;
+	guint ii;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+	g_return_val_if_fail (message_uids != NULL, FALSE);
+
+	camel_folder_freeze (folder);
+
+	camel_operation_push_message (cancellable, _("Removing attachments"));
+
+	for (ii = 0; success && ii < message_uids->len; ii++) {
+		CamelMimeMessage *message;
+		const gchar *uid;
+		gint percent;
+
+		uid = g_ptr_array_index (message_uids, ii);
+
+		message = camel_folder_get_message_sync (
+			folder, uid, cancellable, error);
+
+		if (message == NULL) {
+			success = FALSE;
+			break;
+		}
+
+		success = mail_folder_strip_message (
+			folder, message, uid, cancellable, error);
+
+		percent = ((ii + 1) * 100) / message_uids->len;
+		camel_operation_progress (cancellable, percent);
+
+		g_object_unref (message);
+	}
+
+	camel_operation_pop_message (cancellable);
+
+	if (success)
+		camel_folder_synchronize_sync (
+			folder, FALSE, cancellable, error);
+
+	camel_folder_thaw (folder);
+
+	return success;
+}
+
+void
+e_mail_folder_remove_attachments (CamelFolder *folder,
+                                  GPtrArray *message_uids,
+                                  gint io_priority,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (message_uids != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->ptr_array = g_ptr_array_ref (message_uids);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_remove_attachments);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_remove_attachments_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_attachments_finish (CamelFolder *folder,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_remove_attachments), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_save_messages_thread (GSimpleAsyncResult *simple,
+                                  GObject *object,
+                                  GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_folder_save_messages_sync (
+		CAMEL_FOLDER (object), context->ptr_array,
+		context->destination, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+/* Helper for e_mail_folder_save_messages_sync() */
+static void
+mail_folder_save_prepare_part (CamelMimePart *mime_part)
+{
+	CamelDataWrapper *content;
+
+	content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+
+	if (content == NULL)
+		return;
+
+	if (CAMEL_IS_MULTIPART (content)) {
+		guint n_parts, ii;
+
+		n_parts = camel_multipart_get_number (
+			CAMEL_MULTIPART (content));
+		for (ii = 0; ii < n_parts; ii++) {
+			mime_part = camel_multipart_get_part (
+				CAMEL_MULTIPART (content), ii);
+			mail_folder_save_prepare_part (mime_part);
+		}
+
+	} else if (CAMEL_IS_MIME_MESSAGE (content)) {
+		mail_folder_save_prepare_part (CAMEL_MIME_PART (content));
+
+	} else {
+		CamelContentType *type;
+
+		/* Save textual parts as 8-bit, not encoded. */
+		type = camel_data_wrapper_get_mime_type_field (content);
+		if (camel_content_type_is (type, "text", "*"))
+			camel_mime_part_set_encoding (
+				mime_part, CAMEL_TRANSFER_ENCODING_8BIT);
+	}
+}
+
+gboolean
+e_mail_folder_save_messages_sync (CamelFolder *folder,
+                                  GPtrArray *message_uids,
+                                  GFile *destination,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	GFileOutputStream *file_output_stream;
+	GByteArray *byte_array;
+	CamelStream *base_stream;
+	gboolean success = TRUE;
+	guint ii;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+	g_return_val_if_fail (message_uids != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (destination), FALSE);
+
+	/* Need at least one message UID to save. */
+	g_return_val_if_fail (message_uids->len > 0, FALSE);
+
+	camel_operation_push_message (
+		cancellable, ngettext (
+			"Saving %d message",
+			"Saving %d messages",
+			message_uids->len),
+		message_uids->len);
+
+	file_output_stream = g_file_replace (
+		destination, NULL, FALSE,
+		G_FILE_CREATE_PRIVATE |
+		G_FILE_CREATE_REPLACE_DESTINATION,
+		cancellable, error);
+
+	if (file_output_stream == NULL) {
+		camel_operation_pop_message (cancellable);
+		return FALSE;
+	}
+
+	/* CamelStreamMem takes ownership of the GByteArray. */
+	byte_array = g_byte_array_new ();
+	base_stream = camel_stream_mem_new_with_byte_array (byte_array);
+
+	for (ii = 0; ii < message_uids->len; ii++) {
+		CamelMimeMessage *message;
+		CamelMimeFilter *filter;
+		CamelStream *stream;
+		const gchar *uid;
+		gchar *from_line;
+		gint percent;
+		gint retval;
+
+		uid = g_ptr_array_index (message_uids, ii);
+
+		message = camel_folder_get_message_sync (
+			folder, uid, cancellable, error);
+		if (message == NULL) {
+			success = FALSE;
+			goto exit;
+		}
+
+		mail_folder_save_prepare_part (CAMEL_MIME_PART (message));
+
+		from_line = camel_mime_message_build_mbox_from (message);
+		g_return_val_if_fail (from_line != NULL, FALSE);
+
+		success = g_output_stream_write_all (
+			G_OUTPUT_STREAM (file_output_stream),
+			from_line, strlen (from_line), NULL,
+			cancellable, error);
+
+		g_free (from_line);
+
+		if (!success) {
+			g_object_unref (message);
+			goto exit;
+		}
+
+		filter = camel_mime_filter_from_new ();
+		stream = camel_stream_filter_new (base_stream);
+		camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+
+		retval = camel_data_wrapper_write_to_stream_sync (
+			CAMEL_DATA_WRAPPER (message),
+			stream, cancellable, error);
+
+		g_object_unref (filter);
+		g_object_unref (stream);
+
+		if (retval == -1) {
+			g_object_unref (message);
+			goto exit;
+		}
+
+		g_byte_array_append (byte_array, (guint8 *) "\n", 1);
+
+		success = g_output_stream_write_all (
+			G_OUTPUT_STREAM (file_output_stream),
+			byte_array->data, byte_array->len,
+			NULL, cancellable, error);
+
+		if (!success) {
+			g_object_unref (message);
+			goto exit;
+		}
+
+		percent = ((ii + 1) * 100) / message_uids->len;
+		camel_operation_progress (cancellable, percent);
+
+		/* Flush the buffer for the next message.
+		 * For memory streams this never fails. */
+		g_seekable_seek (
+			G_SEEKABLE (base_stream),
+			0, G_SEEK_SET, NULL, NULL);
+
+		g_object_unref (message);
+	}
+
+exit:
+	g_object_unref (file_output_stream);
+	g_object_unref (base_stream);
+
+	camel_operation_pop_message (cancellable);
+
+	if (!success) {
+		/* Try deleting the destination file. */
+		g_file_delete (destination, NULL, NULL);
+	}
+
+	return success;
+}
+
+void
+e_mail_folder_save_messages (CamelFolder *folder,
+                             GPtrArray *message_uids,
+                             GFile *destination,
+                             gint io_priority,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+	g_return_if_fail (message_uids != NULL);
+	g_return_if_fail (G_IS_FILE (destination));
+
+	/* Need at least one message UID to save. */
+	g_return_if_fail (message_uids->len > 0);
+
+	context = g_slice_new0 (AsyncContext);
+	context->ptr_array = g_ptr_array_ref (message_uids);
+	context->destination = g_object_ref (destination);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (folder), callback, user_data,
+		e_mail_folder_save_messages);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_folder_save_messages_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_save_messages_finish (CamelFolder *folder,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (folder),
+		e_mail_folder_save_messages), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_mail_folder_uri_build:
+ * @store: a #CamelStore
+ * @folder_name: a folder name
+ *
+ * Builds a folder URI string from @store and @folder_name.
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_build (CamelStore *store,
+                         const gchar *folder_name)
+{
+	const gchar *uid;
+	gchar *encoded_name;
+	gchar *encoded_uid;
+	gchar *uri;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+	g_return_val_if_fail (folder_name != NULL, NULL);
+
+	/* Skip the leading slash, if present. */
+	if (*folder_name == '/')
+		folder_name++;
+
+	uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+	encoded_uid = camel_url_encode (uid, ":;@/");
+	encoded_name = camel_url_encode (folder_name, "#");
+
+	uri = g_strdup_printf ("folder://%s/%s", encoded_uid, encoded_name);
+
+	g_free (encoded_uid);
+	g_free (encoded_name);
+
+	return uri;
+}
+
+/**
+ * e_mail_folder_uri_parse:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @out_store: return location for a #CamelStore, or %NULL
+ * @out_folder_name: return location for a folder name, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Parses a folder URI generated by e_mail_folder_uri_build() and
+ * returns the corresponding #CamelStore instance in @out_store and
+ * folder name string in @out_folder_name.  If the URI is malformed
+ * or no corresponding store exists, the function sets @error and
+ * returns %FALSE.
+ *
+ * If the function is able to parse the URI, the #CamelStore instance
+ * set in @out_store should be unreferenced with g_object_unref() when
+ * done with it, and the folder name string set in @out_folder_name
+ * should be freed with g_free().
+ *
+ * The function also handles older style URIs, such as ones where the
+ * #CamelStore's #CamelStore::uri string was embedded directly in the
+ * folder URI, and account-based URIs that used an "email://" prefix.
+ *
+ * Returns: %TRUE if @folder_uri could be parsed, %FALSE otherwise
+ **/
+gboolean
+e_mail_folder_uri_parse (CamelSession *session,
+                         const gchar *folder_uri,
+                         CamelStore **out_store,
+                         gchar **out_folder_name,
+                         GError **error)
+{
+	CamelURL *url;
+	CamelService *service = NULL;
+	gchar *folder_name = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+	g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+	url = camel_url_new (folder_uri, error);
+	if (url == NULL)
+		return FALSE;
+
+	/* Current URI Format: 'folder://' STORE_UID '/' FOLDER_PATH */
+	if (g_strcmp0 (url->protocol, "folder") == 0) {
+
+		if (url->host != NULL) {
+			gchar *uid;
+
+			if (url->user == NULL || *url->user == '\0')
+				uid = g_strdup (url->host);
+			else
+				uid = g_strconcat (
+					url->user, "@", url->host, NULL);
+
+			service = camel_session_get_service (session, uid);
+			g_free (uid);
+		}
+
+		if (url->path != NULL && *url->path == '/')
+			folder_name = camel_url_decode_path (url->path + 1);
+
+	/* This style was used to reference accounts by UID before
+	 * CamelServices themselves had UIDs.  Some examples are:
+	 *
+	 * Special cases:
+	 *
+	 *   'email://local local/' FOLDER_PATH
+	 *   'email://vfolder local/' FOLDER_PATH
+	 *
+	 * General case:
+	 *
+	 *   'email://' ACCOUNT_UID '/' FOLDER_PATH
+	 *
+	 * Note: ACCOUNT_UID is now equivalent to STORE_UID, and
+	 *       the STORE_UIDs for the special cases are 'local'
+	 *       and 'vfolder'.
+	 */
+	} else if (g_strcmp0 (url->protocol, "email") == 0) {
+		gchar *uid = NULL;
+
+		/* Handle the special cases. */
+		if (g_strcmp0 (url->host, "local") == 0) {
+			if (g_strcmp0 (url->user, "local") == 0)
+				uid = g_strdup ("local");
+			if (g_strcmp0 (url->user, "vfolder") == 0)
+				uid = g_strdup ("vfolder");
+		}
+
+		/* Handle the general case. */
+		if (uid == NULL && url->host != NULL) {
+			if (url->user == NULL)
+				uid = g_strdup (url->host);
+			else
+				uid = g_strdup_printf (
+					"%s %s", url->user, url->host);
+		}
+
+		if (uid != NULL) {
+			service = camel_session_get_service (session, uid);
+			g_free (uid);
+		}
+
+		if (url->path != NULL && *url->path == '/')
+			folder_name = camel_url_decode_path (url->path + 1);
+
+	/* CamelFolderInfo URIs used to embed the store's URI, so the
+	 * folder name is appended as either a path part or a fragment
+	 * part, depending whether the store's URI used the path part.
+	 * To determine which it is, you have to check the provider
+	 * flags for CAMEL_URL_FRAGMENT_IS_PATH. */
+	} else {
+		service = camel_session_get_service_by_url (
+			session, url, CAMEL_PROVIDER_STORE);
+
+		if (CAMEL_IS_STORE (service)) {
+			CamelProvider *provider;
+
+			provider = camel_service_get_provider (service);
+
+			if (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
+				folder_name = g_strdup (url->fragment);
+			else if (url->path != NULL && *url->path == '/')
+				folder_name = g_strdup (url->path + 1);
+		}
+	}
+
+	if (CAMEL_IS_STORE (service) && folder_name != NULL) {
+		if (out_store != NULL)
+			*out_store = g_object_ref (service);
+
+		if (out_folder_name != NULL) {
+			*out_folder_name = folder_name;
+			folder_name = NULL;
+		}
+
+		success = TRUE;
+	} else {
+		g_set_error (
+			error, CAMEL_FOLDER_ERROR,
+			CAMEL_FOLDER_ERROR_INVALID,
+			_("Invalid folder URI '%s'"),
+			folder_uri);
+	}
+
+	g_free (folder_name);
+
+	camel_url_free (url);
+
+	return success;
+}
+
+/**
+ * e_mail_folder_uri_equal:
+ * @session: a #CamelSession
+ * @folder_uri_a: a folder URI
+ * @folder_uri_b: another folder URI
+ *
+ * Compares two folder URIs for equality.  If either URI is invalid,
+ * the function returns %FALSE.
+ *
+ * Returns: %TRUE if the URIs are equal, %FALSE if not
+ **/
+gboolean
+e_mail_folder_uri_equal (CamelSession *session,
+                         const gchar *folder_uri_a,
+                         const gchar *folder_uri_b)
+{
+	CamelStore *store_a;
+	CamelStore *store_b;
+	CamelStoreClass *class;
+	gchar *folder_name_a;
+	gchar *folder_name_b;
+	gboolean success_a;
+	gboolean success_b;
+	gboolean equal = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+	g_return_val_if_fail (folder_uri_a != NULL, FALSE);
+	g_return_val_if_fail (folder_uri_b != NULL, FALSE);
+
+	success_a = e_mail_folder_uri_parse (
+		session, folder_uri_a, &store_a, &folder_name_a, NULL);
+
+	success_b = e_mail_folder_uri_parse (
+		session, folder_uri_b, &store_b, &folder_name_b, NULL);
+
+	if (!success_a || !success_b)
+		goto exit;
+
+	if (store_a != store_b)
+		goto exit;
+
+	/* Doesn't matter which store we use since they're the same. */
+	class = CAMEL_STORE_GET_CLASS (store_a);
+	g_return_val_if_fail (class->compare_folder_name != NULL, FALSE);
+
+	equal = class->compare_folder_name (folder_name_a, folder_name_b);
+
+exit:
+	if (success_a) {
+		g_object_unref (store_a);
+		g_free (folder_name_a);
+	}
+
+	if (success_b) {
+		g_object_unref (store_b);
+		g_free (folder_name_b);
+	}
+
+	return equal;
+}
+
+/**
+ * e_mail_folder_uri_from_folder:
+ * @folder: a #CamelFolder
+ *
+ * Convenience function for building a folder URI from a #CamelFolder.
+ * Free the returned URI string with g_free().
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_from_folder (CamelFolder *folder)
+{
+	CamelStore *store;
+	const gchar *folder_name;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+	store = camel_folder_get_parent_store (folder);
+	folder_name = camel_folder_get_full_name (folder);
+
+	return e_mail_folder_uri_build (store, folder_name);
+}
+
+/**
+ * e_mail_folder_uri_to_markup:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Converts @folder_uri to a markup string suitable for displaying to users.
+ * The string consists of the #CamelStore display name (in bold), followed
+ * by the folder path.  If the URI is malformed or no corresponding store
+ * exists, the function sets @error and returns %NULL.  Free the returned
+ * string with g_free().
+ *
+ * Returns: a newly-allocated markup string, or %NULL
+ **/
+gchar *
+e_mail_folder_uri_to_markup (CamelSession *session,
+                             const gchar *folder_uri,
+                             GError **error)
+{
+	CamelStore *store = NULL;
+	const gchar *display_name;
+	gchar *folder_name = NULL;
+	gchar *markup;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
+	g_return_val_if_fail (folder_uri != NULL, NULL);
+
+	success = e_mail_folder_uri_parse (
+		session, folder_uri, &store, &folder_name, error);
+
+	if (!success)
+		return NULL;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+	g_return_val_if_fail (folder_name != NULL, NULL);
+
+	display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
+
+	markup = g_markup_printf_escaped (
+		"<b>%s</b> : %s", display_name, folder_name);
+
+	g_object_unref (store);
+	g_free (folder_name);
+
+	return markup;
+}
diff --git a/libemail-engine/e-mail-folder-utils.h b/libemail-engine/e-mail-folder-utils.h
new file mode 100644
index 0000000..9e8dd0f
--- /dev/null
+++ b/libemail-engine/e-mail-folder-utils.h
@@ -0,0 +1,164 @@
+/*
+ * e-mail-folder-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_FOLDER_UTILS_H
+#define E_MAIL_FOLDER_UTILS_H
+
+/* CamelFolder wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+gboolean	e_mail_folder_append_message_sync
+						(CamelFolder *folder,
+						 CamelMimeMessage *message,
+						 CamelMessageInfo *info,
+						 gchar **appended_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_append_message	(CamelFolder *folder,
+						 CamelMimeMessage *message,
+						 CamelMessageInfo *info,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_folder_append_message_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 gchar **appended_uid,
+						 GError **error);
+
+CamelMimePart *	e_mail_folder_build_attachment_sync
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 gchar **fwd_subject,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_build_attachment	(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+CamelMimePart *	e_mail_folder_build_attachment_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 gchar **fwd_subject,
+						 GError **error);
+
+GHashTable *	e_mail_folder_find_duplicate_messages_sync
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_find_duplicate_messages
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+GHashTable *	e_mail_folder_find_duplicate_messages_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 GError **error);
+
+GHashTable *	e_mail_folder_get_multiple_messages_sync
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_get_multiple_messages
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+GHashTable *	e_mail_folder_get_multiple_messages_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 GError **error);
+
+gboolean	e_mail_folder_remove_sync	(CamelFolder *folder,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_remove		(CamelFolder *folder,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_folder_remove_finish	(CamelFolder *folder,
+						 GAsyncResult *result,
+						 GError **error);
+
+gboolean	e_mail_folder_remove_attachments_sync
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_remove_attachments
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_folder_remove_attachments_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 GError **error);
+
+gboolean	e_mail_folder_save_messages_sync
+						(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 GFile *destination,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_folder_save_messages	(CamelFolder *folder,
+						 GPtrArray *message_uids,
+						 GFile *destination,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_folder_save_messages_finish
+						(CamelFolder *folder,
+						 GAsyncResult *result,
+						 GError **error);
+
+gchar *		e_mail_folder_uri_build		(CamelStore *store,
+						 const gchar *folder_name);
+gboolean	e_mail_folder_uri_parse		(CamelSession *session,
+						 const gchar *folder_uri,
+						 CamelStore **out_store,
+						 gchar **out_folder_name,
+						 GError **error);
+gboolean	e_mail_folder_uri_equal		(CamelSession *session,
+						 const gchar *folder_uri_a,
+						 const gchar *folder_uri_b);
+gchar *		e_mail_folder_uri_from_folder	(CamelFolder *folder);
+gchar *		e_mail_folder_uri_to_markup	(CamelSession *session,
+						 const gchar *folder_uri,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_FOLDER_UTILS_H */
diff --git a/libemail-engine/e-mail-junk-filter.c b/libemail-engine/e-mail-junk-filter.c
new file mode 100644
index 0000000..d682490
--- /dev/null
+++ b/libemail-engine/e-mail-junk-filter.c
@@ -0,0 +1,82 @@
+/*
+ * e-mail-junk-filter.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-junk-filter.h"
+
+#include <e-mail-session.h>
+
+G_DEFINE_ABSTRACT_TYPE (
+	EMailJunkFilter,
+	e_mail_junk_filter,
+	E_TYPE_EXTENSION)
+
+static void
+e_mail_junk_filter_class_init (EMailJunkFilterClass *class)
+{
+	EExtensionClass *extension_class;
+
+	extension_class = E_EXTENSION_CLASS (class);
+	extension_class->extensible_type = E_TYPE_MAIL_SESSION;
+}
+
+static void
+e_mail_junk_filter_init (EMailJunkFilter *junk_filter)
+{
+}
+
+gboolean
+e_mail_junk_filter_available (EMailJunkFilter *junk_filter)
+{
+	EMailJunkFilterClass *class;
+
+	g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), FALSE);
+
+	class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+	g_return_val_if_fail (class->available != NULL, FALSE);
+
+	return class->available (junk_filter);
+}
+
+GtkWidget *
+e_mail_junk_filter_new_config_widget (EMailJunkFilter *junk_filter)
+{
+	EMailJunkFilterClass *class;
+	GtkWidget *widget = NULL;
+
+	g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), NULL);
+
+	class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+
+	if (class->new_config_widget != NULL)
+		widget = class->new_config_widget (junk_filter);
+
+	return widget;
+}
+
+gint
+e_mail_junk_filter_compare (EMailJunkFilter *junk_filter_a,
+                            EMailJunkFilter *junk_filter_b)
+{
+	EMailJunkFilterClass *class_a;
+	EMailJunkFilterClass *class_b;
+
+	class_a = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_a);
+	class_b = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_b);
+
+	return g_utf8_collate (class_a->display_name, class_b->display_name);
+}
diff --git a/libemail-engine/e-mail-junk-filter.h b/libemail-engine/e-mail-junk-filter.h
new file mode 100644
index 0000000..74a7840
--- /dev/null
+++ b/libemail-engine/e-mail-junk-filter.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-junk-filter.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_JUNK_FILTER_H
+#define E_MAIL_JUNK_FILTER_H
+
+#include <gtk/gtk.h>
+#include <libebackend/e-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_JUNK_FILTER \
+	(e_mail_junk_filter_get_type ())
+#define E_MAIL_JUNK_FILTER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilter))
+#define E_MAIL_JUNK_FILTER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass))
+#define E_IS_MAIL_JUNK_FILTER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_JUNK_FILTER))
+#define E_IS_MAIL_JUNK_FILTER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_JUNK_FILTER))
+#define E_MAIL_JUNK_FILTER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailJunkFilter EMailJunkFilter;
+typedef struct _EMailJunkFilterClass EMailJunkFilterClass;
+typedef struct _EMailJunkFilterPrivate EMailJunkFilterPrivate;
+
+struct _EMailJunkFilter {
+	EExtension parent;
+	EMailJunkFilterPrivate *priv;
+};
+
+struct _EMailJunkFilterClass {
+	EExtensionClass parent_class;
+
+	const gchar *filter_name;
+	const gchar *display_name;
+
+	gboolean	(*available)		(EMailJunkFilter *junk_filter);
+	GtkWidget *	(*new_config_widget)	(EMailJunkFilter *junk_filter);
+};
+
+GType		e_mail_junk_filter_get_type	(void) G_GNUC_CONST;
+gboolean	e_mail_junk_filter_available	(EMailJunkFilter *junk_filter);
+GtkWidget *	e_mail_junk_filter_new_config_widget
+						(EMailJunkFilter *junk_filter);
+gint		e_mail_junk_filter_compare	(EMailJunkFilter *junk_filter_a,
+						 EMailJunkFilter *junk_filter_b);
+
+G_END_DECLS
+
+#endif /* E_MAIL_JUNK_FILTER_H */
diff --git a/libemail-engine/e-mail-local.c b/libemail-engine/e-mail-local.c
new file mode 100644
index 0000000..1f33c08
--- /dev/null
+++ b/libemail-engine/e-mail-local.c
@@ -0,0 +1,157 @@
+/*
+ * e-mail-local.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-local.h"
+
+#include <glib/gi18n.h>
+
+#include "e-mail-folder-utils.h"
+
+#define CHECK_LOCAL_FOLDER_TYPE(type) \
+	((type) < G_N_ELEMENTS (default_local_folders))
+
+/* The array elements correspond to EMailLocalFolder. */
+static struct {
+	const gchar *display_name;
+	CamelFolder *folder;
+	gchar *folder_uri;
+} default_local_folders[] = {
+	{ N_("Inbox") },
+	{ N_("Drafts") },
+	{ N_("Outbox") },
+	{ N_("Sent") },
+	{ N_("Templates") },
+	{ "Inbox" }  /* "always local" inbox */
+};
+
+static CamelStore *local_store;
+static gboolean mail_local_initialized = FALSE;
+
+void
+e_mail_local_init (EMailSession *session,
+                   const gchar *data_dir)
+{
+	CamelService *service;
+	CamelURL *url;
+	gchar *temp;
+	gint ii;
+	GError *error = NULL;
+
+	if (mail_local_initialized)
+		return;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (data_dir != NULL);
+
+	mail_local_initialized = TRUE;
+
+	url = camel_url_new ("maildir:", NULL);
+	temp = g_build_filename (data_dir, "local", NULL);
+	camel_url_set_path (url, temp);
+	g_free (temp);
+
+	temp = camel_url_to_string (url, 0);
+	service = camel_session_add_service (
+		CAMEL_SESSION (session), "local", temp,
+		CAMEL_PROVIDER_STORE, &error);
+	g_free (temp);
+
+	camel_service_set_display_name (service, _("On This Computer"));
+
+	/* Shouldn't need to worry about other mail applications
+	 * altering files in our local mail store. */
+	g_object_set (service, "need-summary-check", FALSE, NULL);
+
+	if (error != NULL)
+		goto fail;
+
+	/* Populate the rest of the default_local_folders array. */
+	for (ii = 0; ii < G_N_ELEMENTS (default_local_folders); ii++) {
+		const gchar *display_name;
+
+		display_name = default_local_folders[ii].display_name;
+
+		default_local_folders[ii].folder_uri =
+			e_mail_folder_uri_build (
+			CAMEL_STORE (service), display_name);
+
+		/* FIXME camel_store_get_folder() may block. */
+		if (!strcmp (display_name, "Inbox"))
+			default_local_folders[ii].folder =
+				camel_store_get_inbox_folder_sync (
+				CAMEL_STORE (service), NULL, &error);
+		else
+			default_local_folders[ii].folder =
+				camel_store_get_folder_sync (
+				CAMEL_STORE (service), display_name,
+				CAMEL_STORE_FOLDER_CREATE, NULL, &error);
+
+		if (error != NULL) {
+			g_critical ("%s", error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	camel_url_free (url);
+
+	local_store = g_object_ref (service);
+
+	return;
+
+fail:
+	g_critical (
+		"Could not initialize local store/folder: %s",
+		error->message);
+
+	g_error_free (error);
+	camel_url_free (url);
+}
+
+CamelFolder *
+e_mail_local_get_folder (EMailLocalFolder type)
+{
+	g_return_val_if_fail (mail_local_initialized, NULL);
+	g_return_val_if_fail (CHECK_LOCAL_FOLDER_TYPE (type), NULL);
+
+	return default_local_folders[type].folder;
+}
+
+const gchar *
+e_mail_local_get_folder_uri (EMailLocalFolder type)
+{
+	g_return_val_if_fail (mail_local_initialized, NULL);
+	g_return_val_if_fail (CHECK_LOCAL_FOLDER_TYPE (type), NULL);
+
+	return default_local_folders[type].folder_uri;
+}
+
+CamelStore *
+e_mail_local_get_store (void)
+{
+	g_return_val_if_fail (mail_local_initialized, NULL);
+	g_return_val_if_fail (CAMEL_IS_STORE (local_store), NULL);
+
+	return local_store;
+}
diff --git a/libemail-engine/e-mail-local.h b/libemail-engine/e-mail-local.h
new file mode 100644
index 0000000..d4b5bad
--- /dev/null
+++ b/libemail-engine/e-mail-local.h
@@ -0,0 +1,39 @@
+/*
+ * e-mail-local.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_LOCAL_H
+#define E_MAIL_LOCAL_H
+
+#include <camel/camel.h>
+#include <libemail-engine/e-mail-enums.h>
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+void		e_mail_local_init		(EMailSession *session,
+						 const gchar *data_dir);
+CamelFolder *	e_mail_local_get_folder		(EMailLocalFolder type);
+const gchar *	e_mail_local_get_folder_uri	(EMailLocalFolder type);
+CamelStore *	e_mail_local_get_store		(void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_LOCAL_H */
diff --git a/libemail-engine/e-mail-session-utils.c b/libemail-engine/e-mail-session-utils.c
new file mode 100644
index 0000000..9a55891
--- /dev/null
+++ b/libemail-engine/e-mail-session-utils.c
@@ -0,0 +1,942 @@
+/*
+ * e-mail-session-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-session-utils.h"
+
+#include "e-mail-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "mail-tools.h"
+#include "e-mail-local.h"
+#include "e-mail-folder-utils.h"
+#include <libemail-utils/e-account-utils.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution Mail Data Server" PACKAGE_VERSION)
+
+/* FIXME: Do this after we move filter/ to eds */
+#define E_FILTER_SOURCE_OUTGOING  "outgoing"/* performed on outgoing mail */
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	CamelFolder *sent_folder;
+
+	CamelMimeMessage *message;
+	CamelMessageInfo *info;
+
+	CamelAddress *from;
+	CamelAddress *recipients;
+
+	CamelFilterDriver *driver;
+
+	GCancellable *cancellable;
+	gint io_priority;
+
+	/* X-Evolution headers */
+	struct _camel_header_raw *xev;
+
+	GPtrArray *post_to_uris;
+
+	gchar *folder_uri;
+	gchar *message_uid;
+	gchar *transport_uid;
+	gchar *sent_folder_uri;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+	if (context->sent_folder != NULL)
+		g_object_unref (context->sent_folder);
+
+	if (context->message != NULL)
+		g_object_unref (context->message);
+
+	if (context->info != NULL)
+		camel_message_info_free (context->info);
+
+	if (context->from != NULL)
+		g_object_unref (context->from);
+
+	if (context->recipients != NULL)
+		g_object_unref (context->recipients);
+
+	if (context->driver != NULL)
+		g_object_unref (context->driver);
+
+	if (context->cancellable != NULL) {
+		camel_operation_pop_message (context->cancellable);
+		g_object_unref (context->cancellable);
+	}
+
+	if (context->xev != NULL)
+		camel_header_raw_clear (&context->xev);
+
+	if (context->post_to_uris != NULL) {
+		g_ptr_array_foreach (
+			context->post_to_uris, (GFunc) g_free, NULL);
+		g_ptr_array_free (context->post_to_uris, TRUE);
+	}
+
+	g_free (context->folder_uri);
+	g_free (context->message_uid);
+	g_free (context->transport_uid);
+	g_free (context->sent_folder_uri);
+
+	g_slice_free (AsyncContext, context);
+}
+
+GQuark
+e_mail_error_quark (void)
+{
+	static GQuark quark = 0;
+
+	if (G_UNLIKELY (quark == 0)) {
+		const gchar *string = "e-mail-error-quark";
+		quark = g_quark_from_static_string (string);
+	}
+
+	return quark;
+}
+
+static void
+mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
+                                          EMailSession *session,
+                                          GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_session_handle_draft_headers_sync (
+		session, context->message, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+gboolean
+e_mail_session_handle_draft_headers_sync (EMailSession *session,
+                                          CamelMimeMessage *message,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+	CamelFolder *folder;
+	CamelMedium *medium;
+	const gchar *folder_uri;
+	const gchar *message_uid;
+	const gchar *header_name;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+	medium = CAMEL_MEDIUM (message);
+
+	header_name = "X-Evolution-Draft-Folder";
+	folder_uri = camel_medium_get_header (medium, header_name);
+
+	header_name = "X-Evolution-Draft-Message";
+	message_uid = camel_medium_get_header (medium, header_name);
+
+	/* Don't report errors about missing X-Evolution-Draft
+	 * headers.  These headers are optional, so their absence
+	 * is handled by doing nothing. */
+	if (folder_uri == NULL || message_uid == NULL)
+		return TRUE;
+
+	folder = e_mail_session_uri_to_folder_sync (
+		session, folder_uri, 0, cancellable, error);
+
+	if (folder == NULL)
+		return FALSE;
+
+	camel_folder_set_message_flags (
+		folder, message_uid,
+		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+
+	success = camel_folder_synchronize_message_sync (
+		folder, message_uid, cancellable, error);
+
+	g_object_unref (folder);
+
+	return success;
+}
+
+void
+e_mail_session_handle_draft_headers (EMailSession *session,
+                                     CamelMimeMessage *message,
+                                     gint io_priority,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+	context = g_slice_new0 (AsyncContext);
+	context->message = g_object_ref (message);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback, user_data,
+		e_mail_session_handle_draft_headers);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_handle_draft_headers_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_draft_headers_finish (EMailSession *session,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_handle_draft_headers), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
+                                           EMailSession *session,
+                                           GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_session_handle_source_headers_sync (
+		session, context->message, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+gboolean
+e_mail_session_handle_source_headers_sync (EMailSession *session,
+                                           CamelMimeMessage *message,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+	CamelFolder *folder;
+	CamelMedium *medium;
+	CamelMessageFlags flags = 0;
+	const gchar *folder_uri;
+	const gchar *message_uid;
+	const gchar *flag_string;
+	const gchar *header_name;
+	gboolean success;
+	guint length, ii;
+	gchar **tokens;
+	gchar *string;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+	medium = CAMEL_MEDIUM (message);
+
+	header_name = "X-Evolution-Source-Folder";
+	folder_uri = camel_medium_get_header (medium, header_name);
+
+	header_name = "X-Evolution-Source-Message";
+	message_uid = camel_medium_get_header (medium, header_name);
+
+	header_name = "X-Evolution-Source-Flags";
+	flag_string = camel_medium_get_header (medium, header_name);
+
+	/* Don't report errors about missing X-Evolution-Source
+	 * headers.  These headers are optional, so their absence
+	 * is handled by doing nothing. */
+	if (folder_uri == NULL || message_uid == NULL || flag_string == NULL)
+		return TRUE;
+
+	/* Convert the flag string to CamelMessageFlags. */
+
+	string = g_strstrip (g_strdup (flag_string));
+	tokens = g_strsplit (string, " ", 0);
+	g_free (string);
+
+	/* If tokens is NULL, a length of 0 will skip the loop. */
+	length = (tokens != NULL) ? g_strv_length (tokens) : 0;
+
+	for (ii = 0; ii < length; ii++) {
+		/* Note: We're only checking for flags known to
+		 * be used in X-Evolution-Source-Flags headers.
+		 * Add more as needed. */
+		if (g_strcmp0 (tokens[ii], "ANSWERED") == 0)
+			flags |= CAMEL_MESSAGE_ANSWERED;
+		else if (g_strcmp0 (tokens[ii], "ANSWERED_ALL") == 0)
+			flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+		else if (g_strcmp0 (tokens[ii], "FORWARDED") == 0)
+			flags |= CAMEL_MESSAGE_FORWARDED;
+		else if (g_strcmp0 (tokens[ii], "SEEN") == 0)
+			flags |= CAMEL_MESSAGE_SEEN;
+		else
+			g_warning (
+				"Unknown flag '%s' in %s",
+				tokens[ii], header_name);
+	}
+
+	g_strfreev (tokens);
+
+	folder = e_mail_session_uri_to_folder_sync (
+		session, folder_uri, 0, cancellable, error);
+
+	if (folder == NULL)
+		return FALSE;
+
+	camel_folder_set_message_flags (
+		folder, message_uid, flags, flags);
+
+	success = camel_folder_synchronize_message_sync (
+		folder, message_uid, cancellable, error);
+
+	g_object_unref (folder);
+
+	return success;
+}
+
+void
+e_mail_session_handle_source_headers (EMailSession *session,
+                                      CamelMimeMessage *message,
+                                      gint io_priority,
+                                      GCancellable *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+	context = g_slice_new0 (AsyncContext);
+	context->message = g_object_ref (message);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback, user_data,
+		e_mail_session_handle_source_headers);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_handle_source_headers_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_source_headers_finish (EMailSession *session,
+                                             GAsyncResult *result,
+                                             GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_handle_draft_headers), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_send_to_thread (GSimpleAsyncResult *simple,
+                             EMailSession *session,
+                             GCancellable *cancellable)
+{
+	AsyncContext *context;
+	CamelFolder *local_sent_folder;
+	GString *error_messages;
+	gboolean copy_to_sent = TRUE;
+	guint ii;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	/* Send the message to all recipients. */
+	if (camel_address_length (context->recipients) > 0) {
+		CamelProvider *provider;
+		CamelService *service;
+		gboolean did_connect = FALSE;
+
+		service = camel_session_get_service (
+			CAMEL_SESSION (session), context->transport_uid);
+
+		if (!CAMEL_IS_TRANSPORT (service)) {
+			g_simple_async_result_set_error (simple,
+				CAMEL_SERVICE_ERROR,
+				CAMEL_SERVICE_ERROR_URL_INVALID,
+				_("Cannot get transport for account '%s'"),
+				context->transport_uid);
+			return;
+		}
+
+		if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED) {
+			did_connect = TRUE;
+
+			/* XXX This API does not allow for cancellation. */
+			if (!em_utils_connect_service_sync (service, cancellable, &error)) {
+				g_simple_async_result_set_from_error (simple, error);
+				g_error_free (error);
+				return;
+			}
+		}
+
+		provider = camel_service_get_provider (service);
+
+		if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
+			copy_to_sent = FALSE;
+
+		camel_transport_send_to_sync (
+			CAMEL_TRANSPORT (service),
+			context->message, context->from,
+			context->recipients, cancellable, &error);
+
+		if (did_connect)
+			em_utils_disconnect_service_sync (
+				service, error == NULL,
+				cancellable, error ? NULL : &error);
+
+		if (error != NULL) {
+			g_simple_async_result_set_from_error (simple, error);
+			g_error_free (error);
+			return;
+		}
+	}
+
+	/* Post the message to requested folders. */
+	for (ii = 0; ii < context->post_to_uris->len; ii++) {
+		CamelFolder *folder;
+		const gchar *folder_uri;
+
+		folder_uri = g_ptr_array_index (context->post_to_uris, ii);
+
+		folder = e_mail_session_uri_to_folder_sync (
+			session, folder_uri, 0, cancellable, &error);
+
+		if (error != NULL) {
+			g_warn_if_fail (folder == NULL);
+			g_simple_async_result_set_from_error (simple, error);
+			g_error_free (error);
+			return;
+		}
+
+		g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+		camel_folder_append_message_sync (
+			folder, context->message, context->info,
+			NULL, cancellable, &error);
+
+		g_object_unref (folder);
+
+		if (error != NULL) {
+			g_simple_async_result_set_from_error (simple, error);
+			g_error_free (error);
+			return;
+		}
+	}
+
+	/*** Post Processing ***/
+
+	/* This accumulates error messages during post-processing. */
+	error_messages = g_string_sized_new (256);
+
+	mail_tool_restore_xevolution_headers (context->message, context->xev);
+
+	/* Run filters on the outgoing message. */
+	if (context->driver != NULL) {
+		camel_filter_driver_filter_message (
+			context->driver, context->message, context->info,
+			NULL, NULL, NULL, "", cancellable, &error);
+
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			goto exit;
+
+		if (error != NULL) {
+			g_string_append_printf (
+				error_messages,
+				_("Failed to apply outgoing filters: %s"),
+				error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	if (!copy_to_sent)
+		goto cleanup;
+
+	/* Append the sent message to a Sent folder. */
+
+	local_sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+	/* Try to extract a CamelFolder from the Sent folder URI. */
+	if (context->sent_folder_uri != NULL) {
+		context->sent_folder = e_mail_session_uri_to_folder_sync (
+			session, context->sent_folder_uri, 0,
+			cancellable, &error);
+		if (error != NULL) {
+			g_warn_if_fail (context->sent_folder == NULL);
+			if (error_messages->len > 0)
+				g_string_append (error_messages, "\n\n");
+			g_string_append_printf (
+				error_messages,
+				_("Failed to append to %s: %s\n"
+				  "Appending to local 'Sent' folder instead."),
+				context->sent_folder_uri, error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	/* Fall back to the local Sent folder. */
+	if (context->sent_folder == NULL)
+		context->sent_folder = g_object_ref (local_sent_folder);
+
+	/* Append the message. */
+	camel_folder_append_message_sync (
+		context->sent_folder, context->message,
+		context->info, NULL, cancellable, &error);
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		goto exit;
+
+	if (error == NULL)
+		goto cleanup;
+
+	/* If appending to a remote Sent folder failed,
+	 * try appending to the local Sent folder. */
+	if (context->sent_folder != local_sent_folder) {
+		const gchar *description;
+
+		description = camel_folder_get_description (
+			context->sent_folder);
+
+		if (error_messages->len > 0)
+			g_string_append (error_messages, "\n\n");
+		g_string_append_printf (
+			error_messages,
+			_("Failed to append to %s: %s\n"
+			  "Appending to local 'Sent' folder instead."),
+			description, error->message);
+		g_clear_error (&error);
+
+		camel_folder_append_message_sync (
+			local_sent_folder, context->message,
+			context->info, NULL, cancellable, &error);
+	}
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		goto exit;
+
+	/* We can't even append to the local Sent folder?
+	 * In that case just leave the message in Outbox. */
+	if (error != NULL) {
+		if (error_messages->len > 0)
+			g_string_append (error_messages, "\n\n");
+		g_string_append_printf (
+			error_messages,
+			_("Failed to append to local 'Sent' folder: %s"),
+			error->message);
+		g_clear_error (&error);
+		goto exit;
+	}
+
+cleanup:
+
+	/* The send operation was successful; ignore cleanup errors. */
+
+	/* Mark the draft message for deletion, if present. */
+	e_mail_session_handle_draft_headers_sync (
+		session, context->message, cancellable, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_clear_error (&error);
+	}
+
+	/* Set flags on the original source message, if present.
+	 * Source message refers to the message being forwarded
+	 * or replied to. */
+	e_mail_session_handle_source_headers_sync (
+		session, context->message, cancellable, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_clear_error (&error);
+	}
+
+exit:
+
+	/* If we were cancelled, disregard any other errors. */
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+
+	/* Stuff the accumulated error messages in a GError. */
+	} else if (error_messages->len > 0) {
+		g_simple_async_result_set_error (
+			simple, E_MAIL_ERROR,
+			E_MAIL_ERROR_POST_PROCESSING,
+			"%s", error_messages->str);
+	}
+
+	/* Synchronize the Sent folder. */
+	if (context->sent_folder != NULL)
+		camel_folder_synchronize_sync (
+			context->sent_folder, FALSE, cancellable, NULL);
+
+	g_string_free (error_messages, TRUE);
+}
+
+void
+e_mail_session_send_to (EMailSession *session,
+                        CamelMimeMessage *message,
+                        gint io_priority,
+                        GCancellable *cancellable,
+                        CamelFilterGetFolderFunc get_folder_func,
+                        gpointer get_folder_data,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+	CamelAddress *from;
+	CamelAddress *recipients;
+	CamelMedium *medium;
+	CamelMessageInfo *info;
+	EAccount *account = NULL;
+	GPtrArray *post_to_uris;
+	struct _camel_header_raw *xev;
+	struct _camel_header_raw *header;
+	const gchar *string;
+	const gchar *resent_from;
+	gchar *transport_uid = NULL;
+	gchar *sent_folder_uri = NULL;
+	GError *error = NULL;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+	medium = CAMEL_MEDIUM (message);
+
+	camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+	xev = mail_tool_remove_xevolution_headers (message);
+
+	/* Extract directives from X-Evolution headers. */
+
+	string = camel_header_raw_find (&xev, "X-Evolution-Account", NULL);
+	if (string != NULL) {
+		gchar *account_uid;
+
+		account_uid = g_strstrip (g_strdup (string));
+		account = e_get_account_by_uid (account_uid);
+		g_free (account_uid);
+	}
+
+	if (account != NULL) {
+		if (account->transport != NULL) {
+
+			/* XXX Transport UIDs are kludgy right now.  We
+			 *     use the EAccount's regular UID and tack on
+			 *     "-transport".  Will be better soon. */
+			transport_uid = g_strconcat (
+				account->uid, "-transport", NULL);
+
+			/* to reprompt password on sending if needed */
+			account->transport->get_password_canceled = FALSE;
+		}
+		sent_folder_uri = g_strdup (account->sent_folder_uri);
+	}
+
+	string = camel_header_raw_find (&xev, "X-Evolution-Fcc", NULL);
+	if (sent_folder_uri == NULL && string != NULL)
+		sent_folder_uri = g_strstrip (g_strdup (string));
+
+	string = camel_header_raw_find (&xev, "X-Evolution-Transport", NULL);
+	if (transport_uid == NULL && string != NULL)
+		transport_uid = g_strstrip (g_strdup (string));
+
+	post_to_uris = g_ptr_array_new ();
+	for (header = xev; header != NULL; header = header->next) {
+		gchar *folder_uri;
+
+		if (g_strcmp0 (header->name, "X-Evolution-PostTo") != 0)
+			continue;
+
+		folder_uri = g_strstrip (g_strdup (header->value));
+		g_ptr_array_add (post_to_uris, folder_uri);
+	}
+
+	/* Collect sender and recipients from headers. */
+
+	from = (CamelAddress *) camel_internet_address_new ();
+	recipients = (CamelAddress *) camel_internet_address_new ();
+	resent_from = camel_medium_get_header (medium, "Resent-From");
+
+	if (resent_from != NULL) {
+		const CamelInternetAddress *addr;
+		const gchar *type;
+
+		camel_address_decode (from, resent_from);
+
+		type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+		type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+		type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+	} else {
+		const CamelInternetAddress *addr;
+		const gchar *type;
+
+		addr = camel_mime_message_get_from (message);
+		camel_address_copy (from, CAMEL_ADDRESS (addr));
+
+		type = CAMEL_RECIPIENT_TYPE_TO;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+		type = CAMEL_RECIPIENT_TYPE_CC;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+		type = CAMEL_RECIPIENT_TYPE_BCC;
+		addr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+	}
+
+	/* Miscellaneous preparations. */
+
+	info = camel_message_info_new (NULL);
+	camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+	/* The rest of the processing happens in a thread. */
+
+	context = g_slice_new0 (AsyncContext);
+	context->message = g_object_ref (message);
+	context->io_priority = io_priority;
+	context->from = from;
+	context->recipients = recipients;
+	context->message = g_object_ref (message);
+	context->info = info;
+	context->xev = xev;
+	context->post_to_uris = post_to_uris;
+	context->transport_uid = transport_uid;
+	context->sent_folder_uri = sent_folder_uri;
+
+	if (G_IS_CANCELLABLE (cancellable))
+		context->cancellable = g_object_ref (cancellable);
+
+	/* Failure here emits a runtime warning but is non-fatal. */
+	context->driver = camel_session_get_filter_driver (
+		CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, &error);
+	if (context->driver != NULL && get_folder_func)
+		camel_filter_driver_set_folder_func (
+			context->driver, get_folder_func, get_folder_data);
+	if (error != NULL) {
+		g_warn_if_fail (context->driver == NULL);
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+	/* This gets popped in async_context_free(). */
+	camel_operation_push_message (
+		context->cancellable, _("Sending message"));
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback,
+		user_data, e_mail_session_send_to);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_send_to_thread,
+		context->io_priority,
+		context->cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_send_to_finish (EMailSession *session,
+                               GAsyncResult *result,
+                               GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_send_to), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_unsubscribe_folder_thread (GSimpleAsyncResult *simple,
+                                        EMailSession *session,
+                                        GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_session_unsubscribe_folder_sync (
+		session, context->folder_uri, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+gboolean
+e_mail_session_unsubscribe_folder_sync (EMailSession *session,
+                                        const gchar *folder_uri,
+                                        GCancellable *cancellable,
+                                        GError **error)
+{
+	CamelStore *store = NULL;
+	gchar *folder_name = NULL;
+	const gchar *message;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+	g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+	success = e_mail_folder_uri_parse (
+		CAMEL_SESSION (session), folder_uri,
+		&store, &folder_name, error);
+
+	if (!success)
+		return FALSE;
+
+	message = _("Unsubscribing from folder '%s'");
+	camel_operation_push_message (cancellable, message, folder_name);
+
+	/* FIXME This should take our GCancellable. */
+	success =
+		em_utils_connect_service_sync (
+			CAMEL_SERVICE (store), cancellable, error) &&
+		camel_subscribable_unsubscribe_folder_sync (
+			CAMEL_SUBSCRIBABLE (store),
+			folder_name, cancellable, error);
+
+	camel_operation_pop_message (cancellable);
+
+	g_object_unref (store);
+	g_free (folder_name);
+
+	return success;
+}
+
+void
+e_mail_session_unsubscribe_folder (EMailSession *session,
+                                   const gchar *folder_uri,
+                                   gint io_priority,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (folder_uri != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->folder_uri = g_strdup (folder_uri);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback, user_data,
+		e_mail_session_unsubscribe_folder);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_unsubscribe_folder_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_unsubscribe_folder_finish (EMailSession *session,
+                                          GAsyncResult *result,
+                                          GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_unsubscribe_folder), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/libemail-engine/e-mail-session-utils.h b/libemail-engine/e-mail-session-utils.h
new file mode 100644
index 0000000..2c92216
--- /dev/null
+++ b/libemail-engine/e-mail-session-utils.h
@@ -0,0 +1,97 @@
+/*
+ * e-mail-session-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_SESSION_UTILS_H
+#define E_MAIL_SESSION_UTILS_H
+
+/* High-level operations with Evolution-specific policies. */
+
+#include <libemail-engine/e-mail-session.h>
+
+#define E_MAIL_ERROR (e_mail_error_quark ())
+
+G_BEGIN_DECLS
+
+typedef enum {
+	E_MAIL_ERROR_POST_PROCESSING
+} EMailError;
+
+GQuark		e_mail_error_quark		(void) G_GNUC_CONST;
+gboolean	e_mail_session_handle_draft_headers_sync
+						(EMailSession *session,
+						 CamelMimeMessage *message,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_handle_draft_headers
+						(EMailSession *session,
+						 CamelMimeMessage *message,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_session_handle_draft_headers_finish
+						(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+gboolean	e_mail_session_handle_source_headers_sync
+						(EMailSession *session,
+						 CamelMimeMessage *message,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_handle_source_headers
+						(EMailSession *session,
+						 CamelMimeMessage *message,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_session_handle_source_headers_finish
+						(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+void		e_mail_session_send_to		(EMailSession *session,
+						 CamelMimeMessage *message,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 CamelFilterGetFolderFunc get_folder_func,
+						 gpointer get_folder_data,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_session_send_to_finish	(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+gboolean	e_mail_session_unsubscribe_folder_sync
+						(EMailSession *session,
+						 const gchar *folder_uri,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_unsubscribe_folder
+						(EMailSession *session,
+						 const gchar *folder_uri,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_session_unsubscribe_folder_finish
+						(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SESSION_UTILS_H */
diff --git a/libemail-engine/e-mail-session.c b/libemail-engine/e-mail-session.c
new file mode 100644
index 0000000..b8a9c37
--- /dev/null
+++ b/libemail-engine/e-mail-session.c
@@ -0,0 +1,1517 @@
+/*
+ * e-mail-session.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *   Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+/* mail-session.c: handles the session information and resource manipulation */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#ifdef HAVE_CANBERRA
+#include <canberra-gtk.h>
+#endif
+
+#include <libedataserver/e-flag.h>
+#include <libedataserver/e-proxy.h>
+#include <libebackend/e-extensible.h>
+#include <libedataserverui/e-passwords.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "libemail-utils/e-account-utils.h"
+#include "libemail-utils/mail-mt.h"
+#include "libemail-utils/gconf-bridge.h"
+
+#include "e-mail-junk-filter.h"
+#include "e-mail-local.h"
+#include "e-mail-session.h"
+#include "e-mail-folder-utils.h"
+#include "e-mail-utils.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+
+#define E_MAIL_SESSION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
+
+static guint session_check_junk_notify_id;
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _EMailSessionPrivate {
+	MailFolderCache *folder_cache;
+
+	FILE *filter_logfile;
+	GHashTable *junk_filters;
+	EProxy *proxy;
+};
+
+struct _AsyncContext {
+	/* arguments */
+	CamelStoreGetFolderFlags flags;
+	gchar *uid;
+	gchar *uri;
+
+	/* results */
+	CamelFolder *folder;
+};
+
+enum {
+	PROP_0,
+	PROP_FOLDER_CACHE,
+	PROP_JUNK_FILTER_NAME
+};
+
+enum {
+	FLUSH_OUTBOX,
+	STORE_ADDED,
+	STORE_REMOVED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gchar *mail_data_dir;
+static gchar *mail_cache_dir;
+static gchar *mail_config_dir;
+
+G_DEFINE_TYPE_WITH_CODE (
+	EMailSession,
+	e_mail_session,
+	CAMEL_TYPE_SESSION,
+	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+/* Support for CamelSession.alert_user() *************************************/
+
+static GQueue user_message_queue = { NULL, NULL, 0 };
+
+struct _user_message_msg {
+	MailMsg base;
+
+	CamelSessionAlertType type;
+	gchar *prompt;
+	EFlag *done;
+
+	guint allow_cancel : 1;
+	guint result : 1;
+	guint ismain : 1;
+};
+
+static void user_message_exec (struct _user_message_msg *m,
+                               GCancellable *cancellable,
+                               GError **error);
+
+static void
+user_message_response_free (struct _user_message_msg *m)
+{
+
+	/* check for pendings */
+	if (!g_queue_is_empty (&user_message_queue)) {
+		GCancellable *cancellable;
+
+		m = g_queue_pop_head (&user_message_queue);
+		cancellable = m->base.cancellable;
+		user_message_exec (m, cancellable, &m->base.error);
+		mail_msg_unref (m);
+	}
+}
+
+/* clicked, send back the reply */
+static void
+user_message_response (struct _user_message_msg *m)
+{
+	/* if !allow_cancel, then we've already replied */
+	if (m->allow_cancel) {
+		m->result = TRUE; //If Accepted
+		e_flag_set (m->done);
+	}
+
+	user_message_response_free (m);
+}
+
+static void
+user_message_exec (struct _user_message_msg *m,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+	const gchar *error_type;
+
+	if (!m->ismain) {
+		g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
+		return;
+	}
+
+	switch (m->type) {
+		case CAMEL_SESSION_ALERT_INFO:
+			error_type = m->allow_cancel ?
+				"mail:session-message-info-cancel" :
+				"mail:session-message-info";
+			break;
+		case CAMEL_SESSION_ALERT_WARNING:
+			error_type = m->allow_cancel ?
+				"mail:session-message-warning-cancel" :
+				"mail:session-message-warning";
+			break;
+		case CAMEL_SESSION_ALERT_ERROR:
+			error_type = m->allow_cancel ?
+				"mail:session-message-error-cancel" :
+				"mail:session-message-error";
+			break;
+		default:
+			error_type = NULL;
+			g_return_if_reached ();
+	}
+
+	/* XXX This is a case where we need to be able to construct
+	 *     custom EAlerts without a predefined XML definition. */
+	if (m->ismain) {
+		/* Use DBUS to raise dialogs in clients and reply back. For now say accept all */
+		user_message_response (m);
+	} else {
+	}
+}
+
+static void
+user_message_free (struct _user_message_msg *m)
+{
+	g_free (m->prompt);
+	e_flag_free (m->done);
+}
+
+static MailMsgInfo user_message_info = {
+	sizeof (struct _user_message_msg),
+	(MailMsgDescFunc) NULL,
+	(MailMsgExecFunc) user_message_exec,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) user_message_free
+};
+
+/* Support for CamelSession.get_filter_driver () *****************************/
+
+static CamelFolder *
+get_folder (CamelFilterDriver *d,
+            const gchar *uri,
+            gpointer user_data,
+            GError **error)
+{
+	EMailSession *session = E_MAIL_SESSION (user_data);
+
+	/* FIXME Not passing a GCancellable here. */
+	/* FIXME Need a camel_filter_driver_get_session(). */
+	return e_mail_session_uri_to_folder_sync (
+		session, uri, 0, NULL, error);
+}
+
+static CamelFilterDriver *
+main_get_filter_driver (CamelSession *session,
+                        const gchar *type,
+                        GError **error)
+{
+	CamelFilterDriver *driver;
+	GConfClient *client;
+	EMailSession *ms = (EMailSession *)session;
+
+	client = gconf_client_get_default ();
+
+	driver = camel_filter_driver_new (session);
+	camel_filter_driver_set_folder_func (driver, get_folder, session);
+
+	if (gconf_client_get_bool (client, "/apps/evolution/mail/filters/log", NULL)) {
+		if (ms->priv->filter_logfile == NULL) {
+			gchar *filename;
+
+			filename = gconf_client_get_string (
+				client, "/apps/evolution/mail/filters/logfile", NULL);
+			if (filename) {
+				ms->priv->filter_logfile = g_fopen (filename, "a+");
+				g_free (filename);
+			}
+		}
+
+		if (ms->priv->filter_logfile)
+			camel_filter_driver_set_logfile (driver, ms->priv->filter_logfile);
+	}
+
+	g_object_unref (client);
+
+	return driver;
+}
+
+/* Support for CamelSession.forward_to () ************************************/
+
+static guint preparing_flush = 0;
+
+static gboolean
+forward_to_flush_outbox_cb (EMailSession *session)
+{
+
+	preparing_flush = 0;
+
+	/* Connect to this and call mail_send in the main email client.*/
+	g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
+
+
+	return FALSE;
+}
+
+static void
+ms_forward_to_cb (CamelFolder *folder,
+                  GAsyncResult *result,
+                  EMailSession *session)
+{
+	GConfClient *client;
+
+	/* FIXME Poor error handling. */
+	if (!e_mail_folder_append_message_finish (folder, result, NULL, NULL))
+		return;
+
+	client = gconf_client_get_default ();
+
+	/* do not call mail send immediately, just pile them all in the outbox */
+	if (preparing_flush || gconf_client_get_bool (
+		client, "/apps/evolution/mail/filters/flush-outbox", NULL)) {
+		if (preparing_flush)
+			g_source_remove (preparing_flush);
+
+		preparing_flush = g_timeout_add_seconds (
+			60, (GSourceFunc)
+			forward_to_flush_outbox_cb, session);
+	}
+
+	g_object_unref (client);
+}
+
+static void
+async_context_free (AsyncContext *context)
+{
+	if (context->folder != NULL)
+		g_object_unref (context->folder);
+
+	g_free (context->uid);
+	g_free (context->uri);
+
+	g_slice_free (AsyncContext, context);
+}
+
+static gchar *
+mail_session_make_key (CamelService *service,
+                       const gchar *item)
+{
+	gchar *key;
+
+	if (service != NULL)
+		key = camel_url_to_string (
+			camel_service_get_camel_url (service),
+			CAMEL_URL_HIDE_PARAMS);
+	else
+		key = g_strdup (item);
+
+	return key;
+}
+
+static void
+mail_session_check_junk_notify (GConfClient *gconf,
+                                guint id,
+                                GConfEntry *entry,
+                                CamelSession *session)
+{
+	gchar *key;
+
+	g_return_if_fail (gconf_entry_get_key (entry) != NULL);
+	g_return_if_fail (gconf_entry_get_value (entry) != NULL);
+
+	key = strrchr (gconf_entry_get_key (entry), '/');
+	if (key) {
+		key++;
+		if (strcmp (key, "check_incoming") == 0)
+			camel_session_set_check_junk (
+				session, gconf_value_get_bool (
+				gconf_entry_get_value (entry)));
+	}
+}
+
+static const gchar *
+mail_session_get_junk_filter_name (EMailSession *session)
+{
+	CamelJunkFilter *junk_filter;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	/* XXX This property can be removed once Evolution moves to
+	 *     GSettings and can use transform functions when binding
+	 *     properties to settings.  That's why this is private. */
+
+	g_hash_table_iter_init (&iter, session->priv->junk_filters);
+	junk_filter = camel_session_get_junk_filter (CAMEL_SESSION (session));
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		if (junk_filter == CAMEL_JUNK_FILTER (value))
+			return (const gchar *) key;
+	}
+
+	if (junk_filter != NULL)
+		g_warning (
+			"Camel is using a junk filter "
+			"unknown to Evolution of type %s",
+			G_OBJECT_TYPE_NAME (junk_filter));
+
+	return "";  /* GConfBridge doesn't like NULL strings */
+}
+
+static void
+mail_session_set_junk_filter_name (EMailSession *session,
+                                   const gchar *junk_filter_name)
+{
+	CamelJunkFilter *junk_filter = NULL;
+
+	/* XXX This property can be removed once Evolution moves to
+	 *     GSettings and can use transform functions when binding
+	 *     properties to settings.  That's why this is private. */
+
+	/* An empty string is equivalent to a NULL string. */
+	if (junk_filter_name != NULL && *junk_filter_name == '\0')
+		junk_filter_name = NULL;
+
+	if (junk_filter_name != NULL) {
+		junk_filter = g_hash_table_lookup (
+			session->priv->junk_filters, junk_filter_name);
+		if (junk_filter != NULL) {
+			if (!e_mail_junk_filter_available (
+				E_MAIL_JUNK_FILTER (junk_filter)))
+				junk_filter = NULL;
+		} else {
+			g_warning (
+				"Unrecognized junk filter name "
+				"'%s' in GConf", junk_filter_name);
+		}
+	}
+
+	camel_session_set_junk_filter (CAMEL_SESSION (session), junk_filter);
+
+	/* XXX We emit the "notify" signal in mail_session_notify(). */
+}
+
+static void
+mail_session_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_JUNK_FILTER_NAME:
+			mail_session_set_junk_filter_name (
+				E_MAIL_SESSION (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_session_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_FOLDER_CACHE:
+			g_value_set_object (
+				value,
+				e_mail_session_get_folder_cache (
+				E_MAIL_SESSION (object)));
+			return;
+
+		case PROP_JUNK_FILTER_NAME:
+			g_value_set_string (
+				value,
+				mail_session_get_junk_filter_name (
+				E_MAIL_SESSION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_session_dispose (GObject *object)
+{
+	EMailSessionPrivate *priv;
+
+	priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+	if (priv->folder_cache != NULL) {
+		g_object_unref (priv->folder_cache);
+		priv->folder_cache = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
+}
+
+static void
+mail_session_finalize (GObject *object)
+{
+	EMailSessionPrivate *priv;
+	GConfClient *client;
+
+	priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->junk_filters);
+	g_object_unref (priv->proxy);
+
+	client = gconf_client_get_default ();
+
+	if (session_check_junk_notify_id != 0) {
+		gconf_client_notify_remove (client, session_check_junk_notify_id);
+		session_check_junk_notify_id = 0;
+	}
+
+	g_object_unref (client);
+
+	g_free (mail_data_dir);
+	g_free (mail_config_dir);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object);
+}
+
+static void
+mail_session_notify (GObject *object,
+                     GParamSpec *pspec)
+{
+	/* GObject does not implement this method; do not chain up. */
+
+	/* XXX Delete this once Evolution moves to GSettings and
+	 *     we're able to get rid of PROP_JUNK_FILTER_NAME. */
+	if (g_strcmp0 (pspec->name, "junk-filter") == 0)
+		g_object_notify (object, "junk-filter-name");
+}
+
+static void
+mail_session_constructed (GObject *object)
+{
+	EMailSessionPrivate *priv;
+	EExtensible *extensible;
+	GType extension_type;
+	GList *list, *iter;
+
+	priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
+
+	extensible = E_EXTENSIBLE (object);
+	e_extensible_load_extensions (extensible);
+
+	/* Add junk filter extensions to an internal hash table. */
+
+	extension_type = E_TYPE_MAIL_JUNK_FILTER;
+	list = e_extensible_list_extensions (extensible, extension_type);
+
+	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
+		EMailJunkFilter *junk_filter;
+		EMailJunkFilterClass *class;
+
+		junk_filter = E_MAIL_JUNK_FILTER (iter->data);
+		class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+
+		if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
+			g_warning (
+				"Skipping %s: Does not implement "
+				"CamelJunkFilterInterface",
+				G_OBJECT_TYPE_NAME (junk_filter));
+			continue;
+		}
+
+		if (class->filter_name == NULL) {
+			g_warning (
+				"Skipping %s: filter_name unset",
+				G_OBJECT_TYPE_NAME (junk_filter));
+			continue;
+		}
+
+		if (class->display_name == NULL) {
+			g_warning (
+				"Skipping %s: display_name unset",
+				G_OBJECT_TYPE_NAME (junk_filter));
+			continue;
+		}
+
+		/* No need to reference the EMailJunkFilter since
+		 * EMailSession owns the reference to it already. */
+		g_hash_table_insert (
+			priv->junk_filters,
+			(gpointer) class->filter_name,
+			junk_filter);
+	}
+
+	g_list_free (list);
+
+	/* Bind the "/apps/evolution/mail/junk/default_plugin"
+	 * GConf key to our "junk-filter-name" property. */
+
+	gconf_bridge_bind_property (
+		gconf_bridge_get (),
+		"/apps/evolution/mail/junk/default_plugin",
+		object, "junk-filter-name");
+}
+
+static CamelService *
+mail_session_add_service (CamelSession *session,
+                          const gchar *uid,
+                          const gchar *url_string,
+                          CamelProviderType type,
+                          GError **error)
+{
+	CamelService *service;
+
+	/* Chain up to parents add_service() method. */
+	service = CAMEL_SESSION_CLASS (e_mail_session_parent_class)->
+		add_service (session, uid, url_string, type, error);
+
+	/* Initialize the CamelSettings object from CamelURL parameters.
+	 * This is temporary; soon we'll read settings from key files. */
+
+	if (CAMEL_IS_SERVICE (service)) {
+		CamelSettings *settings;
+		CamelURL *url;
+
+		settings = camel_service_get_settings (service);
+		url = camel_service_get_camel_url (service);
+		camel_settings_load_from_url (settings, url);
+	}
+
+	return service;
+}
+
+static gchar *
+mail_session_get_password (CamelSession *session,
+                           CamelService *service,
+                           const gchar *prompt,
+                           const gchar *item,
+                           guint32 flags,
+                           GError **error)
+{
+	EAccount *account = NULL;
+	const gchar *display_name = NULL;
+	const gchar *uid = NULL;
+	gchar *ret = NULL;
+
+	if (CAMEL_IS_SERVICE (service)) {
+		display_name = camel_service_get_display_name (service);
+		uid = camel_service_get_uid (service);
+		account = e_get_account_by_uid (uid);
+	}
+
+	if (!strcmp(item, "popb4smtp_uid")) {
+		/* not 100% mt safe, but should be ok */
+		ret = g_strdup ((account != NULL) ? account->uid : uid);
+	} else {
+		gchar *key = mail_session_make_key (service, item);
+		EAccountService *config_service = NULL;
+
+		ret = e_passwords_get_password (NULL, key);
+		if (ret == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
+			gboolean remember;
+
+			g_free (ret);
+			ret = NULL;
+
+			if (account != NULL) {
+				if (CAMEL_IS_STORE (service))
+					config_service = account->source;
+				if (CAMEL_IS_TRANSPORT (service))
+					config_service = account->transport;
+			}
+
+			remember = config_service ? config_service->save_passwd : FALSE;
+
+			if (!config_service || (config_service &&
+				!config_service->get_password_canceled)) {
+				guint32 eflags;
+				gchar *title;
+
+				if (flags & CAMEL_SESSION_PASSPHRASE) {
+					if (display_name != NULL)
+						title = g_strdup_printf (
+							_("Enter Passphrase for %s"),
+							display_name);
+					else
+						title = g_strdup (
+							_("Enter Passphrase"));
+				} else {
+					if (display_name != NULL)
+						title = g_strdup_printf (
+							_("Enter Password for %s"),
+							display_name);
+					else
+						title = g_strdup (
+							_("Enter Password"));
+				}
+				if ((flags & CAMEL_SESSION_PASSWORD_STATIC) != 0)
+					eflags = E_PASSWORDS_REMEMBER_NEVER;
+				else if (config_service == NULL)
+					eflags = E_PASSWORDS_REMEMBER_SESSION;
+				else
+					eflags = E_PASSWORDS_REMEMBER_FOREVER;
+
+				if (flags & CAMEL_SESSION_PASSWORD_REPROMPT)
+					eflags |= E_PASSWORDS_REPROMPT;
+
+				if (flags & CAMEL_SESSION_PASSWORD_SECRET)
+					eflags |= E_PASSWORDS_SECRET;
+
+				if (flags & CAMEL_SESSION_PASSPHRASE)
+					eflags |= E_PASSWORDS_PASSPHRASE;
+
+				/* HACK: breaks abstraction ...
+				 * e_account_writable() doesn't use the
+				 * EAccount, it also uses the same writable
+				 * key for source and transport. */
+				if (!e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD))
+					eflags |= E_PASSWORDS_DISABLE_REMEMBER;
+
+				ret = e_passwords_ask_password (
+					title, NULL, key, prompt,
+					eflags, &remember, NULL);
+
+				if (!ret)
+					e_passwords_forget_password (NULL, key);
+
+				g_free (title);
+
+				if (ret && config_service) {
+					config_service->save_passwd = remember;
+					e_account_list_save (e_get_account_list ());
+				}
+
+				if (config_service)
+					config_service->get_password_canceled = ret == NULL;
+			}
+		}
+
+		g_free (key);
+	}
+
+	if (ret == NULL)
+		g_set_error (
+			error, G_IO_ERROR,
+			G_IO_ERROR_CANCELLED,
+			_("User canceled operation."));
+
+	return ret;
+}
+
+static gboolean
+mail_session_forget_password (CamelSession *session,
+                              CamelService *service,
+                              const gchar *item,
+                              GError **error)
+{
+	gchar *key;
+
+	key = mail_session_make_key (service, item);
+
+	e_passwords_forget_password (NULL, key);
+
+	g_free (key);
+
+	return TRUE;
+}
+
+static gboolean
+mail_session_alert_user (CamelSession *session,
+                         CamelSessionAlertType type,
+                         const gchar *prompt,
+                         gboolean cancel)
+{
+	struct _user_message_msg *m;
+	GCancellable *cancellable;
+	gboolean result = TRUE;
+
+	m = mail_msg_new (&user_message_info);
+	m->ismain = mail_in_main_thread ();
+	m->type = type;
+	m->prompt = g_strdup (prompt);
+	m->done = e_flag_new ();
+	m->allow_cancel = cancel;
+
+	if (cancel)
+		mail_msg_ref (m);
+
+	cancellable = m->base.cancellable;
+
+	if (m->ismain)
+		user_message_exec (m, cancellable, &m->base.error);
+	else
+		mail_msg_main_loop_push (m);
+
+	if (cancel) {
+		e_flag_wait (m->done);
+		result = m->result;
+		mail_msg_unref (m);
+	} else if (m->ismain)
+		mail_msg_unref (m);
+
+	return result;
+}
+
+static CamelFilterDriver *
+mail_session_get_filter_driver (CamelSession *session,
+                                const gchar *type,
+                                GError **error)
+{
+	return (CamelFilterDriver *) mail_call_main (
+		MAIL_CALL_p_ppp, (MailMainFunc) main_get_filter_driver,
+		session, type, error);
+}
+
+static gboolean
+mail_session_lookup_addressbook (CamelSession *session,
+                                 const gchar *name)
+{
+	CamelInternetAddress *addr;
+	gboolean ret;
+
+	if (!mail_config_get_lookup_book ())
+		return FALSE;
+
+	addr = camel_internet_address_new ();
+	camel_address_decode ((CamelAddress *) addr, name);
+	ret = em_utils_in_addressbook (
+		addr, mail_config_get_lookup_book_local_only ());
+	g_object_unref (addr);
+
+	return ret;
+}
+
+static gboolean
+mail_session_forward_to (CamelSession *session,
+                         CamelFolder *folder,
+                         CamelMimeMessage *message,
+                         const gchar *address,
+                         GError **error)
+{
+	EAccount *account;
+	CamelMimeMessage *forward;
+	CamelStream *mem;
+	CamelInternetAddress *addr;
+	CamelFolder *out_folder;
+	CamelMessageInfo *info;
+	CamelMedium *medium;
+	const gchar *header_name;
+	struct _camel_header_raw *xev;
+	gchar *subject;
+
+	g_return_val_if_fail (folder != NULL, FALSE);
+	g_return_val_if_fail (message != NULL, FALSE);
+	g_return_val_if_fail (address != NULL, FALSE);
+
+	if (!*address) {
+		g_set_error (
+			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+			_("No destination address provided, forward "
+			  "of the message has been cancelled."));
+		return FALSE;
+	}
+
+	account = em_utils_guess_account_with_recipients (message, folder);
+	if (!account) {
+		g_set_error (
+			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+			_("No account found to use, forward of the "
+			  "message has been cancelled."));
+		return FALSE;
+	}
+
+	forward = camel_mime_message_new ();
+
+	/* make copy of the message, because we are going to modify it */
+	mem = camel_stream_mem_new ();
+	camel_data_wrapper_write_to_stream_sync (
+		CAMEL_DATA_WRAPPER (message), mem, NULL, NULL);
+	g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
+	camel_data_wrapper_construct_from_stream_sync (
+		CAMEL_DATA_WRAPPER (forward), mem, NULL, NULL);
+	g_object_unref (mem);
+
+	/* clear previous recipients */
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_TO, NULL);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_CC, NULL);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_BCC, NULL);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_RESENT_TO, NULL);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_RESENT_CC, NULL);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_RESENT_BCC, NULL);
+
+	medium = CAMEL_MEDIUM (forward);
+
+	/* remove all delivery and notification headers */
+	header_name = "Disposition-Notification-To";
+	while (camel_medium_get_header (medium, header_name))
+		camel_medium_remove_header (medium, header_name);
+
+	header_name = "Delivered-To";
+	while (camel_medium_get_header (medium, header_name))
+		camel_medium_remove_header (medium, header_name);
+
+	/* remove any X-Evolution-* headers that may have been set */
+	xev = mail_tool_remove_xevolution_headers (forward);
+	camel_header_raw_clear (&xev);
+
+	/* from */
+	addr = camel_internet_address_new ();
+	camel_internet_address_add (
+		addr, account->id->name, account->id->address);
+	camel_mime_message_set_from (forward, addr);
+	g_object_unref (addr);
+
+	/* to */
+	addr = camel_internet_address_new ();
+	camel_address_decode (CAMEL_ADDRESS (addr), address);
+	camel_mime_message_set_recipients (
+		forward, CAMEL_RECIPIENT_TYPE_TO, addr);
+	g_object_unref (addr);
+
+	/* subject */
+	subject = mail_tool_generate_forward_subject (message);
+	camel_mime_message_set_subject (forward, subject);
+	g_free (subject);
+
+	/* and send it */
+	info = camel_message_info_new (NULL);
+	out_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+	camel_message_info_set_flags (
+		info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+
+	/* FIXME Pass a GCancellable. */
+	e_mail_folder_append_message (
+		out_folder, forward, info, G_PRIORITY_DEFAULT, NULL,
+		(GAsyncReadyCallback) ms_forward_to_cb, session);
+
+	camel_message_info_free (info);
+
+	return TRUE;
+}
+
+static void
+mail_session_get_socks_proxy (CamelSession *session,
+			      const gchar *for_host,
+			      gchar **host_ret,
+			      gint *port_ret)
+{
+	EMailSession *mail_session;
+	gchar *uri;
+
+	g_return_if_fail (session != NULL);
+	g_return_if_fail (for_host != NULL);
+	g_return_if_fail (host_ret != NULL);
+	g_return_if_fail (port_ret != NULL);
+
+	mail_session = E_MAIL_SESSION (session);
+	g_return_if_fail (mail_session != NULL);
+	g_return_if_fail (mail_session->priv != NULL);
+
+	*host_ret = NULL;
+	*port_ret = 0;
+
+	uri = g_strconcat ("socks://", for_host, NULL);
+
+	if (e_proxy_require_proxy_for_uri (mail_session->priv->proxy, uri)) {
+		SoupURI *suri;
+
+		suri = e_proxy_peek_uri_for (mail_session->priv->proxy, uri);
+		if (suri) {
+			*host_ret = g_strdup (suri->host);
+			*port_ret = suri->port;
+		}
+	}
+
+	g_free (uri);
+}
+
+static void
+e_mail_session_class_init (EMailSessionClass *class)
+{
+	GObjectClass *object_class;
+	CamelSessionClass *session_class;
+
+	g_type_class_add_private (class, sizeof (EMailSessionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = mail_session_set_property;
+	object_class->get_property = mail_session_get_property;
+	object_class->dispose = mail_session_dispose;
+	object_class->finalize = mail_session_finalize;
+	object_class->notify = mail_session_notify;
+	object_class->constructed = mail_session_constructed;
+
+	session_class = CAMEL_SESSION_CLASS (class);
+	session_class->add_service = mail_session_add_service;
+	session_class->get_password = mail_session_get_password;
+	session_class->forget_password = mail_session_forget_password;
+	session_class->alert_user = mail_session_alert_user;
+	session_class->get_filter_driver = mail_session_get_filter_driver;
+	session_class->lookup_addressbook = mail_session_lookup_addressbook;
+	session_class->forward_to = mail_session_forward_to;
+	session_class->get_socks_proxy = mail_session_get_socks_proxy;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FOLDER_CACHE,
+		g_param_spec_object (
+			"folder-cache",
+			NULL,
+			NULL,
+			MAIL_TYPE_FOLDER_CACHE,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
+	/* XXX This property can be removed once Evolution moves to
+	 *     GSettings and can use transform functions when binding
+	 *     properties to settings. */
+	g_object_class_install_property (
+		object_class,
+		PROP_JUNK_FILTER_NAME,
+		g_param_spec_string (
+			"junk-filter-name",
+			NULL,
+			NULL,
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * EMailSession::flush-outbox
+	 * @session: the email session
+	 *
+	 * Emitted if the send folder should be flushed.
+	 **/
+	signals[FLUSH_OUTBOX] = g_signal_new (
+		"flush-outbox",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	/**
+	 * EMailSession::store-added
+	 * @session: the email session
+	 * @store: the CamelStore
+	 *
+	 * Emitted when a store is added
+	 **/
+	signals[STORE_ADDED] = g_signal_new (
+		"store-added",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		CAMEL_TYPE_STORE);
+
+
+	/**
+	 * EMailSession::store-removed
+	 * @session: the email session
+	 * @store: the CamelStore
+	 *
+	 * Emitted when a store is removed 
+	 **/
+	signals[STORE_REMOVED] = g_signal_new (
+		"store-removed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		CAMEL_TYPE_STORE);
+
+
+}
+
+static void
+e_mail_session_init (EMailSession *session)
+{
+	GConfClient *client;
+
+	session->priv = E_MAIL_SESSION_GET_PRIVATE (session);
+	session->priv->folder_cache = mail_folder_cache_new ();
+	session->priv->junk_filters = g_hash_table_new (
+		(GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
+	session->priv->proxy = e_proxy_new ();
+
+	/* Initialize the EAccount setup. */
+	e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD);
+
+	client = gconf_client_get_default ();
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/junk",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+	camel_session_set_check_junk (
+		CAMEL_SESSION (session), gconf_client_get_bool (
+		client, "/apps/evolution/mail/junk/check_incoming", NULL));
+	session_check_junk_notify_id = gconf_client_notify_add (
+		client, "/apps/evolution/mail/junk",
+		(GConfClientNotifyFunc) mail_session_check_junk_notify,
+		session, NULL, NULL);
+
+	mail_config_reload_junk_headers (session);
+
+	e_proxy_setup_proxy (session->priv->proxy);
+
+	g_object_unref (client);
+}
+
+EMailSession *
+e_mail_session_new (void)
+{
+	const gchar *user_data_dir;
+	const gchar *user_cache_dir;
+
+	user_data_dir = mail_session_get_data_dir ();
+	user_cache_dir = mail_session_get_cache_dir ();
+
+	return g_object_new (
+		E_TYPE_MAIL_SESSION,
+		"user-data-dir", user_data_dir,
+		"user-cache-dir", user_cache_dir,
+		NULL);
+}
+
+MailFolderCache *
+e_mail_session_get_folder_cache (EMailSession *session)
+{
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+	return session->priv->folder_cache;
+}
+
+GList *
+e_mail_session_get_available_junk_filters (EMailSession *session)
+{
+	GList *list, *link;
+	GQueue trash = G_QUEUE_INIT;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+	list = g_hash_table_get_values (session->priv->junk_filters);
+
+	/* Discard unavailable junk filters.  (e.g. Junk filter
+	 * requires Bogofilter but Bogofilter is not installed,
+	 * hence the junk filter is unavailable.) */
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		EMailJunkFilter *junk_filter;
+
+		junk_filter = E_MAIL_JUNK_FILTER (link->data);
+		if (!e_mail_junk_filter_available (junk_filter))
+			g_queue_push_tail (&trash, link);
+	}
+
+	while ((link = g_queue_pop_head (&trash)) != NULL)
+		list = g_list_delete_link (list, link);
+
+	/* Sort the remaining junk filters by display name. */
+
+	return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare);
+}
+
+static void
+mail_session_get_inbox_thread (GSimpleAsyncResult *simple,
+                               EMailSession *session,
+                               GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->folder = e_mail_session_get_inbox_sync (
+		session, context->uid, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+CamelFolder *
+e_mail_session_get_inbox_sync (EMailSession *session,
+                               const gchar *service_uid,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	CamelService *service;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+	g_return_val_if_fail (service_uid != NULL, NULL);
+
+	service = camel_session_get_service (
+		CAMEL_SESSION (session), service_uid);
+
+	if (!CAMEL_IS_STORE (service))
+		return NULL;
+
+	if (!em_utils_connect_service_sync (service, cancellable, error))
+		return NULL;
+
+	return camel_store_get_inbox_folder_sync (
+		CAMEL_STORE (service), cancellable, error);
+}
+
+void
+e_mail_session_get_inbox (EMailSession *session,
+                          const gchar *service_uid,
+                          gint io_priority,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (service_uid != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->uid = g_strdup (service_uid);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback,
+		user_data, e_mail_session_get_inbox);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_get_inbox_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_get_inbox_finish (EMailSession *session,
+                                 GAsyncResult *result,
+                                 GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_get_inbox), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+	return g_object_ref (context->folder);
+}
+
+static void
+mail_session_get_trash_thread (GSimpleAsyncResult *simple,
+                               EMailSession *session,
+                               GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->folder = e_mail_session_get_trash_sync (
+		session, context->uid, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+CamelFolder *
+e_mail_session_get_trash_sync (EMailSession *session,
+                               const gchar *service_uid,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	CamelService *service;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+	g_return_val_if_fail (service_uid != NULL, NULL);
+
+	service = camel_session_get_service (
+		CAMEL_SESSION (session), service_uid);
+
+	if (!CAMEL_IS_STORE (service))
+		return NULL;
+
+	if (!em_utils_connect_service_sync (service, cancellable, error))
+		return NULL;
+
+	return camel_store_get_trash_folder_sync (
+		CAMEL_STORE (service), cancellable, error);
+}
+
+void
+e_mail_session_get_trash (EMailSession *session,
+                          const gchar *service_uid,
+                          gint io_priority,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (service_uid != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->uid = g_strdup (service_uid);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback,
+		user_data, e_mail_session_get_trash);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_get_trash_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_get_trash_finish (EMailSession *session,
+                                 GAsyncResult *result,
+                                 GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_get_trash), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+	return g_object_ref (context->folder);
+}
+
+static void
+mail_session_uri_to_folder_thread (GSimpleAsyncResult *simple,
+                                   EMailSession *session,
+                                   GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	context->folder = e_mail_session_uri_to_folder_sync (
+		session, context->uri, context->flags,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+CamelFolder *
+e_mail_session_uri_to_folder_sync (EMailSession *session,
+                                   const gchar *folder_uri,
+                                   CamelStoreGetFolderFlags flags,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+	CamelStore *store;
+	CamelFolder *folder;
+	gchar *folder_name;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+	g_return_val_if_fail (folder_uri != NULL, NULL);
+
+	success = e_mail_folder_uri_parse (
+		CAMEL_SESSION (session), folder_uri,
+		&store, &folder_name, error);
+
+	if (!success)
+		return NULL;
+
+	folder = camel_store_get_folder_sync (
+		store, folder_name, flags, cancellable, error);
+
+	if (folder != NULL) {
+		MailFolderCache *folder_cache;
+		folder_cache = e_mail_session_get_folder_cache (session);
+		mail_folder_cache_note_folder (folder_cache, folder);
+	}
+
+	g_free (folder_name);
+	g_object_unref (store);
+
+	return folder;
+}
+
+void
+e_mail_session_uri_to_folder (EMailSession *session,
+                              const gchar *folder_uri,
+                              CamelStoreGetFolderFlags flags,
+                              gint io_priority,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+	g_return_if_fail (folder_uri != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->uri = g_strdup (folder_uri);
+	context->flags = flags;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (session), callback,
+		user_data, e_mail_session_uri_to_folder);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_session_uri_to_folder_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_uri_to_folder_finish (EMailSession *session,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (session),
+		e_mail_session_uri_to_folder), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+	return g_object_ref (context->folder);
+}
+
+/******************************** Legacy API *********************************/
+
+void
+mail_session_flush_filter_log (EMailSession *session)
+{
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+	if (session->priv->filter_logfile)
+		fflush (session->priv->filter_logfile);
+}
+
+const gchar *
+mail_session_get_data_dir (void)
+{
+	if (G_UNLIKELY (mail_data_dir == NULL))
+		mail_data_dir = g_build_filename (
+			e_get_user_data_dir (), "mail", NULL);
+
+	return mail_data_dir;
+}
+
+const gchar *
+mail_session_get_cache_dir (void)
+{
+	if (G_UNLIKELY (mail_cache_dir == NULL))
+		mail_cache_dir = g_build_filename (
+			e_get_user_cache_dir (), "mail", NULL);
+
+	return mail_cache_dir;
+}
+
+const gchar *
+mail_session_get_config_dir (void)
+{
+	if (G_UNLIKELY (mail_config_dir == NULL))
+		mail_config_dir = g_build_filename (
+			e_get_user_config_dir (), "mail", NULL);
+
+	return mail_config_dir;
+}
+
diff --git a/libemail-engine/e-mail-session.h b/libemail-engine/e-mail-session.h
new file mode 100644
index 0000000..b82c6e9
--- /dev/null
+++ b/libemail-engine/e-mail-session.h
@@ -0,0 +1,124 @@
+/*
+ * e-mail-session.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SESSION_H
+#define E_MAIL_SESSION_H
+
+#include <camel/camel.h>
+#include <libemail-engine/mail-folder-cache.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SESSION \
+	(e_mail_session_get_type ())
+#define E_MAIL_SESSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_SESSION, EMailSession))
+#define E_MAIL_SESSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_SESSION, EMailSessionClass))
+#define E_IS_MAIL_SESSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_SESSION))
+#define E_IS_MAIL_SESSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_SESSION))
+#define E_MAIL_SESSION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_SESSION, EMailSessionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailSession EMailSession;
+typedef struct _EMailSessionClass EMailSessionClass;
+typedef struct _EMailSessionPrivate EMailSessionPrivate;
+
+struct _EMailSession {
+	CamelSession parent;
+	EMailSessionPrivate *priv;
+};
+
+struct _EMailSessionClass {
+	CamelSessionClass parent_class;
+};
+
+GType		e_mail_session_get_type		(void);
+EMailSession *	e_mail_session_new		(void);
+MailFolderCache *
+		e_mail_session_get_folder_cache	(EMailSession *session);
+GList *		e_mail_session_get_available_junk_filters
+						(EMailSession *session);
+CamelFolder *	e_mail_session_get_inbox_sync	(EMailSession *session,
+						 const gchar *service_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_get_inbox	(EMailSession *session,
+						 const gchar *service_uid,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+CamelFolder *	e_mail_session_get_inbox_finish	(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+CamelFolder *	e_mail_session_get_trash_sync	(EMailSession *session,
+						 const gchar *service_uid,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_get_trash	(EMailSession *session,
+						 const gchar *service_uid,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+CamelFolder *	e_mail_session_get_trash_finish	(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+CamelFolder *	e_mail_session_uri_to_folder_sync
+						(EMailSession *session,
+						 const gchar *folder_uri,
+						 CamelStoreGetFolderFlags flags,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_session_uri_to_folder	(EMailSession *session,
+						 const gchar *folder_uri,
+						 CamelStoreGetFolderFlags flags,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+CamelFolder *	e_mail_session_uri_to_folder_finish
+						(EMailSession *session,
+						 GAsyncResult *result,
+						 GError **error);
+
+/*** Legacy API ***/
+
+void		mail_session_flush_filter_log	(EMailSession *session);
+const gchar *	mail_session_get_data_dir	(void);
+const gchar *	mail_session_get_cache_dir	(void);
+const gchar *	mail_session_get_config_dir	(void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SESSION_H */
diff --git a/libemail-engine/e-mail-store-utils.c b/libemail-engine/e-mail-store-utils.c
new file mode 100644
index 0000000..0d31525
--- /dev/null
+++ b/libemail-engine/e-mail-store-utils.c
@@ -0,0 +1,393 @@
+/*
+ * e-mail-store-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-utils.h"
+
+#include "e-mail-store-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	gchar *full_name;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+	g_free (context->full_name);
+
+	g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_store_create_folder_thread (GSimpleAsyncResult *simple,
+                                 GObject *object,
+                                 GCancellable *cancellable)
+{
+	AsyncContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_mail_store_create_folder_sync (
+		CAMEL_STORE (object), context->full_name,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+gboolean
+e_mail_store_create_folder_sync (CamelStore *store,
+                                 const gchar *full_name,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+	CamelFolderInfo *folder_info;
+	gchar *copied_full_name;
+	gchar *display_name;
+	const gchar *parent;
+	gboolean success = TRUE;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
+	g_return_val_if_fail (full_name != NULL, FALSE);
+
+	copied_full_name = g_strdup (full_name);
+	display_name = strrchr (copied_full_name, '/');
+	if (display_name == NULL) {
+		display_name = copied_full_name;
+		parent = "";
+	} else {
+		*display_name++ = '\0';
+		parent = copied_full_name;
+	}
+
+	folder_info = camel_store_create_folder_sync (
+		store, parent, display_name, cancellable, error);
+
+	g_free (copied_full_name);
+
+	if (folder_info == NULL)
+		return FALSE;
+
+	if (CAMEL_IS_SUBSCRIBABLE (store))
+		success = camel_subscribable_subscribe_folder_sync (
+			CAMEL_SUBSCRIBABLE (store),
+			full_name, cancellable, error);
+
+	camel_store_free_folder_info (store, folder_info);
+
+	return success;
+}
+
+void
+e_mail_store_create_folder (CamelStore *store,
+                            const gchar *full_name,
+                            gint io_priority,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *context;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+	g_return_if_fail (full_name != NULL);
+
+	context = g_slice_new0 (AsyncContext);
+	context->full_name = g_strdup (full_name);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (store), callback, user_data,
+		e_mail_store_create_folder);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, mail_store_create_folder_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_create_folder_finish (CamelStore *store,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (store),
+		e_mail_store_create_folder), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_go_offline_thread (GSimpleAsyncResult *simple,
+                              CamelStore *store,
+                              GCancellable *cancellable)
+{
+	CamelService *service;
+	gchar *service_name;
+	GError *error = NULL;
+
+	service = CAMEL_SERVICE (store);
+
+	service_name = camel_service_get_name (service, TRUE);
+	camel_operation_push_message (
+		cancellable, _("Disconnecting from '%s'"), service_name);
+	g_free (service_name);
+
+	if (CAMEL_IS_DISCO_STORE (store)) {
+		CamelDiscoStore *disco_store;
+
+		disco_store = CAMEL_DISCO_STORE (store);
+
+		if (camel_disco_store_can_work_offline (disco_store))
+			camel_disco_store_set_status (
+				disco_store, CAMEL_DISCO_STORE_OFFLINE,
+				cancellable, &error);
+		else
+			em_utils_disconnect_service_sync (service, TRUE, cancellable, &error);
+
+	} else if (CAMEL_IS_OFFLINE_STORE (store)) {
+		CamelOfflineStore *offline_store;
+
+		offline_store = CAMEL_OFFLINE_STORE (store);
+
+		camel_offline_store_set_online_sync (
+			offline_store, FALSE, cancellable, &error);
+
+	} else
+		em_utils_disconnect_service_sync (service, TRUE, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+
+	camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_offline (CamelStore *store,
+                         gint io_priority,
+                         GCancellable *cancellable,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	/* Cancel any pending connect first so the set_offline_op
+	 * thread won't get queued behind a hung connect op. */
+	camel_service_cancel_connect (CAMEL_SERVICE (store));
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (store), callback,
+		user_data, e_mail_store_go_offline);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_store_go_offline_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_offline_finish (CamelStore *store,
+                                GAsyncResult *result,
+                                GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (store), e_mail_store_go_offline), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_go_online_thread (GSimpleAsyncResult *simple,
+                             CamelStore *store,
+                             GCancellable *cancellable)
+{
+	CamelService *service;
+	gchar *service_name;
+	GError *error = NULL;
+
+	service = CAMEL_SERVICE (store);
+
+	service_name = camel_service_get_name (service, TRUE);
+	camel_operation_push_message (
+		cancellable, _("Reconnecting to '%s'"), service_name);
+	g_free (service_name);
+
+	if (CAMEL_IS_DISCO_STORE (store))
+		camel_disco_store_set_status (
+			CAMEL_DISCO_STORE (store),
+			CAMEL_DISCO_STORE_ONLINE,
+			cancellable, &error);
+
+	else if (CAMEL_IS_OFFLINE_STORE (store))
+		camel_offline_store_set_online_sync (
+			CAMEL_OFFLINE_STORE (store),
+			TRUE, cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+
+	camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_online (CamelStore *store,
+                        gint io_priority,
+                        GCancellable *cancellable,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (store), callback,
+		user_data, e_mail_store_go_online);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_store_go_online_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_online_finish (CamelStore *store,
+                               GAsyncResult *result,
+                               GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (store), e_mail_store_go_online), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_prepare_for_offline_thread (GSimpleAsyncResult *simple,
+                                       CamelStore *store,
+                                       GCancellable *cancellable)
+{
+	CamelService *service;
+	gchar *service_name;
+	GError *error = NULL;
+
+	service = CAMEL_SERVICE (store);
+
+	service_name = camel_service_get_name (service, TRUE);
+	camel_operation_push_message (
+		cancellable, _("Preparing account '%s' for offline"),
+		service_name);
+	g_free (service_name);
+
+	if (CAMEL_IS_DISCO_STORE (store))
+		camel_disco_store_prepare_for_offline (
+			CAMEL_DISCO_STORE (store), cancellable, &error);
+
+	else if (CAMEL_IS_OFFLINE_STORE (store))
+		camel_offline_store_prepare_for_offline_sync (
+			CAMEL_OFFLINE_STORE (store), cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+
+	camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_prepare_for_offline (CamelStore *store,
+                                  gint io_priority,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (store), callback, user_data,
+		e_mail_store_prepare_for_offline);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc)
+		mail_store_prepare_for_offline_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_prepare_for_offline_finish (CamelStore *store,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (store),
+		e_mail_store_prepare_for_offline), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/libemail-engine/e-mail-store-utils.h b/libemail-engine/e-mail-store-utils.h
new file mode 100644
index 0000000..de4484c
--- /dev/null
+++ b/libemail-engine/e-mail-store-utils.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-store-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_STORE_UTILS_H
+#define E_MAIL_STORE_UTILS_H
+
+/* CamelStore wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+gboolean	e_mail_store_create_folder_sync	(CamelStore *store,
+						 const gchar *full_name,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_mail_store_create_folder	(CamelStore *store,
+						 const gchar *full_name,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_store_create_folder_finish
+						(CamelStore *store,
+						 GAsyncResult *result,
+						 GError **error);
+
+void		e_mail_store_go_offline		(CamelStore *store,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_store_go_offline_finish	(CamelStore *store,
+						 GAsyncResult *result,
+						 GError **error);
+
+void		e_mail_store_go_online		(CamelStore *store,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_store_go_online_finish	(CamelStore *store,
+						 GAsyncResult *result,
+						 GError **error);
+
+void		e_mail_store_prepare_for_offline
+						(CamelStore *store,
+						 gint io_priority,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_mail_store_prepare_for_offline_finish
+						(CamelStore *store,
+						 GAsyncResult *result,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_STORE_UTILS_H */
diff --git a/libemail-engine/e-mail-store.c b/libemail-engine/e-mail-store.c
new file mode 100644
index 0000000..db5b94e
--- /dev/null
+++ b/libemail-engine/e-mail-store.c
@@ -0,0 +1,464 @@
+/*
+ * e-mail-store.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-store.h"
+
+#include <glib/gi18n.h>
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libedataserver/e-account-list.h>
+
+#include "libemail-utils/e-account-utils.h"
+
+#include "libemail-engine/e-mail-local.h"
+#include "libemail-engine/e-mail-utils.h"
+#include "libemail-engine/mail-folder-cache.h"
+#include "libemail-utils/mail-mt.h"
+#include "libemail-engine/mail-ops.h"
+
+typedef struct _StoreInfo StoreInfo;
+
+typedef void	(*AddStoreCallback)	(MailFolderCache *folder_cache,
+					 CamelStore *store,
+					 CamelFolderInfo *info,
+					 StoreInfo *store_info);
+
+struct _StoreInfo {
+	gint ref_count;
+
+	CamelStore *store;
+
+	/* Hold a reference to keep them alive. */
+	CamelFolder *vtrash;
+	CamelFolder *vjunk;
+
+	AddStoreCallback callback;
+
+	guint removed : 1;
+};
+
+CamelStore *vfolder_store;  /* XXX write a get () function for this */
+static GHashTable *store_table;
+
+static StoreInfo *
+store_info_new (CamelStore *store)
+{
+	StoreInfo *store_info;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+
+	store_info = g_slice_new0 (StoreInfo);
+	store_info->ref_count = 1;
+
+	store_info->store = g_object_ref (store);
+
+	/* If these are vfolders then they need to be opened now,
+	 * otherwise they won't keep track of all folders. */
+	if (store->flags & CAMEL_STORE_VTRASH)
+		store_info->vtrash =
+			camel_store_get_trash_folder_sync (store, NULL, NULL);
+	if (store->flags & CAMEL_STORE_VJUNK)
+		store_info->vjunk =
+			camel_store_get_junk_folder_sync (store, NULL, NULL);
+
+	return store_info;
+}
+
+static StoreInfo *
+store_info_ref (StoreInfo *store_info)
+{
+	g_return_val_if_fail (store_info != NULL, store_info);
+	g_return_val_if_fail (store_info->ref_count > 0, store_info);
+
+	g_atomic_int_inc (&store_info->ref_count);
+
+	return store_info;
+}
+
+static void
+store_info_unref (StoreInfo *store_info)
+{
+	g_return_if_fail (store_info != NULL);
+	g_return_if_fail (store_info->ref_count > 0);
+
+	if (g_atomic_int_dec_and_test (&store_info->ref_count)) {
+
+		g_object_unref (store_info->store);
+
+		if (store_info->vtrash != NULL)
+			g_object_unref (store_info->vtrash);
+
+		if (store_info->vjunk != NULL)
+			g_object_unref (store_info->vjunk);
+
+		g_slice_free (StoreInfo, store_info);
+	}
+}
+
+static void
+store_table_free (StoreInfo *store_info)
+{
+	store_info->removed = 1;
+	store_info_unref (store_info);
+}
+
+static gboolean
+mail_store_note_store_cb (MailFolderCache *folder_cache,
+                          CamelStore *store,
+                          CamelFolderInfo *info,
+                          gpointer user_data)
+{
+	StoreInfo *store_info = user_data;
+
+	if (store_info->callback != NULL)
+		store_info->callback (
+			folder_cache, store, info, store_info);
+
+	if (!store_info->removed) {
+		/* This keeps message counters up-to-date. */
+		if (store_info->vtrash != NULL)
+			mail_folder_cache_note_folder (
+				folder_cache, store_info->vtrash);
+		if (store_info->vjunk != NULL)
+			mail_folder_cache_note_folder (
+				folder_cache, store_info->vjunk);
+	}
+
+	store_info_unref (store_info);
+
+	return TRUE;
+}
+
+static gboolean
+special_mail_store_is_enabled (CamelStore *store)
+{
+	CamelService *service;
+	const gchar *uid, *prop = NULL;
+
+	service = CAMEL_SERVICE (store);
+	g_return_val_if_fail (service, FALSE);
+
+	uid = camel_service_get_uid (service);
+	if (g_strcmp0 (uid, "local") == 0)
+		prop = "mail-enable-local-folders";
+	else if (g_strcmp0 (uid, "vfolder") == 0)
+		prop = "mail-enable-search-folders";
+
+	if (!prop)
+		return TRUE;
+
+
+	/* FIXME: Make this possible*/
+	//shell = e_shell_get_default ();
+	//shell_settings = e_shell_get_shell_settings (shell);
+
+	//return e_shell_settings_get_boolean (shell_settings, prop);
+	return FALSE;
+}
+
+static void
+mail_store_add (EMailSession *session,
+                CamelStore *store,
+                AddStoreCallback callback)
+{
+	MailFolderCache *folder_cache;
+	StoreInfo *store_info;
+
+	g_return_if_fail (store_table != NULL);
+	g_return_if_fail (store != NULL);
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	folder_cache = e_mail_session_get_folder_cache (session);
+
+	store_info = store_info_new (store);
+	store_info->callback = callback;
+
+	g_hash_table_insert (store_table, store, store_info);
+
+	if (special_mail_store_is_enabled (store)) {
+		/* Listen to this in evolution and add to the folder tree model. */
+		g_signal_emit_by_name (session, "store-added", store);
+	}
+
+	mail_folder_cache_note_store (
+		folder_cache, CAMEL_SESSION (session), store, NULL,
+		mail_store_note_store_cb, store_info_ref (store_info));
+}
+
+static void
+mail_store_add_local_done_cb (MailFolderCache *folder_cache,
+                              CamelStore *store,
+                              CamelFolderInfo *info,
+                              StoreInfo *store_info)
+{
+	CamelFolder *folder;
+	gint ii;
+
+	for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
+		folder = e_mail_local_get_folder (ii);
+		if (folder == NULL)
+			continue;
+		mail_folder_cache_note_folder (folder_cache, folder);
+	}
+}
+
+static void
+mail_store_load_accounts (EMailSession *session,
+                          const gchar *data_dir)
+{
+	CamelStore *local_store;
+	EAccountList *account_list;
+	EIterator *iter;
+
+	/* Add the local store. */
+
+	e_mail_local_init (session, data_dir);
+	local_store = e_mail_local_get_store ();
+
+	mail_store_add (
+		session, local_store, (AddStoreCallback)
+		mail_store_add_local_done_cb);
+
+	/* Add mail accounts.. */
+
+	account_list = e_get_account_list ();
+
+	for (iter = e_list_get_iterator ((EList *) account_list);
+	     e_iterator_is_valid (iter); e_iterator_next (iter)) {
+		EAccount *account;
+
+		account = (EAccount *) e_iterator_get (iter);
+
+		if (!account->enabled)
+			continue;
+
+		e_mail_store_add_by_account (session, account);
+	}
+
+	g_object_unref (iter);
+}
+
+void
+e_mail_store_init (EMailSession *session,
+                   const gchar *data_dir)
+{
+	static gboolean initialized = FALSE;
+
+	/* This function is idempotent because mail
+	 * migration code may need to call it early. */
+	if (initialized)
+		return;
+
+	/* Initialize global variables. */
+
+	store_table = g_hash_table_new_full (
+		g_direct_hash, g_direct_equal,
+		(GDestroyNotify) NULL,
+		(GDestroyNotify) store_table_free);
+
+	mail_store_load_accounts (session, data_dir);
+
+	initialized = TRUE;
+}
+
+void
+e_mail_store_add (EMailSession *session,
+                  CamelStore *store)
+{
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	mail_store_add (session, store, NULL);
+}
+
+CamelStore *
+e_mail_store_add_by_account (EMailSession *session,
+                             EAccount *account)
+{
+	CamelService *service = NULL;
+	CamelProvider *provider = NULL;
+	CamelURL *url;
+	gboolean skip = FALSE, transport_only;
+	GError *error = NULL;
+
+	g_return_val_if_fail (E_IS_ACCOUNT (account), NULL);
+
+	/* check whether it's transport-only accounts */
+	transport_only = !account->source || !account->source->url || !*account->source->url;
+	if (transport_only)
+		goto handle_transport;
+
+	/* Load the service, but don't connect.  Check its provider,
+	 * and if this belongs in the folder tree model, add it. */
+
+	provider = camel_provider_get (account->source->url, &error);
+	if (provider == NULL) {
+	/* In case we do not have a provider here, we handle
+	 * the special case of having multiple mail identities
+	 * eg. a dummy account having just SMTP server defined */
+		goto handle_transport;
+	}
+
+	service = camel_session_add_service (
+		CAMEL_SESSION (session),
+		account->uid, account->source->url,
+		CAMEL_PROVIDER_STORE, &error);
+
+	camel_service_set_display_name (service, account->name);
+
+handle_transport:
+
+	if (account->transport) {
+		/* While we're at it, add the account's transport to the
+		 * CamelSession.  The transport's UID is a kludge for now.
+		 * We take the EAccount's UID and tack on "-transport". */
+		gchar *transport_uid;
+		GError *transport_error = NULL;
+
+		transport_uid = g_strconcat (
+			account->uid, "-transport", NULL);
+
+		camel_session_add_service (
+			CAMEL_SESSION (session),
+			transport_uid, account->transport->url,
+			CAMEL_PROVIDER_TRANSPORT, &transport_error);
+
+		g_free (transport_uid);
+
+		if (transport_error) {
+			g_warning (
+				"%s: Failed to add transport service: %s",
+				G_STRFUNC, transport_error->message);
+			g_error_free (transport_error);
+		}
+	}
+
+	if (transport_only)
+		return NULL;
+
+	if (!CAMEL_IS_STORE (service))
+		goto fail;
+
+	/* Do not add local-delivery files,
+	 * but make them ready for later use. */
+	url = camel_url_new (account->source->url, NULL);
+	if (url != NULL) {
+		skip = em_utils_is_local_delivery_mbox_file (url);
+		camel_url_free (url);
+	}
+
+	if (!skip && (provider->flags & CAMEL_PROVIDER_IS_STORAGE))
+		e_mail_store_add (session, CAMEL_STORE (service));
+
+	return CAMEL_STORE (service);
+
+fail:
+	/* FIXME: Show an error dialog. */
+	g_warning (
+		"Couldn't get service: %s: %s", account->name,
+		error ? error->message : "Not a CamelStore");
+	if (error)
+		g_error_free (error);
+
+	return NULL;
+}
+
+void
+e_mail_store_remove (EMailSession *session,
+                     CamelStore *store)
+{
+	MailFolderCache *folder_cache;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+	g_return_if_fail (store_table != NULL);
+
+	/* Because the store table holds a reference to each store used
+	 * as a key in it, none of them will ever be gc'ed, meaning any
+	 * call to camel_session_get_{service,store} with the same URL
+	 * will always return the same object.  So this works. */
+
+	if (g_hash_table_lookup (store_table, store) == NULL)
+		return;
+
+	g_object_ref (store);
+
+	g_hash_table_remove (store_table, store);
+
+	folder_cache = e_mail_session_get_folder_cache (session);
+	mail_folder_cache_note_store_remove (folder_cache, store);
+
+	g_signal_emit_by_name (session, "store-removed", store);
+
+	mail_disconnect_store (store);
+
+	g_object_unref (store);
+}
+
+void
+e_mail_store_remove_by_account (EMailSession *session,
+                                EAccount *account)
+{
+	CamelService *service;
+	CamelProvider *provider;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_ACCOUNT (account));
+
+	uid = account->uid;
+
+	service = camel_session_get_service (CAMEL_SESSION (session), uid);
+	g_return_if_fail (CAMEL_IS_STORE (service));
+
+	provider = camel_service_get_provider (service);
+	g_return_if_fail (provider != NULL);
+
+	if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE))
+		return;
+
+	e_mail_store_remove (session, CAMEL_STORE (service));
+}
+
+void
+e_mail_store_foreach (EMailSession *session,
+                      GFunc func,
+                      gpointer user_data)
+{
+	GList *list, *link;
+
+	/* XXX This is a silly convenience function.
+	 *     Could probably just get rid of it. */
+
+	g_return_if_fail (func != NULL);
+
+	list = camel_session_list_services (CAMEL_SESSION (session));
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		CamelService *service = CAMEL_SERVICE (link->data);
+
+		if (CAMEL_IS_STORE (service))
+			func (service, user_data);
+	}
+
+	g_list_free (list);
+}
diff --git a/libemail-engine/e-mail-store.h b/libemail-engine/e-mail-store.h
new file mode 100644
index 0000000..a60c252
--- /dev/null
+++ b/libemail-engine/e-mail-store.h
@@ -0,0 +1,47 @@
+/*
+ * e-mail-store.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_STORE_H
+#define E_MAIL_STORE_H
+
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+void		e_mail_store_init		(EMailSession *session,
+						 const gchar *data_dir);
+void		e_mail_store_add		(EMailSession *session,
+						 CamelStore *store);
+CamelStore *	e_mail_store_add_by_account	(EMailSession *session,
+						 EAccount *account);
+void		e_mail_store_remove		(EMailSession *session,
+						 CamelStore *store);
+void		e_mail_store_remove_by_account	(EMailSession *session,
+						 EAccount *account);
+void		e_mail_store_foreach		(EMailSession *session,
+						 GFunc func,
+						 gpointer user_data);
+
+G_END_DECLS
+
+#endif /* E_MAIL_STORE_H */
diff --git a/libemail-engine/e-mail-utils.c b/libemail-engine/e-mail-utils.c
new file mode 100644
index 0000000..27f492a
--- /dev/null
+++ b/libemail-engine/e-mail-utils.c
@@ -0,0 +1,1109 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Srinivasa Ragavan <sragavan gnome org>
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+/* Work around namespace clobbage in <windows.h> */
+#define DATADIR windows_DATADIR
+#include <windows.h>
+#undef DATADIR
+#undef interface
+#endif
+
+#include <libebook/e-book-client.h>
+#include <libebook/e-book-query.h>
+
+
+#include <glib/gi18n.h>
+
+#include <gio/gio.h>
+
+#include "libemail-utils/mail-mt.h"
+#include "mail-tools.h"
+#include "e-mail-folder-utils.h"
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-flag.h>
+#include <libedataserver/e-proxy.h>
+#include "libemail-utils/e-account-utils.h"
+
+#include "e-mail-utils.h"
+#include "e-mail-local.h"
+
+#define d(x)
+
+/**
+ * em_utils_folder_is_templates:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Templates folder.
+ *
+ * Returns %TRUE if this is a Templates folder or %FALSE otherwise.
+ **/
+
+gboolean
+em_utils_folder_is_templates (CamelFolder *folder)
+{
+	CamelFolder *local_templates_folder;
+	CamelSession *session;
+	CamelStore *store;
+	EAccountList *account_list;
+	EIterator *iterator;
+	gchar *folder_uri;
+	gboolean is_templates = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+	local_templates_folder =
+		e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_TEMPLATES);
+
+	if (folder == local_templates_folder)
+		return TRUE;
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+
+	store = camel_folder_get_parent_store (folder);
+	session = camel_service_get_session (CAMEL_SERVICE (store));
+
+	account_list = e_get_account_list ();
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (!is_templates && e_iterator_is_valid (iterator)) {
+		EAccount *account;
+
+		/* XXX EIterator misuses const. */
+		account = (EAccount *) e_iterator_get (iterator);
+
+		if (account->templates_folder_uri != NULL)
+			is_templates = e_mail_folder_uri_equal (
+				session, folder_uri,
+				account->templates_folder_uri);
+
+		e_iterator_next (iterator);
+	}
+
+	g_object_unref (iterator);
+	g_free (folder_uri);
+
+	return is_templates;
+}
+
+/**
+ * em_utils_folder_is_drafts:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Drafts folder.
+ *
+ * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_drafts (CamelFolder *folder)
+{
+	CamelFolder *local_drafts_folder;
+	CamelSession *session;
+	CamelStore *store;
+	EAccountList *account_list;
+	EIterator *iterator;
+	gchar *folder_uri;
+	gboolean is_drafts = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+	local_drafts_folder =
+		e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_DRAFTS);
+
+	if (folder == local_drafts_folder)
+		return TRUE;
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+
+	store = camel_folder_get_parent_store (folder);
+	session = camel_service_get_session (CAMEL_SERVICE (store));
+
+	account_list = e_get_account_list ();
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (!is_drafts && e_iterator_is_valid (iterator)) {
+		EAccount *account;
+
+		/* XXX EIterator misuses const. */
+		account = (EAccount *) e_iterator_get (iterator);
+
+		if (account->drafts_folder_uri != NULL)
+			is_drafts = e_mail_folder_uri_equal (
+				session, folder_uri,
+				account->drafts_folder_uri);
+
+		e_iterator_next (iterator);
+	}
+
+	g_object_unref (iterator);
+	g_free (folder_uri);
+
+	return is_drafts;
+}
+
+/**
+ * em_utils_folder_is_sent:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Sent folder.
+ *
+ * Returns %TRUE if this is a Sent folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_sent (CamelFolder *folder)
+{
+	CamelFolder *local_sent_folder;
+	CamelSession *session;
+	CamelStore *store;
+	EAccountList *account_list;
+	EIterator *iterator;
+	gchar *folder_uri;
+	gboolean is_sent = FALSE;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+	local_sent_folder =
+		e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+	if (folder == local_sent_folder)
+		return TRUE;
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+
+	store = camel_folder_get_parent_store (folder);
+	session = camel_service_get_session (CAMEL_SERVICE (store));
+
+	account_list = e_get_account_list ();
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (!is_sent && e_iterator_is_valid (iterator)) {
+		EAccount *account;
+
+		/* XXX EIterator misuses const. */
+		account = (EAccount *) e_iterator_get (iterator);
+
+		if (account->sent_folder_uri != NULL)
+			is_sent = e_mail_folder_uri_equal (
+				session, folder_uri,
+				account->sent_folder_uri);
+
+		e_iterator_next (iterator);
+	}
+
+	g_object_unref (iterator);
+	g_free (folder_uri);
+
+	return is_sent;
+}
+
+/**
+ * em_utils_folder_is_outbox:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is an Outbox folder.
+ *
+ * Returns %TRUE if this is an Outbox folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_outbox (CamelFolder *folder)
+{
+	CamelFolder *local_outbox_folder;
+
+	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+	local_outbox_folder =
+		e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+
+	return (folder == local_outbox_folder);
+}
+
+
+/* ********************************************************************** */
+
+/* runs sync, in main thread */
+static gpointer
+emu_addr_setup (gpointer user_data)
+{
+	GError *err = NULL;
+	ESourceList **psource_list = user_data;
+
+	if (!e_book_client_get_sources (psource_list, &err))
+		g_error_free (err);
+
+	return NULL;
+}
+
+static void
+emu_addr_cancel_stop (gpointer data)
+{
+	gboolean *stop = data;
+
+	g_return_if_fail (stop != NULL);
+
+	*stop = TRUE;
+}
+
+static void
+emu_addr_cancel_cancellable (gpointer data)
+{
+	GCancellable *cancellable = data;
+
+	g_return_if_fail (cancellable != NULL);
+
+	g_cancellable_cancel (cancellable);
+}
+
+struct TryOpenEBookStruct {
+	GError **error;
+	EFlag *flag;
+	gboolean result;
+};
+
+static void
+try_open_book_client_cb (GObject *source_object,
+                         GAsyncResult *result,
+                         gpointer closure)
+{
+	EBookClient *book_client = E_BOOK_CLIENT (source_object);
+	struct TryOpenEBookStruct *data = (struct TryOpenEBookStruct *) closure;
+	GError *error = NULL;
+
+	if (!data)
+		return;
+
+	e_client_open_finish (E_CLIENT (book_client), result, &error);
+
+	data->result = error == NULL;
+
+	if (!data->result) {
+		g_clear_error (data->error);
+		g_propagate_error (data->error, error);
+	}
+
+	e_flag_set (data->flag);
+}
+
+/*
+ * try_open_book_client:
+ * Tries to open address book asynchronously, but acts as synchronous.
+ * The advantage is it checks periodically whether the camel_operation
+ * has been canceled or not, and if so, then stops immediately, with
+ * result FALSE. Otherwise returns same as e_client_open()
+ */
+static gboolean
+try_open_book_client (EBookClient *book_client,
+                      gboolean only_if_exists,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+	struct TryOpenEBookStruct data;
+	gboolean canceled = FALSE;
+	EFlag *flag = e_flag_new ();
+
+	data.error = error;
+	data.flag = flag;
+	data.result = FALSE;
+
+	e_client_open (
+		E_CLIENT (book_client), only_if_exists,
+		cancellable, try_open_book_client_cb, &data);
+
+	while (canceled = camel_operation_cancel_check (NULL),
+			!canceled && !e_flag_is_set (flag)) {
+		GTimeVal wait;
+
+		g_get_current_time (&wait);
+		g_time_val_add (&wait, 250000); /* waits 250ms */
+
+		e_flag_timed_wait (flag, &wait);
+	}
+
+	if (canceled) {
+		g_cancellable_cancel (cancellable);
+
+		g_clear_error (error);
+		g_propagate_error (
+			error, e_client_error_create (
+			E_CLIENT_ERROR_CANCELLED, NULL));
+	}
+
+	e_flag_wait (flag);
+	e_flag_free (flag);
+
+	return data.result && (!error || !*error);
+}
+
+#define NOT_FOUND_BOOK (GINT_TO_POINTER (1))
+
+G_LOCK_DEFINE_STATIC (contact_cache);
+
+/* key is lowercased contact email; value is EBook pointer
+ * (just for comparison) where it comes from */
+static GHashTable *contact_cache = NULL;
+
+/* key is source ID; value is pointer to EBook */
+static GHashTable *emu_books_hash = NULL;
+
+/* key is source ID; value is same pointer as key; this is hash of
+ * broken books, which failed to open for some reason */
+static GHashTable *emu_broken_books_hash = NULL;
+
+static ESourceList *emu_books_source_list = NULL;
+
+static gboolean
+search_address_in_addressbooks (const gchar *address,
+                                gboolean local_only,
+                                gboolean (*check_contact) (EContact *contact,
+                                                           gpointer user_data),
+                                gpointer user_data)
+{
+	gboolean found = FALSE, stop = FALSE, found_any = FALSE;
+	gchar *lowercase_addr;
+	gpointer ptr;
+	EBookQuery *book_query;
+	gchar *query;
+	GSList *s, *g, *addr_sources = NULL;
+	GHook *hook_cancellable;
+	GCancellable *cancellable;
+
+	if (!address || !*address)
+		return FALSE;
+
+	G_LOCK (contact_cache);
+
+	if (!emu_books_source_list) {
+		mail_call_main (
+			MAIL_CALL_p_p, (MailMainFunc)
+			emu_addr_setup, &emu_books_source_list);
+		emu_books_hash = g_hash_table_new_full (
+			g_str_hash, g_str_equal, g_free, g_object_unref);
+		emu_broken_books_hash = g_hash_table_new_full (
+			g_str_hash, g_str_equal, g_free, NULL);
+		contact_cache = g_hash_table_new_full (
+			g_str_hash, g_str_equal, g_free, NULL);
+	}
+
+	if (!emu_books_source_list) {
+		G_UNLOCK (contact_cache);
+		return FALSE;
+	}
+
+	lowercase_addr = g_utf8_strdown (address, -1);
+	ptr = g_hash_table_lookup (contact_cache, lowercase_addr);
+	if (ptr != NULL && (check_contact == NULL || ptr == NOT_FOUND_BOOK)) {
+		g_free (lowercase_addr);
+		G_UNLOCK (contact_cache);
+		return ptr != NOT_FOUND_BOOK;
+	}
+
+	book_query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, address);
+	query = e_book_query_to_string (book_query);
+	e_book_query_unref (book_query);
+
+	for (g = e_source_list_peek_groups (emu_books_source_list);
+			g; g = g_slist_next (g)) {
+		ESourceGroup *group = g->data;
+
+		if (!group)
+			continue;
+
+		if (local_only && !(e_source_group_peek_base_uri (group) &&
+			g_str_has_prefix (
+			e_source_group_peek_base_uri (group), "local:")))
+			continue;
+
+		for (s = e_source_group_peek_sources (group); s; s = g_slist_next (s)) {
+			ESource *source = s->data;
+			const gchar *completion = e_source_get_property (source, "completion");
+
+			if (completion && g_ascii_strcasecmp (completion, "true") == 0) {
+				addr_sources = g_slist_prepend (addr_sources, g_object_ref (source));
+			}
+		}
+	}
+
+	cancellable = g_cancellable_new ();
+	hook_cancellable = mail_cancel_hook_add (emu_addr_cancel_cancellable, cancellable);
+
+	for (s = addr_sources; !stop && !found && s; s = g_slist_next (s)) {
+		ESource *source = s->data;
+		GSList *contacts;
+		EBookClient *book_client = NULL;
+		GHook *hook_stop;
+		gboolean cached_book = FALSE;
+		GError *err = NULL;
+
+		/* failed to load this book last time, skip it now */
+		if (g_hash_table_lookup (emu_broken_books_hash,
+				e_source_peek_uid (source)) != NULL) {
+			d(printf ("%s: skipping broken book '%s'\n",
+				G_STRFUNC, e_source_peek_name (source)));
+			continue;
+		}
+
+		d(printf(" checking '%s'\n", e_source_get_uri(source)));
+
+		hook_stop = mail_cancel_hook_add (emu_addr_cancel_stop, &stop);
+
+		book_client = g_hash_table_lookup (emu_books_hash, e_source_peek_uid (source));
+		if (!book_client) {
+			book_client = e_book_client_new (source, &err);
+
+			if (book_client == NULL) {
+				if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+				    g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+					stop = TRUE;
+				} else if (err) {
+					gchar *source_uid;
+
+					source_uid = g_strdup (
+						e_source_peek_uid (source));
+
+					g_hash_table_insert (
+						emu_broken_books_hash,
+						source_uid, source_uid);
+
+					g_warning (
+						"%s: Unable to create addressbook '%s': %s",
+						G_STRFUNC,
+						e_source_peek_name (source),
+						err->message);
+				}
+				g_clear_error (&err);
+			} else if (!stop && !try_open_book_client (book_client, TRUE, cancellable, &err)) {
+				g_object_unref (book_client);
+				book_client = NULL;
+
+				if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+				    g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+					stop = TRUE;
+				} else if (err) {
+					gchar *source_uid;
+
+					source_uid = g_strdup (
+						e_source_peek_uid (source));
+
+					g_hash_table_insert (
+						emu_broken_books_hash,
+						source_uid, source_uid);
+
+					g_warning (
+						"%s: Unable to open addressbook '%s': %s",
+						G_STRFUNC,
+						e_source_peek_name (source),
+						err->message);
+				}
+				g_clear_error (&err);
+			}
+		} else {
+			cached_book = TRUE;
+		}
+
+		if (book_client && !stop && e_book_client_get_contacts_sync (book_client, query, &contacts, cancellable, &err)) {
+			if (contacts != NULL) {
+				if (!found_any) {
+					g_hash_table_insert (contact_cache, g_strdup (lowercase_addr), book_client);
+				}
+				found_any = TRUE;
+
+				if (check_contact) {
+					GSList *l;
+
+					for (l = contacts; l && !found; l = l->next) {
+						EContact *contact = l->data;
+
+						found = check_contact (contact, user_data);
+					}
+				} else {
+					found = TRUE;
+				}
+
+				g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
+				g_slist_free (contacts);
+			}
+		} else if (book_client) {
+			stop = stop || (err &&
+			    (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+			     g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)));
+			if (err && !stop) {
+				gchar *source_uid = g_strdup (e_source_peek_uid (source));
+
+				g_hash_table_insert (emu_broken_books_hash, source_uid, source_uid);
+
+				g_warning (
+					"%s: Can't get contacts from '%s': %s",
+					G_STRFUNC, e_source_peek_name (source),
+					err->message);
+			}
+			g_clear_error (&err);
+		}
+
+		mail_cancel_hook_remove (hook_stop);
+
+		stop = stop || camel_operation_cancel_check (NULL);
+
+		if (stop && !cached_book && book_client) {
+			g_object_unref (book_client);
+		} else if (!stop && book_client && !cached_book) {
+			g_hash_table_insert (
+				emu_books_hash, g_strdup (
+				e_source_peek_uid (source)), book_client);
+		}
+	}
+
+	mail_cancel_hook_remove (hook_cancellable);
+	g_object_unref (cancellable);
+
+	g_slist_foreach (addr_sources, (GFunc) g_object_unref, NULL);
+	g_slist_free (addr_sources);
+
+	g_free (query);
+
+	if (!found_any) {
+		g_hash_table_insert (contact_cache, lowercase_addr, NOT_FOUND_BOOK);
+		lowercase_addr = NULL;
+	}
+
+	G_UNLOCK (contact_cache);
+
+	g_free (lowercase_addr);
+
+	return found_any;
+}
+
+gboolean
+em_utils_in_addressbook (CamelInternetAddress *iaddr,
+                         gboolean local_only)
+{
+	const gchar *addr;
+
+	/* TODO: check all addresses? */
+	if (iaddr == NULL || !camel_internet_address_get (iaddr, 0, NULL, &addr))
+		return FALSE;
+
+	return search_address_in_addressbooks (addr, local_only, NULL, NULL);
+}
+
+static gboolean
+extract_photo_data (EContact *contact,
+                    gpointer user_data)
+{
+	EContactPhoto **photo = user_data;
+
+	g_return_val_if_fail (contact != NULL, FALSE);
+	g_return_val_if_fail (user_data != NULL, FALSE);
+
+	*photo = e_contact_get (contact, E_CONTACT_PHOTO);
+	if (!*photo)
+		*photo = e_contact_get (contact, E_CONTACT_LOGO);
+
+	return *photo != NULL;
+}
+
+typedef struct _PhotoInfo {
+	gchar *address;
+	EContactPhoto *photo;
+} PhotoInfo;
+
+static void
+emu_free_photo_info (PhotoInfo *pi)
+{
+	if (!pi)
+		return;
+
+	if (pi->address)
+		g_free (pi->address);
+	if (pi->photo)
+		e_contact_photo_free (pi->photo);
+	g_free (pi);
+}
+
+G_LOCK_DEFINE_STATIC (photos_cache);
+static GSList *photos_cache = NULL; /* list of PhotoInfo-s */
+
+CamelMimePart *
+em_utils_contact_photo (CamelInternetAddress *cia,
+                        gboolean local_only)
+{
+	const gchar *addr = NULL;
+	CamelMimePart *part = NULL;
+	EContactPhoto *photo = NULL;
+	GSList *p, *first_not_null = NULL;
+	gint count_not_null = 0;
+
+	if (cia == NULL || !camel_internet_address_get (cia, 0, NULL, &addr) || !addr) {
+		return NULL;
+	}
+
+	G_LOCK (photos_cache);
+
+	/* search a cache first */
+	for (p = photos_cache; p; p = p->next) {
+		PhotoInfo *pi = p->data;
+
+		if (!pi)
+			continue;
+
+		if (pi->photo) {
+			if (!first_not_null)
+				first_not_null = p;
+			count_not_null++;
+		}
+
+		if (g_ascii_strcasecmp (addr, pi->address) == 0) {
+			photo = pi->photo;
+			break;
+		}
+	}
+
+	/* !p means the address had not been found in the cache */
+	if (!p && search_address_in_addressbooks (
+			addr, local_only, extract_photo_data, &photo)) {
+		PhotoInfo *pi;
+
+		if (photo && photo->type != E_CONTACT_PHOTO_TYPE_INLINED) {
+			e_contact_photo_free (photo);
+			photo = NULL;
+		}
+
+		/* keep only up to 10 photos in memory */
+		if (photo && count_not_null >= 10 && first_not_null) {
+			pi = first_not_null->data;
+
+			photos_cache = g_slist_remove (photos_cache, pi);
+
+			emu_free_photo_info (pi);
+		}
+
+		pi = g_new0 (PhotoInfo, 1);
+		pi->address = g_strdup (addr);
+		pi->photo = photo;
+
+		photos_cache = g_slist_append (photos_cache, pi);
+	}
+
+	/* some photo found, use it */
+	if (photo) {
+		/* Form a mime part out of the photo */
+		part = camel_mime_part_new ();
+		camel_mime_part_set_content (part,
+				    (const gchar *) photo->data.inlined.data,
+				    photo->data.inlined.length, "image/jpeg");
+	}
+
+	G_UNLOCK (photos_cache);
+
+	return part;
+}
+
+/* list of email addresses (strings) to remove from local cache of photos and
+ * contacts, but only if the photo doesn't exist or is an not-found contact */
+void
+emu_remove_from_mail_cache (const GSList *addresses)
+{
+	const GSList *a;
+	GSList *p;
+	CamelInternetAddress *cia;
+
+	cia = camel_internet_address_new ();
+
+	for (a = addresses; a; a = a->next) {
+		const gchar *addr = NULL;
+
+		if (!a->data)
+			continue;
+
+		if (camel_address_decode ((CamelAddress *) cia, a->data) != -1 &&
+		    camel_internet_address_get (cia, 0, NULL, &addr) && addr) {
+			gchar *lowercase_addr = g_utf8_strdown (addr, -1);
+
+			G_LOCK (contact_cache);
+			if (g_hash_table_lookup (contact_cache, lowercase_addr) == NOT_FOUND_BOOK)
+				g_hash_table_remove (contact_cache, lowercase_addr);
+			G_UNLOCK (contact_cache);
+
+			g_free (lowercase_addr);
+
+			G_LOCK (photos_cache);
+			for (p = photos_cache; p; p = p->next) {
+				PhotoInfo *pi = p->data;
+
+				if (pi && !pi->photo && g_ascii_strcasecmp (pi->address, addr) == 0) {
+					photos_cache = g_slist_remove (photos_cache, pi);
+					emu_free_photo_info (pi);
+					break;
+				}
+			}
+			G_UNLOCK (photos_cache);
+		}
+	}
+
+	g_object_unref (cia);
+}
+
+void
+emu_remove_from_mail_cache_1 (const gchar *address)
+{
+	GSList *l;
+
+	g_return_if_fail (address != NULL);
+
+	l = g_slist_append (NULL, (gpointer) address);
+
+	emu_remove_from_mail_cache (l);
+
+	g_slist_free (l);
+}
+
+/* frees all data created by call of em_utils_in_addressbook() or
+ * em_utils_contact_photo() */
+void
+emu_free_mail_cache (void)
+{
+	G_LOCK (contact_cache);
+
+	if (emu_books_hash) {
+		g_hash_table_destroy (emu_books_hash);
+		emu_books_hash = NULL;
+	}
+
+	if (emu_broken_books_hash) {
+		g_hash_table_destroy (emu_broken_books_hash);
+		emu_broken_books_hash = NULL;
+	}
+
+	if (emu_books_source_list) {
+		g_object_unref (emu_books_source_list);
+		emu_books_source_list = NULL;
+	}
+
+	if (contact_cache) {
+		g_hash_table_destroy (contact_cache);
+		contact_cache = NULL;
+	}
+
+	G_UNLOCK (contact_cache);
+
+	G_LOCK (photos_cache);
+
+	g_slist_foreach (photos_cache, (GFunc) emu_free_photo_info, NULL);
+	g_slist_free (photos_cache);
+	photos_cache = NULL;
+
+	G_UNLOCK (photos_cache);
+}
+
+static EAccount *
+guess_account_from_folder (CamelFolder *folder)
+{
+	CamelStore *store;
+	const gchar *uid;
+
+	store = camel_folder_get_parent_store (folder);
+	uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+	return e_get_account_by_uid (uid);
+}
+
+static EAccount *
+guess_account_from_message (CamelMimeMessage *message)
+{
+	const gchar *uid;
+
+	uid = camel_mime_message_get_source (message);
+
+	return (uid != NULL) ? e_get_account_by_uid (uid) : NULL;
+}
+
+GHashTable *
+em_utils_generate_account_hash (void)
+{
+	GHashTable *account_hash;
+	EAccount *account, *def;
+	EAccountList *account_list;
+	EIterator *iterator;
+
+	account_list = e_get_account_list ();
+	account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+
+	def = e_get_default_account ();
+
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (e_iterator_is_valid (iterator)) {
+		account = (EAccount *) e_iterator_get (iterator);
+
+		if (account->id->address) {
+			EAccount *acnt;
+
+			/* Accounts with identical email addresses that are
+			 * enabled take precedence over the accounts that
+			 * aren't. If all accounts with matching email
+			 * addresses are disabled, then the first one in
+			 * the list takes precedence. The default account
+			 * always takes precedence no matter what. */
+			acnt = g_hash_table_lookup (
+				account_hash, account->id->address);
+			if (acnt && acnt != def && !acnt->enabled && account->enabled) {
+				g_hash_table_remove (
+					account_hash, acnt->id->address);
+				acnt = NULL;
+			}
+
+			if (!acnt)
+				g_hash_table_insert (
+					account_hash, (gchar *)
+					account->id->address,
+					(gpointer) account);
+		}
+
+		e_iterator_next (iterator);
+	}
+
+	g_object_unref (iterator);
+
+	/* The default account has to be there if none
+	 * of the enabled accounts are present. */
+	if (g_hash_table_size (account_hash) == 0 && def && def->id->address)
+		g_hash_table_insert (
+			account_hash, (gchar *)
+			def->id->address,
+			(gpointer) def);
+
+	return account_hash;
+}
+
+EAccount *
+em_utils_guess_account (CamelMimeMessage *message,
+                        CamelFolder *folder)
+{
+	EAccount *account = NULL;
+
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+	if (folder != NULL)
+		g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+	/* check for newsgroup header */
+	if (folder != NULL
+	    && camel_medium_get_header (CAMEL_MEDIUM (message), "Newsgroups"))
+		account = guess_account_from_folder (folder);
+
+	/* check for source folder */
+	if (account == NULL && folder != NULL)
+		account = guess_account_from_folder (folder);
+
+	/* then message source */
+	if (account == NULL)
+		account = guess_account_from_message (message);
+
+	return account;
+}
+
+EAccount *
+em_utils_guess_account_with_recipients (CamelMimeMessage *message,
+                                        CamelFolder *folder)
+{
+	EAccount *account = NULL;
+	EAccountList *account_list;
+	GHashTable *recipients;
+	EIterator *iterator;
+	CamelInternetAddress *addr;
+	const gchar *type;
+	const gchar *key;
+
+	/* This policy is subject to debate and tweaking,
+	 * but please also document the rational here. */
+
+	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+	/* Build a set of email addresses in which to test for membership.
+	 * Only the keys matter here; the values just need to be non-NULL. */
+	recipients = g_hash_table_new (g_str_hash, g_str_equal);
+
+	type = CAMEL_RECIPIENT_TYPE_TO;
+	addr = camel_mime_message_get_recipients (message, type);
+	if (addr != NULL) {
+		gint index = 0;
+
+		while (camel_internet_address_get (addr, index++, NULL, &key))
+			g_hash_table_insert (
+				recipients, (gpointer) key,
+				GINT_TO_POINTER (1));
+	}
+
+	type = CAMEL_RECIPIENT_TYPE_CC;
+	addr = camel_mime_message_get_recipients (message, type);
+	if (addr != NULL) {
+		gint index = 0;
+
+		while (camel_internet_address_get (addr, index++, NULL, &key))
+			g_hash_table_insert (
+				recipients, (gpointer) key,
+				GINT_TO_POINTER (1));
+	}
+
+	/* First Preference: We were given a folder that maps to an
+	 * enabled account, and that account's email address appears
+	 * in the list of To: or Cc: recipients. */
+
+	if (folder != NULL)
+		account = guess_account_from_folder (folder);
+
+	if (account == NULL || !account->enabled)
+		goto second_preference;
+
+	if ((key = account->id->address) == NULL)
+		goto second_preference;
+
+	if (g_hash_table_lookup (recipients, key) != NULL)
+		goto exit;
+
+second_preference:
+
+	/* Second Preference: Choose any enabled account whose email
+	 * address appears in the list to To: or Cc: recipients. */
+
+	account_list = e_get_account_list ();
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (e_iterator_is_valid (iterator)) {
+		account = (EAccount *) e_iterator_get (iterator);
+		e_iterator_next (iterator);
+
+		if (account == NULL || !account->enabled)
+			continue;
+
+		if ((key = account->id->address) == NULL)
+			continue;
+
+		if (g_hash_table_lookup (recipients, key) != NULL) {
+			g_object_unref (iterator);
+			goto exit;
+		}
+	}
+	g_object_unref (iterator);
+
+	/* Last Preference: Defer to em_utils_guess_account(). */
+	account = em_utils_guess_account (message, folder);
+
+exit:
+	g_hash_table_destroy (recipients);
+
+	return account;
+}
+
+static void
+cancel_service_connect_cb (GCancellable *cancellable,
+                           CamelService *service)
+{
+	g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+	camel_service_cancel_connect (service);
+}
+
+gboolean
+em_utils_connect_service_sync (CamelService *service,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	gboolean res;
+	gulong handler_id = 0;
+
+	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+	if (cancellable != NULL)
+		handler_id = g_cancellable_connect (
+			cancellable,
+			G_CALLBACK (cancel_service_connect_cb),
+			service, NULL);
+
+	res = camel_service_connect_sync (service, error);
+
+	if (handler_id)
+		g_cancellable_disconnect (cancellable, handler_id);
+
+	return res;
+}
+
+gboolean
+em_utils_disconnect_service_sync (CamelService *service,
+                                  gboolean clean,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	gboolean res;
+	gulong handler_id = 0;
+
+	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+	if (cancellable != NULL)
+		handler_id = g_cancellable_connect (
+			cancellable,
+			G_CALLBACK (cancel_service_connect_cb),
+			service, NULL);
+
+	res = camel_service_disconnect_sync (service, clean, error);
+
+	if (handler_id)
+		g_cancellable_disconnect (cancellable, handler_id);
+
+	return res;
+}
+
+/**
+ * em_utils_uids_free:
+ * @uids: array of uids
+ *
+ * Frees the array of uids pointed to by @uids back to the system.
+ **/
+void
+em_utils_uids_free (GPtrArray *uids)
+{
+	gint i;
+
+	for (i = 0; i < uids->len; i++)
+		g_free (uids->pdata[i]);
+
+	g_ptr_array_free (uids, TRUE);
+}
+
+/* Returns TRUE if CamelURL points to a local mbox file. */
+gboolean
+em_utils_is_local_delivery_mbox_file (CamelURL *url)
+{
+        g_return_val_if_fail (url != NULL, FALSE);
+
+        return g_str_equal (url->protocol, "mbox") &&
+                (url->path != NULL) &&
+                g_file_test (url->path, G_FILE_TEST_EXISTS) &&
+                !g_file_test (url->path, G_FILE_TEST_IS_DIR);
+}
+
diff --git a/libemail-engine/e-mail-utils.h b/libemail-engine/e-mail-utils.h
new file mode 100644
index 0000000..62fdcf0
--- /dev/null
+++ b/libemail-engine/e-mail-utils.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Srinivasa Ragavan <sragavan gnome org>
+ *
+ *
+ */
+
+#ifndef __E_MAIL_UTILS_H__
+#define __E_MAIL_UTILS_H__
+
+#include <sys/types.h>
+#include <camel/camel.h>
+
+gboolean	em_utils_folder_is_drafts	(CamelFolder *folder);
+gboolean	em_utils_folder_is_templates	(CamelFolder *folder);
+gboolean	em_utils_folder_is_sent		(CamelFolder *folder);
+gboolean	em_utils_folder_is_outbox	(CamelFolder *folder);
+
+gboolean em_utils_in_addressbook (CamelInternetAddress *addr, gboolean local_only);
+CamelMimePart *em_utils_contact_photo (CamelInternetAddress *addr, gboolean local);
+
+GHashTable * em_utils_generate_account_hash (void);
+struct _EAccount *em_utils_guess_account (CamelMimeMessage *message, CamelFolder *folder);
+struct _EAccount *em_utils_guess_account_with_recipients (CamelMimeMessage *message, CamelFolder *folder);
+
+void emu_remove_from_mail_cache (const GSList *addresses);
+void emu_remove_from_mail_cache_1 (const gchar *address);
+void emu_free_mail_cache (void);
+
+gboolean em_utils_connect_service_sync (CamelService *service, GCancellable *cancellable, GError **error);
+gboolean em_utils_disconnect_service_sync (CamelService *service, gboolean clean, GCancellable *cancellable, GError **error);
+
+void em_utils_uids_free (GPtrArray *uids);
+gboolean em_utils_is_local_delivery_mbox_file (CamelURL *url);
+
+#endif
diff --git a/libemail-engine/libemail-engine.pc.in b/libemail-engine/libemail-engine.pc.in
new file mode 100644
index 0000000..006a9ce
--- /dev/null
+++ b/libemail-engine/libemail-engine.pc.in
@@ -0,0 +1,15 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+privincludedir= privincludedir@
+
+Name: libemail-utils
+Description: Client library for evolution mail
+Version: @VERSION@
+Requires: camel-1.2 libedataserver-1.2 gio-2.0
+Libs: -L${libdir} -lemail-engine
+Cflags: -I${privincludedir}
diff --git a/libemail-engine/mail-config.c b/libemail-engine/mail-config.c
new file mode 100644
index 0000000..47c147d
--- /dev/null
+++ b/libemail-engine/mail-config.c
@@ -0,0 +1,367 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *   Jeffrey Stedfast <fejj ximian com>
+ *   Radek Doulik <rodo ximian com>
+ *   Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include "libemail-utils/e-account-utils.h"
+#include "libemail-utils/e-signature-list.h"
+#include "libemail-utils/e-signature-utils.h"
+#include "libemail-engine/e-mail-folder-utils.h"
+
+#include <gconf/gconf-client.h>
+
+#include "e-mail-local.h"
+#include "mail-config.h"
+#include "mail-tools.h"
+
+typedef struct {
+	GSList *labels;
+
+	gboolean address_compress;
+	gint address_count;
+
+	GSList *jh_header;
+	gboolean jh_check;
+	gboolean book_lookup;
+	gboolean book_lookup_local_only;
+} MailConfig;
+
+extern gint camel_header_param_encode_filenames_in_rfc_2047;
+
+static MailConfig *config = NULL;
+
+static void
+gconf_outlook_filenames_changed (GConfClient *client,
+                                 guint cnxn_id,
+                                 GConfEntry *entry,
+                                 gpointer user_data)
+{
+	const gchar *key;
+
+	g_return_if_fail (client != NULL);
+
+	key = "/apps/evolution/mail/composer/outlook_filenames";
+
+	/* pass option to the camel */
+	if (gconf_client_get_bool (client, key, NULL))
+		camel_header_param_encode_filenames_in_rfc_2047 = 1;
+	else
+		camel_header_param_encode_filenames_in_rfc_2047 = 0;
+}
+
+static void
+gconf_jh_headers_changed (GConfClient *client,
+                          guint cnxn_id,
+                          GConfEntry *entry,
+                          EMailSession *session)
+{
+	GSList *node;
+	GPtrArray *name, *value;
+
+	g_slist_foreach (config->jh_header, (GFunc) g_free, NULL);
+	g_slist_free (config->jh_header);
+
+	config->jh_header = gconf_client_get_list (
+		client, "/apps/evolution/mail/junk/custom_header",
+		GCONF_VALUE_STRING, NULL);
+
+	node = config->jh_header;
+	name = g_ptr_array_new ();
+	value = g_ptr_array_new ();
+	while (node && node->data) {
+		gchar **tok = g_strsplit (node->data, "=", 2);
+		g_ptr_array_add (name, g_strdup (tok[0]));
+		g_ptr_array_add (value, g_strdup (tok[1]));
+		node = node->next;
+		g_strfreev (tok);
+	}
+	camel_session_set_junk_headers (
+		CAMEL_SESSION (session),
+		(const gchar **) name->pdata,
+		(const gchar **) value->pdata, name->len);
+
+	g_ptr_array_foreach (name, (GFunc) g_free, NULL);
+	g_ptr_array_foreach (value, (GFunc) g_free, NULL);
+	g_ptr_array_free (name, TRUE);
+	g_ptr_array_free (value, TRUE);
+}
+
+static void
+gconf_jh_check_changed (GConfClient *client,
+                        guint cnxn_id,
+                        GConfEntry *entry,
+                        EMailSession *session)
+{
+	config->jh_check = gconf_client_get_bool (
+		client, "/apps/evolution/mail/junk/check_custom_header", NULL);
+	if (!config->jh_check) {
+		camel_session_set_junk_headers (
+			CAMEL_SESSION (session), NULL, NULL, 0);
+	} else {
+		gconf_jh_headers_changed (client, 0, NULL, session);
+	}
+}
+
+static void
+gconf_bool_value_changed (GConfClient *client,
+                          guint cnxn_id,
+                          GConfEntry *entry,
+                          gboolean *save_location)
+{
+	GError *error = NULL;
+
+	*save_location = gconf_client_get_bool (client, entry->key, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+gconf_int_value_changed (GConfClient *client,
+                         guint cnxn_id,
+                         GConfEntry *entry,
+                         gint *save_location)
+{
+	GError *error = NULL;
+
+	*save_location = gconf_client_get_int (client, entry->key, &error);
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+}
+
+void
+mail_config_write (void)
+{
+	GConfClient *client;
+	EAccountList *account_list;
+	ESignatureList *signature_list;
+
+	if (!config)
+		return;
+
+	account_list = e_get_account_list ();
+	signature_list = e_get_signature_list ();
+
+	e_account_list_save (account_list);
+	e_signature_list_save (signature_list);
+
+	client = gconf_client_get_default ();
+	gconf_client_suggest_sync (client, NULL);
+	g_object_unref (client);
+}
+
+gint
+mail_config_get_address_count (void)
+{
+	if (!config->address_compress)
+		return -1;
+
+	return config->address_count;
+}
+
+/* timeout interval, in seconds, when to call server update */
+gint
+mail_config_get_sync_timeout (void)
+{
+	GConfClient *client;
+	gint res = 60;
+	GError *error = NULL;
+
+	client = gconf_client_get_default ();
+
+	res = gconf_client_get_int (
+		client, "/apps/evolution/mail/sync_interval", &error);
+
+	/* do not allow recheck sooner than every 30 seconds */
+	if (error || res == 0)
+		res = 60;
+	else if (res < 30)
+		res = 30;
+
+	if (error)
+		g_error_free (error);
+
+	return res;
+}
+
+gchar *
+mail_config_folder_to_cachename (CamelFolder *folder,
+                                 const gchar *prefix)
+{
+	gchar *folder_uri, *basename, *filename;
+	const gchar *config_dir;
+
+	config_dir = mail_session_get_config_dir ();
+
+	basename = g_build_filename (config_dir, "folders", NULL);
+	if (!g_file_test (basename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
+		/* create the folder if does not exist */
+		g_mkdir_with_parents (basename, 0700);
+	}
+	g_free (basename);
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+	e_filename_make_safe (folder_uri);
+	basename = g_strdup_printf ("%s%s", prefix, folder_uri);
+	filename = g_build_filename (config_dir, "folders", basename, NULL);
+	g_free (basename);
+	g_free (folder_uri);
+
+	return filename;
+}
+
+void
+mail_config_reload_junk_headers (EMailSession *session)
+{
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+	/* It automatically sets in the session */
+	if (config == NULL)
+		mail_config_init (session);
+	else {
+		GConfClient *client;
+
+		client = gconf_client_get_default ();
+		gconf_jh_check_changed (client, 0, NULL, session);
+		g_object_unref (client);
+	}
+}
+
+gboolean
+mail_config_get_lookup_book (void)
+{
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	return config->book_lookup;
+}
+
+gboolean
+mail_config_get_lookup_book_local_only (void)
+{
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	return config->book_lookup_local_only;
+}
+
+/* Config struct routines */
+void
+mail_config_init (EMailSession *session)
+{
+	GConfClient *client;
+	GConfClientNotifyFunc func;
+	const gchar *key;
+
+	g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+	if (config)
+		return;
+
+	config = g_new0 (MailConfig, 1);
+
+	client = gconf_client_get_default ();
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/prompts",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	/* Composer Configuration */
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/composer",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	key = "/apps/evolution/mail/composer/outlook_filenames";
+	func = (GConfClientNotifyFunc) gconf_outlook_filenames_changed;
+	gconf_outlook_filenames_changed (client, 0, NULL, NULL);
+	gconf_client_notify_add (client, key, func, NULL, NULL, NULL);
+
+	/* Display Configuration */
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/display",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	key = "/apps/evolution/mail/display/address_compress";
+	func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+	gconf_client_notify_add (
+		client, key, func,
+		&config->address_compress, NULL, NULL);
+	config->address_compress = gconf_client_get_bool (client, key, NULL);
+
+	key = "/apps/evolution/mail/display/address_count";
+	func = (GConfClientNotifyFunc) gconf_int_value_changed;
+	gconf_client_notify_add (
+		client, key, func,
+		&config->address_count, NULL, NULL);
+	config->address_count = gconf_client_get_int (client, key, NULL);
+
+	/* Font Configuration */
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/display/fonts",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	/* Junk Configuration */
+
+	gconf_client_add_dir (
+		client, "/apps/evolution/mail/junk",
+		GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	key = "/apps/evolution/mail/junk/check_custom_header";
+	func = (GConfClientNotifyFunc) gconf_jh_check_changed;
+	gconf_client_notify_add (client, key, func, session, NULL, NULL);
+	config->jh_check = gconf_client_get_bool (client, key, NULL);
+
+	key = "/apps/evolution/mail/junk/custom_header";
+	func = (GConfClientNotifyFunc) gconf_jh_headers_changed;
+	gconf_client_notify_add (client, key, func, session, NULL, NULL);
+
+	key = "/apps/evolution/mail/junk/lookup_addressbook";
+	func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+	gconf_client_notify_add (
+		client, key, func,
+		&config->book_lookup, NULL, NULL);
+	config->book_lookup = gconf_client_get_bool (client, key, NULL);
+
+	key = "/apps/evolution/mail/junk/lookup_addressbook_local_only";
+	func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+	gconf_client_notify_add (
+		client, key, func,
+		&config->book_lookup_local_only, NULL, NULL);
+	config->book_lookup_local_only =
+		gconf_client_get_bool (client, key, NULL);
+
+	gconf_jh_check_changed (client, 0, NULL, session);
+
+	g_object_unref (client);
+}
diff --git a/libemail-engine/mail-config.h b/libemail-engine/mail-config.h
new file mode 100644
index 0000000..0a1c618
--- /dev/null
+++ b/libemail-engine/mail-config.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_CONFIG_H
+#define MAIL_CONFIG_H
+
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+/* Configuration */
+void		mail_config_init		(EMailSession *session);
+void		mail_config_write		(void);
+
+/* General Accessor functions */
+
+gint		mail_config_get_address_count	(void);
+
+/* static utility functions */
+gchar *		mail_config_folder_to_cachename	(CamelFolder *folder,
+						 const gchar *prefix);
+gint		mail_config_get_sync_timeout	(void);
+
+void		mail_config_reload_junk_headers	(EMailSession *session);
+gboolean	mail_config_get_lookup_book	(void);
+gboolean	mail_config_get_lookup_book_local_only (void);
+
+G_END_DECLS
+
+#endif /* MAIL_CONFIG_H */
diff --git a/libemail-engine/mail-folder-cache.c b/libemail-engine/mail-folder-cache.c
new file mode 100644
index 0000000..07761a6
--- /dev/null
+++ b/libemail-engine/mail-folder-cache.c
@@ -0,0 +1,1424 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *   Peter Williams <peterw ximian com>
+ *   Michael Zucchi <notzed ximian com>
+ *   Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+/**
+ * SECTION: mail-folder-cache
+ * @short_description: Stores information about open folders
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libemail-utils/e-marshal.h>
+
+#include "libemail-utils/mail-mt.h"
+#include "mail-folder-cache.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "e-mail-utils.h"
+#include "e-mail-folder-utils.h"
+#include "e-mail-local.h"
+#include "e-mail-store-utils.h"
+#include "mail-config.h"
+
+#define w(x)
+#define d(x)
+
+/* This code is a mess, there is no reason it should be so complicated. */
+
+struct _MailFolderCachePrivate {
+	/* source id for the ping timeout callback */
+	guint ping_id;
+	/* Store to storeinfo table, active stores */
+	GHashTable *stores;
+	/* mutex to protect access to the stores hash */
+	GMutex *stores_mutex;
+	/* List of folder changes to be executed in gui thread */
+	GQueue updates;
+	/* idle source id for flushing all pending updates */
+	guint update_id;
+	/* hack for people who LIKE to have unsent count */
+	gint count_sent;
+	gint count_trash;
+};
+
+enum {
+	FOLDER_AVAILABLE,
+	FOLDER_UNAVAILABLE,
+	FOLDER_DELETED,
+	FOLDER_RENAMED,
+	FOLDER_UNREAD_UPDATED,
+	FOLDER_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _folder_info {
+	struct _store_info *store_info;	/* 'parent' link */
+
+	gchar *full_name;	/* full name of folder/folderinfo */
+
+	guint32 flags;
+	gboolean has_children;
+
+	gpointer folder;	/* if known (weak pointer) */
+	guint timeout;
+};
+
+/* pending list of updates */
+struct _folder_update {
+	guint remove:1;	/* removing from vfolders */
+	guint delete:1;	/* deleting as well? */
+	guint add:1;	/* add to vfolder */
+	guint unsub:1;   /* unsubcribing? */
+	guint new;     /* new mail arrived? */
+
+	gchar *full_name;
+	gchar *oldfull;
+
+	gint unread;
+	CamelStore *store;
+
+	/* for only one new message... */
+	gchar *msg_uid;     /* ... its uid ... */
+	gchar *msg_sender;  /* ... its sender ... */
+	gchar *msg_subject; /* ... and its subject. */
+};
+
+struct _store_info {
+	GHashTable *folders;	/* by full_name */
+	CamelStore *store;	/* the store for these folders */
+
+	/* Outstanding folderinfo requests */
+	GQueue folderinfo_updates;
+};
+
+G_DEFINE_TYPE (MailFolderCache, mail_folder_cache, G_TYPE_OBJECT)
+
+static void
+free_update (struct _folder_update *up)
+{
+	g_free (up->full_name);
+	if (up->store)
+		g_object_unref (up->store);
+	g_free (up->oldfull);
+	g_free (up->msg_uid);
+	g_free (up->msg_sender);
+	g_free (up->msg_subject);
+	g_free (up);
+}
+
+static gboolean
+flush_updates_idle_cb (MailFolderCache *self)
+{
+	struct _folder_update *up;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	while ((up = g_queue_pop_head (&self->priv->updates)) != NULL) {
+		g_mutex_unlock (self->priv->stores_mutex);
+
+		if (up->remove) {
+			if (up->delete) {
+				g_signal_emit (
+					self, signals[FOLDER_DELETED], 0,
+					up->store, up->full_name);
+			} else
+				g_signal_emit (
+					self, signals[FOLDER_UNAVAILABLE], 0,
+					up->store, up->full_name);
+		} else {
+			if (up->oldfull && up->add) {
+				g_signal_emit (
+					self, signals[FOLDER_RENAMED], 0,
+					up->store, up->oldfull, up->full_name);
+			}
+
+			if (!up->oldfull && up->add)
+				g_signal_emit (
+					self, signals[FOLDER_AVAILABLE], 0,
+					up->store, up->full_name);
+		}
+
+		/* update unread counts */
+		g_signal_emit (self, signals[FOLDER_UNREAD_UPDATED], 0,
+			       up->store, up->full_name, up->unread);
+
+		/* indicate that the folder has changed (new mail received, etc) */
+		if (up->store != NULL && up->full_name != NULL) {
+			g_signal_emit (
+				self, signals[FOLDER_CHANGED], 0, up->store,
+				up->full_name, up->new, up->msg_uid,
+				up->msg_sender, up->msg_subject);
+		}
+
+		if (CAMEL_IS_VEE_STORE (up->store) && !up->remove) {
+			/* Normally the vfolder store takes care of the
+			 * folder_opened event itself, but we add folder to
+			 * the noting system later, thus we do not know about
+			 * search folders to update them in a tree, thus
+			 * ensure their changes will be tracked correctly. */
+			CamelFolder *folder;
+
+			/* FIXME camel_store_get_folder_sync() may block. */
+			folder = camel_store_get_folder_sync (
+				up->store, up->full_name, 0, NULL, NULL);
+
+			if (folder) {
+				mail_folder_cache_note_folder (self, folder);
+				g_object_unref (folder);
+			}
+		}
+
+		free_update (up);
+
+		g_mutex_lock (self->priv->stores_mutex);
+	}
+	self->priv->update_id = 0;
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	return FALSE;
+}
+
+static void
+flush_updates (MailFolderCache *self)
+{
+	if (self->priv->update_id > 0)
+		return;
+
+	if (g_queue_is_empty (&self->priv->updates))
+		return;
+
+	self->priv->update_id = g_idle_add (
+		(GSourceFunc) flush_updates_idle_cb, self);
+}
+
+/* This is how unread counts work (and don't work):
+ *
+ * camel_folder_unread_message_count() only gives a correct answer if
+ * the store is paying attention to the folder. (Some stores always
+ * pay attention to all folders, but IMAP can only pay attention to
+ * one folder at a time.) But it doesn't have any way to know when
+ * it's lying, so it's only safe to call it when you know for sure
+ * that the store is paying attention to the folder, such as when it's
+ * just been created, or you get a folder_changed signal on it.
+ *
+ * camel_store_get_folder_info() always gives correct answers for the
+ * folders it checks, but it can also return -1 for a folder, meaning
+ * it didn't check, and so you should stick with your previous answer.
+ *
+ * update_1folder is called from three places: with info != NULL when
+ * the folder is created (or get_folder_info), with info == NULL when
+ * a folder changed event is emitted.
+ *
+ * So if info is NULL, camel_folder_unread_message_count is correct,
+ * and if it's not NULL and its unread_message_count isn't -1, then
+ * it's correct.  */
+
+static void
+update_1folder (MailFolderCache *self,
+                struct _folder_info *mfi,
+                gint new,
+                const gchar *msg_uid,
+                const gchar *msg_sender,
+                const gchar *msg_subject,
+                CamelFolderInfo *info)
+{
+	struct _folder_update *up;
+	CamelFolder *folder;
+	gint unread = -1;
+	gint deleted;
+
+	folder = mfi->folder;
+	if (folder) {
+		gboolean folder_is_sent;
+		gboolean folder_is_drafts;
+		gboolean folder_is_outbox;
+		gboolean folder_is_vtrash;
+		gboolean special_case;
+
+		folder_is_sent = em_utils_folder_is_sent (folder);
+		folder_is_drafts = em_utils_folder_is_drafts (folder);
+		folder_is_outbox = em_utils_folder_is_outbox (folder);
+		folder_is_vtrash = CAMEL_IS_VTRASH_FOLDER (folder);
+
+		special_case =
+			(self->priv->count_trash && folder_is_vtrash) ||
+			(self->priv->count_sent && folder_is_sent) ||
+			folder_is_drafts || folder_is_outbox;
+
+		if (special_case) {
+			d(printf(" total count\n"));
+			unread = camel_folder_get_message_count (folder);
+			if (folder_is_drafts || folder_is_outbox) {
+				guint32 junked = 0;
+
+				if ((deleted = camel_folder_get_deleted_message_count (folder)) > 0)
+					unread -= deleted;
+
+				junked = folder->summary->junk_count;
+				if (junked > 0)
+					unread -= junked;
+			}
+		} else {
+			d(printf(" unread count\n"));
+			if (info)
+				unread = info->unread;
+			else
+				unread = camel_folder_get_unread_message_count (folder);
+		}
+	}
+
+	d(printf("folder updated: unread %d: '%s'\n", unread, mfi->full_name));
+
+	if (unread == -1)
+		return;
+
+	up = g_malloc0 (sizeof (*up));
+	up->full_name = g_strdup (mfi->full_name);
+	up->unread = unread;
+	up->new = new;
+	up->store = g_object_ref (mfi->store_info->store);
+	up->msg_uid = g_strdup (msg_uid);
+	up->msg_sender = g_strdup (msg_sender);
+	up->msg_subject = g_strdup (msg_subject);
+	g_queue_push_tail (&self->priv->updates, up);
+	flush_updates (self);
+}
+
+static void
+mail_sync_folder_done (CamelFolder *folder, gpointer data)
+{
+	/* We don't have to do anything here as of now */
+}
+
+static gboolean
+mail_folder_sync (struct _folder_info *mfi)
+{
+	mail_sync_folder (mfi->folder, mail_sync_folder_done, mfi);
+	mfi->timeout = 0;
+	return FALSE;
+}
+
+static void
+folder_changed_cb (CamelFolder *folder,
+                   CamelFolderChangeInfo *changes,
+                   MailFolderCache *self)
+{
+	static GHashTable *last_newmail_per_folder = NULL;
+	time_t latest_received, new_latest_received;
+	CamelFolder *local_drafts;
+	CamelFolder *local_outbox;
+	CamelFolder *local_sent;
+	CamelStore *parent_store;
+	CamelMessageInfo *info;
+	struct _store_info *si;
+	struct _folder_info *mfi;
+	const gchar *full_name;
+	gint new = 0;
+	gint i;
+	guint32 flags;
+	gchar *uid = NULL, *sender = NULL, *subject = NULL;
+	gboolean sync_changes = FALSE;
+
+	full_name = camel_folder_get_full_name (folder);
+	parent_store = camel_folder_get_parent_store (folder);
+
+	if (!last_newmail_per_folder)
+		last_newmail_per_folder = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	/* it's fine to hash them by folder pointer here */
+	latest_received = GPOINTER_TO_INT (
+		g_hash_table_lookup (last_newmail_per_folder, folder));
+	new_latest_received = latest_received;
+
+	local_drafts = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_DRAFTS);
+	local_outbox = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+	local_sent = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+	if (!CAMEL_IS_VEE_FOLDER (folder)
+	    && folder != local_drafts
+	    && folder != local_outbox
+	    && folder != local_sent
+	    && changes && (changes->uid_added->len > 0)) {
+		/* for each added message, check to see that it is
+		 * brand new, not junk and not already deleted */
+		for (i = 0; i < changes->uid_added->len; i++) {
+			info = camel_folder_get_message_info (
+				folder, changes->uid_added->pdata[i]);
+			if (info) {
+				flags = camel_message_info_flags (info);
+				if (((flags & CAMEL_MESSAGE_SEEN) == 0) &&
+				    ((flags & CAMEL_MESSAGE_JUNK) == 0) &&
+				    ((flags & CAMEL_MESSAGE_DELETED) == 0) &&
+				    (camel_message_info_date_received (info) > latest_received)) {
+					if (camel_message_info_date_received (info) > new_latest_received)
+						new_latest_received = camel_message_info_date_received (info);
+					new++;
+					if (new == 1) {
+						uid = g_strdup (camel_message_info_uid (info));
+						sender = g_strdup (camel_message_info_from (info));
+						subject = g_strdup (camel_message_info_subject (info));
+					} else {
+						g_free (uid);
+						g_free (sender);
+						g_free (subject);
+
+						uid = NULL;
+						sender = NULL;
+						subject = NULL;
+					}
+				}
+				camel_folder_free_message_info (folder, info);
+			}
+		}
+	} else if (!CAMEL_IS_VEE_FOLDER(folder)
+	    && folder != local_drafts
+	    && folder != local_outbox
+	    && folder != local_sent
+	    && changes && changes->uid_changed && changes->uid_changed->len > 0) {
+			/* We must sync this back. */
+			sync_changes = TRUE;
+	}
+
+	if (new > 0)
+		g_hash_table_insert (
+			last_newmail_per_folder, folder,
+			GINT_TO_POINTER (new_latest_received));
+
+	g_mutex_lock (self->priv->stores_mutex);
+	if (self->priv->stores != NULL
+	    && (si = g_hash_table_lookup (self->priv->stores, parent_store)) != NULL
+	    && (mfi = g_hash_table_lookup (si->folders, full_name)) != NULL
+	    && mfi->folder == folder) {
+		update_1folder (self, mfi, new, uid, sender, subject, NULL);
+		if (sync_changes == TRUE) {
+			if (mfi->timeout) {
+				g_source_remove (mfi->timeout);
+			}
+			mfi->timeout = g_timeout_add_seconds (mail_config_get_sync_timeout (), 
+						(GSourceFunc)mail_folder_sync, 
+						mfi);
+			
+		}
+	}
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	g_free (uid);
+	g_free (sender);
+	g_free (subject);
+}
+
+static void
+unset_folder_info (MailFolderCache *self,
+                   struct _folder_info *mfi,
+                   gint delete,
+                   gint unsub)
+{
+	struct _folder_update *up;
+
+	d(printf("unset folderinfo '%s'\n", mfi->uri));
+
+	if (mfi->folder) {
+		CamelFolder *folder = mfi->folder;
+
+		g_signal_handlers_disconnect_by_func (
+			folder, folder_changed_cb, self);
+
+		g_object_remove_weak_pointer (
+			G_OBJECT (mfi->folder), &mfi->folder);
+	}
+
+	if ((mfi->flags & CAMEL_FOLDER_NOSELECT) == 0) {
+		up = g_malloc0 (sizeof (*up));
+
+		up->remove = TRUE;
+		up->delete = delete;
+		up->unsub = unsub;
+		up->store = g_object_ref (mfi->store_info->store);
+		up->full_name = g_strdup (mfi->full_name);
+
+		g_queue_push_tail (&self->priv->updates, up);
+		flush_updates (self);
+	}
+}
+
+static void
+free_folder_info (struct _folder_info *mfi)
+{
+	g_free (mfi->full_name);
+	g_source_remove (mfi->timeout);
+	g_free (mfi);
+}
+
+static void
+setup_folder (MailFolderCache *self,
+              CamelFolderInfo *fi,
+              struct _store_info *si)
+{
+	struct _folder_info *mfi;
+	struct _folder_update *up;
+
+	mfi = g_hash_table_lookup (si->folders, fi->full_name);
+	if (mfi) {
+		update_1folder (self, mfi, 0, NULL, NULL, NULL, fi);
+	} else {
+		mfi = g_malloc0 (sizeof (*mfi));
+		mfi->full_name = g_strdup (fi->full_name);
+		mfi->store_info = si;
+		mfi->flags = fi->flags;
+		mfi->has_children = fi->child != NULL;
+
+		g_hash_table_insert (si->folders, mfi->full_name, mfi);
+
+		up = g_malloc0 (sizeof (*up));
+		up->full_name = g_strdup (mfi->full_name);
+		up->unread = fi->unread;
+		up->store = g_object_ref (si->store);
+
+		if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
+			up->add = TRUE;
+
+		g_queue_push_tail (&self->priv->updates, up);
+		flush_updates (self);
+	}
+}
+
+static void
+create_folders (MailFolderCache *self,
+                CamelFolderInfo *fi,
+                struct _store_info *si)
+{
+	while (fi) {
+		setup_folder (self, fi, si);
+
+		if (fi->child)
+			create_folders (self, fi->child, si);
+
+		fi = fi->next;
+	}
+}
+
+static void
+store_folder_subscribed_cb (CamelStore *store,
+                            CamelFolderInfo *info,
+                            MailFolderCache *self)
+{
+	struct _store_info *si;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	si = g_hash_table_lookup (self->priv->stores, store);
+	if (si)
+		setup_folder (self, info, si);
+	g_mutex_unlock (self->priv->stores_mutex);
+}
+
+static void
+store_folder_created_cb (CamelStore *store,
+                         CamelFolderInfo *info,
+                         MailFolderCache *cache)
+{
+	/* We only want created events to do more work
+	 * if we dont support subscriptions. */
+	if (!CAMEL_IS_SUBSCRIBABLE (store))
+		store_folder_subscribed_cb (store, info, cache);
+}
+
+static void
+store_folder_opened_cb (CamelStore *store,
+                        CamelFolder *folder,
+                        MailFolderCache *self)
+{
+	mail_folder_cache_note_folder (self, folder);
+}
+
+static void
+store_folder_unsubscribed_cb (CamelStore *store,
+                              CamelFolderInfo *info,
+                              MailFolderCache *self)
+{
+	struct _store_info *si;
+	struct _folder_info *mfi;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	si = g_hash_table_lookup (self->priv->stores, store);
+	if (si) {
+		mfi = g_hash_table_lookup (si->folders, info->full_name);
+		if (mfi) {
+			g_hash_table_remove (si->folders, mfi->full_name);
+			unset_folder_info (self, mfi, TRUE, TRUE);
+			free_folder_info (mfi);
+		}
+	}
+	g_mutex_unlock (self->priv->stores_mutex);
+}
+
+static void
+store_folder_deleted_cb (CamelStore *store,
+                         CamelFolderInfo *info,
+                         MailFolderCache *self)
+{
+	/* We only want deleted events to do more work
+	 * if we dont support subscriptions. */
+	if (!CAMEL_IS_SUBSCRIBABLE (store))
+		store_folder_unsubscribed_cb (store, info, self);
+}
+
+static void
+rename_folders (MailFolderCache *self,
+                struct _store_info *si,
+                const gchar *oldbase,
+                const gchar *newbase,
+                CamelFolderInfo *fi)
+{
+	gchar *old, *olduri, *oldfile, *newuri, *newfile;
+	struct _folder_info *mfi;
+	struct _folder_update *up;
+	const gchar *config_dir;
+
+	up = g_malloc0 (sizeof (*up));
+
+	d(printf("oldbase '%s' newbase '%s' new '%s'\n", oldbase, newbase, fi->full_name));
+
+	/* Form what was the old name, and try and look it up */
+	old = g_strdup_printf("%s%s", oldbase, fi->full_name + strlen(newbase));
+	mfi = g_hash_table_lookup (si->folders, old);
+	if (mfi) {
+		up->oldfull = mfi->full_name;
+
+		/* Its a rename op */
+		g_hash_table_remove (si->folders, mfi->full_name);
+		mfi->full_name = g_strdup (fi->full_name);
+		mfi->flags = fi->flags;
+		mfi->has_children = fi->child != NULL;
+
+		g_hash_table_insert (si->folders, mfi->full_name, mfi);
+	} else {
+		/* Its a new op */
+		mfi = g_malloc0 (sizeof (*mfi));
+		mfi->full_name = g_strdup (fi->full_name);
+		mfi->store_info = si;
+		mfi->flags = fi->flags;
+		mfi->has_children = fi->child != NULL;
+
+		g_hash_table_insert (si->folders, mfi->full_name, mfi);
+	}
+
+	up->full_name = g_strdup (mfi->full_name);
+	up->unread = fi->unread==-1 ? 0 : fi->unread;
+	up->store = g_object_ref (si->store);
+
+	if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
+		up->add = TRUE;
+
+	g_queue_push_tail (&self->priv->updates, up);
+	flush_updates (self);
+#if 0
+	if (fi->sibling)
+		rename_folders (self, si, oldbase, newbase, fi->sibling, folders);
+	if (fi->child)
+		rename_folders (self, si, oldbase, newbase, fi->child, folders);
+#endif
+
+	/* rename the meta-data we maintain ourselves */
+	config_dir = mail_session_get_config_dir ();
+	olduri = e_mail_folder_uri_build (si->store, old);
+	e_filename_make_safe (olduri);
+	newuri = e_mail_folder_uri_build (si->store, fi->full_name);
+	e_filename_make_safe (newuri);
+	oldfile = g_strdup_printf("%s/custom_view-%s.xml", config_dir, olduri);
+	newfile = g_strdup_printf("%s/custom_view-%s.xml", config_dir, newuri);
+	g_rename (oldfile, newfile);
+	g_free (oldfile);
+	g_free (newfile);
+	oldfile = g_strdup_printf("%s/current_view-%s.xml", config_dir, olduri);
+	newfile = g_strdup_printf("%s/current_view-%s.xml", config_dir, newuri);
+	g_rename (oldfile, newfile);
+	g_free (oldfile);
+	g_free (newfile);
+	g_free (olduri);
+	g_free (newuri);
+
+	g_free (old);
+}
+
+static void
+get_folders (CamelFolderInfo *fi,
+             GPtrArray *folders)
+{
+	while (fi) {
+		g_ptr_array_add (folders, fi);
+
+		if (fi->child)
+			get_folders (fi->child, folders);
+
+		fi = fi->next;
+	}
+}
+
+static gint
+folder_cmp (gconstpointer ap,
+            gconstpointer bp)
+{
+	const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
+	const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];
+
+	return strcmp (a->full_name, b->full_name);
+}
+
+static void
+store_folder_renamed_cb (CamelStore *store,
+                         const gchar *old_name,
+                         CamelFolderInfo *info,
+                         MailFolderCache *self)
+{
+	struct _store_info *si;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	si = g_hash_table_lookup (self->priv->stores, store);
+	if (si) {
+		GPtrArray *folders = g_ptr_array_new ();
+		CamelFolderInfo *top;
+		gint i;
+
+		/* Ok, so for some reason the folderinfo we have comes in all messed up from
+		 * imap, should find out why ... this makes it workable */
+		get_folders (info, folders);
+		qsort (folders->pdata, folders->len, sizeof (folders->pdata[0]), folder_cmp);
+
+		top = folders->pdata[0];
+		for (i = 0; i < folders->len; i++) {
+			rename_folders (self, si, old_name, top->full_name, folders->pdata[i]);
+		}
+
+		g_ptr_array_free (folders, TRUE);
+
+	}
+	g_mutex_unlock (self->priv->stores_mutex);
+}
+
+struct _update_data {
+	NoteDoneFunc done;
+	gpointer data;
+	MailFolderCache *cache;
+	GCancellable *cancellable;
+};
+
+static void
+unset_folder_info_hash (gchar *path,
+                        struct _folder_info *mfi,
+                        gpointer data)
+{
+	MailFolderCache *self = (MailFolderCache *) data;
+	unset_folder_info (self, mfi, FALSE, FALSE);
+}
+
+static void
+free_folder_info_hash (gchar *path,
+                       struct _folder_info *mfi,
+                       gpointer data)
+{
+	free_folder_info (mfi);
+}
+
+static void
+update_folders (CamelStore *store,
+                GAsyncResult *result,
+                struct _update_data *ud)
+{
+	CamelFolderInfo *fi;
+	struct _store_info *si;
+	GError *error = NULL;
+
+	fi = camel_store_get_folder_info_finish (store, result, &error);
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+	g_mutex_lock (ud->cache->priv->stores_mutex);
+	si = g_hash_table_lookup (ud->cache->priv->stores, store);
+	if (si && !g_cancellable_is_cancelled (ud->cancellable)) {
+		/* The 'si' is still there, so we can remove ourselves from
+		 * its list.  Or else its not, and we're on our own and free
+		 * anyway. */
+		g_queue_remove (&si->folderinfo_updates, ud);
+
+		if (fi != NULL)
+			create_folders (ud->cache, fi, si);
+	}
+	g_mutex_unlock (ud->cache->priv->stores_mutex);
+
+	if (fi != NULL) {
+		gboolean free_fi = TRUE;
+
+		if (ud->done != NULL)
+			free_fi = ud->done (ud->cache, store, fi, ud->data);
+		if (free_fi)
+			camel_store_free_folder_info (store, fi);
+	}
+
+	if (ud->cancellable != NULL)
+		g_object_unref (ud->cancellable);
+
+	g_free (ud);
+}
+
+struct _ping_store_msg {
+	MailMsg base;
+	CamelStore *store;
+};
+
+static gchar *
+ping_store_desc (struct _ping_store_msg *m)
+{
+	gchar *service_name;
+	gchar *msg;
+
+	service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
+	msg = g_strdup_printf (_("Pinging %s"), service_name);
+	g_free (service_name);
+
+	return msg;
+}
+
+static void
+ping_store_exec (struct _ping_store_msg *m,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+	CamelServiceConnectionStatus status;
+	CamelService *service;
+	gboolean online = FALSE;
+
+	service = CAMEL_SERVICE (m->store);
+	status = camel_service_get_connection_status (service);
+
+	if (status == CAMEL_SERVICE_CONNECTED) {
+		if (CAMEL_IS_DISCO_STORE (m->store) &&
+			camel_disco_store_status (
+			CAMEL_DISCO_STORE (m->store)) !=CAMEL_DISCO_STORE_OFFLINE)
+			online = TRUE;
+		else if (CAMEL_IS_OFFLINE_STORE (m->store) &&
+			camel_offline_store_get_online (
+			CAMEL_OFFLINE_STORE (m->store)))
+			online = TRUE;
+	}
+	if (online)
+		camel_store_noop_sync (m->store, cancellable, error);
+}
+
+static void
+ping_store_free (struct _ping_store_msg *m)
+{
+	g_object_unref (m->store);
+}
+
+static MailMsgInfo ping_store_info = {
+	sizeof (struct _ping_store_msg),
+	(MailMsgDescFunc) ping_store_desc,
+	(MailMsgExecFunc) ping_store_exec,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) ping_store_free
+};
+
+static void
+ping_store (CamelStore *store)
+{
+	CamelServiceConnectionStatus status;
+	CamelService *service;
+	struct _ping_store_msg *m;
+
+	service = CAMEL_SERVICE (store);
+	status = camel_service_get_connection_status (service);
+
+	if (status != CAMEL_SERVICE_CONNECTED)
+		return;
+
+	m = mail_msg_new (&ping_store_info);
+	m->store = g_object_ref (store);
+
+	mail_msg_slow_ordered_push (m);
+}
+
+static gboolean
+ping_cb (MailFolderCache *self)
+{
+	g_mutex_lock (self->priv->stores_mutex);
+
+	g_hash_table_foreach (self->priv->stores, (GHFunc) ping_store, NULL);
+
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	return TRUE;
+}
+
+static void
+store_go_online_cb (CamelStore *store,
+                    GAsyncResult *result,
+                    struct _update_data *ud)
+{
+	/* FIXME Not checking result for error. */
+
+	g_mutex_lock (ud->cache->priv->stores_mutex);
+
+	if (g_hash_table_lookup (ud->cache->priv->stores, store) != NULL &&
+		!g_cancellable_is_cancelled (ud->cancellable)) {
+		/* We're already in the store update list. */
+		camel_store_get_folder_info (
+			store, NULL,
+			CAMEL_STORE_FOLDER_INFO_FAST |
+			CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+			CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+			G_PRIORITY_DEFAULT, ud->cancellable,
+			(GAsyncReadyCallback) update_folders, ud);
+	} else {
+		/* The store vanished, that means we were probably cancelled,
+		 * or at any rate, need to clean ourselves up. */
+		if (ud->cancellable != NULL)
+			g_object_unref (ud->cancellable);
+		g_free (ud);
+	}
+
+	g_mutex_unlock (ud->cache->priv->stores_mutex);
+}
+
+struct _find_info {
+	const gchar *folder_uri;
+	struct _folder_info *fi;
+};
+
+static void
+storeinfo_find_folder_info (CamelStore *store,
+                            struct _store_info *si,
+                            struct _find_info *fi)
+{
+	gchar *folder_name;
+	gboolean success;
+
+	if (fi->fi != NULL)
+		return;
+
+	success = e_mail_folder_uri_parse (
+		camel_service_get_session (CAMEL_SERVICE (store)),
+		fi->folder_uri, NULL, &folder_name, NULL);
+
+	if (success) {
+		fi->fi = g_hash_table_lookup (si->folders, folder_name);
+		g_free (folder_name);
+	}
+}
+
+static void
+mail_folder_cache_finalize (GObject *object)
+{
+	MailFolderCache *cache = (MailFolderCache *) object;
+
+	g_hash_table_destroy (cache->priv->stores);
+	g_mutex_free (cache->priv->stores_mutex);
+
+	if (cache->priv->ping_id > 0) {
+		g_source_remove (cache->priv->ping_id);
+		cache->priv->ping_id = 0;
+	}
+
+	if (cache->priv->update_id > 0) {
+		g_source_remove (cache->priv->update_id);
+		cache->priv->update_id = 0;
+	}
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (mail_folder_cache_parent_class)->finalize (object);
+}
+
+static void
+mail_folder_cache_class_init (MailFolderCacheClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (MailFolderCachePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = mail_folder_cache_finalize;
+
+	/**
+	 * MailFolderCache::folder-available
+	 * @store: the #CamelStore containing the folder
+	 * @folder_name: the name of the folder
+	 *
+	 * Emitted when a folder becomes available
+	 **/
+	signals[FOLDER_AVAILABLE] = g_signal_new (
+		"folder-available",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		e_marshal_VOID__OBJECT_STRING,
+		G_TYPE_NONE, 2,
+		CAMEL_TYPE_STORE,
+		G_TYPE_STRING);
+
+	/**
+	 * MailFolderCache::folder-unavailable
+	 * @store: the #CamelStore containing the folder
+	 * @folder_name: the name of the folder
+	 *
+	 * Emitted when a folder becomes unavailable.  This represents a
+	 * transient condition.  See MailFolderCache::folder-deleted to be
+	 * notified when a folder is permanently removed.
+	 **/
+	signals[FOLDER_UNAVAILABLE] = g_signal_new (
+		"folder-unavailable",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		e_marshal_VOID__OBJECT_STRING,
+		G_TYPE_NONE, 2,
+		CAMEL_TYPE_STORE,
+		G_TYPE_STRING);
+
+	/**
+	 * MailFolderCache::folder-deleted
+	 * @store: the #CamelStore containing the folder
+	 * @folder_name: the name of the folder
+	 *
+	 * Emitted when a folder is deleted
+	 **/
+	signals[FOLDER_DELETED] = g_signal_new (
+		"folder-deleted",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		e_marshal_VOID__OBJECT_STRING,
+		G_TYPE_NONE, 2,
+		CAMEL_TYPE_STORE,
+		G_TYPE_STRING);
+
+	/**
+	 * MailFolderCache::folder-renamed
+	 * @store: the #CamelStore containing the folder
+	 * @old_folder_name: the old name of the folder
+	 * @new_folder_name: the new name of the folder
+	 *
+	 * Emitted when a folder is renamed
+	 **/
+	signals[FOLDER_RENAMED] = g_signal_new (
+		"folder-renamed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		e_marshal_VOID__OBJECT_STRING_STRING,
+		G_TYPE_NONE, 3,
+		CAMEL_TYPE_STORE,
+		G_TYPE_STRING,
+		G_TYPE_STRING);
+
+	/**
+	 * MailFolderCache::folder-unread-updated
+	 * @store: the #CamelStore containing the folder
+	 * @name: the name of the folder
+	 * @unread: the number of unread mails in the folder
+	 *
+	 * Emitted when a we receive an update to the unread count for a folder
+	 **/
+	signals[FOLDER_UNREAD_UPDATED] =
+		g_signal_new ("folder-unread-updated",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_FIRST,
+			      0, /* struct offset */
+			      NULL, NULL, /* accumulator */
+			      e_marshal_VOID__OBJECT_STRING_INT,
+			      G_TYPE_NONE, 3,
+			      CAMEL_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_INT);
+
+	/**
+	 * MailFolderCache::folder-changed
+	 * @store: the #CamelStore containing the folder
+	 * @folder_fullname: the full name of the folder
+	 * @new_messages: the number of new messages for the folder
+	 * @msg_uid: uid of the new message, or NULL
+	 * @msg_sender: sender of the new message, or NULL
+	 * @msg_subject: subject of the new message, or NULL
+	 *
+	 * Emitted when a folder has changed.  If @new_messages is not
+	 * exactly 1, @msg_uid, @msg_sender, and @msg_subject will be NULL.
+	 **/
+	signals[FOLDER_CHANGED] = g_signal_new (
+		"folder-changed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, /* struct offset */
+		NULL, NULL, /* accumulator */
+		e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING,
+		G_TYPE_NONE, 6,
+		CAMEL_TYPE_STORE,
+		G_TYPE_STRING,
+		G_TYPE_INT,
+		G_TYPE_STRING,
+		G_TYPE_STRING,
+		G_TYPE_STRING);
+}
+
+static void
+mail_folder_cache_init (MailFolderCache *self)
+{
+	const gchar *buf;
+	guint timeout;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		self, MAIL_TYPE_FOLDER_CACHE, MailFolderCachePrivate);
+
+	/* initialize values */
+	self->priv->stores = g_hash_table_new (NULL, NULL);
+	self->priv->stores_mutex = g_mutex_new ();
+
+	g_queue_init (&self->priv->updates);
+	self->priv->count_sent = getenv("EVOLUTION_COUNT_SENT") != NULL;
+	self->priv->count_trash = getenv("EVOLUTION_COUNT_TRASH") != NULL;
+
+	buf = getenv ("EVOLUTION_PING_TIMEOUT");
+	timeout = buf ? strtoul (buf, NULL, 10) : 600;
+	self->priv->ping_id = g_timeout_add_seconds (
+		timeout, (GSourceFunc) ping_cb, self);
+}
+
+MailFolderCache *
+mail_folder_cache_new (void)
+{
+	return g_object_new (MAIL_TYPE_FOLDER_CACHE, NULL);
+}
+
+/**
+ * mail_folder_cache_note_store:
+ *
+ * Add a store whose folders should appear in the shell The folders are scanned
+ * from the store, and/or added at runtime via the folder_created event.  The
+ * @done function returns if we can free folder info.
+ */
+void
+mail_folder_cache_note_store (MailFolderCache *self,
+                              CamelSession *session,
+                              CamelStore *store,
+                              GCancellable *cancellable,
+                              NoteDoneFunc done,
+                              gpointer data)
+{
+	struct _store_info *si;
+	struct _update_data *ud;
+	gint hook = 0;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+	g_return_if_fail (mail_in_main_thread ());
+
+	g_mutex_lock (self->priv->stores_mutex);
+
+	printf("Loading: %s\n", camel_service_get_display_name ((CamelService *) store));
+	si = g_hash_table_lookup (self->priv->stores, store);
+	if (si == NULL) {
+		si = g_malloc0 (sizeof (*si));
+		si->folders = g_hash_table_new (g_str_hash, g_str_equal);
+		si->store = g_object_ref (store);
+		g_hash_table_insert (self->priv->stores, store, si);
+		g_queue_init (&si->folderinfo_updates);
+		hook = TRUE;
+	}
+
+	ud = g_malloc0 (sizeof (*ud));
+	ud->done = done;
+	ud->data = data;
+	ud->cache = self;
+
+	if (G_IS_CANCELLABLE (cancellable))
+		ud->cancellable = g_object_ref (cancellable);
+
+	/* We might get a race when setting up a store, such that it is
+	 * still left in offline mode, after we've gone online.  This
+	 * catches and fixes it up when the shell opens us. */
+	if (CAMEL_IS_DISCO_STORE (store)) {
+		if (camel_session_get_online (session) &&
+			 camel_disco_store_status (CAMEL_DISCO_STORE (store)) ==
+			CAMEL_DISCO_STORE_OFFLINE) {
+			e_mail_store_go_online (
+				store, G_PRIORITY_DEFAULT, cancellable,
+				(GAsyncReadyCallback) store_go_online_cb, ud);
+		} else {
+			goto normal_setup;
+		}
+	} else if (CAMEL_IS_OFFLINE_STORE (store)) {
+		if (camel_session_get_online (session) &&
+			!camel_offline_store_get_online (
+			CAMEL_OFFLINE_STORE (store))) {
+			e_mail_store_go_online (
+				store, G_PRIORITY_DEFAULT, cancellable,
+				(GAsyncReadyCallback) store_go_online_cb, ud);
+		} else {
+			goto normal_setup;
+		}
+	} else {
+	normal_setup:
+		camel_store_get_folder_info (
+			store, NULL,
+			CAMEL_STORE_FOLDER_INFO_FAST |
+			CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+			CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+			G_PRIORITY_DEFAULT, cancellable,
+			(GAsyncReadyCallback) update_folders, ud);
+	}
+
+	g_queue_push_tail (&si->folderinfo_updates, ud);
+
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	/* there is potential for race here, but it is safe as we check
+	 * for the store anyway */
+	if (hook) {
+		g_signal_connect (
+			store, "folder-opened",
+			G_CALLBACK (store_folder_opened_cb), self);
+		g_signal_connect (
+			store, "folder-created",
+			G_CALLBACK (store_folder_created_cb), self);
+		g_signal_connect (
+			store, "folder-deleted",
+			G_CALLBACK (store_folder_deleted_cb), self);
+		g_signal_connect (
+			store, "folder-renamed",
+			G_CALLBACK (store_folder_renamed_cb), self);
+	}
+
+	if (hook && CAMEL_IS_SUBSCRIBABLE (store)) {
+		g_signal_connect (
+			store, "folder-subscribed",
+			G_CALLBACK (store_folder_subscribed_cb), self);
+		g_signal_connect (
+			store, "folder-unsubscribed",
+			G_CALLBACK (store_folder_unsubscribed_cb), self);
+	}
+}
+
+/**
+ * mail_folder_cache_note_store_remove:
+ *
+ * Notify the cache that the specified @store can be removed from the cache
+ */
+void
+mail_folder_cache_note_store_remove (MailFolderCache *self,
+                                     CamelStore *store)
+{
+	struct _store_info *si;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	if (self->priv->stores == NULL)
+		return;
+
+	d(printf("store removed!!\n"));
+	g_mutex_lock (self->priv->stores_mutex);
+	si = g_hash_table_lookup (self->priv->stores, store);
+	if (si) {
+		GList *link;
+
+		g_hash_table_remove (self->priv->stores, store);
+
+		g_signal_handlers_disconnect_matched (
+			store, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, self);
+
+		g_hash_table_foreach (
+			si->folders, (GHFunc)
+			unset_folder_info_hash, self);
+
+		link = g_queue_peek_head_link (&si->folderinfo_updates);
+
+		while (link != NULL) {
+			struct _update_data *ud = link->data;
+			g_cancellable_cancel (ud->cancellable);
+			link = g_list_next (link);
+		}
+
+		g_object_unref (si->store);
+		g_hash_table_foreach (si->folders, (GHFunc) free_folder_info_hash, NULL);
+		g_hash_table_destroy (si->folders);
+		g_free (si);
+	}
+
+	g_mutex_unlock (self->priv->stores_mutex);
+}
+
+/**
+ * mail_folder_cache_note_folder:
+ *
+ * When a folder has been opened, notify it for watching.  The folder must have
+ * already been created on the store (which has already been noted) before the
+ * folder can be opened
+ */
+void
+mail_folder_cache_note_folder (MailFolderCache *self,
+                               CamelFolder *folder)
+{
+	CamelStore *parent_store;
+	struct _store_info *si;
+	struct _folder_info *mfi;
+	const gchar *full_name;
+
+	full_name = camel_folder_get_full_name (folder);
+	parent_store = camel_folder_get_parent_store (folder);
+
+	g_mutex_lock (self->priv->stores_mutex);
+	if (self->priv->stores == NULL
+	    || (si = g_hash_table_lookup (self->priv->stores, parent_store)) == NULL
+	    || (mfi = g_hash_table_lookup (si->folders, full_name)) == NULL) {
+		w(g_warning("Noting folder before store initialised"));
+		g_mutex_unlock (self->priv->stores_mutex);
+		return;
+	}
+
+	/* dont do anything if we already have this */
+	if (mfi->folder == folder) {
+		g_mutex_unlock (self->priv->stores_mutex);
+		return;
+	}
+
+	mfi->folder = folder;
+
+	g_object_add_weak_pointer (G_OBJECT (folder), &mfi->folder);
+
+	update_1folder (self, mfi, 0, NULL, NULL, NULL, NULL);
+
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	g_signal_connect (
+		folder, "changed",
+		G_CALLBACK (folder_changed_cb), self);
+}
+
+/**
+ * mail_folder_cache_get_folder_from_uri:
+ *
+ * Gets the #CamelFolder for the supplied @uri.
+ *
+ * Returns: %TRUE if the URI is available, folderp is set to a reffed
+ *          folder if the folder has also already been opened
+ */
+gboolean
+mail_folder_cache_get_folder_from_uri (MailFolderCache *self,
+                                       const gchar *uri,
+                                       CamelFolder **folderp)
+{
+	struct _find_info fi = { uri, NULL };
+
+	if (self->priv->stores == NULL)
+		return FALSE;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	g_hash_table_foreach (
+		self->priv->stores, (GHFunc)
+		storeinfo_find_folder_info, &fi);
+	if (folderp) {
+		if (fi.fi && fi.fi->folder)
+			*folderp = g_object_ref (fi.fi->folder);
+		else
+			*folderp = NULL;
+	}
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	return fi.fi != NULL;
+}
+
+gboolean
+mail_folder_cache_get_folder_info_flags (MailFolderCache *self,
+                                         CamelFolder *folder,
+                                         CamelFolderInfoFlags *flags)
+{
+	struct _find_info fi = { NULL, NULL };
+	gchar *folder_uri;
+
+	if (self->priv->stores == NULL)
+		return FALSE;
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+	fi.folder_uri = folder_uri;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	g_hash_table_foreach (
+		self->priv->stores, (GHFunc)
+		storeinfo_find_folder_info, &fi);
+	if (flags) {
+		if (fi.fi)
+			*flags = fi.fi->flags;
+		else
+			*flags = 0;
+	}
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	g_free (folder_uri);
+
+	return fi.fi != NULL;
+}
+
+/* Returns whether folder 'folder' has children based on folder_info->child property.
+ * If not found returns FALSE and sets 'found' to FALSE, if not NULL. */
+gboolean
+mail_folder_cache_get_folder_has_children (MailFolderCache *self,
+                                           CamelFolder *folder,
+                                           gboolean *found)
+{
+	struct _find_info fi = { NULL, NULL };
+	gchar *folder_uri;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (folder != NULL, FALSE);
+
+	if (self->priv->stores == NULL)
+		return FALSE;
+
+	folder_uri = e_mail_folder_uri_from_folder (folder);
+	fi.folder_uri = folder_uri;
+
+	g_mutex_lock (self->priv->stores_mutex);
+	g_hash_table_foreach (
+		self->priv->stores, (GHFunc)
+		storeinfo_find_folder_info, &fi);
+	if (found)
+		*found = fi.fi != NULL;
+	g_mutex_unlock (self->priv->stores_mutex);
+
+	g_free (folder_uri);
+
+	return fi.fi != NULL && fi.fi->has_children;
+}
diff --git a/libemail-engine/mail-folder-cache.h b/libemail-engine/mail-folder-cache.h
new file mode 100644
index 0000000..681c6ef
--- /dev/null
+++ b/libemail-engine/mail-folder-cache.h
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *   Peter Williams <peterw ximian com>
+ *   Michael Zucchi <notzed ximian com>
+ *   Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifndef MAIL_FOLDER_CACHE_H
+#define MAIL_FOLDER_CACHE_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define MAIL_TYPE_FOLDER_CACHE \
+	(mail_folder_cache_get_type ())
+#define MAIL_FOLDER_CACHE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCache))
+#define MAIL_FOLDER_CACHE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), MAIL_TYPE_FOLDER_CACHE, MailFolderCacheClass))
+#define MAIL_IS_FOLDER_CACHE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), MAIL_TYPE_FOLDER_CACHE))
+#define MAIL_IS_FOLDER_CACHE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), MAIL_TYPE_FOLDER_CACHE))
+#define MAIL_FOLDER_CACHE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCacheClass))
+
+G_BEGIN_DECLS
+
+typedef struct _MailFolderCache MailFolderCache;
+typedef struct _MailFolderCacheClass MailFolderCacheClass;
+typedef struct _MailFolderCachePrivate MailFolderCachePrivate;
+
+/**
+ * NoteDoneFunc:
+ *
+ * The signature of a function to be registered as a callback for
+ * mail_folder_cache_note_store()
+ */
+typedef gboolean	(*NoteDoneFunc)		(MailFolderCache *cache,
+						 CamelStore *store,
+						 CamelFolderInfo *info,
+						 gpointer data);
+
+/**
+ * MailFolderCache:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ */
+struct _MailFolderCache {
+	GObject parent;
+	MailFolderCachePrivate *priv;
+};
+
+struct _MailFolderCacheClass {
+	GObjectClass parent_class;
+};
+
+GType		mail_folder_cache_get_type	(void) G_GNUC_CONST;
+MailFolderCache *
+		mail_folder_cache_new		(void);
+void		mail_folder_cache_note_store	(MailFolderCache *self,
+						 CamelSession *session,
+						 CamelStore *store,
+						 GCancellable *cancellable,
+						 NoteDoneFunc done,
+						 gpointer data);
+void		mail_folder_cache_note_store_remove
+						(MailFolderCache *self,
+						 CamelStore *store);
+void		mail_folder_cache_note_folder	(MailFolderCache *self,
+						 CamelFolder *folder);
+gboolean	mail_folder_cache_get_folder_from_uri
+						(MailFolderCache *self,
+						 const gchar *uri,
+						 CamelFolder **folderp);
+gboolean	mail_folder_cache_get_folder_info_flags
+						(MailFolderCache *self,
+						 CamelFolder *folder,
+						 CamelFolderInfoFlags *flags);
+
+gboolean	mail_folder_cache_get_folder_has_children
+						(MailFolderCache *self,
+						 CamelFolder *folder,
+						 gboolean *found);
+
+G_END_DECLS
+
+#endif /* MAIL_FOLDER_CACHE_H */
diff --git a/libemail-engine/mail-ops.c b/libemail-engine/mail-ops.c
new file mode 100644
index 0000000..a12a63a
--- /dev/null
+++ b/libemail-engine/mail-ops.c
@@ -0,0 +1,1680 @@
+/*
+ * mail-ops.c: callbacks for the mail toolbar/menus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *      Dan Winship <danw ximian com>
+ *      Jeffrey Stedfast <fejj ximian com>
+ *      Peter Williams <peterw ximian com>
+ *      Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include "libemail-utils/e-account-utils.h"
+
+#include "e-mail-utils.h"
+#include "libemail-utils/mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "e-mail-session-utils.h"
+#include "e-mail-local.h"
+
+#define w(x)
+#define d(x)
+
+/* XXX Make this a preprocessor definition. */
+const gchar *x_mailer = "Evolution Mail Data Server" PACKAGE_VERSION;
+
+/* used for both just filtering a folder + uid's, and for filtering a whole folder */
+/* used both for fetching mail, and for filtering mail */
+struct _filter_mail_msg {
+	MailMsg base;
+
+	EMailSession *session;
+	CamelFolder *source_folder; /* where they come from */
+	GPtrArray *source_uids;	/* uids to copy, or NULL == copy all */
+	CamelUIDCache *cache;  /* UID cache if we are to cache the uids, NULL otherwise */
+	CamelFilterDriver *driver;
+	gint delete;		/* delete messages after filtering them? */
+	CamelFolder *destination; /* default destination for any messages, NULL for none */
+};
+
+/* since fetching also filters, we subclass the data here */
+struct _fetch_mail_msg {
+	struct _filter_mail_msg fmsg;
+
+	CamelStore *store;
+	GCancellable *cancellable;	/* we have our own cancellation
+					 * struct, the other should be empty */
+	gint keep;		/* keep on server? */
+
+	void (*done)(gpointer data);
+	gpointer data;
+};
+
+static gchar *
+em_filter_folder_element_desc (struct _filter_mail_msg *m)
+{
+	return g_strdup (_("Filtering Selected Messages"));
+}
+
+/* filter a folder, or a subset thereof, uses source_folder/source_uids */
+/* this is shared with fetch_mail */
+static void
+em_filter_folder_element_exec (struct _filter_mail_msg *m,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	CamelFolder *folder;
+	GPtrArray *uids, *folder_uids = NULL;
+
+	folder = m->source_folder;
+
+	if (folder == NULL || camel_folder_get_message_count (folder) == 0)
+		return;
+
+	if (m->destination) {
+		camel_folder_freeze (m->destination);
+		camel_filter_driver_set_default_folder (m->driver, m->destination);
+	}
+
+	camel_folder_freeze (folder);
+
+	if (m->source_uids)
+		uids = m->source_uids;
+	else
+		folder_uids = uids = camel_folder_get_uids (folder);
+
+	camel_filter_driver_filter_folder (
+		m->driver, folder, m->cache, uids, m->delete,
+		cancellable, error);
+	camel_filter_driver_flush (m->driver, error);
+
+	if (folder_uids)
+		camel_folder_free_uids (folder, folder_uids);
+
+	/* sync our source folder */
+	if (!m->cache)
+		camel_folder_synchronize_sync (
+			folder, FALSE, cancellable, error);
+	camel_folder_thaw (folder);
+
+	if (m->destination)
+		camel_folder_thaw (m->destination);
+
+	/* this may thaw/unref source folders, do it here so we dont do
+	 * it in the main thread see also fetch_mail_fetch () below */
+	g_object_unref (m->driver);
+	m->driver = NULL;
+}
+
+static void
+em_filter_folder_element_done (struct _filter_mail_msg *m)
+{
+}
+
+static void
+em_filter_folder_element_free (struct _filter_mail_msg *m)
+{
+	mail_session_flush_filter_log (m->session);
+
+	if (m->session)
+		g_object_unref (m->session);
+
+	if (m->source_folder)
+		g_object_unref (m->source_folder);
+
+	if (m->source_uids)
+		em_utils_uids_free (m->source_uids);
+
+	if (m->destination)
+		g_object_unref (m->destination);
+
+	if (m->driver)
+		g_object_unref (m->driver);
+}
+
+static MailMsgInfo em_filter_folder_element_info = {
+	sizeof (struct _filter_mail_msg),
+	(MailMsgDescFunc) em_filter_folder_element_desc,
+	(MailMsgExecFunc) em_filter_folder_element_exec,
+	(MailMsgDoneFunc) em_filter_folder_element_done,
+	(MailMsgFreeFunc) em_filter_folder_element_free
+};
+
+void
+mail_filter_folder (EMailSession *session,
+                    CamelFolder *source_folder,
+                    GPtrArray *uids,
+                    const gchar *type,
+                    gboolean notify)
+{
+	struct _filter_mail_msg *m;
+
+	m = mail_msg_new (&em_filter_folder_element_info);
+	m->session = g_object_ref (session);
+	m->source_folder = g_object_ref (source_folder);
+	m->source_uids = uids;
+	m->cache = NULL;
+	m->delete = FALSE;
+
+	m->driver = camel_session_get_filter_driver (
+		CAMEL_SESSION (session), type, NULL);
+
+	if (!notify) {
+		/* FIXME: have a #define NOTIFY_FILTER_NAME macro? */
+		/* the filter name has to stay in sync with mail-session::get_filter_driver */
+		camel_filter_driver_remove_rule_by_name (m->driver, "new-mail-notification");
+	}
+
+	mail_msg_unordered_push (m);
+}
+
+/* ********************************************************************** */
+
+static gchar *
+fetch_mail_desc (struct _fetch_mail_msg *m)
+{
+	return g_strdup (_("Fetching Mail"));
+}
+
+static void
+fetch_mail_exec (struct _fetch_mail_msg *m,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+	struct _filter_mail_msg *fm = (struct _filter_mail_msg *) m;
+	CamelFolder *folder = NULL;
+	CamelURL *url;
+	const gchar *uid;
+	gboolean is_local_delivery = FALSE;
+	gint i;
+
+	fm->destination = e_mail_local_get_folder (
+		E_MAIL_LOCAL_FOLDER_LOCAL_INBOX);
+	if (fm->destination == NULL)
+		goto fail;
+	g_object_ref (fm->destination);
+
+	url = camel_service_get_camel_url (CAMEL_SERVICE (m->store));
+	is_local_delivery = em_utils_is_local_delivery_mbox_file (url);
+	if (is_local_delivery) {
+		gchar *path;
+		gchar *url_string;
+
+		path = mail_tool_do_movemail (m->store, error);
+		url_string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+
+		if (path && (!error || !*error)) {
+			camel_folder_freeze (fm->destination);
+			camel_filter_driver_set_default_folder (
+				fm->driver, fm->destination);
+			camel_filter_driver_filter_mbox (
+				fm->driver, path, url_string,
+				cancellable, error);
+			camel_folder_thaw (fm->destination);
+
+			if (!error || !*error)
+				g_unlink (path);
+		}
+
+		g_free (path);
+		g_free (url_string);
+	} else {
+		uid = camel_service_get_uid (CAMEL_SERVICE (m->store));
+
+		folder = fm->source_folder =
+			e_mail_session_get_inbox_sync (
+				fm->session, uid, cancellable, error);
+	}
+
+	if (folder != NULL) {
+		/* This handles 'keep on server' stuff, if we have any new
+		 * uid's to copy across, we need to copy them to a new array
+		 * 'cause of the way fetch_mail_free works. */
+		CamelUIDCache *cache = NULL;
+		CamelStore *parent_store;
+		CamelService *service;
+		const gchar *data_dir;
+		gchar *cachename;
+
+		parent_store = camel_folder_get_parent_store (folder);
+
+		service = CAMEL_SERVICE (parent_store);
+		data_dir = camel_service_get_user_data_dir (service);
+
+		cachename = g_build_filename (data_dir, "uid-cache", NULL);
+		cache = camel_uid_cache_new (cachename);
+		g_free (cachename);
+
+		if (cache) {
+			GPtrArray *folder_uids, *cache_uids, *uids;
+
+			folder_uids = camel_folder_get_uids (folder);
+			cache_uids = camel_uid_cache_get_new_uids (cache, folder_uids);
+			if (cache_uids) {
+				/* need to copy this, sigh */
+				fm->source_uids = uids = g_ptr_array_new ();
+				g_ptr_array_set_size (uids, cache_uids->len);
+				for (i = 0; i < cache_uids->len; i++)
+					uids->pdata[i] = g_strdup (cache_uids->pdata[i]);
+				camel_uid_cache_free_uids (cache_uids);
+
+				fm->cache = cache;
+				em_filter_folder_element_exec (fm, cancellable, error);
+
+				/* need to uncancel so writes/etc. don't fail */
+				if (g_cancellable_is_cancelled (m->cancellable))
+					g_cancellable_reset (m->cancellable);
+
+				/* save the cache of uids that we've just downloaded */
+				camel_uid_cache_save (cache);
+			}
+
+			if (fm->delete && (!error || !*error)) {
+				/* not keep on server - just delete all
+				 * the actual messages on the server */
+				for (i = 0; i < folder_uids->len; i++) {
+					camel_folder_delete_message (
+						folder, folder_uids->pdata[i]);
+				}
+			}
+
+			if ((fm->delete || cache_uids) && (!error || !*error)) {
+				/* expunge messages (downloaded so far) */
+				/* FIXME Not passing a GCancellable or GError here. */
+				camel_folder_synchronize_sync (
+					folder, fm->delete, NULL, NULL);
+			}
+
+			camel_uid_cache_destroy (cache);
+			camel_folder_free_uids (folder, folder_uids);
+		} else {
+			em_filter_folder_element_exec (fm, cancellable, error);
+		}
+
+		/* we unref the source folder here since we
+		 * may now block in finalize (we try to
+		 * disconnect cleanly) */
+		g_object_unref (fm->source_folder);
+		fm->source_folder = NULL;
+	}
+
+fail:
+	/* we unref this here as it may have more work to do (syncing
+	 * folders and whatnot) before we are really done */
+	/* should this be cancellable too? (i.e. above unregister above) */
+	if (fm->driver) {
+		g_object_unref (fm->driver);
+		fm->driver = NULL;
+	}
+
+	/* also disconnect if not a local delivery mbox;
+	 * there is no need to keep the connection alive forever */
+	if (!is_local_delivery)
+		em_utils_disconnect_service_sync (
+			CAMEL_SERVICE (m->store), TRUE, cancellable, NULL);
+}
+
+static void
+fetch_mail_done (struct _fetch_mail_msg *m)
+{
+	if (m->done)
+		m->done (m->data);
+}
+
+static void
+fetch_mail_free (struct _fetch_mail_msg *m)
+{
+	if (m->store != NULL)
+		g_object_unref (m->store);
+
+	if (m->cancellable != NULL)
+		g_object_unref (m->cancellable);
+
+	em_filter_folder_element_free ((struct _filter_mail_msg *) m);
+}
+
+static MailMsgInfo fetch_mail_info = {
+	sizeof (struct _fetch_mail_msg),
+	(MailMsgDescFunc) fetch_mail_desc,
+	(MailMsgExecFunc) fetch_mail_exec,
+	(MailMsgDoneFunc) fetch_mail_done,
+	(MailMsgFreeFunc) fetch_mail_free
+};
+
+/* ouch, a 'do everything' interface ... */
+void
+mail_fetch_mail (CamelStore *store,
+                 gint keep,
+                 const gchar *type,
+                 GCancellable *cancellable,
+                 CamelFilterGetFolderFunc get_folder,
+                 gpointer get_data,
+                 CamelFilterStatusFunc *status,
+                 gpointer status_data,
+                 void (*done)(gpointer data),
+                 gpointer data)
+{
+	struct _fetch_mail_msg *m;
+	struct _filter_mail_msg *fm;
+	CamelSession *session;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	session = camel_service_get_session (CAMEL_SERVICE (store));
+
+	m = mail_msg_new (&fetch_mail_info);
+	fm = (struct _filter_mail_msg *) m;
+	fm->session = g_object_ref (session);
+	m->store = g_object_ref (store);
+	fm->delete = !keep;
+	fm->cache = NULL;
+	if (cancellable)
+		m->cancellable = g_object_ref (cancellable);
+	m->done = done;
+	m->data = data;
+
+	fm->driver = camel_session_get_filter_driver (session, type, NULL);
+	camel_filter_driver_set_folder_func (fm->driver, get_folder, get_data);
+	if (status)
+		camel_filter_driver_set_status_func (fm->driver, status, status_data);
+
+	mail_msg_unordered_push (m);
+}
+
+static gchar *
+escape_percent_sign (const gchar *str)
+{
+	GString *res;
+
+	if (!str)
+		return NULL;
+
+	res = g_string_sized_new (strlen (str));
+	while (*str) {
+		if (*str == '%') {
+			g_string_append (res, "%%");
+		} else {
+			g_string_append_c (res, *str);
+		}
+
+		str++;
+	}
+
+	return g_string_free (res, FALSE);
+}
+
+/* ********************************************************************** */
+/* sending stuff */
+/* ** SEND MAIL *********************************************************** */
+
+static const gchar *normal_recipients[] = {
+	CAMEL_RECIPIENT_TYPE_TO,
+	CAMEL_RECIPIENT_TYPE_CC,
+	CAMEL_RECIPIENT_TYPE_BCC
+};
+
+static const gchar *resent_recipients[] = {
+	CAMEL_RECIPIENT_TYPE_RESENT_TO,
+	CAMEL_RECIPIENT_TYPE_RESENT_CC,
+	CAMEL_RECIPIENT_TYPE_RESENT_BCC
+};
+
+struct _send_queue_msg {
+	MailMsg base;
+
+	EMailSession *session;
+	CamelFolder *queue;
+	CamelTransport *transport;
+
+	CamelFilterDriver *driver;
+
+	/* we use camelfilterstatusfunc, even though its not the filter doing it */
+	CamelFilterStatusFunc *status;
+	gpointer status_data;
+
+	void (*done)(gpointer data);
+	gpointer data;
+};
+
+static void	report_status		(struct _send_queue_msg *m,
+					 enum camel_filter_status_t status,
+					 gint pc,
+					 const gchar *desc,
+					 ...);
+
+/* send 1 message to a specific transport */
+static void
+mail_send_message (struct _send_queue_msg *m,
+                   CamelFolder *queue,
+                   const gchar *uid,
+                   CamelTransport *transport,
+                   CamelFilterDriver *driver,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+	EAccount *account = NULL;
+	EMailSession *session;
+	const CamelInternetAddress *iaddr;
+	CamelAddress *from, *recipients;
+	CamelMessageInfo *info = NULL;
+	CamelProvider *provider;
+	gchar *transport_uid = NULL;
+	gchar *sent_folder_uri = NULL;
+	const gchar *resent_from, *tmp;
+	CamelFolder *folder = NULL;
+	GString *err = NULL;
+	struct _camel_header_raw *xev, *header;
+	CamelMimeMessage *message;
+	gint i;
+	GError *local_error = NULL;
+
+	message = camel_folder_get_message_sync (
+		queue, uid, cancellable, error);
+	if (!message)
+		return;
+
+	camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer", x_mailer);
+
+	err = g_string_new ("");
+	xev = mail_tool_remove_xevolution_headers (message);
+
+	session = m->session;
+
+	tmp = camel_header_raw_find (&xev, "X-Evolution-Account", NULL);
+	if (tmp != NULL) {
+		gchar *name;
+
+		name = g_strstrip (g_strdup (tmp));
+		if ((account = e_get_account_by_uid (name))
+		    /* 'old' x-evolution-account stored the name, how silly */
+		    || (account = e_get_account_by_name (name))) {
+			if (account->transport) {
+				CamelService *service;
+				gchar *transport_uid;
+
+				transport_uid = g_strconcat (
+					account->uid, "-transport", NULL);
+				service = camel_session_get_service (
+					CAMEL_SESSION (session),
+					transport_uid);
+				g_free (transport_uid);
+
+				if (CAMEL_IS_TRANSPORT (service))
+					transport = CAMEL_TRANSPORT (service);
+			}
+
+			sent_folder_uri = g_strdup (account->sent_folder_uri);
+		}
+		g_free (name);
+	}
+
+	if (!account) {
+		/* default back to these headers */
+		tmp = camel_header_raw_find(&xev, "X-Evolution-Transport", NULL);
+		if (tmp)
+			transport_uid = g_strstrip (g_strdup (tmp));
+
+		tmp = camel_header_raw_find(&xev, "X-Evolution-Fcc", NULL);
+		if (tmp)
+			sent_folder_uri = g_strstrip (g_strdup (tmp));
+	}
+
+	if (transport != NULL) {
+		CamelURL *url;
+		gchar *url_string;
+		gchar *escaped;
+
+		url = camel_service_get_camel_url (CAMEL_SERVICE (transport));
+		url_string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+		escaped = escape_percent_sign (url_string);
+
+		/* Let the dialog know the right account it is using. */
+		report_status (m, CAMEL_FILTER_STATUS_ACTION, 0, escaped);
+
+		g_free (escaped);
+		g_free (url_string);
+	}
+
+	/* Check for email sending */
+	from = (CamelAddress *) camel_internet_address_new ();
+	resent_from = camel_medium_get_header (CAMEL_MEDIUM (message), "Resent-From");
+	if (resent_from) {
+		camel_address_decode (from, resent_from);
+	} else {
+		iaddr = camel_mime_message_get_from (message);
+		camel_address_copy (from, CAMEL_ADDRESS (iaddr));
+	}
+
+	recipients = (CamelAddress *) camel_internet_address_new ();
+	for (i = 0; i < 3; i++) {
+		const gchar *type;
+
+		type = resent_from ? resent_recipients[i] : normal_recipients[i];
+		iaddr = camel_mime_message_get_recipients (message, type);
+		camel_address_cat (recipients, CAMEL_ADDRESS (iaddr));
+	}
+
+	if (camel_address_length (recipients) > 0) {
+		if (!em_utils_connect_service_sync (
+			CAMEL_SERVICE (transport), cancellable, error))
+			goto exit;
+
+		if (!camel_transport_send_to_sync (
+			transport, message, from,
+			recipients, cancellable, error))
+			goto exit;
+	}
+
+	/* Now check for posting, failures are ignored */
+	info = camel_message_info_new (NULL);
+	camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+	for (header = xev; header; header = header->next) {
+		gchar *uri;
+
+		if (strcmp(header->name, "X-Evolution-PostTo") != 0)
+			continue;
+
+		/* TODO: don't lose errors */
+
+		uri = g_strstrip (g_strdup (header->value));
+		/* FIXME Not passing a GCancellable or GError here. */
+		folder = e_mail_session_uri_to_folder_sync (
+			session, uri, 0, NULL, NULL);
+		if (folder) {
+			/* FIXME Not passing a GCancellable or GError here. */
+			camel_folder_append_message_sync (
+				folder, message, info, NULL, NULL, NULL);
+			g_object_unref (folder);
+			folder = NULL;
+		}
+		g_free (uri);
+	}
+
+	/* post process */
+	mail_tool_restore_xevolution_headers (message, xev);
+
+	if (driver) {
+		camel_filter_driver_filter_message (
+			driver, message, info, NULL, NULL,
+			NULL, "", cancellable, &local_error);
+
+		if (local_error != NULL) {
+			if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+				goto exit;
+
+			/* sending mail, filtering failed */
+			g_string_append_printf (
+				err, _("Failed to apply outgoing filters: %s"),
+				local_error->message);
+
+			g_clear_error (&local_error);
+		}
+	}
+
+	provider = camel_service_get_provider (CAMEL_SERVICE (transport));
+
+	if (provider == NULL
+	    || !(provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)) {
+		GError *local_error = NULL;
+
+		if (sent_folder_uri) {
+			folder = e_mail_session_uri_to_folder_sync (
+				session, sent_folder_uri, 0,
+				cancellable, &local_error);
+			if (folder == NULL) {
+				g_string_append_printf (
+					err, _("Failed to append to %s: %s\n"
+					"Appending to local 'Sent' folder instead."),
+					sent_folder_uri,
+					local_error ?
+						local_error->message :
+						_("Unknown error"));
+				if (local_error)
+					g_clear_error (&local_error);
+			}
+		}
+
+		if (!folder) {
+			folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+			g_object_ref (folder);
+		}
+
+		if (!camel_folder_append_message_sync (
+			folder, message, info,
+			NULL, cancellable, &local_error)) {
+
+			CamelFolder *sent_folder;
+
+			if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+				goto exit;
+
+			sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+			if (folder != sent_folder) {
+				const gchar *description;
+
+				description = camel_folder_get_description (folder);
+				if (err->len)
+					g_string_append(err, "\n\n");
+				g_string_append_printf (
+					err, _("Failed to append to %s: %s\n"
+					"Appending to local 'Sent' folder instead."),
+					description, local_error->message);
+				g_object_ref (sent_folder);
+				g_object_unref (folder);
+				folder = sent_folder;
+
+				g_clear_error (&local_error);
+				camel_folder_append_message_sync (
+					folder, message, info,
+					NULL, cancellable, &local_error);
+			}
+
+			if (local_error != NULL) {
+				if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+					goto exit;
+
+				if (err->len)
+					g_string_append(err, "\n\n");
+				g_string_append_printf (
+					err, _("Failed to append to local 'Sent' folder: %s"),
+					local_error->message);
+			}
+		}
+	}
+
+	if (local_error == NULL) {
+		/* Mark the draft message for deletion, if present. */
+		e_mail_session_handle_draft_headers_sync (
+			session, message, cancellable, &local_error);
+		if (local_error != NULL) {
+			g_warning ("%s: Failed to handle draft headers: %s", G_STRFUNC, local_error->message);
+			g_clear_error (&local_error);
+		}
+
+		/* Set flags on the original source message, if present.
+		 * Source message refers to the message being forwarded
+		 * or replied to. */
+		e_mail_session_handle_source_headers_sync (
+			session, message, cancellable, &local_error);
+		if (local_error != NULL) {
+			g_warning ("%s: Failed to handle source headers: %s", G_STRFUNC, local_error->message);
+			g_clear_error (&local_error);
+		}
+	}
+
+	if (local_error == NULL) {
+		camel_folder_set_message_flags (
+			queue, uid, CAMEL_MESSAGE_DELETED |
+			CAMEL_MESSAGE_SEEN, ~0);
+		/* Sync it to disk, since if it crashes in between,
+		 * we keep sending it again on next start. */
+		/* FIXME Not passing a GCancellable or GError here. */
+		camel_folder_synchronize_sync (queue, FALSE, NULL, NULL);
+	}
+
+	if (err->len) {
+		/* set the culmulative exception report */
+		g_set_error (
+			&local_error, CAMEL_ERROR,
+			CAMEL_ERROR_GENERIC, "%s", err->str);
+	}
+
+exit:
+	if (local_error != NULL)
+		g_propagate_error (error, local_error);
+
+	/* FIXME Not passing a GCancellable or GError here. */
+	if (folder) {
+		camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
+		g_object_unref (folder);
+	}
+	if (info)
+		camel_message_info_free (info);
+	g_object_unref (recipients);
+	g_object_unref (from);
+	g_free (sent_folder_uri);
+	g_free (transport_uid);
+	camel_header_raw_clear (&xev);
+	g_string_free (err, TRUE);
+	g_object_unref (message);
+}
+
+/* ** SEND MAIL QUEUE ***************************************************** */
+
+static void
+report_status (struct _send_queue_msg *m,
+               enum camel_filter_status_t status,
+               gint pc,
+               const gchar *desc,
+               ...)
+{
+	va_list ap;
+	gchar *str;
+
+	if (m->status) {
+		va_start (ap, desc);
+		str = g_strdup_vprintf (desc, ap);
+		va_end (ap);
+		m->status (m->driver, status, pc, str, m->status_data);
+		g_free (str);
+	}
+}
+
+static void
+send_queue_exec (struct _send_queue_msg *m,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+	CamelFolder *sent_folder;
+	GPtrArray *uids, *send_uids = NULL;
+	gint i, j;
+	GError *local_error = NULL;
+
+	d(printf("sending queue\n"));
+
+	sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+	if (!(uids = camel_folder_get_uids (m->queue)))
+		return;
+
+	send_uids = g_ptr_array_sized_new (uids->len);
+	for (i = 0, j = 0; i < uids->len; i++) {
+		CamelMessageInfo *info;
+
+		info = camel_folder_get_message_info (m->queue, uids->pdata[i]);
+		if (info) {
+			if ((camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED) == 0)
+				send_uids->pdata[j++] = uids->pdata[i];
+			camel_folder_free_message_info (m->queue, info);
+		}
+	}
+
+	send_uids->len = j;
+	if (send_uids->len == 0) {
+		/* nothing to send */
+		camel_folder_free_uids (m->queue, uids);
+		g_ptr_array_free (send_uids, TRUE);
+		return;
+	}
+
+	camel_operation_push_message (cancellable, _("Sending message"));
+
+	/* NB: This code somewhat abuses the 'exception' stuff.  Apart from
+	 *     fatal problems, it is also used as a mechanism to accumualte
+	 *     warning messages and present them back to the user. */
+
+	for (i = 0, j = 0; i < send_uids->len; i++) {
+		gint pc = (100 * i) / send_uids->len;
+
+		report_status (
+			m, CAMEL_FILTER_STATUS_START, pc,
+			_("Sending message %d of %d"), i+1,
+			send_uids->len);
+
+		camel_operation_progress (
+			cancellable, (i + 1) * 100 / send_uids->len);
+
+		mail_send_message (
+			m, m->queue, send_uids->pdata[i], m->transport,
+			m->driver, cancellable, &local_error);
+		if (local_error != NULL) {
+			if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+				/* merge exceptions into one */
+				if (m->base.error != NULL) {
+					gchar *old_message;
+
+					old_message = g_strdup (
+						m->base.error->message);
+					g_clear_error (&m->base.error);
+					g_set_error (
+						&m->base.error, CAMEL_ERROR,
+						CAMEL_ERROR_GENERIC,
+						"%s\n\n%s", old_message,
+						local_error->message);
+					g_free (old_message);
+
+					g_clear_error (&local_error);
+				} else {
+					g_propagate_error (&m->base.error, local_error);
+					local_error = NULL;
+				}
+
+				/* keep track of the number of failures */
+				j++;
+			} else {
+				/* transfer the USER_CANCEL error to the
+				 * async op exception and then break */
+				g_propagate_error (&m->base.error, local_error);
+				local_error = NULL;
+				break;
+			}
+		}
+	}
+
+	j += (send_uids->len - i);
+
+	if (j > 0)
+		report_status (
+			m, CAMEL_FILTER_STATUS_END, 100,
+			_("Failed to send %d of %d messages"),
+			j, send_uids->len);
+	else if (g_error_matches (
+			m->base.error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Canceled."));
+	else
+		report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Complete."));
+
+	if (m->driver) {
+		g_object_unref (m->driver);
+		m->driver = NULL;
+	}
+
+	camel_folder_free_uids (m->queue, uids);
+	g_ptr_array_free (send_uids, TRUE);
+
+	/* FIXME Not passing a GCancellable or GError here. */
+	if (j <= 0 && m->base.error == NULL)
+		camel_folder_synchronize_sync (m->queue, TRUE, NULL, NULL);
+
+	/* FIXME Not passing a GCancellable or GError here. */
+	if (sent_folder)
+		camel_folder_synchronize_sync (sent_folder, FALSE, NULL, NULL);
+
+	camel_operation_pop_message (cancellable);
+}
+
+static void
+send_queue_done (struct _send_queue_msg *m)
+{
+	if (m->done)
+		m->done (m->data);
+}
+
+static gchar *
+send_queue_desc (struct _send_queue_msg *m)
+{
+	return g_strdup (_("Sending message"));
+}
+
+static void
+send_queue_free (struct _send_queue_msg *m)
+{
+	if (m->session != NULL)
+		g_object_unref (m->session);
+	if (m->driver != NULL)
+		g_object_unref (m->driver);
+	if (m->transport != NULL)
+		g_object_unref (m->transport);
+	g_object_unref (m->queue);
+}
+
+static MailMsgInfo send_queue_info = {
+	sizeof (struct _send_queue_msg),
+	(MailMsgDescFunc) send_queue_desc,
+	(MailMsgExecFunc) send_queue_exec,
+	(MailMsgDoneFunc) send_queue_done,
+	(MailMsgFreeFunc) send_queue_free
+};
+
+/* same interface as fetch_mail, just 'cause i'm lazy today
+ * (and we need to run it from the same spot?) */
+void
+mail_send_queue (EMailSession *session,
+                 CamelFolder *queue,
+                 CamelTransport *transport,
+                 const gchar *type,
+                 GCancellable *cancellable,
+                 CamelFilterGetFolderFunc get_folder,
+                 gpointer get_data,
+                 CamelFilterStatusFunc *status,
+                 gpointer status_data,
+                 void (*done)(gpointer data),
+                 gpointer data)
+{
+	struct _send_queue_msg *m;
+
+
+	m = mail_msg_new (&send_queue_info);
+	m->session = g_object_ref (session);
+	m->queue = g_object_ref (queue);
+	m->transport = g_object_ref (transport);
+	if (G_IS_CANCELLABLE (cancellable))
+		m->base.cancellable = cancellable;
+	m->status = status;
+	m->status_data = status_data;
+	m->done = done;
+	m->data = data;
+
+	m->driver = camel_session_get_filter_driver (
+		CAMEL_SESSION (session), type, NULL);
+	camel_filter_driver_set_folder_func (m->driver, get_folder, get_data);
+
+	mail_msg_unordered_push (m);
+}
+
+/* ** TRANSFER MESSAGES **************************************************** */
+
+struct _transfer_msg {
+	MailMsg base;
+
+	EMailSession *session;
+	CamelFolder *source;
+	GPtrArray *uids;
+	gboolean delete;
+	gchar *dest_uri;
+	guint32 dest_flags;
+
+	void (*done)(gboolean ok, gpointer data);
+	gpointer data;
+};
+
+static gchar *
+transfer_messages_desc (struct _transfer_msg *m)
+{
+	return g_strdup_printf (
+		m->delete ?
+			_("Moving messages to '%s'") :
+			_("Copying messages to '%s'"),
+		m->dest_uri);
+
+}
+
+static void
+transfer_messages_exec (struct _transfer_msg *m,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+	CamelFolder *dest;
+
+	dest = e_mail_session_uri_to_folder_sync (
+		m->session, m->dest_uri, m->dest_flags,
+		cancellable, error);
+	if (dest == NULL)
+		return;
+
+	if (dest == m->source) {
+		g_object_unref (dest);
+		/* no-op */
+		return;
+	}
+
+	camel_folder_freeze (m->source);
+	camel_folder_freeze (dest);
+
+	camel_folder_transfer_messages_to_sync (
+		m->source, m->uids, dest, m->delete, NULL,
+		cancellable, error);
+
+	/* make sure all deleted messages are marked as seen */
+
+	if (m->delete) {
+		gint i;
+
+		for (i = 0; i < m->uids->len; i++)
+			camel_folder_set_message_flags (
+				m->source, m->uids->pdata[i],
+				CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+	}
+
+	camel_folder_thaw (m->source);
+	camel_folder_thaw (dest);
+
+	/* FIXME Not passing a GCancellable or GError here. */
+	camel_folder_synchronize_sync (dest, FALSE, NULL, NULL);
+	g_object_unref (dest);
+}
+
+static void
+transfer_messages_done (struct _transfer_msg *m)
+{
+	if (m->done)
+		m->done (m->base.error == NULL, m->data);
+}
+
+static void
+transfer_messages_free (struct _transfer_msg *m)
+{
+	g_object_unref (m->session);
+	g_object_unref (m->source);
+	g_free (m->dest_uri);
+	em_utils_uids_free (m->uids);
+}
+
+static MailMsgInfo transfer_messages_info = {
+	sizeof (struct _transfer_msg),
+	(MailMsgDescFunc) transfer_messages_desc,
+	(MailMsgExecFunc) transfer_messages_exec,
+	(MailMsgDoneFunc) transfer_messages_done,
+	(MailMsgFreeFunc) transfer_messages_free
+};
+
+void
+mail_transfer_messages (EMailSession *session,
+                        CamelFolder *source,
+                        GPtrArray *uids,
+                        gboolean delete_from_source,
+                        const gchar *dest_uri,
+                        guint32 dest_flags,
+                        void (*done) (gboolean ok,
+                                      gpointer data),
+                        gpointer data)
+{
+	struct _transfer_msg *m;
+
+	g_return_if_fail (CAMEL_IS_FOLDER (source));
+	g_return_if_fail (uids != NULL);
+	g_return_if_fail (dest_uri != NULL);
+
+	m = mail_msg_new (&transfer_messages_info);
+	m->session = g_object_ref (session);
+	m->source = g_object_ref (source);
+	m->uids = uids;
+	m->delete = delete_from_source;
+	m->dest_uri = g_strdup (dest_uri);
+	m->dest_flags = dest_flags;
+	m->done = done;
+	m->data = data;
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ** SYNC FOLDER ********************************************************* */
+
+struct _sync_folder_msg {
+	MailMsg base;
+
+	EMailSession *session;
+	CamelFolder *folder;
+	void (*done) (CamelFolder *folder, gpointer data);
+	gpointer data;
+};
+
+static gchar *
+sync_folder_desc (struct _sync_folder_msg *m)
+{
+	return g_strdup_printf (_("Storing folder '%s'"),
+			       camel_folder_get_full_name (m->folder));
+}
+
+static void
+sync_folder_exec (struct _sync_folder_msg *m,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+	camel_folder_synchronize_sync (
+		m->folder, FALSE, cancellable, error);
+}
+
+static void
+sync_folder_done (struct _sync_folder_msg *m)
+{
+	if (m->done)
+		m->done (m->folder, m->data);
+}
+
+static void
+sync_folder_free (struct _sync_folder_msg *m)
+{
+	if (m->session)
+		g_object_unref (m->session);
+
+	if (m->folder)
+		g_object_unref (m->folder);
+}
+
+static MailMsgInfo sync_folder_info = {
+	sizeof (struct _sync_folder_msg),
+	(MailMsgDescFunc) sync_folder_desc,
+	(MailMsgExecFunc) sync_folder_exec,
+	(MailMsgDoneFunc) sync_folder_done,
+	(MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_sync_folder (CamelFolder *folder,
+                  void (*done) (CamelFolder *folder,
+                                gpointer data),
+                  gpointer data)
+{
+	struct _sync_folder_msg *m;
+
+	m = mail_msg_new (&sync_folder_info);
+	m->folder = g_object_ref (folder);
+	m->data = data;
+	m->done = done;
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ** SYNC STORE ********************************************************* */
+
+struct _sync_store_msg {
+	MailMsg base;
+
+	CamelStore *store;
+	gint expunge;
+	void (*done) (CamelStore *store, gpointer data);
+	gpointer data;
+};
+
+static gchar *
+sync_store_desc (struct _sync_store_msg *m)
+{
+	CamelURL *url;
+	gchar *uri, *res;
+
+	url = camel_service_get_camel_url (CAMEL_SERVICE (m->store));
+	uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+
+	res = g_strdup_printf (m->expunge
+			      ?_("Expunging and storing account '%s'")
+			      :_("Storing account '%s'"),
+			      uri);
+	g_free (uri);
+
+	return res;
+}
+
+static void
+sync_store_exec (struct _sync_store_msg *m,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+	camel_store_synchronize_sync (
+		m->store, m->expunge,
+		cancellable, error);
+}
+
+static void
+sync_store_done (struct _sync_store_msg *m)
+{
+	if (m->done)
+		m->done (m->store, m->data);
+}
+
+static void
+sync_store_free (struct _sync_store_msg *m)
+{
+	g_object_unref (m->store);
+}
+
+static MailMsgInfo sync_store_info = {
+	sizeof (struct _sync_store_msg),
+	(MailMsgDescFunc) sync_store_desc,
+	(MailMsgExecFunc) sync_store_exec,
+	(MailMsgDoneFunc) sync_store_done,
+	(MailMsgFreeFunc) sync_store_free
+};
+
+void
+mail_sync_store (CamelStore *store,
+                 gint expunge,
+                 void (*done) (CamelStore *store,
+                               gpointer data),
+                 gpointer data)
+{
+	struct _sync_store_msg *m;
+
+	m = mail_msg_new (&sync_store_info);
+	m->store = g_object_ref (store);
+	m->expunge = expunge;
+	m->data = data;
+	m->done = done;
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+static gchar *
+refresh_folder_desc (struct _sync_folder_msg *m)
+{
+	return g_strdup_printf (
+		_("Refreshing folder '%s'"),
+		camel_folder_get_full_name (m->folder));
+}
+
+static void
+refresh_folder_exec (struct _sync_folder_msg *m,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+	camel_folder_refresh_info_sync (
+		m->folder, cancellable, error);
+}
+
+/* we just use the sync stuff where we can, since it would be the same */
+static MailMsgInfo refresh_folder_info = {
+	sizeof (struct _sync_folder_msg),
+	(MailMsgDescFunc) refresh_folder_desc,
+	(MailMsgExecFunc) refresh_folder_exec,
+	(MailMsgDoneFunc) sync_folder_done,
+	(MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_refresh_folder (CamelFolder *folder,
+                     void (*done) (CamelFolder *folder,
+                                   gpointer data),
+                     gpointer data)
+{
+	struct _sync_folder_msg *m;
+
+	m = mail_msg_new (&refresh_folder_info);
+	m->folder = g_object_ref (folder);
+	m->data = data;
+	m->done = done;
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+static gboolean
+folder_is_from_source_uid (CamelFolder *folder,
+                           const gchar *source_uid)
+{
+	CamelStore *store;
+	const gchar *uid;
+
+	store = camel_folder_get_parent_store (folder);
+	uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+	return (g_strcmp0 (uid, source_uid) == 0);
+}
+
+/* This is because pop3 accounts are hidden under local Inbox,
+ * thus whenever an expunge is done on a local trash or Inbox,
+ * then also all active pop3 accounts should be expunged. */
+static gboolean
+expunge_pop3_stores (CamelFolder *expunging,
+                     EMailSession *session,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+	GHashTable *expunging_uids;
+	GPtrArray *uids;
+	EAccount *account;
+	EIterator *iter;
+	gboolean success = TRUE;
+	guint ii;
+
+	uids = camel_folder_get_uids (expunging);
+
+	if (uids == NULL)
+		return TRUE;
+
+	expunging_uids = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_free);
+
+	for (ii = 0; ii < uids->len; ii++) {
+		CamelMessageInfo *info;
+		CamelMessageFlags flags = 0;
+		CamelMimeMessage *message;
+		const gchar *pop3_uid;
+		const gchar *source_uid;
+
+		info = camel_folder_get_message_info (
+			expunging, uids->pdata[ii]);
+
+		if (info != NULL) {
+			flags = camel_message_info_flags (info);
+			camel_folder_free_message_info (expunging, info);
+		}
+
+		/* Only interested in deleted messages. */
+		if ((flags & CAMEL_MESSAGE_DELETED) == 0)
+			continue;
+
+		/* because the UID in the local store doesn't
+		 * match with the UID in the pop3 store */
+		message = camel_folder_get_message_sync (
+			expunging, uids->pdata[ii], cancellable, NULL);
+
+		if (message == NULL)
+			continue;
+
+		pop3_uid = camel_medium_get_header (
+			CAMEL_MEDIUM (message), "X-Evolution-POP3-UID");
+		source_uid = camel_mime_message_get_source (message);
+
+		if (pop3_uid != NULL)
+			g_hash_table_insert (
+				expunging_uids,
+				g_strstrip (g_strdup (pop3_uid)),
+				g_strstrip (g_strdup (source_uid)));
+
+		g_object_unref (message);
+	}
+
+	camel_folder_free_uids (expunging, uids);
+	uids = NULL;
+
+	if (g_hash_table_size (expunging_uids) == 0) {
+		g_hash_table_destroy (expunging_uids);
+		return TRUE;
+	}
+
+	for (iter = e_list_get_iterator ((EList *) e_get_account_list ());
+	     e_iterator_is_valid (iter); e_iterator_next (iter)) {
+		account = (EAccount *) e_iterator_get (iter);
+
+		if (account->enabled &&
+		    account->source && account->source->url &&
+		    g_str_has_prefix (account->source->url, "pop://")) {
+			CamelFolder *folder;
+			gboolean any_found = FALSE;
+
+			folder = e_mail_session_get_inbox_sync (
+				session, account->uid, cancellable, error);
+
+			/* Abort the loop on error. */
+			if (folder == NULL) {
+				success = FALSE;
+				break;
+			}
+
+			uids = camel_folder_get_uids (folder);
+			if (uids) {
+				for (ii = 0; ii < uids->len; ii++) {
+					/* ensure the ID is from this account,
+					 * as it's generated by evolution */
+					const gchar *source_uid;
+
+					source_uid = g_hash_table_lookup (
+						expunging_uids, uids->pdata[ii]);
+					if (folder_is_from_source_uid (folder, source_uid)) {
+						any_found = TRUE;
+						camel_folder_delete_message (folder, uids->pdata[ii]);
+					}
+				}
+				camel_folder_free_uids (folder, uids);
+			}
+
+			if (any_found)
+				success = camel_folder_synchronize_sync (folder, TRUE, cancellable, error);
+
+			g_object_unref (folder);
+
+			/* Abort the loop on error. */
+			if (!success)
+				break;
+		}
+	}
+
+	if (iter)
+		g_object_unref (iter);
+
+	g_hash_table_destroy (expunging_uids);
+
+	return success;
+}
+
+static gchar *
+expunge_folder_desc (struct _sync_folder_msg *m)
+{
+	return g_strdup_printf (
+		_("Expunging folder '%s'"),
+		camel_folder_get_full_name (m->folder));
+}
+
+static void
+expunge_folder_exec (struct _sync_folder_msg *m,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+	CamelFolder *local_inbox;
+	CamelStore *local_store;
+	CamelStore *parent_store;
+	gboolean is_local_inbox_or_trash;
+	gboolean success = TRUE;
+
+	local_inbox = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_INBOX);
+	is_local_inbox_or_trash = (m->folder == local_inbox);
+
+	local_store = e_mail_local_get_store ();
+	parent_store = camel_folder_get_parent_store (m->folder);
+
+	if (!is_local_inbox_or_trash && local_store == parent_store) {
+		CamelFolder *trash;
+
+		trash = camel_store_get_trash_folder_sync (
+			parent_store, cancellable, error);
+
+		if (trash == NULL)
+			return;
+
+		is_local_inbox_or_trash = (m->folder == trash);
+
+		g_object_unref (trash);
+	}
+
+	/* do this before expunge, to know which messages will be expunged */
+	if (is_local_inbox_or_trash)
+		success = expunge_pop3_stores (
+			m->folder, m->session, cancellable, error);
+
+	if (success)
+		camel_folder_expunge_sync (m->folder, cancellable, error);
+}
+
+/* we just use the sync stuff where we can, since it would be the same */
+static MailMsgInfo expunge_folder_info = {
+	sizeof (struct _sync_folder_msg),
+	(MailMsgDescFunc) expunge_folder_desc,
+	(MailMsgExecFunc) expunge_folder_exec,
+	(MailMsgDoneFunc) sync_folder_done,
+	(MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_expunge_folder (EMailSession *session,
+                     CamelFolder *folder)
+{
+	struct _sync_folder_msg *m;
+
+	m = mail_msg_new (&expunge_folder_info);
+	m->session = g_object_ref (session);
+	m->folder = g_object_ref (folder);
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+struct _empty_trash_msg {
+	MailMsg base;
+
+	EMailSession *session;
+	CamelStore *store;
+};
+
+static gchar *
+empty_trash_desc (struct _empty_trash_msg *m)
+{
+	CamelService *service;
+	const gchar *display_name;
+
+	service = CAMEL_SERVICE (m->store);
+	display_name = camel_service_get_display_name (service);
+
+	return g_strdup_printf (
+		_("Emptying trash in '%s'"), display_name);
+}
+
+static void
+empty_trash_exec (struct _empty_trash_msg *m,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+	CamelService *service;
+	CamelFolder *trash;
+	const gchar *uid;
+	gboolean success = TRUE;
+
+	service = CAMEL_SERVICE (m->store);
+	uid = camel_service_get_uid (service);
+
+	if (!em_utils_connect_service_sync (service, cancellable, error))
+		return;
+
+	trash = camel_store_get_trash_folder_sync (
+		m->store, cancellable, error);
+
+	if (trash == NULL)
+		return;
+
+	/* do this before expunge, to know which messages will be expunged */
+	if (g_strcmp0 (uid, "local") == 0)
+		success = expunge_pop3_stores (
+			trash, m->session, cancellable, error);
+
+	if (success)
+		camel_folder_expunge_sync (trash, cancellable, error);
+
+	g_object_unref (trash);
+}
+
+static void
+empty_trash_done (struct _empty_trash_msg *m)
+{
+}
+
+static void
+empty_trash_free (struct _empty_trash_msg *m)
+{
+	if (m->session)
+		g_object_unref (m->session);
+	if (m->store)
+		g_object_unref (m->store);
+}
+
+static MailMsgInfo empty_trash_info = {
+	sizeof (struct _empty_trash_msg),
+	(MailMsgDescFunc) empty_trash_desc,
+	(MailMsgExecFunc) empty_trash_exec,
+	(MailMsgDoneFunc) empty_trash_done,
+	(MailMsgFreeFunc) empty_trash_free
+};
+
+void
+mail_empty_trash (EMailSession *session,
+                  CamelStore *store)
+{
+	struct _empty_trash_msg *m;
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+
+	m = mail_msg_new (&empty_trash_info);
+	m->session = g_object_ref (session);
+	m->store = g_object_ref (store);
+
+	mail_msg_slow_ordered_push (m);
+}
+
+/* ** Execute Shell Command ************************************************ */
+
+void
+mail_execute_shell_command (CamelFilterDriver *driver,
+                            gint argc,
+                            gchar **argv,
+                            gpointer data)
+{
+	if (argc <= 0)
+		return;
+
+	g_spawn_async (NULL, argv, NULL, 0, NULL, data, NULL, NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct _disconnect_msg {
+	MailMsg base;
+
+	CamelStore *store;
+};
+
+static gchar *
+disconnect_service_desc (struct _disconnect_msg *m)
+{
+	gchar *name, *res;
+
+	name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
+	res = g_strdup_printf (_("Disconnecting %s"), name ? name : "");
+	g_free (name);
+
+	return res;
+}
+
+static void
+disconnect_service_exec (struct _disconnect_msg *m,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+	em_utils_disconnect_service_sync (
+		CAMEL_SERVICE (m->store), TRUE, cancellable, error);
+}
+
+static void
+disconnect_service_free (struct _disconnect_msg *m)
+{
+	g_object_unref (m->store);
+}
+
+static MailMsgInfo disconnect_service_info = {
+	sizeof (struct _disconnect_msg),
+	(MailMsgDescFunc) disconnect_service_desc,
+	(MailMsgExecFunc) disconnect_service_exec,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) disconnect_service_free
+};
+
+gint
+mail_disconnect_store (CamelStore *store)
+{
+	struct _disconnect_msg *m;
+	gint id;
+
+	g_return_val_if_fail (store != NULL, -1);
+
+	m = mail_msg_new (&disconnect_service_info);
+	m->store = g_object_ref (store);
+
+	id = m->base.seq;
+	mail_msg_unordered_push (m);
+
+	return id;
+}
+
diff --git a/libemail-engine/mail-ops.h b/libemail-engine/mail-ops.h
new file mode 100644
index 0000000..63d8528
--- /dev/null
+++ b/libemail-engine/mail-ops.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Peter Williams <peterw ximian com>
+ *		Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_OPS_H
+#define MAIL_OPS_H
+
+G_BEGIN_DECLS
+
+#include <camel/camel.h>
+#include <libemail-engine/e-mail-session.h>
+
+#include <libemail-utils/mail-mt.h>
+
+void		mail_transfer_messages		(EMailSession *session,
+						 CamelFolder *source,
+						 GPtrArray *uids,
+						 gboolean delete_from_source,
+						 const gchar *dest_uri,
+						 guint32 dest_flags,
+						 void (*done) (gboolean ok, gpointer data),
+						 gpointer data);
+
+void mail_sync_folder (CamelFolder *folder,
+		       void (*done) (CamelFolder *folder, gpointer data), gpointer data);
+
+void mail_sync_store (CamelStore *store, gint expunge,
+		     void (*done) (CamelStore *store, gpointer data), gpointer data);
+
+void mail_refresh_folder (CamelFolder *folder,
+			  void (*done) (CamelFolder *folder, gpointer data),
+			  gpointer data);
+
+void		mail_expunge_folder		(EMailSession *session,
+						 CamelFolder *folder);
+
+void		mail_empty_trash		(EMailSession *session,
+						 CamelStore *store);
+
+/* transfer (copy/move) a folder */
+void mail_xfer_folder (const gchar *src_uri, const gchar *dest_uri, gboolean remove_source,
+		       void (*done) (gchar *src_uri, gchar *dest_uri, gboolean remove_source,
+				     CamelFolder *folder, gpointer data),
+		       gpointer data);
+
+/* yeah so this is messy, but it does a lot, maybe i can consolidate all user_data's to be the one */
+void		mail_send_queue			(EMailSession *session,
+						 CamelFolder *queue,
+						 CamelTransport *transport,
+						 const gchar *type,
+						 GCancellable *cancellable,
+						 CamelFilterGetFolderFunc get_folder,
+						 gpointer get_data,
+						 CamelFilterStatusFunc *status,
+						 gpointer status_data,
+						 void (*done)(gpointer data),
+						 gpointer data);
+
+void		mail_fetch_mail			(CamelStore *store,
+						 gint keep,
+						 const gchar *type,
+						 GCancellable *cancellable,
+						 CamelFilterGetFolderFunc get_folder,
+						 gpointer get_data,
+						 CamelFilterStatusFunc *status,
+						 gpointer status_data,
+						 void (*done)(gpointer data),
+						 gpointer data);
+
+void		mail_filter_folder		(EMailSession *session,
+						 CamelFolder *source_folder,
+						 GPtrArray *uids,
+						 const gchar *type,
+						 gboolean notify);
+
+/* filter driver execute shell command async callback */
+void mail_execute_shell_command (CamelFilterDriver *driver, gint argc, gchar **argv, gpointer data);
+
+gint mail_disconnect_store (CamelStore *store);
+
+G_END_DECLS
+
+#endif /* MAIL_OPS_H */
diff --git a/libemail-engine/mail-tools.c b/libemail-engine/mail-tools.c
new file mode 100644
index 0000000..135df53
--- /dev/null
+++ b/libemail-engine/mail-tools.c
@@ -0,0 +1,234 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Dan Winship <danw ximian com>
+ *		Peter Williams <peterw ximian com>
+ *		Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <glib/gi18n.h>
+
+#include "e-mail-session.h"
+#include "mail-folder-cache.h"
+#include "mail-tools.h"
+
+/* **************************************** */
+
+#ifndef G_OS_WIN32
+
+static gchar *
+mail_tool_get_local_movemail_path (CamelStore *store,
+                                   GError **error)
+{
+	const gchar *uid;
+	guchar *safe_uid, *c;
+	const gchar *data_dir;
+	gchar *path, *full;
+	struct stat st;
+
+	uid = camel_service_get_uid (CAMEL_SERVICE (store));
+	safe_uid = (guchar *) g_strdup ((const gchar *) uid);
+	for (c = safe_uid; *c; c++)
+		if (strchr("/:;=|%&#!*^()\\, ", *c) || !isprint((gint) *c))
+			*c = '_';
+
+	data_dir = mail_session_get_data_dir ();
+	path = g_build_filename (data_dir, "spool", NULL);
+
+	if (g_stat (path, &st) == -1 && g_mkdir_with_parents (path, 0700) == -1) {
+		g_set_error (
+			error, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			_("Could not create spool directory '%s': %s"),
+			path, g_strerror (errno));
+		g_free (path);
+		return NULL;
+	}
+
+	full = g_strdup_printf("%s/movemail.%s", path, safe_uid);
+	g_free (path);
+	g_free (safe_uid);
+
+	return full;
+}
+
+#endif
+
+gchar *
+mail_tool_do_movemail (CamelStore *store,
+                       GError **error)
+{
+#ifndef G_OS_WIN32
+	gchar *dest_path;
+	struct stat sb;
+	CamelURL *url;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+
+	url = camel_service_get_camel_url (CAMEL_SERVICE (store));
+
+	if (strcmp (url->protocol, "mbox") != 0) {
+		/* This is really only an internal error anyway */
+		g_set_error (
+			error, CAMEL_SERVICE_ERROR,
+			CAMEL_SERVICE_ERROR_URL_INVALID,
+			_("Trying to movemail a non-mbox source '%s'"),
+			camel_service_get_uid (CAMEL_SERVICE (store)));
+		return NULL;
+	}
+
+	/* Set up our destination. */
+	dest_path = mail_tool_get_local_movemail_path (store, error);
+	if (dest_path == NULL)
+		return NULL;
+
+	/* Movemail from source (source_url) to dest_path */
+	success = camel_movemail (url->path, dest_path, error) != -1;
+
+	if (g_stat (dest_path, &sb) < 0 || sb.st_size == 0) {
+		g_unlink (dest_path); /* Clean up the movemail.foo file. */
+		g_free (dest_path);
+		return NULL;
+	}
+
+	if (!success) {
+		g_free (dest_path);
+		return NULL;
+	}
+
+	return dest_path;
+#else
+	/* Unclear yet whether camel-movemail etc makes any sense on
+	 * Win32, at least it is not ported yet.
+	 */
+	g_warning("%s: Not implemented", __FUNCTION__);
+	return NULL;
+#endif
+}
+
+gchar *
+mail_tool_generate_forward_subject (CamelMimeMessage *msg)
+{
+	const gchar *subject;
+	gchar *fwd_subj;
+	const gint max_subject_length = 1024;
+
+	subject = camel_mime_message_get_subject (msg);
+
+	if (subject && *subject) {
+		/* Truncate insanely long subjects */
+		if (strlen (subject) < max_subject_length) {
+			fwd_subj = g_strdup_printf ("[Fwd: %s]", subject);
+		} else {
+			/* We can't use %.*s because it depends on the
+			 * locale being C/POSIX or UTF-8 to work correctly
+			 * in glibc. */
+			fwd_subj = g_malloc (max_subject_length + 11);
+			memcpy (fwd_subj, "[Fwd: ", 6);
+			memcpy (fwd_subj + 6, subject, max_subject_length);
+			memcpy (fwd_subj + 6 + max_subject_length, "...]", 5);
+		}
+	} else {
+		const CamelInternetAddress *from;
+		gchar *fromstr;
+
+		from = camel_mime_message_get_from (msg);
+		if (from) {
+			fromstr = camel_address_format (CAMEL_ADDRESS (from));
+			fwd_subj = g_strdup_printf ("[Fwd: %s]", fromstr);
+			g_free (fromstr);
+		} else
+			fwd_subj = g_strdup ("[Fwd: No Subject]");
+	}
+
+	return fwd_subj;
+}
+
+struct _camel_header_raw *
+mail_tool_remove_xevolution_headers (CamelMimeMessage *message)
+{
+	struct _camel_header_raw *scan, *list = NULL;
+
+	for (scan = ((CamelMimePart *) message)->headers; scan; scan = scan->next)
+		if (!strncmp(scan->name, "X-Evolution", 11))
+			camel_header_raw_append (&list, scan->name, scan->value, scan->offset);
+
+	for (scan = list; scan; scan = scan->next)
+		camel_medium_remove_header ((CamelMedium *) message, scan->name);
+
+	return list;
+}
+
+void
+mail_tool_restore_xevolution_headers (CamelMimeMessage *message,
+                                      struct _camel_header_raw *xev)
+{
+	CamelMedium *medium;
+
+	medium = CAMEL_MEDIUM (message);
+
+	for (; xev; xev = xev->next)
+		camel_medium_add_header (medium, xev->name, xev->value);
+}
+
+CamelMimePart *
+mail_tool_make_message_attachment (CamelMimeMessage *message)
+{
+	CamelMimePart *part;
+	const gchar *subject;
+	struct _camel_header_raw *xev;
+	gchar *desc;
+
+	subject = camel_mime_message_get_subject (message);
+	if (subject)
+		desc = g_strdup_printf (_("Forwarded message - %s"), subject);
+	else
+		desc = g_strdup (_("Forwarded message"));
+
+	/* rip off the X-Evolution headers */
+	xev = mail_tool_remove_xevolution_headers (message);
+	camel_header_raw_clear (&xev);
+
+	/* remove Bcc headers */
+	camel_medium_remove_header (CAMEL_MEDIUM (message), "Bcc");
+
+	part = camel_mime_part_new ();
+	camel_mime_part_set_disposition (part, "inline");
+	camel_mime_part_set_description (part, desc);
+	camel_medium_set_content (
+		CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (message));
+	camel_mime_part_set_content_type (part, "message/rfc822");
+	g_free (desc);
+
+	return part;
+}
diff --git a/libemail-engine/mail-tools.h b/libemail-engine/mail-tools.h
new file mode 100644
index 0000000..94b19c0
--- /dev/null
+++ b/libemail-engine/mail-tools.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Peter Williams <peterw ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_TOOLS_H
+#define MAIL_TOOLS_H
+
+#include <camel/camel.h>
+
+/* Does a camel_movemail into the local movemail folder
+ * and returns the path to the new movemail folder that was created. which shoudl be freed later */
+gchar *mail_tool_do_movemail (CamelStore *store, GError **error);
+
+struct _camel_header_raw *mail_tool_remove_xevolution_headers (CamelMimeMessage *message);
+void mail_tool_restore_xevolution_headers (CamelMimeMessage *message, struct _camel_header_raw *);
+
+/* Generates the subject for a message forwarding @msg */
+gchar *mail_tool_generate_forward_subject (CamelMimeMessage *msg);
+
+/* Make a message into an attachment */
+CamelMimePart *mail_tool_make_message_attachment (CamelMimeMessage *message);
+
+#endif
diff --git a/libemail-utils/Makefile.am b/libemail-utils/Makefile.am
new file mode 100644
index 0000000..6bc3164
--- /dev/null
+++ b/libemail-utils/Makefile.am
@@ -0,0 +1,57 @@
+
+lib_LTLIBRARIES = libemail-utils.la
+
+AM_CFLAGS = -I. -I$(top_builddir) @GNOME_PLATFORM_CFLAGS@ -Wall -Werror -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\""
+AM_LDFLAGS = @GNOME_PLATFORM_LIBS@ 
+
+include $(top_srcdir)/glib-gen.mak
+glib_enum_prefix=e
+MARSHAL_GENERATED = e-marshal.c e-marshal.h
+
+libemail_utils_la_CPPFLAGS = \
+	$(AM_CPPFLAGS)	\
+	$(CAMEL_CFLAGS)		\
+	$(GNOME_PLATFORM_CFLAGS) \
+	$(EMAIL_UTILS_CFLAGS)	\
+	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"	
+
+libemail_utils_la_LIBADD = 	\
+		$(CAMEL_LIBS)		\
+		$(GNOME_PLATFORM_LIBS)	\
+		$(EMAIL_UTILS_LIBS)
+
+libemail_utils_la_LDFLAGS = 
+
+libemail_utils_la_SOURCES = 	\
+		e-marshal.c	\
+		e-account-utils.c	\
+		mail-mt.c	\
+		e-signature.c	\
+		e-signature-list.c	\
+		e-signature-utils.c	\
+		gconf-bridge.c
+
+
+libmailutilsincludedir = $(privincludedir)/libemail-utils
+libmailutilsinclude_HEADERS = 	\
+		e-marshal.h	\
+		e-account-utils.h	\
+		mail-mt.h	\
+		e-signature.h	\
+		e-signature-list.h	\
+		e-signature-utils.h	\
+		gconf-bridge.h
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libemail-utils.pc
+
+BUILT_SOURCES = $(MARSHAL_GENERATED) 
+
+EXTRA_DIST = e-marshal.list
+CLEANFILES    = $(BUILT_SOURCES)
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+dist-hook:
+	cd $(distdir); rm -f $(BUILT_SOURCES)
+
diff --git a/libemail-utils/e-account-utils.c b/libemail-utils/e-account-utils.c
new file mode 100644
index 0000000..6e64d45
--- /dev/null
+++ b/libemail-utils/e-account-utils.c
@@ -0,0 +1,252 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+/**
+ * SECTION: e-account-utils
+ * @include: e-util/e-account-utils.h
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-account-utils.h"
+
+#include <string.h>
+#include <gconf/gconf-client.h>
+
+static EAccountList *global_account_list;
+
+static gboolean
+account_has_transport_url (EAccount *account)
+{
+	return (account != NULL) &&
+		(account->enabled) &&
+		(account->transport != NULL) &&
+		(account->transport->url != NULL) &&
+		(account->transport->url[0] != '\0');
+}
+
+/**
+ * e_get_account_list:
+ *
+ * Returns the global #EAccountList.
+ *
+ * Returns: the global #EAccountList
+ **/
+EAccountList *
+e_get_account_list (void)
+{
+	if (G_UNLIKELY (global_account_list == NULL)) {
+		GConfClient *client;
+
+		client = gconf_client_get_default ();
+		global_account_list = e_account_list_new (client);
+		g_object_unref (client);
+	}
+
+	g_return_val_if_fail (global_account_list != NULL, NULL);
+
+	return global_account_list;
+}
+
+/**
+ * e_get_default_account:
+ *
+ * Returns the #EAccount marked as the default mail account.
+ *
+ * Returns: the default #EAccount
+ **/
+EAccount *
+e_get_default_account (void)
+{
+	EAccountList *account_list;
+	const EAccount *account;
+
+	account_list = e_get_account_list ();
+	account = e_account_list_get_default (account_list);
+
+	/* XXX EAccountList misuses const. */
+	return (EAccount *) account;
+}
+
+/**
+ * e_set_default_account:
+ * @account: an #EAccount
+ *
+ * Marks @account as the default mail account.
+ **/
+void
+e_set_default_account (EAccount *account)
+{
+	EAccountList *account_list;
+
+	g_return_if_fail (E_IS_ACCOUNT (account));
+
+	account_list = e_get_account_list ();
+	e_account_list_set_default (account_list, account);
+}
+
+/**
+ * e_get_account_by_name:
+ * @name: a mail account name
+ *
+ * Returns the #EAccount with the given name, or %NULL if no such
+ * account exists.
+ *
+ * Returns: an #EAccount having the given account name, or %NULL
+ **/
+EAccount *
+e_get_account_by_name (const gchar *name)
+{
+	EAccountList *account_list;
+	const EAccount *account;
+	e_account_find_t find;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	find = E_ACCOUNT_FIND_NAME;
+	account_list = e_get_account_list ();
+	account = e_account_list_find (account_list, find, name);
+
+	/* XXX EAccountList misuses const. */
+	return (EAccount *) account;
+}
+
+/**
+ * e_get_account_by_uid:
+ * @uid: a mail account UID
+ *
+ * Returns the #EAccount corresponding to the given unique identity (UID),
+ * or %NULL if no such account exists.  The @uid can refer to an #EAccount
+ * UID, a #CamelStore UID, or even a #CamelTransport UID.
+ *
+ * Returns: the corresponding #EAccount, or %NULL
+ **/
+EAccount *
+e_get_account_by_uid (const gchar *uid)
+{
+	EAccountList *account_list;
+	const EAccount *account;
+	e_account_find_t find;
+	gchar *account_uid;
+
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	/* EAccounts have the following invariant:
+	 *
+	 *       CamelStore UID == EAccount UID
+	 *   CamelTransport UID == EAccount UID + "-transport"
+	 *
+	 * Therefore we can detect CamelTransport UIDs and convert them.
+	 */
+	if (g_str_has_suffix (uid, "-transport"))
+		account_uid = g_strndup (uid, strlen (uid) - 10);
+	else
+		account_uid = g_strdup (uid);
+
+	find = E_ACCOUNT_FIND_UID;
+	account_list = e_get_account_list ();
+	account = e_account_list_find (account_list, find, account_uid);
+
+	g_free (account_uid);
+
+	/* XXX EAccountList misuses const. */
+	return (EAccount *) account;
+}
+
+/**
+ * e_get_any_enabled_account:
+ *
+ * Returns the default mail account if it's enabled, otherwise the first
+ * enabled mail account in the global #EAccountList, or finally %NULL if
+ * all mail accounts are disabled or none exist.
+ *
+ * Returns: an enabled #EAccount, or %NULL if there are none
+ **/
+EAccount *
+e_get_any_enabled_account (void)
+{
+	EAccount *account;
+	EAccountList *account_list;
+	EIterator *iter;
+
+	account = e_get_default_account ();
+	if (account != NULL && account->enabled)
+		return account;
+
+	account = NULL;
+
+	account_list = e_get_account_list ();
+	iter = e_list_get_iterator (E_LIST (account_list));
+
+	while (e_iterator_is_valid (iter) && account == NULL) {
+		EAccount *candidate;
+
+		/* XXX EIterator misuses const. */
+		candidate = (EAccount *) e_iterator_get (iter);
+
+		if (candidate->enabled)
+			account = candidate;
+		else
+			e_iterator_next (iter);
+	}
+
+	g_object_unref (iter);
+
+	return account;
+}
+
+/**
+ * e_get_default_transport:
+ *
+ * Returns transport information for the default account if it's enabled and
+ * has transport information, or else from the first enabled mail account in
+ * the global #EAccountList that has transport information, or finally %NULL
+ * if no transport information could be found.
+ *
+ * Returns: an #EAccount with transport info, or %NULL
+ **/
+EAccount *
+e_get_default_transport (void)
+{
+	EAccountList *account_list;
+	EAccount *account;
+	EIterator *iterator;
+
+	account = e_get_default_account ();
+	if (account_has_transport_url (account))
+		return account;
+
+	account_list = e_get_account_list ();
+	iterator = e_list_get_iterator (E_LIST (account_list));
+
+	while (e_iterator_is_valid (iterator)) {
+		/* XXX EIterator misuses const. */
+		account = (EAccount *) e_iterator_get (iterator);
+		if (account_has_transport_url (account)) {
+			g_object_unref (iterator);
+			return account;
+		}
+		e_iterator_next (iterator);
+	}
+
+	g_object_unref (iterator);
+
+	return NULL;
+}
+
diff --git a/libemail-utils/e-account-utils.h b/libemail-utils/e-account-utils.h
new file mode 100644
index 0000000..d7dbd28
--- /dev/null
+++ b/libemail-utils/e-account-utils.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_ACCOUNT_UTILS_H
+#define E_ACCOUNT_UTILS_H
+
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libedataserver/e-account-list.h>
+
+G_BEGIN_DECLS
+
+EAccountList *	e_get_account_list		(void);
+EAccount *	e_get_default_account		(void);
+void		e_set_default_account		(EAccount *account);
+EAccount *	e_get_account_by_name		(const gchar *name);
+EAccount *	e_get_account_by_uid		(const gchar *uid);
+EAccount *	e_get_any_enabled_account	(void);
+EAccount *	e_get_default_transport		(void);
+
+G_END_DECLS
+
+#endif /* E_ACCOUNT_UTILS_H */
diff --git a/libemail-utils/e-marshal.c b/libemail-utils/e-marshal.c
new file mode 100644
index 0000000..0c7c621
--- /dev/null
+++ b/libemail-utils/e-marshal.c
@@ -0,0 +1,2492 @@
+#include "e-marshal.h"
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:BOXED (e-marshal.list:1) */
+void
+e_marshal_BOOLEAN__BOXED (GClosure     *closure,
+                          GValue       *return_value G_GNUC_UNUSED,
+                          guint         n_param_values,
+                          const GValue *param_values,
+                          gpointer      invocation_hint G_GNUC_UNUSED,
+                          gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer     data1,
+                                                   gpointer     arg_1,
+                                                   gpointer     data2);
+  register GMarshalFunc_BOOLEAN__BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:BOXED,STRING (e-marshal.list:2) */
+void
+e_marshal_BOOLEAN__BOXED_STRING (GClosure     *closure,
+                                 GValue       *return_value G_GNUC_UNUSED,
+                                 guint         n_param_values,
+                                 const GValue *param_values,
+                                 gpointer      invocation_hint G_GNUC_UNUSED,
+                                 gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_STRING) (gpointer     data1,
+                                                          gpointer     arg_1,
+                                                          gpointer     arg_2,
+                                                          gpointer     data2);
+  register GMarshalFunc_BOOLEAN__BOXED_STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__BOXED_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       g_marshal_value_peek_string (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:BOXED,POINTER,POINTER (e-marshal.list:3) */
+void
+e_marshal_BOOLEAN__BOXED_POINTER_POINTER (GClosure     *closure,
+                                          GValue       *return_value G_GNUC_UNUSED,
+                                          guint         n_param_values,
+                                          const GValue *param_values,
+                                          gpointer      invocation_hint G_GNUC_UNUSED,
+                                          gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_POINTER_POINTER) (gpointer     data1,
+                                                                   gpointer     arg_1,
+                                                                   gpointer     arg_2,
+                                                                   gpointer     arg_3,
+                                                                   gpointer     data2);
+  register GMarshalFunc_BOOLEAN__BOXED_POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       g_marshal_value_peek_pointer (param_values + 3),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,INT,BOXED (e-marshal.list:4) */
+void
+e_marshal_BOOLEAN__INT_INT_BOXED (GClosure     *closure,
+                                  GValue       *return_value G_GNUC_UNUSED,
+                                  guint         n_param_values,
+                                  const GValue *param_values,
+                                  gpointer      invocation_hint G_GNUC_UNUSED,
+                                  gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_BOXED) (gpointer     data1,
+                                                           gint         arg_1,
+                                                           gint         arg_2,
+                                                           gpointer     arg_3,
+                                                           gpointer     data2);
+  register GMarshalFunc_BOOLEAN__INT_INT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_int (param_values + 2),
+                       g_marshal_value_peek_boxed (param_values + 3),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,INT,OBJECT,INT,INT,UINT (e-marshal.list:5) */
+void
+e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT (GClosure     *closure,
+                                                GValue       *return_value G_GNUC_UNUSED,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                                gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT) (gpointer     data1,
+                                                                         gint         arg_1,
+                                                                         gint         arg_2,
+                                                                         gpointer     arg_3,
+                                                                         gint         arg_4,
+                                                                         gint         arg_5,
+                                                                         guint        arg_6,
+                                                                         gpointer     data2);
+  register GMarshalFunc_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 7);
+
+  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_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_int (param_values + 2),
+                       g_marshal_value_peek_object (param_values + 3),
+                       g_marshal_value_peek_int (param_values + 4),
+                       g_marshal_value_peek_int (param_values + 5),
+                       g_marshal_value_peek_uint (param_values + 6),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,POINTER,INT,BOXED (e-marshal.list:6) */
+void
+e_marshal_BOOLEAN__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                          GValue       *return_value G_GNUC_UNUSED,
+                                          guint         n_param_values,
+                                          const GValue *param_values,
+                                          gpointer      invocation_hint G_GNUC_UNUSED,
+                                          gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_POINTER_INT_BOXED) (gpointer     data1,
+                                                                   gint         arg_1,
+                                                                   gpointer     arg_2,
+                                                                   gint         arg_3,
+                                                                   gpointer     arg_4,
+                                                                   gpointer     data2);
+  register GMarshalFunc_BOOLEAN__INT_POINTER_INT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 5);
+
+  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_BOOLEAN__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       g_marshal_value_peek_int (param_values + 3),
+                       g_marshal_value_peek_boxed (param_values + 4),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT (e-marshal.list:7) */
+void
+e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT (GClosure     *closure,
+                                                        GValue       *return_value G_GNUC_UNUSED,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                                        gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT) (gpointer     data1,
+                                                                                 gint         arg_1,
+                                                                                 gpointer     arg_2,
+                                                                                 gint         arg_3,
+                                                                                 gpointer     arg_4,
+                                                                                 gint         arg_5,
+                                                                                 gint         arg_6,
+                                                                                 guint        arg_7,
+                                                                                 gpointer     data2);
+  register GMarshalFunc_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 8);
+
+  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_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       g_marshal_value_peek_int (param_values + 3),
+                       g_marshal_value_peek_object (param_values + 4),
+                       g_marshal_value_peek_int (param_values + 5),
+                       g_marshal_value_peek_int (param_values + 6),
+                       g_marshal_value_peek_uint (param_values + 7),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:NONE (e-marshal.list:8) */
+void
+e_marshal_BOOLEAN__VOID (GClosure     *closure,
+                         GValue       *return_value G_GNUC_UNUSED,
+                         guint         n_param_values,
+                         const GValue *param_values,
+                         gpointer      invocation_hint G_GNUC_UNUSED,
+                         gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__VOID) (gpointer     data1,
+                                                  gpointer     data2);
+  register GMarshalFunc_BOOLEAN__VOID callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 1);
+
+  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_BOOLEAN__VOID) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:OBJECT (e-marshal.list:9) */
+void
+e_marshal_BOOLEAN__OBJECT (GClosure     *closure,
+                           GValue       *return_value G_GNUC_UNUSED,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint G_GNUC_UNUSED,
+                           gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     data2);
+  register GMarshalFunc_BOOLEAN__OBJECT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_object (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:10) */
+void
+e_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                                 GValue       *return_value G_GNUC_UNUSED,
+                                                 guint         n_param_values,
+                                                 const GValue *param_values,
+                                                 gpointer      invocation_hint G_GNUC_UNUSED,
+                                                 gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer     data1,
+                                                                          gpointer     arg_1,
+                                                                          gdouble      arg_2,
+                                                                          gdouble      arg_3,
+                                                                          gboolean     arg_4,
+                                                                          gpointer     data2);
+  register GMarshalFunc_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 5);
+
+  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_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_object (param_values + 1),
+                       g_marshal_value_peek_double (param_values + 2),
+                       g_marshal_value_peek_double (param_values + 3),
+                       g_marshal_value_peek_boolean (param_values + 4),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER (e-marshal.list:11) */
+void
+e_marshal_BOOLEAN__POINTER (GClosure     *closure,
+                            GValue       *return_value G_GNUC_UNUSED,
+                            guint         n_param_values,
+                            const GValue *param_values,
+                            gpointer      invocation_hint G_GNUC_UNUSED,
+                            gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer     data1,
+                                                     gpointer     arg_1,
+                                                     gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,BOOLEAN,POINTER (e-marshal.list:12) */
+void
+e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER (GClosure     *closure,
+                                            GValue       *return_value G_GNUC_UNUSED,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint G_GNUC_UNUSED,
+                                            gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_BOOLEAN_POINTER) (gpointer     data1,
+                                                                     gpointer     arg_1,
+                                                                     gboolean     arg_2,
+                                                                     gpointer     arg_3,
+                                                                     gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_BOOLEAN_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__POINTER_BOOLEAN_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_boolean (param_values + 2),
+                       g_marshal_value_peek_pointer (param_values + 3),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,POINTER (e-marshal.list:13) */
+void
+e_marshal_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                    GValue       *return_value G_GNUC_UNUSED,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint G_GNUC_UNUSED,
+                                    gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
+                                                             gpointer     arg_1,
+                                                             gpointer     arg_2,
+                                                             gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,POINTER,POINTER,POINTER (e-marshal.list:14) */
+void
+e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER (GClosure     *closure,
+                                                    GValue       *return_value G_GNUC_UNUSED,
+                                                    guint         n_param_values,
+                                                    const GValue *param_values,
+                                                    gpointer      invocation_hint G_GNUC_UNUSED,
+                                                    gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER_POINTER_POINTER) (gpointer     data1,
+                                                                             gpointer     arg_1,
+                                                                             gpointer     arg_2,
+                                                                             gpointer     arg_3,
+                                                                             gpointer     arg_4,
+                                                                             gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_POINTER_POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 5);
+
+  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_BOOLEAN__POINTER_POINTER_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       g_marshal_value_peek_pointer (param_values + 3),
+                       g_marshal_value_peek_pointer (param_values + 4),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:STRING (e-marshal.list:15) */
+void
+e_marshal_BOOLEAN__STRING (GClosure     *closure,
+                           GValue       *return_value G_GNUC_UNUSED,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint G_GNUC_UNUSED,
+                           gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     data2);
+  register GMarshalFunc_BOOLEAN__STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_string (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:STRING,INT (e-marshal.list:16) */
+void
+e_marshal_BOOLEAN__STRING_INT (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_INT) (gpointer     data1,
+                                                        gpointer     arg_1,
+                                                        gint         arg_2,
+                                                        gpointer     data2);
+  register GMarshalFunc_BOOLEAN__STRING_INT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_BOOLEAN__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_string (param_values + 1),
+                       g_marshal_value_peek_int (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:17) */
+void
+e_marshal_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                                GValue       *return_value G_GNUC_UNUSED,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                                gpointer      marshal_data)
+{
+  typedef gdouble (*GMarshalFunc_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer     data1,
+                                                                        gpointer     arg_1,
+                                                                        gdouble      arg_2,
+                                                                        gdouble      arg_3,
+                                                                        gboolean     arg_4,
+                                                                        gpointer     data2);
+  register GMarshalFunc_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gdouble v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 5);
+
+  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_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_object (param_values + 1),
+                       g_marshal_value_peek_double (param_values + 2),
+                       g_marshal_value_peek_double (param_values + 3),
+                       g_marshal_value_peek_boolean (param_values + 4),
+                       data2);
+
+  g_value_set_double (return_value, v_return);
+}
+
+/* INT:BOXED (e-marshal.list:18) */
+void
+e_marshal_INT__BOXED (GClosure     *closure,
+                      GValue       *return_value G_GNUC_UNUSED,
+                      guint         n_param_values,
+                      const GValue *param_values,
+                      gpointer      invocation_hint G_GNUC_UNUSED,
+                      gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__BOXED) (gpointer     data1,
+                                           gpointer     arg_1,
+                                           gpointer     data2);
+  register GMarshalFunc_INT__BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_INT__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT (e-marshal.list:19) */
+void
+e_marshal_INT__INT (GClosure     *closure,
+                    GValue       *return_value G_GNUC_UNUSED,
+                    guint         n_param_values,
+                    const GValue *param_values,
+                    gpointer      invocation_hint G_GNUC_UNUSED,
+                    gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__INT) (gpointer     data1,
+                                         gint         arg_1,
+                                         gpointer     data2);
+  register GMarshalFunc_INT__INT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_INT__INT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT,INT,BOXED (e-marshal.list:20) */
+void
+e_marshal_INT__INT_INT_BOXED (GClosure     *closure,
+                              GValue       *return_value G_GNUC_UNUSED,
+                              guint         n_param_values,
+                              const GValue *param_values,
+                              gpointer      invocation_hint G_GNUC_UNUSED,
+                              gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__INT_INT_BOXED) (gpointer     data1,
+                                                   gint         arg_1,
+                                                   gint         arg_2,
+                                                   gpointer     arg_3,
+                                                   gpointer     data2);
+  register GMarshalFunc_INT__INT_INT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_INT__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_int (param_values + 2),
+                       g_marshal_value_peek_boxed (param_values + 3),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT,POINTER,INT,BOXED (e-marshal.list:21) */
+void
+e_marshal_INT__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                      GValue       *return_value G_GNUC_UNUSED,
+                                      guint         n_param_values,
+                                      const GValue *param_values,
+                                      gpointer      invocation_hint G_GNUC_UNUSED,
+                                      gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__INT_POINTER_INT_BOXED) (gpointer     data1,
+                                                           gint         arg_1,
+                                                           gpointer     arg_2,
+                                                           gint         arg_3,
+                                                           gpointer     arg_4,
+                                                           gpointer     data2);
+  register GMarshalFunc_INT__INT_POINTER_INT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 5);
+
+  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_INT__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_int (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       g_marshal_value_peek_int (param_values + 3),
+                       g_marshal_value_peek_boxed (param_values + 4),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* INT:OBJECT,BOXED (e-marshal.list:22) */
+void
+e_marshal_INT__OBJECT_BOXED (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__OBJECT_BOXED) (gpointer     data1,
+                                                  gpointer     arg_1,
+                                                  gpointer     arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_INT__OBJECT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  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_INT__OBJECT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_object (param_values + 1),
+                       g_marshal_value_peek_boxed (param_values + 2),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* INT:POINTER (e-marshal.list:23) */
+void
+e_marshal_INT__POINTER (GClosure     *closure,
+                        GValue       *return_value G_GNUC_UNUSED,
+                        guint         n_param_values,
+                        const GValue *param_values,
+                        gpointer      invocation_hint G_GNUC_UNUSED,
+                        gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__POINTER) (gpointer     data1,
+                                             gpointer     arg_1,
+                                             gpointer     data2);
+  register GMarshalFunc_INT__POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  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_INT__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
+/* NONE:BOXED,INT (e-marshal.list:24) */
+void
+e_marshal_VOID__BOXED_INT (GClosure     *closure,
+                           GValue       *return_value G_GNUC_UNUSED,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint G_GNUC_UNUSED,
+                           gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__BOXED_INT) (gpointer     data1,
+                                                gpointer     arg_1,
+                                                gint         arg_2,
+                                                gpointer     data2);
+  register GMarshalFunc_VOID__BOXED_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__BOXED_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_boxed (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            data2);
+}
+
+/* NONE:ENUM,OBJECT,OBJECT (e-marshal.list:25) */
+void
+e_marshal_VOID__ENUM_OBJECT_OBJECT (GClosure     *closure,
+                                    GValue       *return_value G_GNUC_UNUSED,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint G_GNUC_UNUSED,
+                                    gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__ENUM_OBJECT_OBJECT) (gpointer     data1,
+                                                         gint         arg_1,
+                                                         gpointer     arg_2,
+                                                         gpointer     arg_3,
+                                                         gpointer     data2);
+  register GMarshalFunc_VOID__ENUM_OBJECT_OBJECT 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_OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_enum (param_values + 1),
+            g_marshal_value_peek_object (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            data2);
+}
+
+/* NONE:INT,INT (e-marshal.list:26) */
+void
+e_marshal_VOID__INT_INT (GClosure     *closure,
+                         GValue       *return_value G_GNUC_UNUSED,
+                         guint         n_param_values,
+                         const GValue *param_values,
+                         gpointer      invocation_hint G_GNUC_UNUSED,
+                         gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer     data1,
+                                              gint         arg_1,
+                                              gint         arg_2,
+                                              gpointer     data2);
+  register GMarshalFunc_VOID__INT_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__INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            data2);
+}
+
+/* NONE:INT,INT,BOXED (e-marshal.list:27) */
+void
+e_marshal_VOID__INT_INT_BOXED (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_BOXED) (gpointer     data1,
+                                                    gint         arg_1,
+                                                    gint         arg_2,
+                                                    gpointer     arg_3,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_BOXED 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__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_boxed (param_values + 3),
+            data2);
+}
+
+/* NONE:INT,INT,OBJECT (e-marshal.list:28) */
+void
+e_marshal_VOID__INT_INT_OBJECT (GClosure     *closure,
+                                GValue       *return_value G_GNUC_UNUSED,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT) (gpointer     data1,
+                                                     gint         arg_1,
+                                                     gint         arg_2,
+                                                     gpointer     arg_3,
+                                                     gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_OBJECT 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__INT_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            data2);
+}
+
+/* NONE:INT,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:29) */
+void
+e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT (GClosure     *closure,
+                                                GValue       *return_value G_GNUC_UNUSED,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_BOXED_UINT_UINT) (gpointer     data1,
+                                                                     gint         arg_1,
+                                                                     gint         arg_2,
+                                                                     gpointer     arg_3,
+                                                                     gpointer     arg_4,
+                                                                     guint        arg_5,
+                                                                     guint        arg_6,
+                                                                     gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_OBJECT_BOXED_UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 7);
+
+  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__INT_INT_OBJECT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            g_marshal_value_peek_boxed (param_values + 4),
+            g_marshal_value_peek_uint (param_values + 5),
+            g_marshal_value_peek_uint (param_values + 6),
+            data2);
+}
+
+/* NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:30) */
+void
+e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure     *closure,
+                                                        GValue       *return_value G_GNUC_UNUSED,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                                        gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (gpointer     data1,
+                                                                             gint         arg_1,
+                                                                             gint         arg_2,
+                                                                             gpointer     arg_3,
+                                                                             gint         arg_4,
+                                                                             gint         arg_5,
+                                                                             gpointer     arg_6,
+                                                                             guint        arg_7,
+                                                                             guint        arg_8,
+                                                                             gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 9);
+
+  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__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            g_marshal_value_peek_int (param_values + 4),
+            g_marshal_value_peek_int (param_values + 5),
+            g_marshal_value_peek_boxed (param_values + 6),
+            g_marshal_value_peek_uint (param_values + 7),
+            g_marshal_value_peek_uint (param_values + 8),
+            data2);
+}
+
+/* NONE:INT,INT,OBJECT,UINT (e-marshal.list:31) */
+void
+e_marshal_VOID__INT_INT_OBJECT_UINT (GClosure     *closure,
+                                     GValue       *return_value G_GNUC_UNUSED,
+                                     guint         n_param_values,
+                                     const GValue *param_values,
+                                     gpointer      invocation_hint G_GNUC_UNUSED,
+                                     gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_UINT) (gpointer     data1,
+                                                          gint         arg_1,
+                                                          gint         arg_2,
+                                                          gpointer     arg_3,
+                                                          guint        arg_4,
+                                                          gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_OBJECT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  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__INT_INT_OBJECT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            g_marshal_value_peek_uint (param_values + 4),
+            data2);
+}
+
+/* NONE:INT,OBJECT (e-marshal.list:32) */
+void
+e_marshal_VOID__INT_OBJECT (GClosure     *closure,
+                            GValue       *return_value G_GNUC_UNUSED,
+                            guint         n_param_values,
+                            const GValue *param_values,
+                            gpointer      invocation_hint G_GNUC_UNUSED,
+                            gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_OBJECT) (gpointer     data1,
+                                                 gint         arg_1,
+                                                 gpointer     arg_2,
+                                                 gpointer     data2);
+  register GMarshalFunc_VOID__INT_OBJECT 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__INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_object (param_values + 2),
+            data2);
+}
+
+/* NONE:INT,POINTER (e-marshal.list:33) */
+void
+e_marshal_VOID__INT_POINTER (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer     data1,
+                                                  gint         arg_1,
+                                                  gpointer     arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER 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__INT_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            data2);
+}
+
+/* NONE:INT,POINTER,INT,BOXED (e-marshal.list:34) */
+void
+e_marshal_VOID__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                       GValue       *return_value G_GNUC_UNUSED,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint G_GNUC_UNUSED,
+                                       gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_BOXED) (gpointer     data1,
+                                                            gint         arg_1,
+                                                            gpointer     arg_2,
+                                                            gint         arg_3,
+                                                            gpointer     arg_4,
+                                                            gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER_INT_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  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__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_boxed (param_values + 4),
+            data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT (e-marshal.list:35) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT (GClosure     *closure,
+                                        GValue       *return_value G_GNUC_UNUSED,
+                                        guint         n_param_values,
+                                        const GValue *param_values,
+                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                        gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT) (gpointer     data1,
+                                                             gint         arg_1,
+                                                             gpointer     arg_2,
+                                                             gint         arg_3,
+                                                             gpointer     arg_4,
+                                                             gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  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__INT_POINTER_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_object (param_values + 4),
+            data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:36) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT (GClosure     *closure,
+                                                        GValue       *return_value G_GNUC_UNUSED,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                                        gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT) (gpointer     data1,
+                                                                             gint         arg_1,
+                                                                             gpointer     arg_2,
+                                                                             gint         arg_3,
+                                                                             gpointer     arg_4,
+                                                                             gpointer     arg_5,
+                                                                             guint        arg_6,
+                                                                             guint        arg_7,
+                                                                             gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 8);
+
+  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__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_object (param_values + 4),
+            g_marshal_value_peek_boxed (param_values + 5),
+            g_marshal_value_peek_uint (param_values + 6),
+            g_marshal_value_peek_uint (param_values + 7),
+            data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:37) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure     *closure,
+                                                                GValue       *return_value G_GNUC_UNUSED,
+                                                                guint         n_param_values,
+                                                                const GValue *param_values,
+                                                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (gpointer     data1,
+                                                                                     gint         arg_1,
+                                                                                     gpointer     arg_2,
+                                                                                     gint         arg_3,
+                                                                                     gpointer     arg_4,
+                                                                                     gint         arg_5,
+                                                                                     gint         arg_6,
+                                                                                     gpointer     arg_7,
+                                                                                     guint        arg_8,
+                                                                                     guint        arg_9,
+                                                                                     gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 10);
+
+  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__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_object (param_values + 4),
+            g_marshal_value_peek_int (param_values + 5),
+            g_marshal_value_peek_int (param_values + 6),
+            g_marshal_value_peek_boxed (param_values + 7),
+            g_marshal_value_peek_uint (param_values + 8),
+            g_marshal_value_peek_uint (param_values + 9),
+            data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,UINT (e-marshal.list:38) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT (GClosure     *closure,
+                                             GValue       *return_value G_GNUC_UNUSED,
+                                             guint         n_param_values,
+                                             const GValue *param_values,
+                                             gpointer      invocation_hint G_GNUC_UNUSED,
+                                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_UINT) (gpointer     data1,
+                                                                  gint         arg_1,
+                                                                  gpointer     arg_2,
+                                                                  gint         arg_3,
+                                                                  gpointer     arg_4,
+                                                                  guint        arg_5,
+                                                                  gpointer     data2);
+  register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 6);
+
+  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__INT_POINTER_INT_OBJECT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_object (param_values + 4),
+            g_marshal_value_peek_uint (param_values + 5),
+            data2);
+}
+
+/* NONE:LONG,LONG (e-marshal.list:39) */
+void
+e_marshal_VOID__LONG_LONG (GClosure     *closure,
+                           GValue       *return_value G_GNUC_UNUSED,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint G_GNUC_UNUSED,
+                           gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__LONG_LONG) (gpointer     data1,
+                                                glong        arg_1,
+                                                glong        arg_2,
+                                                gpointer     data2);
+  register GMarshalFunc_VOID__LONG_LONG 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__LONG_LONG) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_long (param_values + 1),
+            g_marshal_value_peek_long (param_values + 2),
+            data2);
+}
+
+/* NONE:OBJECT,BOOLEAN (e-marshal.list:40) */
+void
+e_marshal_VOID__OBJECT_BOOLEAN (GClosure     *closure,
+                                GValue       *return_value G_GNUC_UNUSED,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_BOOLEAN) (gpointer     data1,
+                                                     gpointer     arg_1,
+                                                     gboolean     arg_2,
+                                                     gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_BOOLEAN 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__OBJECT_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_boolean (param_values + 2),
+            data2);
+}
+
+/* NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:41) */
+void
+e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                              GValue       *return_value G_GNUC_UNUSED,
+                                              guint         n_param_values,
+                                              const GValue *param_values,
+                                              gpointer      invocation_hint G_GNUC_UNUSED,
+                                              gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer     data1,
+                                                                   gpointer     arg_1,
+                                                                   gdouble      arg_2,
+                                                                   gdouble      arg_3,
+                                                                   gboolean     arg_4,
+                                                                   gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  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__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_double (param_values + 2),
+            g_marshal_value_peek_double (param_values + 3),
+            g_marshal_value_peek_boolean (param_values + 4),
+            data2);
+}
+
+/* NONE:OBJECT,OBJECT (e-marshal.list:42) */
+void
+e_marshal_VOID__OBJECT_OBJECT (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_OBJECT 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__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_object (param_values + 2),
+            data2);
+}
+
+/* NONE:OBJECT,STRING (e-marshal.list:43) */
+void
+e_marshal_VOID__OBJECT_STRING (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_STRING) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_STRING 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__OBJECT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            data2);
+}
+
+/* NONE:OBJECT,STRING,INT (e-marshal.list:44) */
+void
+e_marshal_VOID__OBJECT_STRING_INT (GClosure     *closure,
+                                   GValue       *return_value G_GNUC_UNUSED,
+                                   guint         n_param_values,
+                                   const GValue *param_values,
+                                   gpointer      invocation_hint G_GNUC_UNUSED,
+                                   gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_STRING_INT) (gpointer     data1,
+                                                        gpointer     arg_1,
+                                                        gpointer     arg_2,
+                                                        gint         arg_3,
+                                                        gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_STRING_INT 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__OBJECT_STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            data2);
+}
+
+/* NONE:OBJECT,STRING,INT,STRING,STRING,STRING (e-marshal.list:45) */
+void
+e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING (GClosure     *closure,
+                                                        GValue       *return_value G_GNUC_UNUSED,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                                        gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_STRING_INT_STRING_STRING_STRING) (gpointer     data1,
+                                                                             gpointer     arg_1,
+                                                                             gpointer     arg_2,
+                                                                             gint         arg_3,
+                                                                             gpointer     arg_4,
+                                                                             gpointer     arg_5,
+                                                                             gpointer     arg_6,
+                                                                             gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_STRING_INT_STRING_STRING_STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 7);
+
+  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__OBJECT_STRING_INT_STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_string (param_values + 4),
+            g_marshal_value_peek_string (param_values + 5),
+            g_marshal_value_peek_string (param_values + 6),
+            data2);
+}
+
+/* NONE:OBJECT,STRING,STRING (e-marshal.list:46) */
+void
+e_marshal_VOID__OBJECT_STRING_STRING (GClosure     *closure,
+                                      GValue       *return_value G_GNUC_UNUSED,
+                                      guint         n_param_values,
+                                      const GValue *param_values,
+                                      gpointer      invocation_hint G_GNUC_UNUSED,
+                                      gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_STRING_STRING) (gpointer     data1,
+                                                           gpointer     arg_1,
+                                                           gpointer     arg_2,
+                                                           gpointer     arg_3,
+                                                           gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_STRING_STRING 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__OBJECT_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            g_marshal_value_peek_string (param_values + 3),
+            data2);
+}
+
+/* NONE:OBJECT,STRING,UINT (e-marshal.list:47) */
+void
+e_marshal_VOID__OBJECT_STRING_UINT (GClosure     *closure,
+                                    GValue       *return_value G_GNUC_UNUSED,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint G_GNUC_UNUSED,
+                                    gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_STRING_UINT) (gpointer     data1,
+                                                         gpointer     arg_1,
+                                                         gpointer     arg_2,
+                                                         guint        arg_3,
+                                                         gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_STRING_UINT 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__OBJECT_STRING_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            g_marshal_value_peek_uint (param_values + 3),
+            data2);
+}
+
+/* NONE:POINTER,INT (e-marshal.list:48) */
+void
+e_marshal_VOID__POINTER_INT (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_INT) (gpointer     data1,
+                                                  gpointer     arg_1,
+                                                  gint         arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_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__POINTER_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            data2);
+}
+
+/* NONE:POINTER,INT,INT,INT,INT (e-marshal.list:49) */
+void
+e_marshal_VOID__POINTER_INT_INT_INT_INT (GClosure     *closure,
+                                         GValue       *return_value G_GNUC_UNUSED,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint G_GNUC_UNUSED,
+                                         gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_INT_INT_INT_INT) (gpointer     data1,
+                                                              gpointer     arg_1,
+                                                              gint         arg_2,
+                                                              gint         arg_3,
+                                                              gint         arg_4,
+                                                              gint         arg_5,
+                                                              gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_INT_INT_INT_INT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 6);
+
+  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__POINTER_INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_int (param_values + 4),
+            g_marshal_value_peek_int (param_values + 5),
+            data2);
+}
+
+/* NONE:POINTER,INT,OBJECT (e-marshal.list:50) */
+void
+e_marshal_VOID__POINTER_INT_OBJECT (GClosure     *closure,
+                                    GValue       *return_value G_GNUC_UNUSED,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint G_GNUC_UNUSED,
+                                    gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_INT_OBJECT) (gpointer     data1,
+                                                         gpointer     arg_1,
+                                                         gint         arg_2,
+                                                         gpointer     arg_3,
+                                                         gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_INT_OBJECT 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__POINTER_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_object (param_values + 3),
+            data2);
+}
+
+/* NONE:POINTER,OBJECT (e-marshal.list:51) */
+void
+e_marshal_VOID__POINTER_OBJECT (GClosure     *closure,
+                                GValue       *return_value G_GNUC_UNUSED,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_OBJECT) (gpointer     data1,
+                                                     gpointer     arg_1,
+                                                     gpointer     arg_2,
+                                                     gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_OBJECT 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__POINTER_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_object (param_values + 2),
+            data2);
+}
+
+/* NONE:POINTER,POINTER (e-marshal.list:52) */
+void
+e_marshal_VOID__POINTER_POINTER (GClosure     *closure,
+                                 GValue       *return_value G_GNUC_UNUSED,
+                                 guint         n_param_values,
+                                 const GValue *param_values,
+                                 gpointer      invocation_hint G_GNUC_UNUSED,
+                                 gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer     data1,
+                                                      gpointer     arg_1,
+                                                      gpointer     arg_2,
+                                                      gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_POINTER 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__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            data2);
+}
+
+/* NONE:POINTER,POINTER,INT (e-marshal.list:53) */
+void
+e_marshal_VOID__POINTER_POINTER_INT (GClosure     *closure,
+                                     GValue       *return_value G_GNUC_UNUSED,
+                                     guint         n_param_values,
+                                     const GValue *param_values,
+                                     gpointer      invocation_hint G_GNUC_UNUSED,
+                                     gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_POINTER_INT) (gpointer     data1,
+                                                          gpointer     arg_1,
+                                                          gpointer     arg_2,
+                                                          gint         arg_3,
+                                                          gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_POINTER_INT 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__POINTER_POINTER_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            data2);
+}
+
+/* NONE:STRING,DOUBLE (e-marshal.list:54) */
+void
+e_marshal_VOID__STRING_DOUBLE (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_DOUBLE) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gdouble      arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__STRING_DOUBLE 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__STRING_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_double (param_values + 2),
+            data2);
+}
+
+/* NONE:STRING,INT (e-marshal.list:55) */
+void
+e_marshal_VOID__STRING_INT (GClosure     *closure,
+                            GValue       *return_value G_GNUC_UNUSED,
+                            guint         n_param_values,
+                            const GValue *param_values,
+                            gpointer      invocation_hint G_GNUC_UNUSED,
+                            gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer     data1,
+                                                 gpointer     arg_1,
+                                                 gint         arg_2,
+                                                 gpointer     data2);
+  register GMarshalFunc_VOID__STRING_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__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            data2);
+}
+
+/* NONE:STRING,INT,INT (e-marshal.list:56) */
+void
+e_marshal_VOID__STRING_INT_INT (GClosure     *closure,
+                                GValue       *return_value G_GNUC_UNUSED,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint G_GNUC_UNUSED,
+                                gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_INT_INT) (gpointer     data1,
+                                                     gpointer     arg_1,
+                                                     gint         arg_2,
+                                                     gint         arg_3,
+                                                     gpointer     data2);
+  register GMarshalFunc_VOID__STRING_INT_INT 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__STRING_INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            data2);
+}
+
+/* NONE:STRING,POINTER,POINTER (e-marshal.list:57) */
+void
+e_marshal_VOID__STRING_POINTER_POINTER (GClosure     *closure,
+                                        GValue       *return_value G_GNUC_UNUSED,
+                                        guint         n_param_values,
+                                        const GValue *param_values,
+                                        gpointer      invocation_hint G_GNUC_UNUSED,
+                                        gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_POINTER_POINTER) (gpointer     data1,
+                                                             gpointer     arg_1,
+                                                             gpointer     arg_2,
+                                                             gpointer     arg_3,
+                                                             gpointer     data2);
+  register GMarshalFunc_VOID__STRING_POINTER_POINTER 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__STRING_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            g_marshal_value_peek_pointer (param_values + 3),
+            data2);
+}
+
+/* NONE:STRING,STRING (e-marshal.list:58) */
+void
+e_marshal_VOID__STRING_STRING (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__STRING_STRING 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__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            data2);
+}
+
+/* NONE:UINT,STRING (e-marshal.list:59) */
+void
+e_marshal_VOID__UINT_STRING (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__UINT_STRING) (gpointer     data1,
+                                                  guint        arg_1,
+                                                  gpointer     arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_VOID__UINT_STRING 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__UINT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_uint (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            data2);
+}
+
+/* STRING:NONE (e-marshal.list:60) */
+void
+e_marshal_STRING__VOID (GClosure     *closure,
+                        GValue       *return_value G_GNUC_UNUSED,
+                        guint         n_param_values,
+                        const GValue *param_values,
+                        gpointer      invocation_hint G_GNUC_UNUSED,
+                        gpointer      marshal_data)
+{
+  typedef gchar* (*GMarshalFunc_STRING__VOID) (gpointer     data1,
+                                               gpointer     data2);
+  register GMarshalFunc_STRING__VOID callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gchar* v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 1);
+
+  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_STRING__VOID) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       data2);
+
+  g_value_take_string (return_value, v_return);
+}
+
diff --git a/libemail-utils/e-marshal.h b/libemail-utils/e-marshal.h
new file mode 100644
index 0000000..92c89d2
--- /dev/null
+++ b/libemail-utils/e-marshal.h
@@ -0,0 +1,530 @@
+
+#ifndef __e_marshal_MARSHAL_H__
+#define __e_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* BOOLEAN:BOXED (e-marshal.list:1) */
+extern void e_marshal_BOOLEAN__BOXED (GClosure     *closure,
+                                      GValue       *return_value,
+                                      guint         n_param_values,
+                                      const GValue *param_values,
+                                      gpointer      invocation_hint,
+                                      gpointer      marshal_data);
+
+/* BOOLEAN:BOXED,STRING (e-marshal.list:2) */
+extern void e_marshal_BOOLEAN__BOXED_STRING (GClosure     *closure,
+                                             GValue       *return_value,
+                                             guint         n_param_values,
+                                             const GValue *param_values,
+                                             gpointer      invocation_hint,
+                                             gpointer      marshal_data);
+
+/* BOOLEAN:BOXED,POINTER,POINTER (e-marshal.list:3) */
+extern void e_marshal_BOOLEAN__BOXED_POINTER_POINTER (GClosure     *closure,
+                                                      GValue       *return_value,
+                                                      guint         n_param_values,
+                                                      const GValue *param_values,
+                                                      gpointer      invocation_hint,
+                                                      gpointer      marshal_data);
+
+/* BOOLEAN:INT,INT,BOXED (e-marshal.list:4) */
+extern void e_marshal_BOOLEAN__INT_INT_BOXED (GClosure     *closure,
+                                              GValue       *return_value,
+                                              guint         n_param_values,
+                                              const GValue *param_values,
+                                              gpointer      invocation_hint,
+                                              gpointer      marshal_data);
+
+/* BOOLEAN:INT,INT,OBJECT,INT,INT,UINT (e-marshal.list:5) */
+extern void e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT (GClosure     *closure,
+                                                            GValue       *return_value,
+                                                            guint         n_param_values,
+                                                            const GValue *param_values,
+                                                            gpointer      invocation_hint,
+                                                            gpointer      marshal_data);
+
+/* BOOLEAN:INT,POINTER,INT,BOXED (e-marshal.list:6) */
+extern void e_marshal_BOOLEAN__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                                      GValue       *return_value,
+                                                      guint         n_param_values,
+                                                      const GValue *param_values,
+                                                      gpointer      invocation_hint,
+                                                      gpointer      marshal_data);
+
+/* BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT (e-marshal.list:7) */
+extern void e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT (GClosure     *closure,
+                                                                    GValue       *return_value,
+                                                                    guint         n_param_values,
+                                                                    const GValue *param_values,
+                                                                    gpointer      invocation_hint,
+                                                                    gpointer      marshal_data);
+
+/* BOOLEAN:NONE (e-marshal.list:8) */
+extern void e_marshal_BOOLEAN__VOID (GClosure     *closure,
+                                     GValue       *return_value,
+                                     guint         n_param_values,
+                                     const GValue *param_values,
+                                     gpointer      invocation_hint,
+                                     gpointer      marshal_data);
+#define e_marshal_BOOLEAN__NONE	e_marshal_BOOLEAN__VOID
+
+/* BOOLEAN:OBJECT (e-marshal.list:9) */
+extern void e_marshal_BOOLEAN__OBJECT (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data);
+
+/* BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:10) */
+extern void e_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                                             GValue       *return_value,
+                                                             guint         n_param_values,
+                                                             const GValue *param_values,
+                                                             gpointer      invocation_hint,
+                                                             gpointer      marshal_data);
+
+/* BOOLEAN:POINTER (e-marshal.list:11) */
+extern void e_marshal_BOOLEAN__POINTER (GClosure     *closure,
+                                        GValue       *return_value,
+                                        guint         n_param_values,
+                                        const GValue *param_values,
+                                        gpointer      invocation_hint,
+                                        gpointer      marshal_data);
+
+/* BOOLEAN:POINTER,BOOLEAN,POINTER (e-marshal.list:12) */
+extern void e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER (GClosure     *closure,
+                                                        GValue       *return_value,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint,
+                                                        gpointer      marshal_data);
+
+/* BOOLEAN:POINTER,POINTER (e-marshal.list:13) */
+extern void e_marshal_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                GValue       *return_value,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint,
+                                                gpointer      marshal_data);
+
+/* BOOLEAN:POINTER,POINTER,POINTER,POINTER (e-marshal.list:14) */
+extern void e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER (GClosure     *closure,
+                                                                GValue       *return_value,
+                                                                guint         n_param_values,
+                                                                const GValue *param_values,
+                                                                gpointer      invocation_hint,
+                                                                gpointer      marshal_data);
+
+/* BOOLEAN:STRING (e-marshal.list:15) */
+extern void e_marshal_BOOLEAN__STRING (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data);
+
+/* BOOLEAN:STRING,INT (e-marshal.list:16) */
+extern void e_marshal_BOOLEAN__STRING_INT (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+
+/* DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:17) */
+extern void e_marshal_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                                            GValue       *return_value,
+                                                            guint         n_param_values,
+                                                            const GValue *param_values,
+                                                            gpointer      invocation_hint,
+                                                            gpointer      marshal_data);
+
+/* INT:BOXED (e-marshal.list:18) */
+extern void e_marshal_INT__BOXED (GClosure     *closure,
+                                  GValue       *return_value,
+                                  guint         n_param_values,
+                                  const GValue *param_values,
+                                  gpointer      invocation_hint,
+                                  gpointer      marshal_data);
+
+/* INT:INT (e-marshal.list:19) */
+extern void e_marshal_INT__INT (GClosure     *closure,
+                                GValue       *return_value,
+                                guint         n_param_values,
+                                const GValue *param_values,
+                                gpointer      invocation_hint,
+                                gpointer      marshal_data);
+
+/* INT:INT,INT,BOXED (e-marshal.list:20) */
+extern void e_marshal_INT__INT_INT_BOXED (GClosure     *closure,
+                                          GValue       *return_value,
+                                          guint         n_param_values,
+                                          const GValue *param_values,
+                                          gpointer      invocation_hint,
+                                          gpointer      marshal_data);
+
+/* INT:INT,POINTER,INT,BOXED (e-marshal.list:21) */
+extern void e_marshal_INT__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                                  GValue       *return_value,
+                                                  guint         n_param_values,
+                                                  const GValue *param_values,
+                                                  gpointer      invocation_hint,
+                                                  gpointer      marshal_data);
+
+/* INT:OBJECT,BOXED (e-marshal.list:22) */
+extern void e_marshal_INT__OBJECT_BOXED (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+
+/* INT:POINTER (e-marshal.list:23) */
+extern void e_marshal_INT__POINTER (GClosure     *closure,
+                                    GValue       *return_value,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint,
+                                    gpointer      marshal_data);
+
+/* NONE:BOXED,INT (e-marshal.list:24) */
+extern void e_marshal_VOID__BOXED_INT (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data);
+#define e_marshal_NONE__BOXED_INT	e_marshal_VOID__BOXED_INT
+
+/* NONE:ENUM,OBJECT,OBJECT (e-marshal.list:25) */
+extern void e_marshal_VOID__ENUM_OBJECT_OBJECT (GClosure     *closure,
+                                                GValue       *return_value,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint,
+                                                gpointer      marshal_data);
+#define e_marshal_NONE__ENUM_OBJECT_OBJECT	e_marshal_VOID__ENUM_OBJECT_OBJECT
+
+/* NONE:INT,INT (e-marshal.list:26) */
+extern void e_marshal_VOID__INT_INT (GClosure     *closure,
+                                     GValue       *return_value,
+                                     guint         n_param_values,
+                                     const GValue *param_values,
+                                     gpointer      invocation_hint,
+                                     gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT	e_marshal_VOID__INT_INT
+
+/* NONE:INT,INT,BOXED (e-marshal.list:27) */
+extern void e_marshal_VOID__INT_INT_BOXED (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT_BOXED	e_marshal_VOID__INT_INT_BOXED
+
+/* NONE:INT,INT,OBJECT (e-marshal.list:28) */
+extern void e_marshal_VOID__INT_INT_OBJECT (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT	e_marshal_VOID__INT_INT_OBJECT
+
+/* NONE:INT,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:29) */
+extern void e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT (GClosure     *closure,
+                                                            GValue       *return_value,
+                                                            guint         n_param_values,
+                                                            const GValue *param_values,
+                                                            gpointer      invocation_hint,
+                                                            gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_BOXED_UINT_UINT	e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT
+
+/* NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:30) */
+extern void e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure     *closure,
+                                                                    GValue       *return_value,
+                                                                    guint         n_param_values,
+                                                                    const GValue *param_values,
+                                                                    gpointer      invocation_hint,
+                                                                    gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT	e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT
+
+/* NONE:INT,INT,OBJECT,UINT (e-marshal.list:31) */
+extern void e_marshal_VOID__INT_INT_OBJECT_UINT (GClosure     *closure,
+                                                 GValue       *return_value,
+                                                 guint         n_param_values,
+                                                 const GValue *param_values,
+                                                 gpointer      invocation_hint,
+                                                 gpointer      marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_UINT	e_marshal_VOID__INT_INT_OBJECT_UINT
+
+/* NONE:INT,OBJECT (e-marshal.list:32) */
+extern void e_marshal_VOID__INT_OBJECT (GClosure     *closure,
+                                        GValue       *return_value,
+                                        guint         n_param_values,
+                                        const GValue *param_values,
+                                        gpointer      invocation_hint,
+                                        gpointer      marshal_data);
+#define e_marshal_NONE__INT_OBJECT	e_marshal_VOID__INT_OBJECT
+
+/* NONE:INT,POINTER (e-marshal.list:33) */
+extern void e_marshal_VOID__INT_POINTER (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER	e_marshal_VOID__INT_POINTER
+
+/* NONE:INT,POINTER,INT,BOXED (e-marshal.list:34) */
+extern void e_marshal_VOID__INT_POINTER_INT_BOXED (GClosure     *closure,
+                                                   GValue       *return_value,
+                                                   guint         n_param_values,
+                                                   const GValue *param_values,
+                                                   gpointer      invocation_hint,
+                                                   gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_BOXED	e_marshal_VOID__INT_POINTER_INT_BOXED
+
+/* NONE:INT,POINTER,INT,OBJECT (e-marshal.list:35) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT (GClosure     *closure,
+                                                    GValue       *return_value,
+                                                    guint         n_param_values,
+                                                    const GValue *param_values,
+                                                    gpointer      invocation_hint,
+                                                    gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT	e_marshal_VOID__INT_POINTER_INT_OBJECT
+
+/* NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:36) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT (GClosure     *closure,
+                                                                    GValue       *return_value,
+                                                                    guint         n_param_values,
+                                                                    const GValue *param_values,
+                                                                    gpointer      invocation_hint,
+                                                                    gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT	e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT
+
+/* NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:37) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure     *closure,
+                                                                            GValue       *return_value,
+                                                                            guint         n_param_values,
+                                                                            const GValue *param_values,
+                                                                            gpointer      invocation_hint,
+                                                                            gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT	e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT
+
+/* NONE:INT,POINTER,INT,OBJECT,UINT (e-marshal.list:38) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT (GClosure     *closure,
+                                                         GValue       *return_value,
+                                                         guint         n_param_values,
+                                                         const GValue *param_values,
+                                                         gpointer      invocation_hint,
+                                                         gpointer      marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_UINT	e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT
+
+/* NONE:LONG,LONG (e-marshal.list:39) */
+extern void e_marshal_VOID__LONG_LONG (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data);
+#define e_marshal_NONE__LONG_LONG	e_marshal_VOID__LONG_LONG
+
+/* NONE:OBJECT,BOOLEAN (e-marshal.list:40) */
+extern void e_marshal_VOID__OBJECT_BOOLEAN (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_BOOLEAN	e_marshal_VOID__OBJECT_BOOLEAN
+
+/* NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:41) */
+extern void e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure     *closure,
+                                                          GValue       *return_value,
+                                                          guint         n_param_values,
+                                                          const GValue *param_values,
+                                                          gpointer      invocation_hint,
+                                                          gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_DOUBLE_DOUBLE_BOOLEAN	e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN
+
+/* NONE:OBJECT,OBJECT (e-marshal.list:42) */
+extern void e_marshal_VOID__OBJECT_OBJECT (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_OBJECT	e_marshal_VOID__OBJECT_OBJECT
+
+/* NONE:OBJECT,STRING (e-marshal.list:43) */
+extern void e_marshal_VOID__OBJECT_STRING (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_STRING	e_marshal_VOID__OBJECT_STRING
+
+/* NONE:OBJECT,STRING,INT (e-marshal.list:44) */
+extern void e_marshal_VOID__OBJECT_STRING_INT (GClosure     *closure,
+                                               GValue       *return_value,
+                                               guint         n_param_values,
+                                               const GValue *param_values,
+                                               gpointer      invocation_hint,
+                                               gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_INT	e_marshal_VOID__OBJECT_STRING_INT
+
+/* NONE:OBJECT,STRING,INT,STRING,STRING,STRING (e-marshal.list:45) */
+extern void e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING (GClosure     *closure,
+                                                                    GValue       *return_value,
+                                                                    guint         n_param_values,
+                                                                    const GValue *param_values,
+                                                                    gpointer      invocation_hint,
+                                                                    gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_INT_STRING_STRING_STRING	e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING
+
+/* NONE:OBJECT,STRING,STRING (e-marshal.list:46) */
+extern void e_marshal_VOID__OBJECT_STRING_STRING (GClosure     *closure,
+                                                  GValue       *return_value,
+                                                  guint         n_param_values,
+                                                  const GValue *param_values,
+                                                  gpointer      invocation_hint,
+                                                  gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_STRING	e_marshal_VOID__OBJECT_STRING_STRING
+
+/* NONE:OBJECT,STRING,UINT (e-marshal.list:47) */
+extern void e_marshal_VOID__OBJECT_STRING_UINT (GClosure     *closure,
+                                                GValue       *return_value,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint,
+                                                gpointer      marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_UINT	e_marshal_VOID__OBJECT_STRING_UINT
+
+/* NONE:POINTER,INT (e-marshal.list:48) */
+extern void e_marshal_VOID__POINTER_INT (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_INT	e_marshal_VOID__POINTER_INT
+
+/* NONE:POINTER,INT,INT,INT,INT (e-marshal.list:49) */
+extern void e_marshal_VOID__POINTER_INT_INT_INT_INT (GClosure     *closure,
+                                                     GValue       *return_value,
+                                                     guint         n_param_values,
+                                                     const GValue *param_values,
+                                                     gpointer      invocation_hint,
+                                                     gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_INT_INT_INT_INT	e_marshal_VOID__POINTER_INT_INT_INT_INT
+
+/* NONE:POINTER,INT,OBJECT (e-marshal.list:50) */
+extern void e_marshal_VOID__POINTER_INT_OBJECT (GClosure     *closure,
+                                                GValue       *return_value,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint,
+                                                gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_INT_OBJECT	e_marshal_VOID__POINTER_INT_OBJECT
+
+/* NONE:POINTER,OBJECT (e-marshal.list:51) */
+extern void e_marshal_VOID__POINTER_OBJECT (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_OBJECT	e_marshal_VOID__POINTER_OBJECT
+
+/* NONE:POINTER,POINTER (e-marshal.list:52) */
+extern void e_marshal_VOID__POINTER_POINTER (GClosure     *closure,
+                                             GValue       *return_value,
+                                             guint         n_param_values,
+                                             const GValue *param_values,
+                                             gpointer      invocation_hint,
+                                             gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_POINTER	e_marshal_VOID__POINTER_POINTER
+
+/* NONE:POINTER,POINTER,INT (e-marshal.list:53) */
+extern void e_marshal_VOID__POINTER_POINTER_INT (GClosure     *closure,
+                                                 GValue       *return_value,
+                                                 guint         n_param_values,
+                                                 const GValue *param_values,
+                                                 gpointer      invocation_hint,
+                                                 gpointer      marshal_data);
+#define e_marshal_NONE__POINTER_POINTER_INT	e_marshal_VOID__POINTER_POINTER_INT
+
+/* NONE:STRING,DOUBLE (e-marshal.list:54) */
+extern void e_marshal_VOID__STRING_DOUBLE (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+#define e_marshal_NONE__STRING_DOUBLE	e_marshal_VOID__STRING_DOUBLE
+
+/* NONE:STRING,INT (e-marshal.list:55) */
+extern void e_marshal_VOID__STRING_INT (GClosure     *closure,
+                                        GValue       *return_value,
+                                        guint         n_param_values,
+                                        const GValue *param_values,
+                                        gpointer      invocation_hint,
+                                        gpointer      marshal_data);
+#define e_marshal_NONE__STRING_INT	e_marshal_VOID__STRING_INT
+
+/* NONE:STRING,INT,INT (e-marshal.list:56) */
+extern void e_marshal_VOID__STRING_INT_INT (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data);
+#define e_marshal_NONE__STRING_INT_INT	e_marshal_VOID__STRING_INT_INT
+
+/* NONE:STRING,POINTER,POINTER (e-marshal.list:57) */
+extern void e_marshal_VOID__STRING_POINTER_POINTER (GClosure     *closure,
+                                                    GValue       *return_value,
+                                                    guint         n_param_values,
+                                                    const GValue *param_values,
+                                                    gpointer      invocation_hint,
+                                                    gpointer      marshal_data);
+#define e_marshal_NONE__STRING_POINTER_POINTER	e_marshal_VOID__STRING_POINTER_POINTER
+
+/* NONE:STRING,STRING (e-marshal.list:58) */
+extern void e_marshal_VOID__STRING_STRING (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+#define e_marshal_NONE__STRING_STRING	e_marshal_VOID__STRING_STRING
+
+/* NONE:UINT,STRING (e-marshal.list:59) */
+extern void e_marshal_VOID__UINT_STRING (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+#define e_marshal_NONE__UINT_STRING	e_marshal_VOID__UINT_STRING
+
+/* STRING:NONE (e-marshal.list:60) */
+extern void e_marshal_STRING__VOID (GClosure     *closure,
+                                    GValue       *return_value,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint,
+                                    gpointer      marshal_data);
+#define e_marshal_STRING__NONE	e_marshal_STRING__VOID
+
+G_END_DECLS
+
+#endif /* __e_marshal_MARSHAL_H__ */
+
diff --git a/libemail-utils/e-marshal.list b/libemail-utils/e-marshal.list
new file mode 100644
index 0000000..c42078b
--- /dev/null
+++ b/libemail-utils/e-marshal.list
@@ -0,0 +1,60 @@
+BOOLEAN:BOXED
+BOOLEAN:BOXED,STRING
+BOOLEAN:BOXED,POINTER,POINTER
+BOOLEAN:INT,INT,BOXED
+BOOLEAN:INT,INT,OBJECT,INT,INT,UINT
+BOOLEAN:INT,POINTER,INT,BOXED
+BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT
+BOOLEAN:NONE
+BOOLEAN:OBJECT
+BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+BOOLEAN:POINTER
+BOOLEAN:POINTER,BOOLEAN,POINTER
+BOOLEAN:POINTER,POINTER
+BOOLEAN:POINTER,POINTER,POINTER,POINTER
+BOOLEAN:STRING
+BOOLEAN:STRING,INT
+DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+INT:BOXED
+INT:INT
+INT:INT,INT,BOXED
+INT:INT,POINTER,INT,BOXED
+INT:OBJECT,BOXED
+INT:POINTER
+NONE:BOXED,INT
+NONE:ENUM,OBJECT,OBJECT
+NONE:INT,INT
+NONE:INT,INT,BOXED
+NONE:INT,INT,OBJECT
+NONE:INT,INT,OBJECT,BOXED,UINT,UINT
+NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT
+NONE:INT,INT,OBJECT,UINT
+NONE:INT,OBJECT
+NONE:INT,POINTER
+NONE:INT,POINTER,INT,BOXED
+NONE:INT,POINTER,INT,OBJECT
+NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT
+NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT
+NONE:INT,POINTER,INT,OBJECT,UINT
+NONE:LONG,LONG
+NONE:OBJECT,BOOLEAN
+NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+NONE:OBJECT,OBJECT
+NONE:OBJECT,STRING
+NONE:OBJECT,STRING,INT
+NONE:OBJECT,STRING,INT,STRING,STRING,STRING
+NONE:OBJECT,STRING,STRING
+NONE:OBJECT,STRING,UINT
+NONE:POINTER,INT
+NONE:POINTER,INT,INT,INT,INT
+NONE:POINTER,INT,OBJECT
+NONE:POINTER,OBJECT
+NONE:POINTER,POINTER
+NONE:POINTER,POINTER,INT
+NONE:STRING,DOUBLE
+NONE:STRING,INT
+NONE:STRING,INT,INT
+NONE:STRING,POINTER,POINTER
+NONE:STRING,STRING
+NONE:UINT,STRING
+STRING:NONE
diff --git a/libemail-utils/e-signature-list.c b/libemail-utils/e-signature-list.c
new file mode 100644
index 0000000..1f03cd3
--- /dev/null
+++ b/libemail-utils/e-signature-list.c
@@ -0,0 +1,494 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signature-list.h"
+
+#include <string.h>
+
+#include <libedataserver/e-uid.h>
+
+struct _ESignatureListPrivate {
+	GConfClient *gconf;
+	guint notify_id;
+	gboolean resave;
+};
+
+enum {
+	SIGNATURE_ADDED,
+	SIGNATURE_CHANGED,
+	SIGNATURE_REMOVED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (
+	ESignatureList,
+	e_signature_list,
+	E_TYPE_LIST)
+
+static void
+e_signature_list_dispose (GObject *object)
+{
+	ESignatureList *list = (ESignatureList *) object;
+
+	if (list->priv->gconf) {
+		if (list->priv->notify_id != 0)
+			gconf_client_notify_remove (
+				list->priv->gconf, list->priv->notify_id);
+		g_object_unref (list->priv->gconf);
+		list->priv->gconf = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_signature_list_parent_class)->dispose (object);
+}
+
+static void
+e_signature_list_class_init (ESignatureListClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESignatureListPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = e_signature_list_dispose;
+
+	signals[SIGNATURE_ADDED] = g_signal_new (
+		"signature-added",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESignatureListClass, signature_added),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SIGNATURE);
+
+	signals[SIGNATURE_CHANGED] = g_signal_new (
+		"signature-changed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESignatureListClass, signature_changed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SIGNATURE);
+
+	signals[SIGNATURE_REMOVED] = g_signal_new (
+		"signature-removed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESignatureListClass, signature_removed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SIGNATURE);
+}
+
+static void
+e_signature_list_init (ESignatureList *signature_list)
+{
+	signature_list->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		signature_list, E_TYPE_SIGNATURE_LIST, ESignatureListPrivate);
+}
+
+static GSList *
+add_autogen (ESignatureList *list,
+             GSList *new_sigs)
+{
+	ESignature *autogen;
+
+	autogen = e_signature_new ();
+	e_signature_set_autogenerated (autogen, TRUE);
+
+	e_list_append (E_LIST (list), autogen);
+
+	return g_slist_prepend (new_sigs, autogen);
+}
+
+static void
+gconf_signatures_changed (GConfClient *client,
+                          guint cnxn_id,
+                          GConfEntry *entry,
+                          gpointer user_data)
+{
+	ESignatureList *signature_list = user_data;
+	GSList *list, *l, *n, *new_sigs = NULL;
+	gboolean have_autogen = FALSE;
+	gboolean resave = FALSE;
+	ESignature *signature;
+	EList *old_sigs;
+	EIterator *iter;
+	gboolean found;
+	gchar *uid;
+
+	old_sigs = e_list_duplicate (E_LIST (signature_list));
+
+	list = gconf_client_get_list (
+		client, "/apps/evolution/mail/signatures",
+		GCONF_VALUE_STRING, NULL);
+	for (l = list; l; l = l->next) {
+		found = FALSE;
+		if ((uid = e_signature_uid_from_xml (l->data))) {
+			/* See if this is an existing signature */
+			iter = e_list_get_iterator (old_sigs);
+			while (e_iterator_is_valid (iter)) {
+				const gchar *signature_uid;
+
+				signature = (ESignature *) e_iterator_get (iter);
+				signature_uid = e_signature_get_uid (signature);
+				if (!strcmp (signature_uid, uid)) {
+					/* The signature still exists, so remove
+					 * it from "old_sigs" and update it. */
+					found = TRUE;
+					e_iterator_delete (iter);
+					if (e_signature_set_from_xml (
+							signature, l->data))
+						g_signal_emit (
+							signature_list,
+							signals[SIGNATURE_CHANGED],
+							0, signature);
+
+					have_autogen |=
+						e_signature_get_autogenerated (
+						signature);
+
+					break;
+				}
+
+				e_iterator_next (iter);
+			}
+
+			g_object_unref (iter);
+		}
+
+		if (!found) {
+			resave = TRUE;
+
+			/* Must be a new signature */
+			signature = e_signature_new_from_xml (l->data);
+			if (signature) {
+				have_autogen |=
+					e_signature_get_autogenerated (signature);
+
+				e_list_append (E_LIST (signature_list), signature);
+				new_sigs = g_slist_prepend (new_sigs, signature);
+			}
+		}
+
+		g_free (uid);
+	}
+
+	if (!have_autogen) {
+		new_sigs = add_autogen (signature_list, new_sigs);
+		resave = TRUE;
+	}
+
+	if (new_sigs != NULL) {
+		/* Now emit signals for each added signature. */
+		l = g_slist_reverse (new_sigs);
+		while (l != NULL) {
+			n = l->next;
+			signature = l->data;
+			g_signal_emit (
+				signature_list,
+				signals[SIGNATURE_ADDED], 0,
+				signature);
+			g_object_unref (signature);
+			g_slist_free_1 (l);
+			l = n;
+		}
+	}
+
+	/* Anything left in old_sigs must have been deleted */
+	iter = e_list_get_iterator (old_sigs);
+	while (e_iterator_is_valid (iter)) {
+		signature = (ESignature *) e_iterator_get (iter);
+		e_list_remove (E_LIST (signature_list), signature);
+		g_signal_emit (
+			signature_list, signals[SIGNATURE_REMOVED], 0,
+			signature);
+		e_iterator_next (iter);
+	}
+
+	g_object_unref (iter);
+	g_object_unref (old_sigs);
+
+	signature_list->priv->resave = resave;
+}
+
+static gpointer
+copy_func (gconstpointer data,
+           gpointer closure)
+{
+	GObject *object = (GObject *) data;
+
+	g_object_ref (object);
+
+	return object;
+}
+
+static void
+free_func (gpointer data,
+           gpointer closure)
+{
+	g_object_unref (data);
+}
+
+/**
+ * e_signature_list_new:
+ * @gconf: a #GConfClient
+ *
+ * Reads the list of signaturess from @gconf and listens for changes.
+ * Will emit #signature_added, #signature_changed, and #signature_removed
+ * signals according to notifications from GConf.
+ *
+ * You can modify the list using e_list_append(), e_list_remove(), and
+ * e_iterator_delete(). After adding, removing, or changing accounts,
+ * you must call e_signature_list_save() to push the changes back to
+ * GConf.
+ *
+ * Return value: the list of signatures
+ **/
+ESignatureList *
+e_signature_list_new (GConfClient *gconf)
+{
+	ESignatureList *signature_list;
+
+	g_return_val_if_fail (GCONF_IS_CLIENT (gconf), NULL);
+
+	signature_list = g_object_new (E_TYPE_SIGNATURE_LIST, NULL);
+	e_signature_list_construct (signature_list, gconf);
+
+	return signature_list;
+}
+
+void
+e_signature_list_construct (ESignatureList *signature_list,
+                            GConfClient *gconf)
+{
+	g_return_if_fail (GCONF_IS_CLIENT (gconf));
+
+	e_list_construct (E_LIST (signature_list), copy_func, free_func, NULL);
+	signature_list->priv->gconf = gconf;
+	g_object_ref (gconf);
+
+	gconf_client_add_dir (signature_list->priv->gconf,
+			      "/apps/evolution/mail/signatures",
+			      GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+	signature_list->priv->notify_id =
+		gconf_client_notify_add (signature_list->priv->gconf,
+					 "/apps/evolution/mail/signatures",
+					 gconf_signatures_changed, signature_list,
+					 NULL, NULL);
+
+	gconf_signatures_changed (signature_list->priv->gconf,
+				  signature_list->priv->notify_id,
+				  NULL, signature_list);
+
+	if (signature_list->priv->resave) {
+		e_signature_list_save (signature_list);
+		signature_list->priv->resave = FALSE;
+	}
+}
+
+/**
+ * e_signature_list_save:
+ * @signature_list: an #ESignatureList
+ *
+ * Saves @signature_list to GConf. Signals will be emitted for changes.
+ **/
+void
+e_signature_list_save (ESignatureList *signature_list)
+{
+	GSList *list = NULL;
+	ESignature *signature;
+	EIterator *iter;
+	gchar *xmlbuf;
+
+	for (iter = e_list_get_iterator (E_LIST (signature_list));
+	     e_iterator_is_valid (iter);
+	     e_iterator_next (iter)) {
+		signature = (ESignature *) e_iterator_get (iter);
+
+		if ((xmlbuf = e_signature_to_xml (signature)))
+			list = g_slist_append (list, xmlbuf);
+	}
+
+	g_object_unref (iter);
+
+	gconf_client_set_list (signature_list->priv->gconf,
+			       "/apps/evolution/mail/signatures",
+			       GCONF_VALUE_STRING, list, NULL);
+
+	while (list) {
+		g_free (list->data);
+		list = g_slist_remove (list, list->data);
+	}
+
+	gconf_client_suggest_sync (signature_list->priv->gconf, NULL);
+}
+
+/**
+ * e_signature_list_add:
+ * @signature_list: signature list
+ * @signature: signature to add
+ *
+ * Add an signature to the signature list.  Will emit the signature-changed
+ * event.
+ **/
+void
+e_signature_list_add (ESignatureList *signature_list,
+                      ESignature *signature)
+{
+	e_list_append ((EList *) signature_list, signature);
+	g_signal_emit (signature_list, signals[SIGNATURE_ADDED], 0, signature);
+}
+
+/**
+ * e_signature_list_change:
+ * @signature_list: signature list
+ * @signature: signature to change
+ *
+ * Signal that the details of an signature have changed.
+ **/
+void
+e_signature_list_change (ESignatureList *signature_list,
+                         ESignature *signature)
+{
+	/* maybe the signature should do this itself ... */
+	g_signal_emit (signature_list, signals[SIGNATURE_CHANGED], 0, signature);
+}
+
+/**
+ * e_signature_list_remove:
+ * @signature_list: signature list
+ * @signature: signature
+ *
+ * Remove an signature from the signature list, and emit the
+ * signature-removed signal.  If the signature was the default signature,
+ * then reset the default to the first signature.
+ **/
+void
+e_signature_list_remove (ESignatureList *signature_list,
+                         ESignature *signature)
+{
+	/* not sure if need to ref but no harm */
+	g_object_ref (signature);
+	e_list_remove ((EList *) signature_list, signature);
+	g_signal_emit (signature_list, signals[SIGNATURE_REMOVED], 0, signature);
+	g_object_unref (signature);
+}
+
+/**
+ * e_signature_list_find_by_name:
+ * @signature_list: an #ESignatureList
+ * @name: the signature name to find
+ *
+ * Searches @signature_list for the given signature name.
+ *
+ * Returns: the matching signature or %NULL if it doesn't exist
+ **/
+ESignature *
+e_signature_list_find_by_name (ESignatureList *signature_list,
+                               const gchar *signature_name)
+{
+	ESignature *signature = NULL;
+	EIterator *it;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_LIST (signature_list), NULL);
+
+	/* this could use a callback for more flexibility ...
+	 * ... but this makes the common cases easier */
+
+	if (signature_name == NULL)
+		return NULL;
+
+	for (it = e_list_get_iterator (E_LIST (signature_list));
+	     e_iterator_is_valid (it);
+	     e_iterator_next (it)) {
+		const gchar *value;
+
+		/* XXX EIterator misuses const. */
+		signature = (ESignature *) e_iterator_get (it);
+		value = e_signature_get_name (signature);
+
+		if (g_strcmp0 (value, signature_name) == 0)
+			break;
+
+		signature = NULL;
+	}
+
+	g_object_unref (it);
+
+	return signature;
+}
+
+/**
+ * e_signature_list_find_by_uid:
+ * @signature_list: an #ESignatureList
+ * @name: the signature UID to find
+ *
+ * Searches @signature_list for the given signature UID.
+ *
+ * Returns: the matching signature or %NULL if it doesn't exist
+ **/
+ESignature *
+e_signature_list_find_by_uid (ESignatureList *signature_list,
+                              const gchar *signature_uid)
+{
+	ESignature *signature = NULL;
+	EIterator *it;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_LIST (signature_list), NULL);
+
+	/* this could use a callback for more flexibility ...
+	 * ... but this makes the common cases easier */
+
+	if (signature_uid == NULL)
+		return NULL;
+
+	for (it = e_list_get_iterator (E_LIST (signature_list));
+	     e_iterator_is_valid (it);
+	     e_iterator_next (it)) {
+		const gchar *value = NULL;
+
+		/* XXX EIterator misuses const. */
+		signature = (ESignature *) e_iterator_get (it);
+		value = e_signature_get_uid (signature);
+
+		if (g_strcmp0 (value, signature_uid) == 0)
+			break;
+
+		signature = NULL;
+	}
+
+	g_object_unref (it);
+
+	return signature;
+}
diff --git a/libemail-utils/e-signature-list.h b/libemail-utils/e-signature-list.h
new file mode 100644
index 0000000..4e929c8
--- /dev/null
+++ b/libemail-utils/e-signature-list.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_LIST_H
+#define E_SIGNATURE_LIST_H
+
+#include <libedataserver/e-list.h>
+#include "libemail-utils/e-signature.h"
+
+#include <gconf/gconf-client.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_LIST \
+	(e_signature_list_get_type ())
+#define E_SIGNATURE_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE_LIST, ESignatureList))
+#define E_SIGNATURE_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE_LIST, ESignatureListClass))
+#define E_IS_SIGNATURE_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE_LIST))
+#define E_IS_SIGNATURE_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE_LIST))
+#define E_SIGNATURE_LIST_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE_LIST, ESignatureListClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureList ESignatureList;
+typedef struct _ESignatureListClass ESignatureListClass;
+typedef struct _ESignatureListPrivate ESignatureListPrivate;
+
+struct _ESignatureList {
+	EList parent;
+	ESignatureListPrivate *priv;
+};
+
+struct _ESignatureListClass {
+	EListClass parent_class;
+
+	/* Signals */
+	void		(*signature_added)	(ESignatureList *signature_list,
+						 ESignature *signature);
+	void		(*signature_changed)	(ESignatureList *signature_list,
+						 ESignature *signature);
+	void		(*signature_removed)	(ESignatureList *signature_list,
+						 ESignature *signature);
+};
+
+GType		e_signature_list_get_type	(void);
+ESignatureList *e_signature_list_new		(GConfClient *client);
+void		e_signature_list_construct	(ESignatureList *signature_list,
+						 GConfClient *client);
+void		e_signature_list_save		(ESignatureList *signature_list);
+void		e_signature_list_add		(ESignatureList *signature_list,
+						 ESignature *signature);
+void		e_signature_list_change		(ESignatureList *signature_list,
+						 ESignature *signature);
+void		e_signature_list_remove		(ESignatureList *signature_list,
+						 ESignature *signature);
+ESignature *	e_signature_list_find_by_name	(ESignatureList *signature_list,
+						 const gchar *signature_name);
+ESignature *	e_signature_list_find_by_uid	(ESignatureList *signature_list,
+						 const gchar *signature_uid);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_LIST_H */
diff --git a/libemail-utils/e-signature-utils.c b/libemail-utils/e-signature-utils.c
new file mode 100644
index 0000000..a7eea3e
--- /dev/null
+++ b/libemail-utils/e-signature-utils.c
@@ -0,0 +1,342 @@
+/*
+ * e-signature-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signature-utils.h"
+
+#include <errno.h>
+#include <camel/camel.h>
+#include <glib/gstdio.h>
+#include <gconf/gconf-client.h>
+
+#ifndef G_OS_WIN32
+#include <sys/wait.h>
+#endif
+
+#include <libedataserver/e-data-server-util.h>
+
+static ESignatureList *global_signature_list;
+
+ESignatureList *
+e_get_signature_list (void)
+{
+	if (G_UNLIKELY (global_signature_list == NULL)) {
+		GConfClient *client;
+
+		client = gconf_client_get_default ();
+		global_signature_list = e_signature_list_new (client);
+		g_object_unref (client);
+	}
+
+	g_return_val_if_fail (global_signature_list != NULL, NULL);
+
+	return global_signature_list;
+}
+
+ESignature *
+e_get_signature_by_name (const gchar *name)
+{
+	ESignatureList *signature_list;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	signature_list = e_get_signature_list ();
+
+	return e_signature_list_find_by_name (signature_list, name);
+}
+
+ESignature *
+e_get_signature_by_uid (const gchar *uid)
+{
+	ESignatureList *signature_list;
+
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	signature_list = e_get_signature_list ();
+
+	return e_signature_list_find_by_uid (signature_list, uid);
+}
+
+gchar *
+e_create_signature_file (GError **error)
+{
+	const gchar *data_dir;
+	gchar basename[32];
+	gchar *filename;
+	gchar *pathname;
+	gint32 ii;
+
+	data_dir = e_get_user_data_dir ();
+	pathname = g_build_filename (data_dir, "signatures", NULL);
+	filename = NULL;
+
+	if (g_mkdir_with_parents (pathname, 0700) < 0) {
+		g_set_error (
+			error, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			"%s: %s", pathname, g_strerror (errno));
+		g_free (pathname);
+		return NULL;
+	}
+
+	for (ii = 0; ii < G_MAXINT32; ii++) {
+
+		g_snprintf (
+			basename, sizeof (basename),
+			"signature-%" G_GINT32_FORMAT, ii);
+
+		g_free (filename);
+		filename = g_build_filename (pathname, basename, NULL);
+
+		if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+			gint fd;
+
+			fd = g_creat (filename, 0600);
+			if (fd >= 0) {
+				close (fd);
+				break;
+			}
+
+			/* If we failed once we're probably going
+			 * to continue failing, so just give up. */
+			g_set_error (
+				error, G_FILE_ERROR,
+				g_file_error_from_errno (errno),
+				"%s: %s", filename, g_strerror (errno));
+			g_free (filename);
+			filename = NULL;
+			break;
+		}
+	}
+
+	/* If there are actually G_MAXINT32 signature files, the
+	 * most recent signature file we be overwritten.  Sorry. */
+
+	return filename;
+}
+
+gchar *
+e_read_signature_file (ESignature *signature,
+                       gboolean convert_to_html,
+                       GError **error)
+{
+	CamelStream *input_stream;
+	CamelStream *output_stream;
+	GByteArray *buffer;
+	const gchar *filename;
+	gboolean is_html;
+	gchar *content;
+	gsize length;
+	gint fd;
+
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+	filename = e_signature_get_filename (signature);
+	is_html = e_signature_get_is_html (signature);
+
+	fd = g_open (filename, O_RDONLY, 0);
+	if (fd < 0) {
+		g_set_error (
+			error, G_FILE_ERROR,
+			g_file_error_from_errno (errno),
+			"%s: %s", filename, g_strerror (errno));
+		return NULL;
+	}
+
+	input_stream = camel_stream_fs_new_with_fd (fd);
+
+	if (!is_html && convert_to_html) {
+		CamelStream *filtered_stream;
+		CamelMimeFilter *filter;
+		gint32 flags;
+
+		filtered_stream =
+			camel_stream_filter_new (input_stream);
+		g_object_unref (input_stream);
+
+		flags =
+			CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES |
+			CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
+		filter = camel_mime_filter_tohtml_new (flags, 0);
+		camel_stream_filter_add (
+			CAMEL_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
+
+		input_stream = filtered_stream;
+	}
+
+	buffer = g_byte_array_new ();
+	output_stream = camel_stream_mem_new ();
+	camel_stream_mem_set_byte_array (
+		CAMEL_STREAM_MEM (output_stream), buffer);
+	camel_stream_write_to_stream (input_stream, output_stream, NULL, NULL);
+	g_object_unref (output_stream);
+	g_object_unref (input_stream);
+
+	/* Make sure the buffer is nul-terminated. */
+	length = (gsize) buffer->len;
+	g_byte_array_append (buffer, (guint8 *) "", 1);
+	content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+	/* Signatures are saved as UTF-8, but we still need to check that
+	 * the signature is valid UTF-8 because the user may be opening
+	 * a signature file that is in his/her locale character set.  If
+	 * it's not in UTF-8 then try converting from the current locale. */
+	if (!g_utf8_validate (content, length, NULL)) {
+		gchar *utf8;
+
+		utf8 = g_locale_to_utf8 (content, length, NULL, NULL, error);
+		g_free (content);
+		content = utf8;
+	}
+
+	return content;
+}
+
+gchar *
+e_run_signature_script (const gchar *filename)
+{
+	/* FIXME Make this cross-platform, prefer GLib functions over
+	 *       POSIX, and report errors via GError instead of dumping
+	 *       messages to the terminal where users won't see them. */
+
+#ifndef G_OS_WIN32
+	gint in_fds[2];
+	pid_t pid;
+
+	g_return_val_if_fail (filename != NULL, NULL);
+
+	if (pipe (in_fds) == -1) {
+		g_warning (
+			"Failed to create pipe to '%s': %s",
+			filename, g_strerror (errno));
+		return NULL;
+	}
+
+	pid = fork ();
+
+	/* Child Process */
+	if (pid == 0) {
+		gint maxfd, ii;
+
+		close (in_fds[0]);
+		if (dup2 (in_fds[1], STDOUT_FILENO) < 0)
+			_exit (255);
+		close (in_fds[1]);
+
+		setsid ();
+
+		maxfd = sysconf (_SC_OPEN_MAX);
+		for (ii = 3; ii < maxfd; ii++) {
+			if (ii == STDIN_FILENO)
+				continue;
+			if (ii == STDOUT_FILENO)
+				continue;
+			if (ii == STDERR_FILENO)
+				continue;
+			fcntl (ii, F_SETFD, FD_CLOEXEC);
+		}
+
+		execlp ("/bin/sh", "/bin/sh", "-c", filename, NULL);
+
+		g_warning (
+			"Could not execute '%s': %s",
+			filename, g_strerror (errno));
+
+		_exit (255);
+
+	/* Parent Process */
+	} else if (pid > 0) {
+		CamelStream *output_stream;
+		CamelStream *input_stream;
+		GByteArray *buffer;
+		gchar *content;
+		gsize length;
+		gint result;
+		gint status;
+
+		close (in_fds[1]);
+
+		buffer = g_byte_array_new ();
+		output_stream = camel_stream_mem_new ();
+		camel_stream_mem_set_byte_array (
+			CAMEL_STREAM_MEM (output_stream), buffer);
+
+		input_stream = camel_stream_fs_new_with_fd (in_fds[0]);
+		camel_stream_write_to_stream (
+			input_stream, output_stream, NULL, NULL);
+		g_object_unref (input_stream);
+
+		g_object_unref (output_stream);
+
+		/* Make sure the buffer is nul-terminated. */
+		length = (gsize) buffer->len;
+		g_byte_array_append (buffer, (guchar *) "", 1);
+		content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+		/* Signature scripts are supposed to generate UTF-8 content,
+		 * but because users are known to never read the manual, we
+		 * try to do our best if the content isn't valid UTF-8 by
+		 * assuming that the content is in the user's locale
+		 * character set. */
+		if (!g_utf8_validate (content, length, NULL)) {
+			gchar *utf8;
+
+			/* XXX Should pass a GError here. */
+			utf8 = g_locale_to_utf8 (
+				content, length, NULL, NULL, NULL);
+			g_free (content);
+			content = utf8;
+		}
+
+		/* Wait for the script process to terminate. */
+		result = waitpid (pid, &status, 0);
+
+		if (result == -1 && errno == EINTR) {
+			/* Child process is hanging... */
+			kill (pid, SIGTERM);
+			sleep (1);
+			result = waitpid (pid, &status, WNOHANG);
+			if (result == 0) {
+				/* ...still hanging, set phasers to KILL. */
+				kill (pid, SIGKILL);
+				sleep (1);
+				waitpid (pid, &status, WNOHANG);
+			}
+		}
+
+		return content;
+
+	/* Forking Failed */
+	} else {
+		g_warning (
+			"Failed to create child process '%s': %s",
+			filename, g_strerror (errno));
+		close (in_fds[0]);
+		close (in_fds[1]);
+	}
+#endif
+
+	return NULL;
+}
diff --git a/libemail-utils/e-signature-utils.h b/libemail-utils/e-signature-utils.h
new file mode 100644
index 0000000..a642a13
--- /dev/null
+++ b/libemail-utils/e-signature-utils.h
@@ -0,0 +1,40 @@
+/*
+ * e-signature-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_SIGNATURE_UTILS_H
+#define E_SIGNATURE_UTILS_H
+
+#include <gtk/gtk.h>
+#include <libemail-utils/e-signature.h>
+#include <libemail-utils/e-signature-list.h>
+
+G_BEGIN_DECLS
+
+ESignatureList *e_get_signature_list		(void);
+ESignature *	e_get_signature_by_name		(const gchar *name);
+ESignature *	e_get_signature_by_uid		(const gchar *uid);
+gchar *		e_create_signature_file		(GError **error);
+gchar *		e_read_signature_file		(ESignature *signature,
+						 gboolean convert_to_html,
+						 GError **error);
+gchar *		e_run_signature_script		(const gchar *filename);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_UTILS_H */
diff --git a/libemail-utils/e-signature.c b/libemail-utils/e-signature.c
new file mode 100644
index 0000000..185378d
--- /dev/null
+++ b/libemail-utils/e-signature.c
@@ -0,0 +1,747 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <glib/gi18n-lib.h>
+#include <gconf/gconf-client.h>
+
+#include <libedataserver/e-uid.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "e-signature.h"
+
+struct _ESignaturePrivate {
+	gchar *filename;
+	gchar *name;
+	gchar *uid;
+
+	gboolean autogenerated;
+	gboolean is_html;
+	gboolean is_script;
+};
+
+enum {
+	PROP_0,
+	PROP_AUTOGENERATED,
+	PROP_FILENAME,
+	PROP_IS_HTML,
+	PROP_IS_SCRIPT,
+	PROP_NAME,
+	PROP_UID
+};
+
+G_DEFINE_TYPE (
+	ESignature,
+	e_signature,
+	G_TYPE_OBJECT)
+
+static gboolean
+xml_set_bool (xmlNodePtr node,
+              const gchar *name,
+              gboolean *val)
+{
+	gboolean v_boolean;
+	gchar *buf;
+
+	if ((buf = (gchar *) xmlGetProp (node, (xmlChar *) name))) {
+		v_boolean = (!strcmp (buf, "true") || !strcmp (buf, "yes"));
+		xmlFree (buf);
+
+		if (v_boolean != *val) {
+			*val = v_boolean;
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static gboolean
+xml_set_prop (xmlNodePtr node,
+              const gchar *name,
+              gchar **val)
+{
+	gchar *buf, *new_val;
+
+	buf = (gchar *) xmlGetProp (node, (xmlChar *) name);
+	new_val = g_strdup (buf);
+	xmlFree (buf);
+
+	/* We can use strcmp here whether the value is UTF8 or
+	 * not, since we only care if the bytes changed.
+	 */
+	if (!*val || strcmp (*val, new_val)) {
+		g_free (*val);
+		*val = new_val;
+		return TRUE;
+	} else {
+		g_free (new_val);
+		return FALSE;
+	}
+}
+
+static gboolean
+xml_set_content (xmlNodePtr node,
+                 gchar **val)
+{
+	gchar *buf, *new_val;
+
+	buf = (gchar *) xmlNodeGetContent (node);
+	new_val = g_strdup (buf);
+	xmlFree (buf);
+
+	/* We can use strcmp here whether the value is UTF8 or
+	 * not, since we only care if the bytes changed. */
+	if (!*val || strcmp (*val, new_val)) {
+		g_free (*val);
+		*val = new_val;
+		return TRUE;
+	} else {
+		g_free (new_val);
+		return FALSE;
+	}
+}
+
+static void
+signature_set_property (GObject *object,
+                        guint property_id,
+                        const GValue *value,
+                        GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTOGENERATED:
+			e_signature_set_autogenerated (
+				E_SIGNATURE (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_FILENAME:
+			e_signature_set_filename (
+				E_SIGNATURE (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_IS_HTML:
+			e_signature_set_is_html (
+				E_SIGNATURE (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_IS_SCRIPT:
+			e_signature_set_is_script (
+				E_SIGNATURE (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_NAME:
+			e_signature_set_name (
+				E_SIGNATURE (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_UID:
+			e_signature_set_uid (
+				E_SIGNATURE (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_get_property (GObject *object,
+                        guint property_id,
+                        GValue *value,
+                        GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTOGENERATED:
+			g_value_set_boolean (
+				value, e_signature_get_autogenerated (
+				E_SIGNATURE (object)));
+			return;
+
+		case PROP_FILENAME:
+			g_value_set_string (
+				value, e_signature_get_filename (
+				E_SIGNATURE (object)));
+			return;
+
+		case PROP_IS_HTML:
+			g_value_set_boolean (
+				value, e_signature_get_is_html (
+				E_SIGNATURE (object)));
+			return;
+
+		case PROP_IS_SCRIPT:
+			g_value_set_boolean (
+				value, e_signature_get_is_script (
+				E_SIGNATURE (object)));
+			return;
+
+		case PROP_NAME:
+			g_value_set_string (
+				value, e_signature_get_name (
+				E_SIGNATURE (object)));
+			return;
+
+		case PROP_UID:
+			g_value_set_string (
+				value, e_signature_get_uid (
+				E_SIGNATURE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_finalize (GObject *object)
+{
+	ESignaturePrivate *priv;
+
+	priv = E_SIGNATURE (object)->priv;
+
+	g_free (priv->filename);
+	g_free (priv->name);
+	g_free (priv->uid);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_signature_parent_class)->finalize (object);
+}
+
+static void
+e_signature_class_init (ESignatureClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESignaturePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = signature_set_property;
+	object_class->get_property = signature_get_property;
+	object_class->finalize = signature_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_AUTOGENERATED,
+		g_param_spec_boolean (
+			"autogenerated",
+			"Autogenerated",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILENAME,
+		g_param_spec_string (
+			"filename",
+			"Filename",
+			NULL,
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_IS_HTML,
+		g_param_spec_boolean (
+			"is-html",
+			"Is HTML",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_IS_SCRIPT,
+		g_param_spec_boolean (
+			"is-script",
+			"Is Script",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_NAME,
+		g_param_spec_string (
+			"name",
+			"Name",
+			NULL,
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_UID,
+		g_param_spec_string (
+			"uid",
+			"UID",
+			NULL,
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+}
+
+static void
+e_signature_init (ESignature *signature)
+{
+	signature->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		signature, E_TYPE_SIGNATURE, ESignaturePrivate);
+}
+
+/**
+ * e_signature_new:
+ *
+ * Returns a new signature which can be filled in and
+ * added to an #ESignatureList.
+ *
+ * Returns: a new #ESignature
+ **/
+ESignature *
+e_signature_new (void)
+{
+	ESignature *signature;
+
+	signature = g_object_new (E_TYPE_SIGNATURE, NULL);
+	signature->priv->uid = e_uid_new ();
+
+	return signature;
+}
+
+/**
+ * e_signature_new_from_xml:
+ * @xml: an XML signature description
+ *
+ * Return value: a new #ESignature based on the data in @xml, or %NULL
+ * if @xml could not be parsed as valid signature data.
+ **/
+ESignature *
+e_signature_new_from_xml (const gchar *xml)
+{
+	ESignature *signature;
+
+	signature = g_object_new (E_TYPE_SIGNATURE, NULL);
+
+	if (!e_signature_set_from_xml (signature, xml)) {
+		g_object_unref (signature);
+		return NULL;
+	}
+
+	return signature;
+}
+
+/**
+ * e_signature_uid_from_xml:
+ * @xml: an XML signature description
+ *
+ * Return value: the permanent UID of the signature described by @xml
+ * (or %NULL if @xml could not be parsed or did not contain a uid).
+ * The caller must free this string.
+ **/
+gchar *
+e_signature_uid_from_xml (const gchar *xml)
+{
+	xmlNodePtr node;
+	xmlDocPtr doc;
+	gchar *uid = NULL;
+
+	if (!(doc = xmlParseDoc ((xmlChar *) xml)))
+		return NULL;
+
+	node = doc->children;
+	if (strcmp ((gchar *)node->name, "signature") != 0) {
+		xmlFreeDoc (doc);
+		return NULL;
+	}
+
+	xml_set_prop (node, "uid", &uid);
+	xmlFreeDoc (doc);
+
+	return uid;
+}
+
+/**
+ * e_signature_set_from_xml:
+ * @signature: an #ESignature
+ * @xml: an XML signature description.
+ *
+ * Changes @signature to match @xml.
+ *
+ * Returns: %TRUE if the signature was loaded or %FALSE otherwise
+ **/
+gboolean
+e_signature_set_from_xml (ESignature *signature,
+                          const gchar *xml)
+{
+	gboolean changed = FALSE;
+	xmlNodePtr node, cur;
+	xmlDocPtr doc;
+	gboolean bool;
+	gchar *buf;
+
+	if (!(doc = xmlParseDoc ((xmlChar *) xml)))
+		return FALSE;
+
+	node = doc->children;
+	if (strcmp ((gchar *)node->name, "signature") != 0) {
+		xmlFreeDoc (doc);
+		return FALSE;
+	}
+
+	buf = NULL;
+	xml_set_prop (node, "uid", &buf);
+
+	if (buf && *buf) {
+		g_free (signature->priv->uid);
+		signature->priv->uid = buf;
+	}
+
+	changed |= xml_set_prop (node, "name", &signature->priv->name);
+	changed |= xml_set_bool (node, "auto", &signature->priv->autogenerated);
+
+	if (e_signature_get_autogenerated (signature)) {
+		xmlFreeDoc (doc);
+
+		return changed;
+	}
+
+	buf = NULL;
+	xml_set_prop (node, "format", &buf);
+	if (buf && !strcmp (buf, "text/html"))
+		bool = TRUE;
+	else
+		bool = FALSE;
+	g_free (buf);
+
+	if (e_signature_get_is_html (signature) != bool) {
+		e_signature_set_is_html (signature, bool);
+		changed = TRUE;
+	}
+
+	cur = node->children;
+	while (cur) {
+		if (!strcmp ((gchar *)cur->name, "filename")) {
+			changed |= xml_set_content (
+				cur, &signature->priv->filename);
+			changed |= xml_set_bool (
+				cur, "script", &signature->priv->is_script);
+			break;
+		} else if (!strcmp ((gchar *)cur->name, "script")) {
+			/* this is for handling 1.4 signature script definitions */
+			changed |= xml_set_content (
+				cur, &signature->priv->filename);
+			if (!e_signature_get_is_script (signature)) {
+				e_signature_set_is_script (signature, TRUE);
+				changed = TRUE;
+			}
+			break;
+		}
+		cur = cur->next;
+	}
+
+	/* If the signature is not a script, replace the directory
+	 * part with the current signatures directory.  This makes
+	 * moving the signatures directory transparent. */
+	if (!e_signature_get_is_script (signature)) {
+		const gchar *user_data_dir;
+		gchar *basename;
+		gchar *filename;
+
+		user_data_dir = e_get_user_data_dir ();
+
+		filename = signature->priv->filename;
+		basename = g_path_get_basename (filename);
+		signature->priv->filename = g_build_filename (
+			user_data_dir, "signatures", basename, NULL);
+		g_free (basename);
+		g_free (filename);
+	}
+
+	xmlFreeDoc (doc);
+
+	return changed;
+}
+
+/**
+ * e_signature_to_xml:
+ * @signature: an #ESignature
+ *
+ * Return value: an XML representation of @signature, which the caller
+ * must free.
+ **/
+gchar *
+e_signature_to_xml (ESignature *signature)
+{
+	xmlChar *xmlbuf;
+	gchar *tmp;
+	xmlNodePtr root, node;
+	xmlDocPtr doc;
+	const gchar *string;
+	gint n;
+
+	doc = xmlNewDoc ((xmlChar *) "1.0");
+
+	root = xmlNewDocNode (doc, NULL, (xmlChar *) "signature", NULL);
+	xmlDocSetRootElement (doc, root);
+
+	string = e_signature_get_name (signature);
+	xmlSetProp (root, (xmlChar *) "name", (xmlChar *) string);
+
+	string = e_signature_get_uid (signature);
+	xmlSetProp (root, (xmlChar *) "uid", (xmlChar *) string);
+
+	if (e_signature_get_autogenerated (signature))
+		string = "true";
+	else
+		string = "false";
+	xmlSetProp (root, (xmlChar *) "auto", (xmlChar *) string);
+
+	if (!e_signature_get_autogenerated (signature)) {
+		if (e_signature_get_is_html (signature))
+			string = "text/html";
+		else
+			string = "text/plain";
+		xmlSetProp (root, (xmlChar *) "format", (xmlChar *) string);
+
+		string = e_signature_get_filename (signature);
+		if (string != NULL) {
+
+			/* For scripts we save the full filename,
+			 * for normal signatures just the basename. */
+			if (e_signature_get_is_script (signature)) {
+				node = xmlNewTextChild (
+					root, NULL, (xmlChar *) "filename",
+					(xmlChar *) string);
+				xmlSetProp (
+					node, (xmlChar *) "script",
+					(xmlChar *) "true");
+			} else {
+				gchar *basename;
+
+				basename = g_path_get_basename (string);
+				node = xmlNewTextChild (
+					root, NULL, (xmlChar *) "filename",
+					(xmlChar *) basename);
+				g_free (basename);
+			}
+		}
+	} else {
+		/* this is to make Evolution-1.4 and older 1.5 versions happy */
+		xmlSetProp (root, (xmlChar *) "format", (xmlChar *) "text/html");
+	}
+
+	xmlDocDumpMemory (doc, &xmlbuf, &n);
+	xmlFreeDoc (doc);
+
+	/* remap to glib memory */
+	tmp = g_malloc (n + 1);
+	memcpy (tmp, xmlbuf, n);
+	tmp[n] = '\0';
+	xmlFree (xmlbuf);
+
+	return tmp;
+}
+
+gboolean
+e_signature_is_equal (ESignature *signature1,
+                      ESignature *signature2)
+{
+	const gchar *uid1;
+	const gchar *uid2;
+
+	g_return_val_if_fail (E_IS_SIGNATURE (signature1), FALSE);
+	g_return_val_if_fail (E_IS_SIGNATURE (signature2), FALSE);
+
+	/* XXX Simply compares the UIDs.  Not fool-proof. */
+	uid1 = e_signature_get_uid (signature1);
+	uid2 = e_signature_get_uid (signature2);
+
+	return (g_strcmp0 (uid1, uid2) == 0);
+}
+
+gboolean
+e_signature_get_autogenerated (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+	return signature->priv->autogenerated;
+}
+
+void
+e_signature_set_autogenerated (ESignature *signature,
+                               gboolean autogenerated)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	if (signature->priv->autogenerated == autogenerated)
+		return;
+
+	signature->priv->autogenerated = autogenerated;
+
+	/* Autogenerated flags overrides several properties. */
+	g_object_freeze_notify (G_OBJECT (signature));
+	g_object_notify (G_OBJECT (signature), "autogenerated");
+	g_object_notify (G_OBJECT (signature), "filename");
+	g_object_notify (G_OBJECT (signature), "is-html");
+	g_object_notify (G_OBJECT (signature), "is-script");
+	g_object_notify (G_OBJECT (signature), "name");
+	g_object_thaw_notify (G_OBJECT (signature));
+}
+
+const gchar *
+e_signature_get_filename (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+	/* Autogenerated flags overrides the filename property. */
+	if (e_signature_get_autogenerated (signature))
+		return NULL;
+
+	return signature->priv->filename;
+}
+
+void
+e_signature_set_filename (ESignature *signature,
+                          const gchar *filename)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	g_free (signature->priv->filename);
+	signature->priv->filename = g_strdup (filename);
+
+	g_object_notify (G_OBJECT (signature), "filename");
+}
+
+gboolean
+e_signature_get_is_html (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+	/* Autogenerated flag overrides the is-html property. */
+	if (e_signature_get_autogenerated (signature))
+		return FALSE;
+
+	return signature->priv->is_html;
+}
+
+void
+e_signature_set_is_html (ESignature *signature,
+                         gboolean is_html)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	if (signature->priv->is_html == is_html)
+		return;
+
+	signature->priv->is_html = is_html;
+
+	g_object_notify (G_OBJECT (signature), "is-html");
+}
+
+gboolean
+e_signature_get_is_script (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+	/* Autogenerated flags overrides the is-script property. */
+	if (e_signature_get_autogenerated (signature))
+		return FALSE;
+
+	return signature->priv->is_script;
+}
+
+void
+e_signature_set_is_script (ESignature *signature,
+                           gboolean is_script)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	if (signature->priv->is_script == is_script)
+		return;
+
+	signature->priv->is_script = is_script;
+
+	g_object_notify (G_OBJECT (signature), "is-script");
+}
+
+const gchar *
+e_signature_get_name (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+	/* Autogenerated flag overrides the name property. */
+	if (e_signature_get_autogenerated (signature))
+		return _("Autogenerated");
+
+	return signature->priv->name;
+}
+
+void
+e_signature_set_name (ESignature *signature,
+                      const gchar *name)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	g_free (signature->priv->name);
+	signature->priv->name = g_strdup (name);
+
+	g_object_notify (G_OBJECT (signature), "name");
+}
+
+const gchar *
+e_signature_get_uid (ESignature *signature)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+	return signature->priv->uid;
+}
+
+void
+e_signature_set_uid (ESignature *signature,
+                     const gchar *uid)
+{
+	g_return_if_fail (E_IS_SIGNATURE (signature));
+
+	g_free (signature->priv->uid);
+
+	if (uid == NULL)
+		signature->priv->uid = e_uid_new ();
+	else
+		signature->priv->uid = g_strdup (uid);
+
+	g_object_notify (G_OBJECT (signature), "uid");
+}
diff --git a/libemail-utils/e-signature.h b/libemail-utils/e-signature.h
new file mode 100644
index 0000000..fad1faf
--- /dev/null
+++ b/libemail-utils/e-signature.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_H
+#define E_SIGNATURE_H
+
+#include <glib-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE \
+	(e_signature_get_type ())
+#define E_SIGNATURE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE, ESignature))
+#define E_SIGNATURE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE, ESignatureClass))
+#define E_IS_SIGNATURE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE))
+#define E_IS_SIGNATURE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE))
+#define E_SIGNATURE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE, ESignatureClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignature ESignature;
+typedef struct _ESignatureClass ESignatureClass;
+typedef struct _ESignaturePrivate ESignaturePrivate;
+
+struct _ESignature {
+	GObject parent;
+	ESignaturePrivate *priv;
+};
+
+struct _ESignatureClass {
+	GObjectClass parent_class;
+};
+
+GType		e_signature_get_type		(void);
+ESignature *	e_signature_new			(void);
+ESignature *	e_signature_new_from_xml	(const gchar *xml);
+gchar *		e_signature_uid_from_xml	(const gchar *xml);
+gboolean	e_signature_set_from_xml	(ESignature *signature,
+						 const gchar *xml);
+gchar *		e_signature_to_xml		(ESignature *signature);
+gboolean	e_signature_is_equal		(ESignature *signature1,
+						 ESignature *signature2);
+gboolean	e_signature_get_autogenerated	(ESignature *signature);
+void		e_signature_set_autogenerated	(ESignature *signature,
+						 gboolean autogenerated);
+const gchar *	e_signature_get_filename	(ESignature *signature);
+void		e_signature_set_filename	(ESignature *signature,
+						 const gchar *filename);
+gboolean	e_signature_get_is_html		(ESignature *signature);
+void		e_signature_set_is_html		(ESignature *signature,
+						 gboolean is_html);
+gboolean	e_signature_get_is_script	(ESignature *signature);
+void		e_signature_set_is_script	(ESignature *signature,
+						 gboolean is_script);
+const gchar *	e_signature_get_name		(ESignature *signature);
+void		e_signature_set_name		(ESignature *signature,
+						 const gchar *name);
+const gchar *	e_signature_get_uid		(ESignature *signature);
+void		e_signature_set_uid		(ESignature *signature,
+						 const gchar *uid);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_H */
diff --git a/libemail-utils/gconf-bridge.c b/libemail-utils/gconf-bridge.c
new file mode 100644
index 0000000..3de48b6
--- /dev/null
+++ b/libemail-utils/gconf-bridge.c
@@ -0,0 +1,1364 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "gconf-bridge.h"
+
+struct _GConfBridge {
+        GConfClient *client;
+
+        GHashTable *bindings;
+};
+
+/* The data structures for the different kinds of bindings */
+typedef enum {
+        BINDING_PROP,
+        BINDING_WINDOW,
+        BINDING_LIST_STORE
+} BindingType;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gboolean delayed_mode;
+
+        gchar *key;
+        guint val_notify_id;
+        GSList *val_changes; /* List of changes made to GConf value,
+                                that have not received change notification
+                                yet. */
+
+        GObject *object;
+        GParamSpec *prop;
+        gulong prop_notify_id;
+
+        guint sync_timeout_id; /* Used in delayed mode */
+} PropBinding;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gboolean bind_size;
+        gboolean bind_pos;
+
+        gchar *key_prefix;
+
+        GtkWindow *window;
+        gulong configure_event_id;
+        gulong window_state_event_id;
+        gulong unmap_id;
+        guint sync_timeout_id;
+} WindowBinding;
+
+typedef struct {
+        BindingType type;
+        guint id;
+
+        gchar *key;
+        guint val_notify_id;
+        GSList *val_changes; /* List of changes made to GConf value,
+                                that have not received change notification
+                                yet. */
+
+        GtkListStore *list_store;
+        guint row_inserted_id;
+        guint row_changed_id;
+        guint row_deleted_id;
+        guint rows_reordered_id;
+
+        guint sync_idle_id;
+} ListStoreBinding;
+
+/* Some trickery to be able to treat the data structures generically */
+typedef union {
+        BindingType      type;
+
+        PropBinding      prop_binding;
+        WindowBinding    window_binding;
+        ListStoreBinding list_store_binding;
+} Binding;
+
+/* Function prototypes */
+static void
+unbind (Binding *binding);
+
+static GConfBridge *bridge = NULL; /* Global GConfBridge object */
+
+/* Free up all resources allocated by the GConfBridge. Called on exit. */
+static void
+destroy_bridge (void)
+{
+	g_hash_table_destroy (bridge->bindings);
+	g_object_unref (bridge->client);
+
+	g_free (bridge);
+}
+
+/**
+ * gconf_bridge_get
+ *
+ * Returns the #GConfBridge. This is a singleton object.
+ *
+ * Return value: The #GConfBridge.
+ **/
+GConfBridge *
+gconf_bridge_get (void)
+{
+	if (bridge)
+		return bridge;
+
+	gconf_bridge_install_default_error_handler ();
+
+	bridge = g_new (GConfBridge, 1);
+
+	bridge->client = gconf_client_get_default ();
+	bridge->bindings = g_hash_table_new_full (NULL, NULL, NULL,
+						  (GDestroyNotify) unbind);
+
+	g_atexit (destroy_bridge);
+
+	return bridge;
+}
+
+/**
+ * gconf_bridge_get_client
+ * @bridge: A #GConfBridge
+ *
+ * Returns the #GConfClient used by @bridge. This is the same #GConfClient
+ * as returned by gconf_client_get_default().
+ *
+ * Return value: A #GConfClient.
+ **/
+GConfClient *
+gconf_bridge_get_client (GConfBridge *bridge)
+{
+	g_return_val_if_fail (bridge != NULL, NULL);
+
+	return bridge->client;
+}
+
+/* Generate an ID for a new binding */
+static guint
+new_id (void)
+{
+	static guint id_counter = 0;
+
+	id_counter++;
+
+	return id_counter;
+}
+
+/*
+ * Property bindings
+ */
+
+/* Syncs a value from GConf to an object property */
+static void
+prop_binding_sync_pref_to_prop (PropBinding *binding,
+                                GConfValue *pref_value)
+{
+	GValue src_value, value;
+
+        /* Make sure we don't enter an infinite synchronizing loop */
+	g_signal_handler_block (binding->object, binding->prop_notify_id);
+
+	memset (&src_value, 0, sizeof (GValue));
+
+        /* First, convert GConfValue to GValue */
+	switch (pref_value->type) {
+	case GCONF_VALUE_STRING:
+		g_value_init (&src_value, G_TYPE_STRING);
+		g_value_set_string (&src_value,
+				    gconf_value_get_string (pref_value));
+		break;
+	case GCONF_VALUE_INT:
+		g_value_init (&src_value, G_TYPE_INT);
+		g_value_set_int (&src_value,
+				 gconf_value_get_int (pref_value));
+		break;
+	case GCONF_VALUE_BOOL:
+		g_value_init (&src_value, G_TYPE_BOOLEAN);
+		g_value_set_boolean (&src_value,
+				     gconf_value_get_bool (pref_value));
+		break;
+	case GCONF_VALUE_FLOAT:
+		g_value_init (&src_value, G_TYPE_FLOAT);
+		g_value_set_float (&src_value,
+				   gconf_value_get_float (pref_value));
+		break;
+	default:
+                g_warning ("prop_binding_sync_pref_to_prop: Unhandled value "
+                           "type '%d'.\n", pref_value->type);
+
+		return;
+	}
+
+        /* Then convert to the type expected by the object, if necessary */
+	memset (&value, 0, sizeof (GValue));
+	g_value_init (&value,
+		      G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+
+	if (src_value.g_type != value.g_type) {
+		if (!g_value_transform (&src_value, &value)) {
+                        g_warning ("prop_binding_sync_pref_to_prop: Failed to "
+                                   "transform a \"%s\" to a \"%s\".",
+				   g_type_name (src_value.g_type),
+				   g_type_name (value.g_type));
+
+			goto done;
+		}
+
+		g_object_set_property (binding->object,
+				       binding->prop->name, &value);
+	} else {
+		g_object_set_property (binding->object,
+				       binding->prop->name, &src_value);
+	}
+
+done:
+	g_value_unset (&src_value);
+	g_value_unset (&value);
+
+	g_signal_handler_unblock (binding->object, binding->prop_notify_id);
+}
+
+/* Syncs an object property to GConf */
+static void
+prop_binding_sync_prop_to_pref (PropBinding *binding)
+{
+	GValue value;
+	GConfValue *gconf_value;
+
+	memset (&value, 0, sizeof (GValue));
+
+	g_value_init (&value,
+		      G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+	g_object_get_property (binding->object,
+			       binding->prop->name,
+			       &value);
+
+	switch (value.g_type) {
+	case G_TYPE_STRING:
+		gconf_value = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (gconf_value,
+					g_value_get_string (&value));
+		break;
+	case G_TYPE_INT:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_int (&value));
+		break;
+	case G_TYPE_UINT:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uint (&value));
+		break;
+	case G_TYPE_LONG:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_long (&value));
+		break;
+	case G_TYPE_ULONG:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_ulong (&value));
+		break;
+	case G_TYPE_INT64:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_int64 (&value));
+		break;
+	case G_TYPE_UINT64:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uint64 (&value));
+		break;
+	case G_TYPE_CHAR:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_char (&value));
+		break;
+	case G_TYPE_UCHAR:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_uchar (&value));
+		break;
+	case G_TYPE_ENUM:
+		gconf_value = gconf_value_new (GCONF_VALUE_INT);
+		gconf_value_set_int (gconf_value,
+				     g_value_get_enum (&value));
+		break;
+	case G_TYPE_BOOLEAN:
+		gconf_value = gconf_value_new (GCONF_VALUE_BOOL);
+		gconf_value_set_bool (gconf_value,
+				      g_value_get_boolean (&value));
+		break;
+	case G_TYPE_DOUBLE:
+		gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+#ifdef HAVE_CORBA_GCONF
+                /* FIXME we cast to a gfloat explicitly as CORBA GConf
+                 * uses doubles in its API, but treats them as floats
+                 * when transporting them over CORBA. See #322837 */
+		gconf_value_set_float (gconf_value,
+				       (gfloat) g_value_get_double (&value));
+#else
+		gconf_value_set_float (gconf_value,
+				       g_value_get_double (&value));
+#endif
+		break;
+	case G_TYPE_FLOAT:
+		gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+		gconf_value_set_float (gconf_value,
+				       g_value_get_float (&value));
+		break;
+	default:
+		if (g_type_is_a (value.g_type, G_TYPE_ENUM)) {
+			gconf_value = gconf_value_new (GCONF_VALUE_INT);
+			gconf_value_set_int (gconf_value,
+					     g_value_get_enum (&value));
+		} else {
+                        g_warning ("prop_binding_sync_prop_to_pref: "
+                                   "Unhandled value type '%s'.\n",
+				   g_type_name (value.g_type));
+
+			goto done;
+		}
+
+		break;
+	}
+
+        /* Set to GConf */
+	gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+        /* Store until change notification comes in, so that we are able
+         * to ignore it */
+	binding->val_changes = g_slist_append (binding->val_changes,
+					       gconf_value);
+
+done:
+	g_value_unset (&value);
+}
+
+/* Called when a GConf value bound to an object property has changed */
+static void
+prop_binding_pref_changed (GConfClient *client,
+                           guint cnxn_id,
+                           GConfEntry *entry,
+                           gpointer user_data)
+{
+	GConfValue *gconf_value;
+	PropBinding *binding;
+	GSList *l;
+
+	gconf_value = gconf_entry_get_value (entry);
+	if (!gconf_value)
+		return; /* NULL means that the value has been unset */
+
+	binding = (PropBinding *) user_data;
+
+        /* Check that this notification is not caused by sync_prop_to_pref() */
+	l = g_slist_find_custom (binding->val_changes,
+				 gconf_value,
+				 (GCompareFunc) gconf_value_compare);
+	if (l) {
+		gconf_value_free (l->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, l);
+
+		return;
+	}
+
+	prop_binding_sync_pref_to_prop (binding, gconf_value);
+}
+
+/* Performs a scheduled prop-to-pref sync for a prop binding in
+ * delay mode */
+static gboolean
+prop_binding_perform_scheduled_sync (PropBinding *binding)
+{
+	prop_binding_sync_prop_to_pref (binding);
+
+	binding->sync_timeout_id = 0;
+
+	g_object_unref (binding->object);
+
+	return FALSE;
+}
+
+#define PROP_BINDING_SYNC_DELAY 100 /* Delay for bindings with "delayed"
+                                       set to TRUE, in ms */
+
+/* Called when an object property has changed */
+static void
+prop_binding_prop_changed (GObject *object,
+                           GParamSpec *param_spec,
+                           PropBinding *binding)
+{
+	if (binding->delayed_mode) {
+                /* Just schedule a sync */
+		if (binding->sync_timeout_id == 0) {
+                        /* We keep a reference on the object as long as
+                         * we haven't synced yet to make sure we don't
+                         * lose any data */
+			g_object_ref (binding->object);
+
+			binding->sync_timeout_id =
+				g_timeout_add
+					(PROP_BINDING_SYNC_DELAY,
+					 (GSourceFunc)
+					    prop_binding_perform_scheduled_sync,
+					 binding);
+		}
+	} else {
+                /* Directly sync */
+		prop_binding_sync_prop_to_pref (binding);
+	}
+}
+
+/* Called when an object is destroyed */
+static void
+prop_binding_object_destroyed (gpointer user_data,
+                               GObject *where_the_object_was)
+{
+	PropBinding *binding;
+
+	binding = (PropBinding *) user_data;
+	binding->object = NULL; /* Don't do anything with the object
+                                 * at unbind() */
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_property_full
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ * @delayed_sync: TRUE if there should be a delay between property changes
+ * and syncs to GConf. Set to TRUE when binding to a rapidly-changing
+ * property, for example the "value" property on a #GtkAdjustment.
+ *
+ * Binds @key to @prop, causing them to have the same value at all times.
+ *
+ * The types of @key and @prop should be compatible. Floats and doubles, and
+ * ints, uints, longs, unlongs, int64s, uint64s, chars, uchars and enums
+ * can be matched up. Booleans and strings can only be matched to their
+ * respective types.
+ *
+ * On calling this function the current value of @key will be set to @prop.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_property_full (GConfBridge *bridge,
+                                 const gchar *key,
+                                 GObject *object,
+                                 const gchar *prop,
+                                 gboolean delayed_sync)
+{
+	GParamSpec *pspec;
+	PropBinding *binding;
+	gchar *signal;
+	GConfValue *val;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key != NULL, 0);
+	g_return_val_if_fail (G_IS_OBJECT (object), 0);
+	g_return_val_if_fail (prop != NULL, 0);
+
+        /* First, try to fetch the propertys GParamSpec off the object */
+	pspec = g_object_class_find_property
+				(G_OBJECT_GET_CLASS (object), prop);
+	if (G_UNLIKELY (pspec == NULL)) {
+                g_warning ("gconf_bridge_bind_property_full: A property \"%s\" "
+                           "was not found. Please make sure you are passing "
+                           "the right property name.", prop);
+
+		return 0;
+	}
+
+        /* GParamSpec found: All good, create new binding. */
+	binding = g_new (PropBinding, 1);
+
+	binding->type = BINDING_PROP;
+	binding->id = new_id ();
+	binding->delayed_mode = delayed_sync;
+	binding->val_changes = NULL;
+	binding->key = g_strdup (key);
+	binding->object = object;
+	binding->prop = pspec;
+	binding->sync_timeout_id = 0;
+
+        /* Watch GConf key */
+	binding->val_notify_id =
+		gconf_client_notify_add (bridge->client, key,
+					 prop_binding_pref_changed,
+					 binding, NULL, NULL);
+
+        /* Connect to property change notifications */
+        signal = g_strconcat ("notify::", prop, NULL);
+	binding->prop_notify_id =
+		g_signal_connect (object, signal,
+				  G_CALLBACK (prop_binding_prop_changed),
+				  binding);
+	g_free (signal);
+
+        /* Sync object to value from GConf, if set */
+	val = gconf_client_get (bridge->client, key, NULL);
+	if (val) {
+		prop_binding_sync_pref_to_prop (binding, val);
+		gconf_value_free (val);
+	}
+
+        /* Handle case where watched object gets destroyed */
+	g_object_weak_ref (object,
+			   prop_binding_object_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+static void
+prop_binding_block_cb (gpointer hkey,
+                       PropBinding *binding,
+                       const gchar *key)
+{
+	g_return_if_fail (binding != NULL);
+	g_return_if_fail (key != NULL);
+
+	if (binding->type == BINDING_PROP && binding->key &&
+		g_ascii_strcasecmp (binding->key, key) == 0)
+		g_signal_handler_block (
+			binding->object, binding->prop_notify_id);
+}
+
+static void
+prop_binding_unblock_cb (gpointer hkey,
+                         PropBinding *binding,
+                         const gchar *key)
+{
+	g_return_if_fail (binding != NULL);
+	g_return_if_fail (key != NULL);
+
+	if (binding->type == BINDING_PROP && binding->key &&
+		g_ascii_strcasecmp (binding->key, key) == 0)
+		g_signal_handler_unblock (
+			binding->object, binding->prop_notify_id);
+}
+
+void
+gconf_bridge_block_property_bindings (GConfBridge *bridge,
+                                      const gchar *key)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (key != NULL);
+
+	g_hash_table_foreach (
+		bridge->bindings, (GHFunc)
+		prop_binding_block_cb, (gpointer) key);
+}
+
+void
+gconf_bridge_unblock_property_bindings (GConfBridge *bridge,
+                                        const gchar *key)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (key != NULL);
+
+	g_hash_table_foreach (
+		bridge->bindings, (GHFunc)
+		prop_binding_unblock_cb, (gpointer) key);
+}
+
+/* Unbinds a property binding */
+static void
+prop_binding_unbind (PropBinding *binding)
+{
+	if (binding->delayed_mode && binding->sync_timeout_id > 0) {
+                /* Perform any scheduled syncs */
+		g_source_remove (binding->sync_timeout_id);
+
+                /* The object will still be around as we have
+                 * a reference */
+		prop_binding_perform_scheduled_sync (binding);
+	}
+
+	gconf_client_notify_remove (bridge->client,
+				    binding->val_notify_id);
+	g_free (binding->key);
+
+	while (binding->val_changes) {
+		gconf_value_free (binding->val_changes->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, binding->val_changes);
+	}
+
+        /* The object might have been destroyed .. */
+	if (binding->object) {
+		g_signal_handler_disconnect (binding->object,
+					     binding->prop_notify_id);
+
+		g_object_weak_unref (binding->object,
+				     prop_binding_object_destroyed, binding);
+	}
+}
+
+/*
+ * Window bindings
+ */
+
+/* Performs a scheduled dimensions-to-prefs sync for a window binding */
+static gboolean
+window_binding_perform_scheduled_sync (WindowBinding *binding)
+{
+	if (binding->bind_size) {
+		gint width, height;
+		gchar *key;
+		GdkWindowState state;
+		GdkWindow *window;
+
+		window = gtk_widget_get_window (GTK_WIDGET (binding->window));
+		state = gdk_window_get_state (window);
+
+		if (state & GDK_WINDOW_STATE_MAXIMIZED) {
+                        key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+			gconf_client_set_bool (bridge->client, key, TRUE, NULL);
+			g_free (key);
+		} else {
+			gtk_window_get_size (binding->window, &width, &height);
+
+                        key = g_strconcat (binding->key_prefix, "_width", NULL);
+			gconf_client_set_int (bridge->client, key, width, NULL);
+			g_free (key);
+
+                        key = g_strconcat (binding->key_prefix, "_height", NULL);
+			gconf_client_set_int (bridge->client, key, height, NULL);
+			g_free (key);
+
+                        key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+			gconf_client_set_bool (bridge->client, key, FALSE, NULL);
+			g_free (key);
+		}
+	}
+
+	if (binding->bind_pos) {
+		gint x, y;
+		gchar *key;
+
+		gtk_window_get_position (binding->window, &x, &y);
+
+                key = g_strconcat (binding->key_prefix, "_x", NULL);
+		gconf_client_set_int (bridge->client, key, x, NULL);
+		g_free (key);
+
+                key = g_strconcat (binding->key_prefix, "_y", NULL);
+		gconf_client_set_int (bridge->client, key, y, NULL);
+		g_free (key);
+	}
+
+	binding->sync_timeout_id = 0;
+
+	return FALSE;
+}
+
+#define WINDOW_BINDING_SYNC_DELAY 1 /* Delay before syncing new window
+                                          dimensions to GConf, in s */
+
+/* Called when the window han been resized or moved */
+static gboolean
+window_binding_configure_event_cb (GtkWindow *window,
+                                   GdkEventConfigure *event,
+                                   WindowBinding *binding)
+{
+        /* re-postpone by cancel of the previous request */
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+        /* Schedule a sync */
+	binding->sync_timeout_id = g_timeout_add_seconds (WINDOW_BINDING_SYNC_DELAY,
+		(GSourceFunc) window_binding_perform_scheduled_sync,
+		binding);
+
+	return FALSE;
+}
+
+/* Called when the window state is being changed */
+static gboolean
+window_binding_state_event_cb (GtkWindow *window,
+                               GdkEventWindowState *event,
+                               WindowBinding *binding)
+{
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	if (event
+	    && (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0
+	    && (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) == 0) {
+		/* the window was restored from a maximized state; make sure its
+		 * width and height is the one user stored before maximization */
+		gint width, height;
+
+		width = GPOINTER_TO_INT (g_object_get_data (
+			G_OBJECT (window), "binding-premax-width"));
+		height = GPOINTER_TO_INT (g_object_get_data (
+			G_OBJECT (window), "binding-premax-height"));
+
+		if (width && height) {
+			gtk_window_resize (window, width, height);
+
+			/* Do this only once, as it is restored
+			 * after loading maximized state. */
+			g_object_set_data (
+				G_OBJECT (window),
+				"binding-premax-width", NULL);
+			g_object_set_data (
+				G_OBJECT (window),
+				"binding-premax-height", NULL);
+		}
+	}
+
+	window_binding_perform_scheduled_sync (binding);
+
+	return FALSE;
+}
+
+/* Called when the window is being unmapped */
+static gboolean
+window_binding_unmap_cb (GtkWindow *window,
+                         WindowBinding *binding)
+{
+        /* Force sync */
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+        /* XXX It's too late to record the window position.
+         *     gtk_window_get_position() will report (0, 0). */
+	binding->bind_pos = FALSE;
+
+	window_binding_perform_scheduled_sync (binding);
+
+	return FALSE;
+}
+
+/* Called when a window is destroyed */
+static void
+window_binding_window_destroyed (gpointer user_data,
+                                 GObject *where_the_object_was)
+{
+	WindowBinding *binding;
+
+	binding = (WindowBinding *) user_data;
+	binding->window = NULL; /* Don't do anything with the window
+                                 * at unbind() */
+
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_window
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ * @bind_size: TRUE to bind the size of @window
+ * @bind_pos: TRUE to bind the position of @window
+ *
+ * On calling this function @window will be resized to the values
+ * specified by "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height"
+ * and maximixed if "@key_prefix<!-- -->_maximized is TRUE if
+ * @bind_size is TRUE, and moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y" if @bind_pos is TRUE.
+ * The respective GConf values will be updated when the window is resized
+ * and/or moved.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_window (GConfBridge *bridge,
+                          const gchar *key_prefix,
+                          GtkWindow *window,
+                          gboolean bind_size,
+                          gboolean bind_pos)
+{
+	WindowBinding *binding;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key_prefix != NULL, 0);
+	g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+        /* Create new binding. */
+	binding = g_new (WindowBinding, 1);
+
+	binding->type = BINDING_WINDOW;
+	binding->id = new_id ();
+	binding->bind_size = bind_size;
+	binding->bind_pos = bind_pos;
+	binding->key_prefix = g_strdup (key_prefix);
+	binding->window = window;
+	binding->sync_timeout_id = 0;
+
+        /* Set up GConf keys & sync window to GConf values */
+	if (bind_size) {
+		gchar *key;
+		GConfValue *width_val, *height_val, *maximized_val;
+
+                key = g_strconcat (key_prefix, "_width", NULL);
+		width_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_height", NULL);
+		height_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_maximized", NULL);
+		maximized_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+		if (width_val && height_val) {
+			gtk_window_resize (window,
+					   gconf_value_get_int (width_val),
+					   gconf_value_get_int (height_val));
+
+			gconf_value_free (width_val);
+			gconf_value_free (height_val);
+		} else if (width_val) {
+			gconf_value_free (width_val);
+		} else if (height_val) {
+			gconf_value_free (height_val);
+		}
+
+		if (maximized_val) {
+			if (gconf_value_get_bool (maximized_val)) {
+				/* Maximize is not done immediately, but to
+				 * count with proper window size, resize it
+				 * before. The previous size is restored
+				 * after the maximization is changed,
+				 * in window_binding_state_event_cb(). */
+				gint width = 0, height = 0;
+				GdkScreen *screen;
+
+				gtk_window_get_size (window, &width, &height);
+				g_object_set_data (
+					G_OBJECT (window),
+					"binding-premax-width",
+					GINT_TO_POINTER (width));
+				g_object_set_data (
+					G_OBJECT (window),
+					"binding-premax-height",
+					GINT_TO_POINTER (height));
+
+				screen = gtk_window_get_screen (window);
+				gtk_window_resize (window,
+					gdk_screen_get_width (screen),
+					gdk_screen_get_height (screen));
+
+				gtk_window_maximize (window);
+			}
+			gconf_value_free (maximized_val);
+		}
+	}
+
+	if (bind_pos) {
+		gchar *key;
+		GConfValue *x_val, *y_val;
+
+                key = g_strconcat (key_prefix, "_x", NULL);
+		x_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+                key = g_strconcat (key_prefix, "_y", NULL);
+		y_val = gconf_client_get (bridge->client, key, NULL);
+		g_free (key);
+
+		if (x_val && y_val) {
+			gtk_window_move (window,
+					 gconf_value_get_int (x_val),
+					 gconf_value_get_int (y_val));
+
+			gconf_value_free (x_val);
+			gconf_value_free (y_val);
+		} else if (x_val) {
+			gconf_value_free (x_val);
+		} else if (y_val) {
+			gconf_value_free (y_val);
+		}
+	}
+
+        /* Connect to window size change notifications */
+	binding->configure_event_id =
+		g_signal_connect (window,
+                                  "configure-event",
+				  G_CALLBACK
+					(window_binding_configure_event_cb),
+				  binding);
+
+	binding->window_state_event_id =
+		g_signal_connect (window,
+                                  "window_state_event",
+				  G_CALLBACK
+					(window_binding_state_event_cb),
+				  binding);
+	binding->unmap_id =
+		g_signal_connect (window,
+                                  "unmap",
+				  G_CALLBACK (window_binding_unmap_cb),
+				  binding);
+
+        /* Handle case where window gets destroyed */
+	g_object_weak_ref (G_OBJECT (window),
+			   window_binding_window_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+/* Unbinds a window binding */
+static void
+window_binding_unbind (WindowBinding *binding)
+{
+	if (binding->sync_timeout_id > 0)
+		g_source_remove (binding->sync_timeout_id);
+
+	g_free (binding->key_prefix);
+
+        /* The window might have been destroyed .. */
+	if (binding->window) {
+		g_signal_handler_disconnect (binding->window,
+					     binding->configure_event_id);
+		g_signal_handler_disconnect (binding->window,
+					     binding->window_state_event_id);
+		g_signal_handler_disconnect (binding->window,
+					     binding->unmap_id);
+
+		g_object_weak_unref (G_OBJECT (binding->window),
+				     window_binding_window_destroyed, binding);
+	}
+}
+
+/*
+ * List store bindings
+ */
+
+/* Fills a GtkListStore with the string list from @value */
+static void
+list_store_binding_sync_pref_to_store (ListStoreBinding *binding,
+                                       GConfValue *value)
+{
+	GSList *list, *l;
+	GtkTreeIter iter;
+
+        /* Make sure we don't enter an infinite synchronizing loop */
+	g_signal_handler_block (binding->list_store,
+				binding->row_inserted_id);
+	g_signal_handler_block (binding->list_store,
+				binding->row_deleted_id);
+
+	gtk_list_store_clear (binding->list_store);
+
+	list = gconf_value_get_list (value);
+	for (l = list; l; l = l->next) {
+		GConfValue *l_value;
+		const gchar *string;
+
+		l_value = (GConfValue *) l->data;
+		string = gconf_value_get_string (l_value);
+
+		gtk_list_store_insert_with_values (binding->list_store,
+						   &iter, -1,
+						   0, string,
+						   -1);
+	}
+
+	g_signal_handler_unblock (binding->list_store,
+				  binding->row_inserted_id);
+	g_signal_handler_unblock (binding->list_store,
+				  binding->row_deleted_id);
+}
+
+/* Sets a GConf value to the contents of a GtkListStore */
+static gboolean
+list_store_binding_sync_store_to_pref (ListStoreBinding *binding)
+{
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	GSList *list;
+	gint res;
+	GConfValue *gconf_value;
+
+	tree_model = GTK_TREE_MODEL (binding->list_store);
+
+        /* Build list */
+	list = NULL;
+	res = gtk_tree_model_get_iter_first (tree_model, &iter);
+	while (res) {
+		gchar *string;
+		GConfValue *tmp_value;
+
+		gtk_tree_model_get (tree_model, &iter,
+				    0, &string, -1);
+
+		tmp_value = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (tmp_value, string);
+
+		list = g_slist_append (list, tmp_value);
+
+		res = gtk_tree_model_iter_next (tree_model, &iter);
+	}
+
+        /* Create value */
+	gconf_value = gconf_value_new (GCONF_VALUE_LIST);
+	gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING);
+	gconf_value_set_list_nocopy (gconf_value, list);
+
+        /* Set */
+	gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+        /* Store until change notification comes in, so that we are able
+         * to ignore it */
+	binding->val_changes = g_slist_append (binding->val_changes,
+					       gconf_value);
+
+	binding->sync_idle_id = 0;
+
+	g_object_unref (binding->list_store);
+
+	return FALSE;
+}
+
+/* Pref changed: sync */
+static void
+list_store_binding_pref_changed (GConfClient *client,
+                                 guint cnxn_id,
+                                 GConfEntry *entry,
+                                 gpointer user_data)
+{
+	GConfValue *gconf_value;
+	ListStoreBinding *binding;
+	GSList *l;
+
+	gconf_value = gconf_entry_get_value (entry);
+	if (!gconf_value)
+		return; /* NULL means that the value has been unset */
+
+	binding = (ListStoreBinding *) user_data;
+
+        /* Check that this notification is not caused by
+         * sync_store_to_pref() */
+	l = g_slist_find_custom (binding->val_changes,
+				 gconf_value,
+				 (GCompareFunc) gconf_value_compare);
+	if (l) {
+		gconf_value_free (l->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, l);
+
+		return;
+	}
+
+	list_store_binding_sync_pref_to_store (binding, gconf_value);
+}
+
+/* Called when an object is destroyed */
+static void
+list_store_binding_store_destroyed (gpointer user_data,
+                                    GObject *where_the_object_was)
+{
+	ListStoreBinding *binding;
+
+	binding = (ListStoreBinding *) user_data;
+	binding->list_store = NULL; /* Don't do anything with the store
+                                     * at unbind() */
+
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id));
+}
+
+/* List store changed: Sync */
+static void
+list_store_binding_store_changed_cb (ListStoreBinding *binding)
+{
+	if (binding->sync_idle_id == 0) {
+		g_object_ref (binding->list_store);
+
+		binding->sync_idle_id = g_idle_add
+			((GSourceFunc) list_store_binding_sync_store_to_pref,
+			 binding);
+	}
+}
+
+/**
+ * gconf_bridge_bind_string_list_store
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @list_store: A #GtkListStore
+ *
+ * On calling this function single string column #GtkListStore @list_store
+ * will be kept synchronized with the GConf string list value pointed to by
+ * @key. On calling this function @list_store will be populated with the
+ * strings specified by the value of @key.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+                                     const gchar *key,
+                                     GtkListStore *list_store)
+{
+	GtkTreeModel *tree_model;
+	gboolean have_one_column, is_string_column;
+	ListStoreBinding *binding;
+	GConfValue *val;
+
+	g_return_val_if_fail (bridge != NULL, 0);
+	g_return_val_if_fail (key != NULL, 0);
+	g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), 0);
+
+        /* Check list store suitability */
+	tree_model = GTK_TREE_MODEL (list_store);
+	have_one_column = (gtk_tree_model_get_n_columns (tree_model) == 1);
+	is_string_column = (gtk_tree_model_get_column_type
+					(tree_model, 0) == G_TYPE_STRING);
+	if (G_UNLIKELY (!have_one_column || !is_string_column)) {
+                g_warning ("gconf_bridge_bind_string_list_store: Only "
+                           "GtkListStores with exactly one string column are "
+                           "supported.");
+
+		return 0;
+	}
+
+        /* Create new binding. */
+	binding = g_new (ListStoreBinding, 1);
+
+	binding->type = BINDING_LIST_STORE;
+	binding->id = new_id ();
+	binding->key = g_strdup (key);
+	binding->val_changes = NULL;
+	binding->list_store = list_store;
+	binding->sync_idle_id = 0;
+
+        /* Watch GConf key */
+	binding->val_notify_id =
+		gconf_client_notify_add (bridge->client, key,
+					 list_store_binding_pref_changed,
+					 binding, NULL, NULL);
+
+        /* Connect to ListStore change notifications */
+	binding->row_inserted_id =
+                g_signal_connect_swapped (list_store, "row-inserted",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->row_changed_id =
+                g_signal_connect_swapped (list_store, "row-changed",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->row_deleted_id =
+                g_signal_connect_swapped (list_store, "row-deleted",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+	binding->rows_reordered_id =
+                g_signal_connect_swapped (list_store, "rows-reordered",
+					  G_CALLBACK
+					  (list_store_binding_store_changed_cb),
+					  binding);
+
+        /* Sync object to value from GConf, if set */
+	val = gconf_client_get (bridge->client, key, NULL);
+	if (val) {
+		list_store_binding_sync_pref_to_store (binding, val);
+		gconf_value_free (val);
+	}
+
+        /* Handle case where watched object gets destroyed */
+	g_object_weak_ref (G_OBJECT (list_store),
+			   list_store_binding_store_destroyed, binding);
+
+        /* Insert binding */
+	g_hash_table_insert (bridge->bindings,
+			     GUINT_TO_POINTER (binding->id), binding);
+
+        /* Done */
+	return binding->id;
+}
+
+/* Unbinds a list store binding */
+static void
+list_store_binding_unbind (ListStoreBinding *binding)
+{
+        /* Perform any scheduled syncs */
+	if (binding->sync_idle_id > 0) {
+		g_source_remove (binding->sync_idle_id);
+
+                /* The store will still be around as we added a reference */
+		list_store_binding_sync_store_to_pref (binding);
+	}
+
+	g_free (binding->key);
+
+	while (binding->val_changes) {
+		gconf_value_free (binding->val_changes->data);
+
+		binding->val_changes = g_slist_delete_link
+			(binding->val_changes, binding->val_changes);
+	}
+
+        /* The store might have been destroyed .. */
+	if (binding->list_store) {
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_inserted_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_changed_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->row_deleted_id);
+		g_signal_handler_disconnect (binding->list_store,
+					     binding->rows_reordered_id);
+
+		g_object_weak_unref (G_OBJECT (binding->list_store),
+				     list_store_binding_store_destroyed,
+				     binding);
+	}
+}
+
+/*
+ * Generic unbinding
+ */
+
+/* Unbinds a binding */
+static void
+unbind (Binding *binding)
+{
+        /* Call specialized unbinding function */
+	switch (binding->type) {
+	case BINDING_PROP:
+		prop_binding_unbind ((PropBinding *) binding);
+		break;
+	case BINDING_WINDOW:
+		window_binding_unbind ((WindowBinding *) binding);
+		break;
+	case BINDING_LIST_STORE:
+		list_store_binding_unbind ((ListStoreBinding *) binding);
+		break;
+	default:
+                g_warning ("Unknown binding type '%d'\n", binding->type);
+		break;
+	}
+
+	g_free (binding);
+}
+
+/**
+ * gconf_bridge_unbind
+ * @bridge: A #GConfBridge
+ * @binding_id: The ID of the binding to be removed
+ *
+ * Removes the binding with ID @binding_id.
+ **/
+void
+gconf_bridge_unbind (GConfBridge *bridge,
+                     guint binding_id)
+{
+	g_return_if_fail (bridge != NULL);
+	g_return_if_fail (binding_id > 0);
+
+        /* This will trigger the hash tables value destruction
+         * function, which will take care of further cleanup */
+	g_hash_table_remove (bridge->bindings,
+			     GUINT_TO_POINTER (binding_id));
+}
+
+/*
+ * Error handling
+ */
+
+/* This is the same dialog as used in eel */
+static void
+error_handler (GConfClient *client,
+               GError *error)
+{
+	static gboolean shown_dialog = FALSE;
+
+        g_warning ("GConf error:\n  %s", error->message);
+
+	if (!shown_dialog) {
+		gchar *message;
+		GtkWidget *dlg;
+
+                message = g_strdup_printf (_("GConf error: %s"),
+					   error->message);
+		dlg = gtk_message_dialog_new (NULL, 0,
+					      GTK_MESSAGE_ERROR,
+					      GTK_BUTTONS_OK,
+                                              "%s",
+					      message);
+		g_free (message);
+
+		gtk_message_dialog_format_secondary_text
+			(GTK_MESSAGE_DIALOG (dlg),
+                         _("All further errors shown only on terminal."));
+                gtk_window_set_title (GTK_WINDOW (dlg), "");
+
+		gtk_dialog_run (GTK_DIALOG (dlg));
+
+		gtk_widget_destroy (dlg);
+
+		shown_dialog = TRUE;
+	}
+}
+
+/**
+ * gconf_bridge_install_default_error_handler
+ *
+ * Sets up the default error handler. Any unhandled GConf errors will
+ * automatically be handled by presenting the user an error dialog.
+ **/
+void
+gconf_bridge_install_default_error_handler (void)
+{
+	gconf_client_set_global_default_error_handler (error_handler);
+}
diff --git a/libemail-utils/gconf-bridge.h b/libemail-utils/gconf-bridge.h
new file mode 100644
index 0000000..371f117
--- /dev/null
+++ b/libemail-utils/gconf-bridge.h
@@ -0,0 +1,134 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GCONF_BRIDGE_H__
+#define __GCONF_BRIDGE_H__
+
+#include <gconf/gconf-client.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void gconf_bridge_install_default_error_handler (void);
+
+typedef struct _GConfBridge GConfBridge;
+
+GConfBridge *gconf_bridge_get                    (void);
+
+GConfClient *gconf_bridge_get_client             (GConfBridge  *bridge);
+
+guint        gconf_bridge_bind_property_full     (GConfBridge  *bridge,
+                                                  const gchar   *key,
+                                                  GObject      *object,
+                                                  const gchar   *prop,
+                                                  gboolean      delayed_sync);
+
+/**
+ * gconf_bridge_bind_property
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop without delays, causing them to have the same value at all times. See
+ * #gconf_bridge_bind_property_full for more details.
+ *
+ **/
+#define gconf_bridge_bind_property(bridge, key, object, prop) \
+        gconf_bridge_bind_property_full ((bridge), (key), \
+                                         (object), (prop), FALSE)
+
+/**
+ * gconf_bridge_bind_property_delayed
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop with a delay, causing them to have the same value at all
+ * times. See #gconf_bridge_bind_property_full for more details.
+ **/
+#define gconf_bridge_bind_property_delayed(bridge, key, object, prop) \
+        gconf_bridge_bind_property_full ((bridge), (key), \
+                                         (object), (prop), TRUE)
+
+/**
+ * gconf_bridge_block_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be blocked
+ *
+ * Blocks property bindings for @key. To unblock it call #gconf_bridge_unblock_property_bindings.
+ **/
+void         gconf_bridge_block_property_bindings (GConfBridge  *bridge, const gchar *key);
+
+/**
+ * gconf_bridge_unblock_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be unblocked
+ *
+ * Unblocks property bindings for @key, these should be previously blocked with #gconf_bridge_unblock_property_bindings.
+ **/
+void         gconf_bridge_unblock_property_bindings (GConfBridge  *bridge, const gchar *key);
+
+guint        gconf_bridge_bind_window            (GConfBridge  *bridge,
+                                                  const gchar   *key_prefix,
+                                                  GtkWindow    *window,
+                                                  gboolean      bind_size,
+                                                  gboolean      bind_pos);
+
+/**
+ * gconf_bridge_bind_window_size
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be resized to the values specified by
+ * "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height".  The respective
+ * GConf values will be updated when the window is resized. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_size(bridge, key_prefix, window) \
+        gconf_bridge_bind_window ((bridge), (key_prefix), (window), TRUE, FALSE)
+
+/**
+ * gconf_bridge_bind_window_pos
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y". The respective GConf
+ * values will be updated when the window is moved. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_pos(bridge, key_prefix, window) \
+        gconf_bridge_bind_window ((bridge), (key_prefix), (window), FALSE, TRUE)
+
+guint        gconf_bridge_bind_string_list_store (GConfBridge  *bridge,
+                                                  const gchar   *key,
+                                                  GtkListStore *list_store);
+
+void         gconf_bridge_unbind                 (GConfBridge  *bridge,
+                                                  guint         binding_id);
+
+G_END_DECLS
+
+#endif /* __GCONF_BRIDGE_H__ */
diff --git a/libemail-utils/libemail-utils.pc.in b/libemail-utils/libemail-utils.pc.in
new file mode 100644
index 0000000..384147b
--- /dev/null
+++ b/libemail-utils/libemail-utils.pc.in
@@ -0,0 +1,15 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+privincludedir= privincludedir@
+
+Name: libemail-utils
+Description: Client library for evolution mail
+Version: @VERSION@
+Requires: camel-1.2 libedataserver-1.2 gio-2.0
+Libs: -L${libdir} -lemail-utils
+Cflags: -I${privincludedir}
diff --git a/libemail-utils/mail-mt.c b/libemail-utils/mail-mt.c
new file mode 100644
index 0000000..4c3ee34
--- /dev/null
+++ b/libemail-utils/mail-mt.c
@@ -0,0 +1,639 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/e-flag.h>
+
+#include "mail-mt.h"
+
+/*#define MALLOC_CHECK*/
+#define d(x)
+
+/* XXX This is a dirty hack on a dirty hack.  We really need
+ *     to rework or get rid of the functions that use this. */
+const gchar *shell_builtin_backend = "mail";
+
+static guint mail_msg_seq; /* sequence number of each message */
+
+/* Table of active messages.  Must hold mail_msg_lock to access. */
+static GHashTable *mail_msg_active_table;
+static GMutex *mail_msg_lock;
+static GCond *mail_msg_cond;
+
+static MailMsgCreateActivityFunc create_activity = NULL;
+static MailMsgSubmitActivityFunc submit_acitivity = NULL;
+static MailMsgFreeActivityFunc free_activity = NULL;
+static MailMsgCompleteActivityFunc complete_activity = NULL;
+static MailMsgAlertErrorFunc alert_error = NULL;
+static MailMsgCancelActivityFunc cancel_activity = NULL;
+
+void mail_msg_register_activities (MailMsgCreateActivityFunc acreate,
+				   MailMsgSubmitActivityFunc asubmit,
+				   MailMsgFreeActivityFunc freeact,
+				   MailMsgCompleteActivityFunc comp_act,
+				   MailMsgCancelActivityFunc cancel_act,
+				   MailMsgAlertErrorFunc ealert)
+{
+	/* This is a utter hack to keep EActivity out of EDS and still let Evolution do EActivity */
+	create_activity = acreate;
+	submit_acitivity = asubmit;
+	free_activity = freeact;
+	complete_activity = comp_act;
+	cancel_activity = cancel_act;
+	alert_error = ealert;
+}
+
+static void
+mail_msg_cancelled (CamelOperation *operation,
+                    gpointer user_data)
+{
+	mail_msg_cancel (GPOINTER_TO_UINT (user_data));
+}
+
+
+static gboolean
+mail_msg_submit (CamelOperation *cancellable)
+{
+
+	if (submit_acitivity)
+		submit_acitivity ((GCancellable *)cancellable);
+	return FALSE;
+}
+
+gpointer
+mail_msg_new (MailMsgInfo *info)
+{
+	MailMsg *msg;
+
+	g_mutex_lock (mail_msg_lock);
+
+	msg = g_slice_alloc0 (info->size);
+	msg->info = info;
+	msg->ref_count = 1;
+	msg->seq = mail_msg_seq++;
+
+	msg->cancellable = camel_operation_new ();
+	
+	if (create_activity)
+		create_activity (msg->cancellable);
+
+	g_signal_connect (
+		msg->cancellable, "cancelled",
+		G_CALLBACK (mail_msg_cancelled),
+		GINT_TO_POINTER (msg->seq));
+
+	g_hash_table_insert (
+		mail_msg_active_table, GINT_TO_POINTER (msg->seq), msg);
+
+	d(printf("New message %p\n", msg));
+
+	g_mutex_unlock (mail_msg_lock);
+
+	return msg;
+}
+
+#ifdef MALLOC_CHECK
+#include <mcheck.h>
+
+static void
+checkmem (gpointer p)
+{
+	if (p) {
+		gint status = mprobe (p);
+
+		switch (status) {
+		case MCHECK_HEAD:
+			printf("Memory underrun at %p\n", p);
+			abort ();
+		case MCHECK_TAIL:
+			printf("Memory overrun at %p\n", p);
+			abort ();
+		case MCHECK_FREE:
+			printf("Double free %p\n", p);
+			abort ();
+		}
+	}
+}
+#endif
+
+static gboolean
+mail_msg_free (MailMsg *mail_msg)
+{
+	/* This is an idle callback. */
+
+	if (free_activity)
+		free_activity (mail_msg->cancellable);
+
+	if (mail_msg->cancellable != NULL)
+		g_object_unref (mail_msg->cancellable);
+
+	if (mail_msg->error != NULL)
+		g_error_free (mail_msg->error);
+
+	g_slice_free1 (mail_msg->info->size, mail_msg);
+
+	return FALSE;
+}
+
+gpointer
+mail_msg_ref (gpointer msg)
+{
+	MailMsg *mail_msg = msg;
+
+	g_return_val_if_fail (mail_msg != NULL, msg);
+	g_return_val_if_fail (mail_msg->ref_count > 0, msg);
+
+	g_atomic_int_inc (&mail_msg->ref_count);
+
+	return msg;
+}
+
+void
+mail_msg_unref (gpointer msg)
+{
+	MailMsg *mail_msg = msg;
+
+	g_return_if_fail (mail_msg != NULL);
+	g_return_if_fail (mail_msg->ref_count > 0);
+
+	if (g_atomic_int_dec_and_test (&mail_msg->ref_count)) {
+
+#ifdef MALLOC_CHECK
+		checkmem (mail_msg);
+		checkmem (mail_msg->cancel);
+		checkmem (mail_msg->priv);
+#endif
+		d(printf("Free message %p\n", msg));
+
+		if (mail_msg->info->free)
+			mail_msg->info->free (mail_msg);
+
+		g_mutex_lock (mail_msg_lock);
+
+		g_hash_table_remove (
+			mail_msg_active_table,
+			GINT_TO_POINTER (mail_msg->seq));
+		g_cond_broadcast (mail_msg_cond);
+
+		g_mutex_unlock (mail_msg_lock);
+
+		/* Destroy the message from an idle callback
+		 * so we know we're in the main loop thread. */
+		g_idle_add ((GSourceFunc) mail_msg_free, mail_msg);
+	}
+}
+
+void
+mail_msg_check_error (gpointer msg)
+{
+	MailMsg *m = msg;
+
+#ifdef MALLOC_CHECK
+	checkmem (m);
+	checkmem (m->cancel);
+	checkmem (m->priv);
+#endif
+
+	if (m->error == NULL)
+		return;
+
+	if (complete_activity)
+		complete_activity (m->cancellable);
+
+	if (g_error_matches (m->error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		if (cancel_activity)
+			cancel_activity (m->cancellable);
+		return;
+	}	
+
+	/* XXX Hmm, no explanation of why this is needed.  It looks like
+	 *     a lame hack and will be removed at some point, if only to
+	 *     reintroduce whatever issue made this necessary so we can
+	 *     document it the source code this time. */
+	if (g_error_matches (m->error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_UID))
+		return;
+
+	/* FIXME: Submit an error on the dbus */
+	if (alert_error) {
+		char *what;
+
+		if (m->info->desc && (what = m->info->desc (m))) {
+			alert_error (what, m->error->message);
+			g_free (what);
+		} else
+			alert_error (NULL, m->error->message);
+	}
+}	
+
+void
+mail_msg_cancel (guint msgid)
+{
+	MailMsg *msg;
+	GCancellable *cancellable = NULL;
+
+	g_mutex_lock (mail_msg_lock);
+
+	msg = g_hash_table_lookup (
+		mail_msg_active_table, GINT_TO_POINTER (msgid));
+
+	/* Hold a reference to the GCancellable so it doesn't finalize
+	 * itself on us between unlocking the mutex and cancelling. */
+	if (msg != NULL) {
+		cancellable = msg->cancellable;
+		if (g_cancellable_is_cancelled (cancellable))
+			cancellable = NULL;
+		else
+			g_object_ref (cancellable);
+	}
+
+	g_mutex_unlock (mail_msg_lock);
+
+	if (cancellable != NULL) {
+		camel_operation_cancel (CAMEL_OPERATION (cancellable));
+		g_object_unref (cancellable);
+	}
+}
+
+gboolean
+mail_msg_active (void)
+{
+	gboolean active;
+
+	g_mutex_lock (mail_msg_lock);
+	active = g_hash_table_size (mail_msg_active_table) > 0;
+	g_mutex_unlock (mail_msg_lock);
+
+	return active;
+}
+
+/* **************************************** */
+
+static GHookList cancel_hook_list;
+
+GHook *
+mail_cancel_hook_add (GHookFunc func,
+                      gpointer data)
+{
+	GHook *hook;
+
+	g_mutex_lock (mail_msg_lock);
+
+	if (!cancel_hook_list.is_setup)
+		g_hook_list_init (&cancel_hook_list, sizeof (GHook));
+
+	hook = g_hook_alloc (&cancel_hook_list);
+	hook->func = func;
+	hook->data = data;
+
+	g_hook_append (&cancel_hook_list, hook);
+
+	g_mutex_unlock (mail_msg_lock);
+
+	return hook;
+}
+
+void
+mail_cancel_hook_remove (GHook *hook)
+{
+	g_mutex_lock (mail_msg_lock);
+
+	g_return_if_fail (cancel_hook_list.is_setup);
+	g_hook_destroy_link (&cancel_hook_list, hook);
+
+	g_mutex_unlock (mail_msg_lock);
+}
+
+void
+mail_cancel_all (void)
+{
+	camel_operation_cancel (NULL);
+
+	g_mutex_lock (mail_msg_lock);
+
+	if (cancel_hook_list.is_setup)
+		g_hook_list_invoke (&cancel_hook_list, FALSE);
+
+	g_mutex_unlock (mail_msg_lock);
+}
+
+static guint idle_source_id = 0;
+G_LOCK_DEFINE_STATIC (idle_source_id);
+static GAsyncQueue *main_loop_queue = NULL;
+static GAsyncQueue *msg_reply_queue = NULL;
+static GThread *main_thread = NULL;
+
+static gboolean
+mail_msg_idle_cb (void)
+{
+	MailMsg *msg;
+
+	g_return_val_if_fail (main_loop_queue != NULL, FALSE);
+	g_return_val_if_fail (msg_reply_queue != NULL, FALSE);
+
+	G_LOCK (idle_source_id);
+	idle_source_id = 0;
+	G_UNLOCK (idle_source_id);
+	/* check the main loop queue */
+	while ((msg = g_async_queue_try_pop (main_loop_queue)) != NULL) {
+		GCancellable *cancellable;
+
+		cancellable = msg->cancellable;
+
+		g_idle_add_full (
+			G_PRIORITY_DEFAULT,
+			(GSourceFunc) mail_msg_submit,
+			g_object_ref (msg->cancellable),
+			(GDestroyNotify) g_object_unref);
+		if (msg->info->exec != NULL)
+			msg->info->exec (msg, cancellable, &msg->error);
+		if (msg->info->done != NULL)
+			msg->info->done (msg);
+		mail_msg_unref (msg);
+	}
+
+	/* check the reply queue */
+	while ((msg = g_async_queue_try_pop (msg_reply_queue)) != NULL) {
+		if (msg->info->done != NULL)
+			msg->info->done (msg);
+		mail_msg_check_error (msg);
+		mail_msg_unref (msg);
+	}
+	return FALSE;
+}
+
+static void
+mail_msg_proxy (MailMsg *msg)
+{
+	GCancellable *cancellable;
+
+	cancellable = msg->cancellable;
+
+	if (msg->info->desc != NULL) {
+		gchar *text = msg->info->desc (msg);
+		camel_operation_push_message (cancellable, "%s", text);
+		g_free (text);
+	}
+
+	g_idle_add_full (
+		G_PRIORITY_DEFAULT,
+		(GSourceFunc) mail_msg_submit,
+		g_object_ref (msg->cancellable),
+		(GDestroyNotify) g_object_unref);
+
+	if (msg->info->exec != NULL)
+		msg->info->exec (msg, cancellable, &msg->error);
+
+	if (msg->info->desc != NULL)
+		camel_operation_pop_message (cancellable);
+
+	g_async_queue_push (msg_reply_queue, msg);
+
+	G_LOCK (idle_source_id);
+	if (idle_source_id == 0)
+		idle_source_id = g_idle_add (
+			(GSourceFunc) mail_msg_idle_cb, NULL);
+	G_UNLOCK (idle_source_id);
+}
+
+void
+mail_msg_init (void)
+{
+	mail_msg_lock = g_mutex_new ();
+	mail_msg_cond = g_cond_new ();
+
+	main_loop_queue = g_async_queue_new ();
+	msg_reply_queue = g_async_queue_new ();
+
+	mail_msg_active_table = g_hash_table_new (NULL, NULL);
+	main_thread = g_thread_self ();
+}
+
+static gint
+mail_msg_compare (const MailMsg *msg1,
+                  const MailMsg *msg2)
+{
+	gint priority1 = msg1->priority;
+	gint priority2 = msg2->priority;
+
+	if (priority1 == priority2)
+		return 0;
+
+	return (priority1 < priority2) ? 1 : -1;
+}
+
+static gpointer
+create_thread_pool (gpointer data)
+{
+	GThreadPool *thread_pool;
+	gint max_threads = GPOINTER_TO_INT (data);
+
+	/* once created, run forever */
+	thread_pool = g_thread_pool_new (
+		(GFunc) mail_msg_proxy, NULL, max_threads, FALSE, NULL);
+	g_thread_pool_set_sort_function (
+		thread_pool, (GCompareDataFunc) mail_msg_compare, NULL);
+
+	return thread_pool;
+}
+
+void
+mail_msg_main_loop_push (gpointer msg)
+{
+	g_async_queue_push_sorted (main_loop_queue, msg,
+		(GCompareDataFunc) mail_msg_compare, NULL);
+
+	G_LOCK (idle_source_id);
+	if (idle_source_id == 0)
+		idle_source_id = g_idle_add (
+			(GSourceFunc) mail_msg_idle_cb, NULL);
+	G_UNLOCK (idle_source_id);
+}
+
+void
+mail_msg_unordered_push (gpointer msg)
+{
+	static GOnce once = G_ONCE_INIT;
+
+	g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (10));
+
+	g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+void
+mail_msg_fast_ordered_push (gpointer msg)
+{
+	static GOnce once = G_ONCE_INIT;
+
+	g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1));
+
+	g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+void
+mail_msg_slow_ordered_push (gpointer msg)
+{
+	static GOnce once = G_ONCE_INIT;
+
+	g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1));
+
+	g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+gboolean
+mail_in_main_thread (void)
+{
+	return (g_thread_self () == main_thread);
+}
+
+/* ********************************************************************** */
+
+struct _call_msg {
+	MailMsg base;
+
+	mail_call_t type;
+	MailMainFunc func;
+	gpointer ret;
+	va_list ap;
+	EFlag *done;
+};
+
+static void
+do_call (struct _call_msg *m,
+         GCancellable *cancellable,
+         GError **error)
+{
+	gpointer p1, *p2, *p3, *p4, *p5;
+	gint i1;
+	va_list ap;
+
+	G_VA_COPY (ap, m->ap);
+
+	switch (m->type) {
+	case MAIL_CALL_p_p:
+		p1 = va_arg (ap, gpointer );
+		m->ret = m->func (p1);
+		break;
+	case MAIL_CALL_p_pp:
+		p1 = va_arg (ap, gpointer );
+		p2 = va_arg (ap, gpointer );
+		m->ret = m->func (p1, p2);
+		break;
+	case MAIL_CALL_p_ppp:
+		p1 = va_arg (ap, gpointer );
+		p2 = va_arg (ap, gpointer );
+		p3 = va_arg (ap, gpointer );
+		m->ret = m->func (p1, p2, p3);
+		break;
+	case MAIL_CALL_p_pppp:
+		p1 = va_arg (ap, gpointer );
+		p2 = va_arg (ap, gpointer );
+		p3 = va_arg (ap, gpointer );
+		p4 = va_arg (ap, gpointer );
+		m->ret = m->func (p1, p2, p3, p4);
+		break;
+	case MAIL_CALL_p_ppppp:
+		p1 = va_arg (ap, gpointer );
+		p2 = va_arg (ap, gpointer );
+		p3 = va_arg (ap, gpointer );
+		p4 = va_arg (ap, gpointer );
+		p5 = va_arg (ap, gpointer );
+		m->ret = m->func (p1, p2, p3, p4, p5);
+		break;
+	case MAIL_CALL_p_ppippp:
+		p1 = va_arg (ap, gpointer );
+		p2 = va_arg (ap, gpointer );
+		i1 = va_arg (ap, gint);
+		p3 = va_arg (ap, gpointer );
+		p4 = va_arg (ap, gpointer );
+		p5 = va_arg (ap, gpointer );
+		m->ret = m->func (p1, p2, i1, p3, p4, p5);
+		break;
+	}
+
+	if (g_cancellable_is_cancelled (cancellable)) {
+		if (cancel_activity)
+			cancel_activity (cancellable);
+	} else {
+		if (complete_activity)
+			complete_activity (cancellable);
+	}
+
+	if (m->done != NULL)
+		e_flag_set (m->done);
+}
+
+static MailMsgInfo mail_call_info = {
+	sizeof (struct _call_msg),
+	(MailMsgDescFunc) NULL,
+	(MailMsgExecFunc) do_call,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) NULL
+};
+
+gpointer
+mail_call_main (mail_call_t type,
+                MailMainFunc func,
+                ...)
+{
+	GCancellable *cancellable;
+	struct _call_msg *m;
+	gpointer ret;
+	va_list ap;
+
+	va_start (ap, func);
+
+	m = mail_msg_new (&mail_call_info);
+	m->type = type;
+	m->func = func;
+	G_VA_COPY (m->ap, ap);
+
+	cancellable = m->base.cancellable;
+
+	if (mail_in_main_thread ())
+		do_call (m, cancellable, &m->base.error);
+	else {
+		mail_msg_ref (m);
+		m->done = e_flag_new ();
+		mail_msg_main_loop_push (m);
+		e_flag_wait (m->done);
+		e_flag_free (m->done);
+	}
+
+	va_end (ap);
+
+	ret = m->ret;
+	mail_msg_unref (m);
+
+	return ret;
+}
+
+void
+mail_mt_set_backend (gchar *backend)
+{
+	shell_builtin_backend = backend;
+}
+
diff --git a/libemail-utils/mail-mt.h b/libemail-utils/mail-mt.h
new file mode 100644
index 0000000..edae135
--- /dev/null
+++ b/libemail-utils/mail-mt.h
@@ -0,0 +1,116 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _MAIL_MT
+#define _MAIL_MT
+
+#include <camel/camel.h>
+
+typedef struct _MailMsg MailMsg;
+typedef struct _MailMsgInfo MailMsgInfo;
+
+typedef gchar *	(*MailMsgDescFunc)		(MailMsg *msg);
+typedef void	(*MailMsgExecFunc)		(MailMsg *msg,
+						 GCancellable *cancellable,
+						 GError **error);
+typedef void	(*MailMsgDoneFunc)		(MailMsg *msg);
+typedef void	(*MailMsgFreeFunc)		(MailMsg *msg);
+typedef void	(*MailMsgDispatchFunc)		(gpointer msg);
+
+
+typedef void    (*MailMsgCreateActivityFunc)	(GCancellable *cancellable);
+typedef void    (*MailMsgSubmitActivityFunc)	(GCancellable *cancellable);
+typedef void    (*MailMsgFreeActivityFunc)	(GCancellable *cancellable);
+typedef void    (*MailMsgCompleteActivityFunc)	(GCancellable *cancellable);
+typedef void    (*MailMsgCancelActivityFunc)	(GCancellable *cancellable);
+typedef void    (*MailMsgAlertErrorFunc)	(const char *what, const char *message);
+
+struct _MailMsg {
+	MailMsgInfo *info;
+	volatile gint ref_count;
+	guint seq;			/* seq number for synchronisation */
+	gint priority;			/* priority (default = 0) */
+	GCancellable *cancellable;
+	GError *error;			/* up to the caller to use this */
+};
+
+struct _MailMsgInfo {
+	gsize size;
+	MailMsgDescFunc desc;
+	MailMsgExecFunc exec;
+	MailMsgDoneFunc done;
+	MailMsgFreeFunc free;
+};
+
+/* setup ports */
+void mail_msg_init (void);
+void mail_msg_register_activities (MailMsgCreateActivityFunc,
+				   MailMsgSubmitActivityFunc,
+				   MailMsgFreeActivityFunc,
+				   MailMsgCompleteActivityFunc,
+				   MailMsgCancelActivityFunc,				   
+				   MailMsgAlertErrorFunc);
+
+gboolean mail_in_main_thread (void);
+
+/* allocate a new message */
+gpointer mail_msg_new (MailMsgInfo *info);
+gpointer mail_msg_ref (gpointer msg);
+void mail_msg_unref (gpointer msg);
+void mail_msg_check_error (gpointer msg);
+void mail_msg_cancel (guint msgid);
+gboolean mail_msg_active (void);
+
+/* dispatch a message */
+void mail_msg_main_loop_push (gpointer msg);
+void mail_msg_unordered_push (gpointer msg);
+void mail_msg_fast_ordered_push (gpointer msg);
+void mail_msg_slow_ordered_push (gpointer msg);
+
+/* To implement the stop button */
+GHook * mail_cancel_hook_add (GHookFunc func, gpointer data);
+void mail_cancel_hook_remove (GHook *hook);
+void mail_cancel_all (void);
+
+/* request a string/password */
+gchar *mail_get_password (CamelService *service, const gchar *prompt,
+			 gboolean secret, gboolean *cache);
+
+void mail_mt_set_backend (gchar *backend);
+
+/* Call a function in the GUI thread, wait for it to return, type is
+ * the marshaller to use.  FIXME This thing is horrible, please put
+ * it out of its misery. */
+typedef enum {
+	MAIL_CALL_p_p,
+	MAIL_CALL_p_pp,
+	MAIL_CALL_p_ppp,
+	MAIL_CALL_p_pppp,
+	MAIL_CALL_p_ppppp,
+	MAIL_CALL_p_ppippp
+} mail_call_t;
+
+typedef gpointer (*MailMainFunc)();
+
+gpointer mail_call_main (mail_call_t type, MailMainFunc func, ...);
+
+#endif /* _MAIL_MT */
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 8deeeaa..3d4fc61 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -198,6 +198,8 @@ SMIME_LIBS =						\
 endif
 
 libevolution_mail_la_LIBADD =				\
+	$(top_builddir)/libemail-utils/libemail-utils.la		\
+	$(top_builddir)/libemail-engine/libemail-engine.la		\
 	$(top_builddir)/e-util/libeutil.la		\
 	$(top_builddir)/shell/libeshell.la		\
 	$(top_builddir)/composer/libcomposer.la		\
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
index c4c39bf..6805d17 100644
--- a/modules/mail/Makefile.am
+++ b/modules/mail/Makefile.am
@@ -53,6 +53,8 @@ libevolution_module_mail_la_SOURCES =					\
 	em-network-prefs.h
 
 libevolution_module_mail_la_LIBADD =					\
+	$(top_builddir)/libemail-utils/libemail-utils.la		\
+	$(top_builddir)/libemail-engine/libemail-engine.la		\
 	$(top_builddir)/e-util/libeutil.la				\
 	$(top_builddir)/em-format/libemformat.la			\
 	$(top_builddir)/filter/libfilter.la				\
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 9f6e658..afae3c5 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -60,7 +60,6 @@ libeshell_la_CPPFLAGS =						\
 	-DG_LOG_DOMAIN=\"evolution-shell\"			\
 	$(EVOLUTION_DATA_SERVER_CFLAGS)				\
 	$(GNOME_PLATFORM_CFLAGS)				\
-	$(EMAIL_UTILS_CFLAGS)					\
 	$(EGG_SMCLIENT_CFLAGS)					\
 	$(GTKHTML_CFLAGS)					\
 	$(CLUTTER_CFLAGS)
@@ -91,13 +90,13 @@ libeshell_la_SOURCES =				\
 libeshell_la_LDFLAGS = $(NO_UNDEFINED)
 
 libeshell_la_LIBADD =					\
+	$(top_builddir)/libemail-utils/libemail-utils.la		\
 	$(top_builddir)/e-util/libeutil.la		\
 	$(top_builddir)/filter/libfilter.la		\
 	$(top_builddir)/smclient/libeggsmclient.la	\
 	$(top_builddir)/widgets/misc/libemiscwidgets.la	\
 	$(top_builddir)/widgets/menus/libmenus.la	\
 	$(EVOLUTION_DATA_SERVER_LIBS)			\
-	$(EMAIL_UTILS_LIBS)				\
 	$(GNOME_PLATFORM_LIBS)				\
 	$(EGG_SMCLIENT_LIBS)				\
 	$(CLUTTER_LIBS)
@@ -145,11 +144,11 @@ evolution_LDADD =							\
 	$(top_builddir)/widgets/e-timezone-dialog/libetimezonedialog.la	\
 	$(top_builddir)/widgets/menus/libmenus.la			\
 	$(top_builddir)/widgets/misc/libemiscwidgets.la			\
+	$(top_builddir)/libemail-utils/libemail-utils.la		\
 	$(top_builddir)/e-util/libeutil.la				\
 	$(top_builddir)/filter/libfilter.la				\
 	$(EVOLUTION_DATA_SERVER_LIBS)					\
 	$(GNOME_PLATFORM_LIBS)						\
-	$(EMAIL_UTILS_LIBS)						\
 	$(CLUTTER_LIBS)							\
 	$(EVOLUTIONICON)
 



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