[evolution-data-server/email-factory-3-4: 6/13] Add more complete libemail-engine



commit 3c6c089ea2244c570a89cc81df3291011d0022c0
Author: Srinivasa Ragavan <sragavan gnome org>
Date:   Mon Oct 10 15:09:20 2011 +0530

    Add more complete libemail-engine

 mail/libemail-engine/Makefile.am            |   12 +-
 mail/libemail-engine/e-mail-enums.h         |   74 ++
 mail/libemail-engine/e-mail-folder-utils.c  | 1680 +++++++++++++++++++++++++++
 mail/libemail-engine/e-mail-folder-utils.h  |  164 +++
 mail/libemail-engine/e-mail-junk-filter.c   |   82 ++
 mail/libemail-engine/e-mail-junk-filter.h   |   74 ++
 mail/libemail-engine/e-mail-local.c         |  157 +++
 mail/libemail-engine/e-mail-local.h         |   39 +
 mail/libemail-engine/e-mail-session-utils.c |  942 +++++++++++++++
 mail/libemail-engine/e-mail-session-utils.h |   97 ++
 mail/libemail-engine/e-mail-session.c       | 1453 +++++++++++++++++++++++
 mail/libemail-engine/e-mail-session.h       |  124 ++
 mail/libemail-engine/e-mail-store-utils.c   |  393 +++++++
 mail/libemail-engine/e-mail-store-utils.h   |   74 ++
 mail/libemail-engine/e-mail-utils.c         | 1109 ++++++++++++++++++
 mail/libemail-engine/e-mail-utils.h         |   50 +
 mail/libemail-engine/mail-config.c          |  367 ++++++
 mail/libemail-engine/mail-config.h          |   49 +
 mail/libemail-engine/mail-folder-cache.c    | 1389 ++++++++++++++++++++++
 mail/libemail-engine/mail-folder-cache.h    |  112 ++
 mail/libemail-engine/mail-ops.c             | 1680 +++++++++++++++++++++++++++
 mail/libemail-engine/mail-ops.h             |  102 ++
 mail/libemail-engine/mail-tools.c           |  234 ++++
 mail/libemail-engine/mail-tools.h           |   41 +
 24 files changed, 10494 insertions(+), 4 deletions(-)
---
diff --git a/mail/libemail-engine/Makefile.am b/mail/libemail-engine/Makefile.am
index 7b20cd4..5e1c781 100644
--- a/mail/libemail-engine/Makefile.am
+++ b/mail/libemail-engine/Makefile.am
@@ -17,8 +17,10 @@ libemail_engine_la_LIBADD = 	\
 		$(GNOME_PLATFORM_LIBS)	\
 		$(top_builddir)/camel/libcamel-1.2.la			\
 		$(top_builddir)/camel/libcamel-provider-1.2.la			\
+		$(top_builddir)/libebackend/libebackend-1.2.la		\
 		$(top_builddir)/libedataserver/libedataserver-1.2.la		\
-		$(top_builddir)/mail/libemailutils/libemail-utils.la
+		$(top_builddir)/libedataserverui/libedataserverui-3.0.la		\
+		$(top_builddir)/mail/libemail-utils/libemail-utils.la
 		
 
 libemail_engine_la_LDFLAGS = 
@@ -26,13 +28,15 @@ 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		\
-		e-mail-junk-filter.c	\
+		mail-folder-cache.c	\
+		e-mail-session-utils.c	\
 		mail-ops.c		\
-		e-mail-session.c	\
-		mail-folder-cache.c	
+		e-mail-junk-filter.c	\
+		e-mail-session.c	
 
 libmailengineincludedir = $(includedir)/libemail-engine
 libmailengineinclude_HEADERS = 	\
diff --git a/mail/libemail-engine/e-mail-enums.h b/mail/libemail-engine/e-mail-enums.h
new file mode 100644
index 0000000..e0ad3ad
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-folder-utils.c b/mail/libemail-engine/e-mail-folder-utils.c
new file mode 100644
index 0000000..7f1e86e
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-folder-utils.h b/mail/libemail-engine/e-mail-folder-utils.h
new file mode 100644
index 0000000..9e8dd0f
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-junk-filter.c b/mail/libemail-engine/e-mail-junk-filter.c
new file mode 100644
index 0000000..d682490
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-junk-filter.h b/mail/libemail-engine/e-mail-junk-filter.h
new file mode 100644
index 0000000..74a7840
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-local.c b/mail/libemail-engine/e-mail-local.c
new file mode 100644
index 0000000..1f33c08
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-local.h b/mail/libemail-engine/e-mail-local.h
new file mode 100644
index 0000000..77f116d
--- /dev/null
+++ b/mail/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 <e-mail-enums.h>
+#include <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/mail/libemail-engine/e-mail-session-utils.c b/mail/libemail-engine/e-mail-session-utils.c
new file mode 100644
index 0000000..9a55891
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-session-utils.h b/mail/libemail-engine/e-mail-session-utils.h
new file mode 100644
index 0000000..cd9d6ce
--- /dev/null
+++ b/mail/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 "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/mail/libemail-engine/e-mail-session.c b/mail/libemail-engine/e-mail-session.c
new file mode 100644
index 0000000..cc49954
--- /dev/null
+++ b/mail/libemail-engine/e-mail-session.c
@@ -0,0 +1,1453 @@
+/*
+ * 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
+};
+
+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;
+	//mail_send ();
+
+	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));
+}
+
+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/mail/libemail-engine/e-mail-session.h b/mail/libemail-engine/e-mail-session.h
new file mode 100644
index 0000000..6dac2d5
--- /dev/null
+++ b/mail/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 <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/mail/libemail-engine/e-mail-store-utils.c b/mail/libemail-engine/e-mail-store-utils.c
new file mode 100644
index 0000000..0d31525
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-store-utils.h b/mail/libemail-engine/e-mail-store-utils.h
new file mode 100644
index 0000000..de4484c
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-utils.c b/mail/libemail-engine/e-mail-utils.c
new file mode 100644
index 0000000..27f492a
--- /dev/null
+++ b/mail/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/mail/libemail-engine/e-mail-utils.h b/mail/libemail-engine/e-mail-utils.h
new file mode 100644
index 0000000..62fdcf0
--- /dev/null
+++ b/mail/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/mail/libemail-engine/mail-config.c b/mail/libemail-engine/mail-config.c
new file mode 100644
index 0000000..47c147d
--- /dev/null
+++ b/mail/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/mail/libemail-engine/mail-config.h b/mail/libemail-engine/mail-config.h
new file mode 100644
index 0000000..7bde31d
--- /dev/null
+++ b/mail/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 <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/mail/libemail-engine/mail-folder-cache.c b/mail/libemail-engine/mail-folder-cache.c
new file mode 100644
index 0000000..72dbc29
--- /dev/null
+++ b/mail/libemail-engine/mail-folder-cache.c
@@ -0,0 +1,1389 @@
+/*
+ * 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"
+
+#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) */
+};
+
+/* 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
+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;
+
+	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);
+			}
+		}
+	}
+
+	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);
+	}
+	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_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);
+
+	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/mail/libemail-engine/mail-folder-cache.h b/mail/libemail-engine/mail-folder-cache.h
new file mode 100644
index 0000000..681c6ef
--- /dev/null
+++ b/mail/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/mail/libemail-engine/mail-ops.c b/mail/libemail-engine/mail-ops.c
new file mode 100644
index 0000000..a12a63a
--- /dev/null
+++ b/mail/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/mail/libemail-engine/mail-ops.h b/mail/libemail-engine/mail-ops.h
new file mode 100644
index 0000000..5ac7ffe
--- /dev/null
+++ b/mail/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 <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/mail/libemail-engine/mail-tools.c b/mail/libemail-engine/mail-tools.c
new file mode 100644
index 0000000..135df53
--- /dev/null
+++ b/mail/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/mail/libemail-engine/mail-tools.h b/mail/libemail-engine/mail-tools.h
new file mode 100644
index 0000000..94b19c0
--- /dev/null
+++ b/mail/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



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