[evolution/email-factory-3-4: 3/47] Split mail to independent library to be used by a mail daemon.
- From: Srinivasa Ragavan <sragavan src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/email-factory-3-4: 3/47] Split mail to independent library to be used by a mail daemon.
- Date: Tue, 3 Jan 2012 09:35:39 +0000 (UTC)
commit aa8e0ce687bd81f4501dbdab79469e30337629d3
Author: Srinivasa Ragavan <sragavan gnome org>
Date: Mon Oct 17 23:37:27 2011 +0530
Split mail to independent library to be used by a mail daemon.
Makefile.am | 2 +
composer/e-composer-actions.c | 5 +
configure.ac | 8 +-
e-util/Makefile.am | 1 +
libemail-engine/Makefile.am | 65 +
libemail-engine/e-mail-enums.h | 74 +
libemail-engine/e-mail-folder-utils.c | 1680 +++++++++++++++++++++
libemail-engine/e-mail-folder-utils.h | 164 +++
libemail-engine/e-mail-junk-filter.c | 82 ++
libemail-engine/e-mail-junk-filter.h | 74 +
libemail-engine/e-mail-local.c | 157 ++
libemail-engine/e-mail-local.h | 39 +
libemail-engine/e-mail-session-utils.c | 942 ++++++++++++
libemail-engine/e-mail-session-utils.h | 97 ++
libemail-engine/e-mail-session.c | 1517 +++++++++++++++++++
libemail-engine/e-mail-session.h | 124 ++
libemail-engine/e-mail-store-utils.c | 393 +++++
libemail-engine/e-mail-store-utils.h | 74 +
libemail-engine/e-mail-store.c | 464 ++++++
libemail-engine/e-mail-store.h | 47 +
libemail-engine/e-mail-utils.c | 1109 ++++++++++++++
libemail-engine/e-mail-utils.h | 50 +
libemail-engine/libemail-engine.pc.in | 15 +
libemail-engine/mail-config.c | 367 +++++
libemail-engine/mail-config.h | 49 +
libemail-engine/mail-folder-cache.c | 1424 ++++++++++++++++++
libemail-engine/mail-folder-cache.h | 112 ++
libemail-engine/mail-ops.c | 1680 +++++++++++++++++++++
libemail-engine/mail-ops.h | 102 ++
libemail-engine/mail-tools.c | 234 +++
libemail-engine/mail-tools.h | 41 +
libemail-utils/Makefile.am | 57 +
libemail-utils/e-account-utils.c | 252 ++++
libemail-utils/e-account-utils.h | 37 +
libemail-utils/e-marshal.c | 2492 ++++++++++++++++++++++++++++++++
libemail-utils/e-marshal.h | 530 +++++++
libemail-utils/e-marshal.list | 60 +
libemail-utils/e-signature-list.c | 494 +++++++
libemail-utils/e-signature-list.h | 92 ++
libemail-utils/e-signature-utils.c | 342 +++++
libemail-utils/e-signature-utils.h | 40 +
libemail-utils/e-signature.c | 747 ++++++++++
libemail-utils/e-signature.h | 90 ++
libemail-utils/gconf-bridge.c | 1364 +++++++++++++++++
libemail-utils/gconf-bridge.h | 134 ++
libemail-utils/libemail-utils.pc.in | 15 +
libemail-utils/mail-mt.c | 639 ++++++++
libemail-utils/mail-mt.h | 116 ++
mail/Makefile.am | 2 +
modules/mail/Makefile.am | 2 +
shell/Makefile.am | 5 +-
51 files changed, 18697 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a5e0d9f..d1f0d19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,8 @@ SUBDIRS = \
data \
smclient \
libgnomecanvas \
+ libemail-utils \
+ libemail-engine \
e-util \
a11y \
filter \
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index cd50f7a..f8ed9b5 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -196,6 +196,11 @@ action_save_cb (GtkAction *action,
gtkhtml_editor_run_command (GTKHTML_EDITOR (composer), "saved");
}
+int gtkhtml_editor_file_chooser_dialog_run (GtkhtmlEditor *e, GtkWidget *w)
+{
+ return 0;
+}
+
static void
action_save_as_cb (GtkAction *action,
EMsgComposer *composer)
diff --git a/configure.ac b/configure.ac
index ee9e002..ee46cd2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1225,14 +1225,14 @@ AC_SUBST(CERT_UI_LIBS)
dnl ************
dnl EMAILS_UTILS Flags
dnl ************
-EVO_SET_COMPILE_FLAGS(EMAIL_UTILS, libemail-utils)
+EVO_SET_COMPILE_FLAGS(EMAIL_UTILS, camel-1.2 libedataserver-1.2)
AC_SUBST(EMAIL_UTILS_CFLAGS)
AC_SUBST(EMAIL_UTILS_LIBS)
dnl ************
dnl EMAIL_ENGINE Flags
dnl ************
-EVO_SET_COMPILE_FLAGS(EMAIL_ENGINE, libemail-engine)
+EVO_SET_COMPILE_FLAGS(EMAIL_ENGINE, camel-1.2 libedataserverui-3.0 libebackend-1.2 libedataserver-1.2)
AC_SUBST(EMAIL_ENGINE_CFLAGS)
AC_SUBST(EMAIL_ENGINE_LIBS)
@@ -1632,6 +1632,10 @@ help/quickref/pl/Makefile
help/quickref/pt/Makefile
help/quickref/sv/Makefile
help/quickref/sq/Makefile
+libemail-utils/Makefile
+libemail-utils/libemail-utils.pc
+libemail-engine/Makefile
+libemail-engine/libemail-engine.pc
libgnomecanvas/Makefile
shell/Makefile
shell/evolution-nognome
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 6510cf9..b8cb56f 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -122,6 +122,7 @@ libeutil_la_SOURCES = \
libeutil_la_LDFLAGS = $(NO_UNDEFINED)
libeutil_la_LIBADD = \
+ $(top_builddir)/libemail-utils/libemail-utils.la \
$(ICONV_LIBS) \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(EMAIL_UTILS_LIBS) \
diff --git a/libemail-engine/Makefile.am b/libemail-engine/Makefile.am
new file mode 100644
index 0000000..fd62daf
--- /dev/null
+++ b/libemail-engine/Makefile.am
@@ -0,0 +1,65 @@
+
+lib_LTLIBRARIES = libemail-engine.la
+
+AM_CFLAGS = -I$(top_builddir) @GNOME_PLATFORM_CFLAGS@ -Wall -Werror -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\""
+AM_LDFLAGS = @GNOME_PLATFORM_LIBS@
+
+
+libemail_engine_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(EMAIL_ENGINE_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS)
+
+libemail_engine_la_LIBADD = \
+ $(CAMEL_LIBS) \
+ $(EMAIL_ENGINE_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(top_builddir)/libemail-utils/libemail-utils.la
+
+
+libemail_engine_la_LDFLAGS =
+
+libemail_engine_la_SOURCES = \
+ e-mail-local.c \
+ e-mail-folder-utils.c \
+ e-mail-store-utils.c \
+ mail-tools.c \
+ e-mail-utils.c \
+ mail-config.c \
+ mail-folder-cache.c \
+ e-mail-session-utils.c \
+ mail-ops.c \
+ e-mail-junk-filter.c \
+ e-mail-session.c \
+ e-mail-store.c
+
+libmailengineincludedir = $(privincludedir)/libemail-engine
+libmailengineinclude_HEADERS = \
+ e-mail-session.h \
+ e-mail-enums.h \
+ mail-folder-cache.h \
+ e-mail-local.h \
+ e-mail-folder-utils.h \
+ e-mail-store-utils.h \
+ mail-tools.h \
+ e-mail-utils.h \
+ mail-config.h \
+ e-mail-session-utils.h \
+ mail-ops.h \
+ e-mail-junk-filter.h \
+ e-mail-store.h
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libemail-engine.pc
+
+BUILT_SOURCES =
+
+EXTRA_DIST =
+CLEANFILES = $(BUILT_SOURCES)
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+dist-hook:
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
diff --git a/libemail-engine/e-mail-enums.h b/libemail-engine/e-mail-enums.h
new file mode 100644
index 0000000..e0ad3ad
--- /dev/null
+++ b/libemail-engine/e-mail-enums.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-enums.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_ENUMS_H
+#define E_MAIL_ENUMS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ E_MAIL_DISPLAY_STYLE_NORMAL,
+ E_MAIL_DISPLAY_STYLE_FULL_HEADERS,
+ E_MAIL_DISPLAY_STYLE_SOURCE
+} EMailDisplayStyle;
+
+typedef enum {
+ E_MAIL_FORWARD_STYLE_ATTACHED,
+ E_MAIL_FORWARD_STYLE_INLINE,
+ E_MAIL_FORWARD_STYLE_QUOTED
+} EMailForwardStyle;
+
+typedef enum {
+ E_MAIL_IMAGE_LOADING_POLICY_NEVER,
+ E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES,
+ E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
+} EMailImageLoadingPolicy;
+
+/* XXX E_MAIL_FOLDER_TEMPLATES is a prime example of why templates
+ * should be a core feature: the mailer now has to know about
+ * this specific plugin, which defeats the purpose of plugins. */
+typedef enum {
+ E_MAIL_LOCAL_FOLDER_INBOX,
+ E_MAIL_LOCAL_FOLDER_DRAFTS,
+ E_MAIL_LOCAL_FOLDER_OUTBOX,
+ E_MAIL_LOCAL_FOLDER_SENT,
+ E_MAIL_LOCAL_FOLDER_TEMPLATES,
+ E_MAIL_LOCAL_FOLDER_LOCAL_INBOX,
+ E_MAIL_NUM_LOCAL_FOLDERS
+} EMailLocalFolder;
+
+typedef enum {
+ E_MAIL_REPLY_STYLE_QUOTED,
+ E_MAIL_REPLY_STYLE_DO_NOT_QUOTE,
+ E_MAIL_REPLY_STYLE_ATTACH,
+ E_MAIL_REPLY_STYLE_OUTLOOK
+} EMailReplyStyle;
+
+typedef enum {
+ E_MAIL_REPLY_TO_SENDER,
+ E_MAIL_REPLY_TO_RECIPIENT,
+ E_MAIL_REPLY_TO_FROM,
+ E_MAIL_REPLY_TO_ALL,
+ E_MAIL_REPLY_TO_LIST
+} EMailReplyType;
+
+G_END_DECLS
+
+#endif /* E_MAIL_ENUMS_H */
diff --git a/libemail-engine/e-mail-folder-utils.c b/libemail-engine/e-mail-folder-utils.c
new file mode 100644
index 0000000..7f1e86e
--- /dev/null
+++ b/libemail-engine/e-mail-folder-utils.c
@@ -0,0 +1,1680 @@
+/*
+ * e-mail-folder-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-folder-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "mail-tools.h"
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution Mail Data Server" PACKAGE_VERSION)
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+ CamelMimePart *part;
+ GHashTable *hash_table;
+ GPtrArray *ptr_array;
+ GFile *destination;
+ gchar *fwd_subject;
+ gchar *message_uid;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->info != NULL)
+ camel_message_info_free (context->info);
+
+ if (context->part != NULL)
+ g_object_unref (context->part);
+
+ if (context->hash_table != NULL)
+ g_hash_table_unref (context->hash_table);
+
+ if (context->ptr_array != NULL)
+ g_ptr_array_unref (context->ptr_array);
+
+ if (context->destination != NULL)
+ g_object_unref (context->destination);
+
+ g_free (context->fwd_subject);
+ g_free (context->message_uid);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_folder_append_message_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_append_message_sync (
+ CAMEL_FOLDER (object), context->message,
+ context->info, &context->message_uid,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_folder_append_message_sync (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gchar **appended_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelMedium *medium;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ camel_operation_push_message (
+ cancellable,
+ _("Saving message to folder '%s'"),
+ camel_folder_get_full_name (folder));
+
+ if (camel_medium_get_header (medium, "X-Mailer") == NULL)
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+
+ success = camel_folder_append_message_sync (
+ folder, message, info, appended_uid, cancellable, error);
+
+ camel_operation_pop_message (cancellable);
+
+ return success;
+}
+
+void
+e_mail_folder_append_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ if (info != NULL)
+ context->info = camel_message_info_ref (info);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_append_message);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_append_message_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_append_message_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_append_message), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (appended_uid != NULL) {
+ *appended_uid = context->message_uid;
+ context->message_uid = NULL;
+ }
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_build_attachment_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->part = e_mail_folder_build_attachment_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ &context->fwd_subject, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gchar **fwd_subject,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *hash_table;
+ CamelMimeMessage *message;
+ CamelMimePart *part;
+ const gchar *uid;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ /* Need at least one message UID to make an attachment. */
+ g_return_val_if_fail (message_uids->len > 0, NULL);
+
+ hash_table = e_mail_folder_get_multiple_messages_sync (
+ folder, message_uids, cancellable, error);
+
+ if (hash_table == NULL)
+ return NULL;
+
+ /* Create the forward subject from the first message. */
+
+ uid = g_ptr_array_index (message_uids, 0);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ message = g_hash_table_lookup (hash_table, uid);
+ g_return_val_if_fail (message != NULL, NULL);
+
+ if (fwd_subject != NULL)
+ *fwd_subject = mail_tool_generate_forward_subject (message);
+
+ if (message_uids->len == 1) {
+ part = mail_tool_make_message_attachment (message);
+
+ } else {
+ CamelMultipart *multipart;
+ guint ii;
+
+ multipart = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (
+ CAMEL_DATA_WRAPPER (multipart), "multipart/digest");
+ camel_multipart_set_boundary (multipart, NULL);
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ uid = g_ptr_array_index (message_uids, ii);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ message = g_hash_table_lookup (hash_table, uid);
+ g_return_val_if_fail (message != NULL, NULL);
+
+ part = mail_tool_make_message_attachment (message);
+ camel_multipart_add_part (multipart, part);
+ g_object_unref (part);
+ }
+
+ part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ CAMEL_DATA_WRAPPER (multipart));
+
+ camel_mime_part_set_description (
+ part, _("Forwarded messages"));
+
+ g_object_unref (multipart);
+ }
+
+ g_hash_table_unref (hash_table);
+
+ return part;
+}
+
+void
+e_mail_folder_build_attachment (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ /* Need at least one message UID to make an attachment. */
+ g_return_if_fail (message_uids->len > 0);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_build_attachment);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_build_attachment_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **fwd_subject,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_build_attachment), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ if (fwd_subject != NULL) {
+ *fwd_subject = context->fwd_subject;
+ context->fwd_subject = NULL;
+ }
+
+ g_return_val_if_fail (CAMEL_IS_MIME_PART (context->part), NULL);
+
+ return g_object_ref (context->part);
+}
+
+static void
+mail_folder_find_duplicate_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->hash_table = e_mail_folder_find_duplicate_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GQueue trash = G_QUEUE_INIT;
+ GHashTable *hash_table;
+ GHashTable *unique_ids;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ /* hash_table = { MessageUID : CamelMessage } */
+ hash_table = e_mail_folder_get_multiple_messages_sync (
+ folder, message_uids, cancellable, error);
+
+ if (hash_table == NULL)
+ return NULL;
+
+ camel_operation_push_message (
+ cancellable, _("Scanning messages for duplicates"));
+
+ unique_ids = g_hash_table_new_full (
+ (GHashFunc) g_int64_hash,
+ (GEqualFunc) g_int64_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ g_hash_table_iter_init (&iter, hash_table);
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const CamelSummaryMessageID *message_id;
+ CamelDataWrapper *content;
+ CamelMessageFlags flags;
+ CamelMessageInfo *info;
+ CamelStream *stream;
+ GByteArray *buffer;
+ gboolean duplicate;
+ gssize n_bytes;
+ gchar *digest;
+
+ info = camel_folder_get_message_info (folder, key);
+ message_id = camel_message_info_message_id (info);
+ flags = camel_message_info_flags (info);
+
+ /* Skip messages marked for deletion. */
+ if (flags & CAMEL_MESSAGE_DELETED) {
+ g_queue_push_tail (&trash, key);
+ camel_message_info_free (info);
+ continue;
+ }
+
+ /* Generate a digest string from the message's content. */
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (value));
+
+ if (content == NULL) {
+ g_queue_push_tail (&trash, key);
+ camel_message_info_free (info);
+ continue;
+ }
+
+ stream = camel_stream_mem_new ();
+
+ n_bytes = camel_data_wrapper_decode_to_stream_sync (
+ content, stream, cancellable, error);
+
+ if (n_bytes < 0) {
+ camel_message_info_free (info);
+ g_object_unref (stream);
+ goto fail;
+ }
+
+ /* The CamelStreamMem owns the buffer. */
+ buffer = camel_stream_mem_get_byte_array (
+ CAMEL_STREAM_MEM (stream));
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ digest = g_compute_checksum_for_data (
+ G_CHECKSUM_SHA256, buffer->data, buffer->len);
+
+ g_object_unref (stream);
+
+ /* Determine if the message a duplicate. */
+
+ value = g_hash_table_lookup (unique_ids, &message_id->id.id);
+ duplicate = (value != NULL) && g_str_equal (digest, value);
+
+ if (duplicate)
+ g_free (digest);
+ else {
+ gint64 *v_int64;
+
+ /* XXX Might be better to create a GArray
+ * of 64-bit integers and have the hash
+ * table keys point to array elements. */
+ v_int64 = g_new0 (gint64, 1);
+ *v_int64 = (gint64) message_id->id.id;
+
+ g_hash_table_insert (unique_ids, v_int64, digest);
+ g_queue_push_tail (&trash, key);
+ }
+
+ camel_message_info_free (info);
+ }
+
+ /* Delete all non-duplicate messages from the hash table. */
+ while ((key = g_queue_pop_head (&trash)) != NULL)
+ g_hash_table_remove (hash_table, key);
+
+ goto exit;
+
+fail:
+ g_hash_table_destroy (hash_table);
+ hash_table = NULL;
+
+exit:
+ camel_operation_pop_message (cancellable);
+
+ g_hash_table_destroy (unique_ids);
+
+ return hash_table;
+}
+
+void
+e_mail_folder_find_duplicate_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_find_duplicate_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_find_duplicate_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_find_duplicate_messages), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_get_multiple_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->hash_table = e_mail_folder_get_multiple_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *hash_table;
+ CamelMimeMessage *message;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ camel_operation_push_message (
+ cancellable,
+ ngettext (
+ "Retrieving %d message",
+ "Retrieving %d messages",
+ message_uids->len),
+ message_uids->len);
+
+ hash_table = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ /* This is an all or nothing operation. Destroy the
+ * hash table if we fail to retrieve any message. */
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ const gchar *uid;
+ gint percent;
+
+ uid = g_ptr_array_index (message_uids, ii);
+ percent = ((ii + 1) * 100) / message_uids->len;
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+
+ camel_operation_progress (cancellable, percent);
+
+ if (CAMEL_IS_MIME_MESSAGE (message)) {
+ g_hash_table_insert (
+ hash_table, g_strdup (uid), message);
+ } else {
+ g_hash_table_destroy (hash_table);
+ hash_table = NULL;
+ break;
+ }
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ return hash_table;
+}
+
+void
+e_mail_folder_get_multiple_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_get_multiple_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_get_multiple_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_get_multiple_messages), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_remove_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+
+ e_mail_folder_remove_sync (
+ CAMEL_FOLDER (object), cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+mail_folder_remove_recursive (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ while (folder_info != NULL) {
+ CamelFolder *folder;
+
+ if (folder_info->child != NULL) {
+ success = mail_folder_remove_recursive (
+ store, folder_info->child, cancellable, error);
+ if (!success)
+ break;
+ }
+
+ folder = camel_store_get_folder_sync (
+ store, folder_info->full_name, 0, cancellable, error);
+ if (folder == NULL) {
+ success = FALSE;
+ break;
+ }
+
+ if (!CAMEL_IS_VEE_FOLDER (folder)) {
+ GPtrArray *uids;
+ guint ii;
+
+ /* Delete every message in this folder,
+ * then expunge it. */
+
+ camel_folder_freeze (folder);
+
+ uids = camel_folder_get_uids (folder);
+
+ for (ii = 0; ii < uids->len; ii++)
+ camel_folder_delete_message (
+ folder, uids->pdata[ii]);
+
+ camel_folder_free_uids (folder, uids);
+
+ success = camel_folder_synchronize_sync (
+ folder, TRUE, cancellable, error);
+
+ camel_folder_thaw (folder);
+ }
+
+ g_object_unref (folder);
+
+ if (!success)
+ break;
+
+ /* If the store supports subscriptions,
+ * then unsubscribe from this folder. */
+ if (CAMEL_IS_SUBSCRIBABLE (store)) {
+ success = camel_subscribable_unsubscribe_folder_sync (
+ CAMEL_SUBSCRIBABLE (store),
+ folder_info->full_name,
+ cancellable, error);
+ if (!success)
+ break;
+ }
+
+ success = camel_store_delete_folder_sync (
+ store, folder_info->full_name, cancellable, error);
+ if (!success)
+ break;
+
+ folder_info = folder_info->next;
+ }
+
+ return success;
+}
+
+static void
+follow_cancel_cb (GCancellable *cancellable,
+ GCancellable *transparent_cancellable)
+{
+ g_cancellable_cancel (transparent_cancellable);
+}
+
+gboolean
+e_mail_folder_remove_sync (CamelFolder *folder,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolderInfo *folder_info;
+ CamelFolderInfo *to_remove;
+ CamelFolderInfo *next = NULL;
+ CamelStore *parent_store;
+ const gchar *full_name;
+ gboolean success = TRUE;
+ GCancellable *transparent_cancellable = NULL;
+ gulong cbid = 0;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ full_name = camel_folder_get_full_name (folder);
+ parent_store = camel_folder_get_parent_store (folder);
+
+ folder_info = camel_store_get_folder_info_sync (
+ parent_store, full_name,
+ CAMEL_STORE_FOLDER_INFO_FAST |
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+ cancellable, error);
+
+ if (folder_info == NULL)
+ return FALSE;
+
+ to_remove = folder_info;
+
+ /* For cases when the top-level folder_info contains siblings,
+ * such as when full_name contains a wildcard letter, compare
+ * the folder name against folder_info->full_name to avoid
+ * removing more folders than requested. */
+ if (folder_info->next != NULL) {
+ while (to_remove != NULL) {
+ if (g_strcmp0 (to_remove->full_name, full_name) == 0)
+ break;
+ to_remove = to_remove->next;
+ }
+
+ /* XXX Should we set a GError and return FALSE here? */
+ if (to_remove == NULL) {
+ g_warning (
+ "%s: Failed to find folder '%s'",
+ G_STRFUNC, full_name);
+ camel_store_free_folder_info (
+ parent_store, folder_info);
+ return TRUE;
+ }
+
+ /* Prevent iterating over siblings. */
+ next = to_remove->next;
+ to_remove->next = NULL;
+ }
+
+ camel_operation_push_message (
+ cancellable, _("Removing folder '%s'"),
+ camel_folder_get_full_name (folder));
+
+ if (cancellable) {
+ transparent_cancellable = g_cancellable_new ();
+ cbid = g_cancellable_connect (cancellable, G_CALLBACK (follow_cancel_cb), transparent_cancellable, NULL);
+ }
+
+ success = mail_folder_remove_recursive (
+ parent_store, to_remove, transparent_cancellable, error);
+
+ if (transparent_cancellable) {
+ g_cancellable_disconnect (cancellable, cbid);
+ g_object_unref (transparent_cancellable);
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ /* Restore the folder_info tree to its original
+ * state so we don't leak folder_info nodes. */
+ to_remove->next = next;
+
+ camel_store_free_folder_info (parent_store, folder_info);
+
+ return success;
+}
+
+void
+e_mail_folder_remove (CamelFolder *folder,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback,
+ user_data, e_mail_folder_remove);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_remove_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_remove), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_remove_attachments_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_remove_attachments_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+/* Helper for e_mail_folder_remove_attachments_sync() */
+static gboolean
+mail_folder_strip_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ const gchar *message_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelDataWrapper *content;
+ CamelMultipart *multipart;
+ gboolean modified = FALSE;
+ gboolean success = TRUE;
+ guint ii, n_parts;
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (message));
+
+ if (!CAMEL_IS_MULTIPART (content))
+ return TRUE;
+
+ multipart = CAMEL_MULTIPART (content);
+ n_parts = camel_multipart_get_number (multipart);
+
+ /* Replace MIME parts with "attachment" or "inline" dispositions
+ * with a small "text/plain" part saying the file was removed. */
+ for (ii = 0; ii < n_parts; ii++) {
+ CamelMimePart *mime_part;
+ const gchar *disposition;
+ gboolean is_attachment;
+
+ mime_part = camel_multipart_get_part (multipart, ii);
+ disposition = camel_mime_part_get_disposition (mime_part);
+
+ is_attachment =
+ (g_strcmp0 (disposition, "attachment") == 0) ||
+ (g_strcmp0 (disposition, "inline") == 0);
+
+ if (is_attachment) {
+ const gchar *filename;
+ const gchar *content_type;
+ gchar *content;
+
+ disposition = "inline";
+ content_type = "text/plain";
+ filename = camel_mime_part_get_filename (mime_part);
+
+ if (filename != NULL && *filename != '\0')
+ content = g_strdup_printf (
+ _("File \"%s\" has been removed."),
+ filename);
+ else
+ content = g_strdup (
+ _("File has been removed."));
+
+ camel_mime_part_set_content (
+ mime_part, content,
+ strlen (content), content_type);
+ camel_mime_part_set_content_type (
+ mime_part, content_type);
+ camel_mime_part_set_disposition (
+ mime_part, disposition);
+
+ modified = TRUE;
+ }
+ }
+
+ /* Append the modified message with removed attachments to
+ * the folder and mark the original message for deletion. */
+ if (modified) {
+ CamelMessageInfo *orig_info;
+ CamelMessageInfo *copy_info;
+ CamelMessageFlags flags;
+
+ orig_info =
+ camel_folder_get_message_info (folder, message_uid);
+ copy_info =
+ camel_message_info_new_from_header (
+ NULL, CAMEL_MIME_PART (message)->headers);
+
+ flags = camel_folder_get_message_flags (folder, message_uid);
+ camel_message_info_set_flags (copy_info, flags, flags);
+
+ success = camel_folder_append_message_sync (
+ folder, message, copy_info, NULL, cancellable, error);
+ if (success)
+ camel_message_info_set_flags (
+ orig_info,
+ CAMEL_MESSAGE_DELETED,
+ CAMEL_MESSAGE_DELETED);
+
+ camel_folder_free_message_info (folder, orig_info);
+ camel_message_info_free (copy_info);
+ }
+
+ return success;
+}
+
+gboolean
+e_mail_folder_remove_attachments_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = TRUE;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (message_uids != NULL, FALSE);
+
+ camel_folder_freeze (folder);
+
+ camel_operation_push_message (cancellable, _("Removing attachments"));
+
+ for (ii = 0; success && ii < message_uids->len; ii++) {
+ CamelMimeMessage *message;
+ const gchar *uid;
+ gint percent;
+
+ uid = g_ptr_array_index (message_uids, ii);
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+
+ if (message == NULL) {
+ success = FALSE;
+ break;
+ }
+
+ success = mail_folder_strip_message (
+ folder, message, uid, cancellable, error);
+
+ percent = ((ii + 1) * 100) / message_uids->len;
+ camel_operation_progress (cancellable, percent);
+
+ g_object_unref (message);
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ if (success)
+ camel_folder_synchronize_sync (
+ folder, FALSE, cancellable, error);
+
+ camel_folder_thaw (folder);
+
+ return success;
+}
+
+void
+e_mail_folder_remove_attachments (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_remove_attachments);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_remove_attachments_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_attachments_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_remove_attachments), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_save_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_save_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ context->destination, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+/* Helper for e_mail_folder_save_messages_sync() */
+static void
+mail_folder_save_prepare_part (CamelMimePart *mime_part)
+{
+ CamelDataWrapper *content;
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+
+ if (content == NULL)
+ return;
+
+ if (CAMEL_IS_MULTIPART (content)) {
+ guint n_parts, ii;
+
+ n_parts = camel_multipart_get_number (
+ CAMEL_MULTIPART (content));
+ for (ii = 0; ii < n_parts; ii++) {
+ mime_part = camel_multipart_get_part (
+ CAMEL_MULTIPART (content), ii);
+ mail_folder_save_prepare_part (mime_part);
+ }
+
+ } else if (CAMEL_IS_MIME_MESSAGE (content)) {
+ mail_folder_save_prepare_part (CAMEL_MIME_PART (content));
+
+ } else {
+ CamelContentType *type;
+
+ /* Save textual parts as 8-bit, not encoded. */
+ type = camel_data_wrapper_get_mime_type_field (content);
+ if (camel_content_type_is (type, "text", "*"))
+ camel_mime_part_set_encoding (
+ mime_part, CAMEL_TRANSFER_ENCODING_8BIT);
+ }
+}
+
+gboolean
+e_mail_folder_save_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileOutputStream *file_output_stream;
+ GByteArray *byte_array;
+ CamelStream *base_stream;
+ gboolean success = TRUE;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (message_uids != NULL, FALSE);
+ g_return_val_if_fail (G_IS_FILE (destination), FALSE);
+
+ /* Need at least one message UID to save. */
+ g_return_val_if_fail (message_uids->len > 0, FALSE);
+
+ camel_operation_push_message (
+ cancellable, ngettext (
+ "Saving %d message",
+ "Saving %d messages",
+ message_uids->len),
+ message_uids->len);
+
+ file_output_stream = g_file_replace (
+ destination, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE |
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ cancellable, error);
+
+ if (file_output_stream == NULL) {
+ camel_operation_pop_message (cancellable);
+ return FALSE;
+ }
+
+ /* CamelStreamMem takes ownership of the GByteArray. */
+ byte_array = g_byte_array_new ();
+ base_stream = camel_stream_mem_new_with_byte_array (byte_array);
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ CamelMimeMessage *message;
+ CamelMimeFilter *filter;
+ CamelStream *stream;
+ const gchar *uid;
+ gchar *from_line;
+ gint percent;
+ gint retval;
+
+ uid = g_ptr_array_index (message_uids, ii);
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+ if (message == NULL) {
+ success = FALSE;
+ goto exit;
+ }
+
+ mail_folder_save_prepare_part (CAMEL_MIME_PART (message));
+
+ from_line = camel_mime_message_build_mbox_from (message);
+ g_return_val_if_fail (from_line != NULL, FALSE);
+
+ success = g_output_stream_write_all (
+ G_OUTPUT_STREAM (file_output_stream),
+ from_line, strlen (from_line), NULL,
+ cancellable, error);
+
+ g_free (from_line);
+
+ if (!success) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ filter = camel_mime_filter_from_new ();
+ stream = camel_stream_filter_new (base_stream);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+
+ retval = camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message),
+ stream, cancellable, error);
+
+ g_object_unref (filter);
+ g_object_unref (stream);
+
+ if (retval == -1) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ g_byte_array_append (byte_array, (guint8 *) "\n", 1);
+
+ success = g_output_stream_write_all (
+ G_OUTPUT_STREAM (file_output_stream),
+ byte_array->data, byte_array->len,
+ NULL, cancellable, error);
+
+ if (!success) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ percent = ((ii + 1) * 100) / message_uids->len;
+ camel_operation_progress (cancellable, percent);
+
+ /* Flush the buffer for the next message.
+ * For memory streams this never fails. */
+ g_seekable_seek (
+ G_SEEKABLE (base_stream),
+ 0, G_SEEK_SET, NULL, NULL);
+
+ g_object_unref (message);
+ }
+
+exit:
+ g_object_unref (file_output_stream);
+ g_object_unref (base_stream);
+
+ camel_operation_pop_message (cancellable);
+
+ if (!success) {
+ /* Try deleting the destination file. */
+ g_file_delete (destination, NULL, NULL);
+ }
+
+ return success;
+}
+
+void
+e_mail_folder_save_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+ g_return_if_fail (G_IS_FILE (destination));
+
+ /* Need at least one message UID to save. */
+ g_return_if_fail (message_uids->len > 0);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+ context->destination = g_object_ref (destination);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_save_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_save_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_save_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_save_messages), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_mail_folder_uri_build:
+ * @store: a #CamelStore
+ * @folder_name: a folder name
+ *
+ * Builds a folder URI string from @store and @folder_name.
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_build (CamelStore *store,
+ const gchar *folder_name)
+{
+ const gchar *uid;
+ gchar *encoded_name;
+ gchar *encoded_uid;
+ gchar *uri;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (folder_name != NULL, NULL);
+
+ /* Skip the leading slash, if present. */
+ if (*folder_name == '/')
+ folder_name++;
+
+ uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+ encoded_uid = camel_url_encode (uid, ":;@/");
+ encoded_name = camel_url_encode (folder_name, "#");
+
+ uri = g_strdup_printf ("folder://%s/%s", encoded_uid, encoded_name);
+
+ g_free (encoded_uid);
+ g_free (encoded_name);
+
+ return uri;
+}
+
+/**
+ * e_mail_folder_uri_parse:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @out_store: return location for a #CamelStore, or %NULL
+ * @out_folder_name: return location for a folder name, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Parses a folder URI generated by e_mail_folder_uri_build() and
+ * returns the corresponding #CamelStore instance in @out_store and
+ * folder name string in @out_folder_name. If the URI is malformed
+ * or no corresponding store exists, the function sets @error and
+ * returns %FALSE.
+ *
+ * If the function is able to parse the URI, the #CamelStore instance
+ * set in @out_store should be unreferenced with g_object_unref() when
+ * done with it, and the folder name string set in @out_folder_name
+ * should be freed with g_free().
+ *
+ * The function also handles older style URIs, such as ones where the
+ * #CamelStore's #CamelStore::uri string was embedded directly in the
+ * folder URI, and account-based URIs that used an "email://" prefix.
+ *
+ * Returns: %TRUE if @folder_uri could be parsed, %FALSE otherwise
+ **/
+gboolean
+e_mail_folder_uri_parse (CamelSession *session,
+ const gchar *folder_uri,
+ CamelStore **out_store,
+ gchar **out_folder_name,
+ GError **error)
+{
+ CamelURL *url;
+ CamelService *service = NULL;
+ gchar *folder_name = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+ g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+ url = camel_url_new (folder_uri, error);
+ if (url == NULL)
+ return FALSE;
+
+ /* Current URI Format: 'folder://' STORE_UID '/' FOLDER_PATH */
+ if (g_strcmp0 (url->protocol, "folder") == 0) {
+
+ if (url->host != NULL) {
+ gchar *uid;
+
+ if (url->user == NULL || *url->user == '\0')
+ uid = g_strdup (url->host);
+ else
+ uid = g_strconcat (
+ url->user, "@", url->host, NULL);
+
+ service = camel_session_get_service (session, uid);
+ g_free (uid);
+ }
+
+ if (url->path != NULL && *url->path == '/')
+ folder_name = camel_url_decode_path (url->path + 1);
+
+ /* This style was used to reference accounts by UID before
+ * CamelServices themselves had UIDs. Some examples are:
+ *
+ * Special cases:
+ *
+ * 'email://local local/' FOLDER_PATH
+ * 'email://vfolder local/' FOLDER_PATH
+ *
+ * General case:
+ *
+ * 'email://' ACCOUNT_UID '/' FOLDER_PATH
+ *
+ * Note: ACCOUNT_UID is now equivalent to STORE_UID, and
+ * the STORE_UIDs for the special cases are 'local'
+ * and 'vfolder'.
+ */
+ } else if (g_strcmp0 (url->protocol, "email") == 0) {
+ gchar *uid = NULL;
+
+ /* Handle the special cases. */
+ if (g_strcmp0 (url->host, "local") == 0) {
+ if (g_strcmp0 (url->user, "local") == 0)
+ uid = g_strdup ("local");
+ if (g_strcmp0 (url->user, "vfolder") == 0)
+ uid = g_strdup ("vfolder");
+ }
+
+ /* Handle the general case. */
+ if (uid == NULL && url->host != NULL) {
+ if (url->user == NULL)
+ uid = g_strdup (url->host);
+ else
+ uid = g_strdup_printf (
+ "%s %s", url->user, url->host);
+ }
+
+ if (uid != NULL) {
+ service = camel_session_get_service (session, uid);
+ g_free (uid);
+ }
+
+ if (url->path != NULL && *url->path == '/')
+ folder_name = camel_url_decode_path (url->path + 1);
+
+ /* CamelFolderInfo URIs used to embed the store's URI, so the
+ * folder name is appended as either a path part or a fragment
+ * part, depending whether the store's URI used the path part.
+ * To determine which it is, you have to check the provider
+ * flags for CAMEL_URL_FRAGMENT_IS_PATH. */
+ } else {
+ service = camel_session_get_service_by_url (
+ session, url, CAMEL_PROVIDER_STORE);
+
+ if (CAMEL_IS_STORE (service)) {
+ CamelProvider *provider;
+
+ provider = camel_service_get_provider (service);
+
+ if (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
+ folder_name = g_strdup (url->fragment);
+ else if (url->path != NULL && *url->path == '/')
+ folder_name = g_strdup (url->path + 1);
+ }
+ }
+
+ if (CAMEL_IS_STORE (service) && folder_name != NULL) {
+ if (out_store != NULL)
+ *out_store = g_object_ref (service);
+
+ if (out_folder_name != NULL) {
+ *out_folder_name = folder_name;
+ folder_name = NULL;
+ }
+
+ success = TRUE;
+ } else {
+ g_set_error (
+ error, CAMEL_FOLDER_ERROR,
+ CAMEL_FOLDER_ERROR_INVALID,
+ _("Invalid folder URI '%s'"),
+ folder_uri);
+ }
+
+ g_free (folder_name);
+
+ camel_url_free (url);
+
+ return success;
+}
+
+/**
+ * e_mail_folder_uri_equal:
+ * @session: a #CamelSession
+ * @folder_uri_a: a folder URI
+ * @folder_uri_b: another folder URI
+ *
+ * Compares two folder URIs for equality. If either URI is invalid,
+ * the function returns %FALSE.
+ *
+ * Returns: %TRUE if the URIs are equal, %FALSE if not
+ **/
+gboolean
+e_mail_folder_uri_equal (CamelSession *session,
+ const gchar *folder_uri_a,
+ const gchar *folder_uri_b)
+{
+ CamelStore *store_a;
+ CamelStore *store_b;
+ CamelStoreClass *class;
+ gchar *folder_name_a;
+ gchar *folder_name_b;
+ gboolean success_a;
+ gboolean success_b;
+ gboolean equal = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+ g_return_val_if_fail (folder_uri_a != NULL, FALSE);
+ g_return_val_if_fail (folder_uri_b != NULL, FALSE);
+
+ success_a = e_mail_folder_uri_parse (
+ session, folder_uri_a, &store_a, &folder_name_a, NULL);
+
+ success_b = e_mail_folder_uri_parse (
+ session, folder_uri_b, &store_b, &folder_name_b, NULL);
+
+ if (!success_a || !success_b)
+ goto exit;
+
+ if (store_a != store_b)
+ goto exit;
+
+ /* Doesn't matter which store we use since they're the same. */
+ class = CAMEL_STORE_GET_CLASS (store_a);
+ g_return_val_if_fail (class->compare_folder_name != NULL, FALSE);
+
+ equal = class->compare_folder_name (folder_name_a, folder_name_b);
+
+exit:
+ if (success_a) {
+ g_object_unref (store_a);
+ g_free (folder_name_a);
+ }
+
+ if (success_b) {
+ g_object_unref (store_b);
+ g_free (folder_name_b);
+ }
+
+ return equal;
+}
+
+/**
+ * e_mail_folder_uri_from_folder:
+ * @folder: a #CamelFolder
+ *
+ * Convenience function for building a folder URI from a #CamelFolder.
+ * Free the returned URI string with g_free().
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_from_folder (CamelFolder *folder)
+{
+ CamelStore *store;
+ const gchar *folder_name;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ store = camel_folder_get_parent_store (folder);
+ folder_name = camel_folder_get_full_name (folder);
+
+ return e_mail_folder_uri_build (store, folder_name);
+}
+
+/**
+ * e_mail_folder_uri_to_markup:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Converts @folder_uri to a markup string suitable for displaying to users.
+ * The string consists of the #CamelStore display name (in bold), followed
+ * by the folder path. If the URI is malformed or no corresponding store
+ * exists, the function sets @error and returns %NULL. Free the returned
+ * string with g_free().
+ *
+ * Returns: a newly-allocated markup string, or %NULL
+ **/
+gchar *
+e_mail_folder_uri_to_markup (CamelSession *session,
+ const gchar *folder_uri,
+ GError **error)
+{
+ CamelStore *store = NULL;
+ const gchar *display_name;
+ gchar *folder_name = NULL;
+ gchar *markup;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
+ g_return_val_if_fail (folder_uri != NULL, NULL);
+
+ success = e_mail_folder_uri_parse (
+ session, folder_uri, &store, &folder_name, error);
+
+ if (!success)
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (folder_name != NULL, NULL);
+
+ display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
+
+ markup = g_markup_printf_escaped (
+ "<b>%s</b> : %s", display_name, folder_name);
+
+ g_object_unref (store);
+ g_free (folder_name);
+
+ return markup;
+}
diff --git a/libemail-engine/e-mail-folder-utils.h b/libemail-engine/e-mail-folder-utils.h
new file mode 100644
index 0000000..9e8dd0f
--- /dev/null
+++ b/libemail-engine/e-mail-folder-utils.h
@@ -0,0 +1,164 @@
+/*
+ * e-mail-folder-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_FOLDER_UTILS_H
+#define E_MAIL_FOLDER_UTILS_H
+
+/* CamelFolder wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+gboolean e_mail_folder_append_message_sync
+ (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gchar **appended_uid,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_append_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_folder_append_message_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error);
+
+CamelMimePart * e_mail_folder_build_attachment_sync
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gchar **fwd_subject,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_build_attachment (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelMimePart * e_mail_folder_build_attachment_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **fwd_subject,
+ GError **error);
+
+GHashTable * e_mail_folder_find_duplicate_messages_sync
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_find_duplicate_messages
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GHashTable * e_mail_folder_find_duplicate_messages_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error);
+
+GHashTable * e_mail_folder_get_multiple_messages_sync
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_get_multiple_messages
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GHashTable * e_mail_folder_get_multiple_messages_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean e_mail_folder_remove_sync (CamelFolder *folder,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_remove (CamelFolder *folder,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_folder_remove_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean e_mail_folder_remove_attachments_sync
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_remove_attachments
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_folder_remove_attachments_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean e_mail_folder_save_messages_sync
+ (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_folder_save_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_folder_save_messages_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error);
+
+gchar * e_mail_folder_uri_build (CamelStore *store,
+ const gchar *folder_name);
+gboolean e_mail_folder_uri_parse (CamelSession *session,
+ const gchar *folder_uri,
+ CamelStore **out_store,
+ gchar **out_folder_name,
+ GError **error);
+gboolean e_mail_folder_uri_equal (CamelSession *session,
+ const gchar *folder_uri_a,
+ const gchar *folder_uri_b);
+gchar * e_mail_folder_uri_from_folder (CamelFolder *folder);
+gchar * e_mail_folder_uri_to_markup (CamelSession *session,
+ const gchar *folder_uri,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_FOLDER_UTILS_H */
diff --git a/libemail-engine/e-mail-junk-filter.c b/libemail-engine/e-mail-junk-filter.c
new file mode 100644
index 0000000..d682490
--- /dev/null
+++ b/libemail-engine/e-mail-junk-filter.c
@@ -0,0 +1,82 @@
+/*
+ * e-mail-junk-filter.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-junk-filter.h"
+
+#include <e-mail-session.h>
+
+G_DEFINE_ABSTRACT_TYPE (
+ EMailJunkFilter,
+ e_mail_junk_filter,
+ E_TYPE_EXTENSION)
+
+static void
+e_mail_junk_filter_class_init (EMailJunkFilterClass *class)
+{
+ EExtensionClass *extension_class;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_MAIL_SESSION;
+}
+
+static void
+e_mail_junk_filter_init (EMailJunkFilter *junk_filter)
+{
+}
+
+gboolean
+e_mail_junk_filter_available (EMailJunkFilter *junk_filter)
+{
+ EMailJunkFilterClass *class;
+
+ g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), FALSE);
+
+ class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+ g_return_val_if_fail (class->available != NULL, FALSE);
+
+ return class->available (junk_filter);
+}
+
+GtkWidget *
+e_mail_junk_filter_new_config_widget (EMailJunkFilter *junk_filter)
+{
+ EMailJunkFilterClass *class;
+ GtkWidget *widget = NULL;
+
+ g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), NULL);
+
+ class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+
+ if (class->new_config_widget != NULL)
+ widget = class->new_config_widget (junk_filter);
+
+ return widget;
+}
+
+gint
+e_mail_junk_filter_compare (EMailJunkFilter *junk_filter_a,
+ EMailJunkFilter *junk_filter_b)
+{
+ EMailJunkFilterClass *class_a;
+ EMailJunkFilterClass *class_b;
+
+ class_a = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_a);
+ class_b = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_b);
+
+ return g_utf8_collate (class_a->display_name, class_b->display_name);
+}
diff --git a/libemail-engine/e-mail-junk-filter.h b/libemail-engine/e-mail-junk-filter.h
new file mode 100644
index 0000000..74a7840
--- /dev/null
+++ b/libemail-engine/e-mail-junk-filter.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-junk-filter.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_JUNK_FILTER_H
+#define E_MAIL_JUNK_FILTER_H
+
+#include <gtk/gtk.h>
+#include <libebackend/e-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_JUNK_FILTER \
+ (e_mail_junk_filter_get_type ())
+#define E_MAIL_JUNK_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilter))
+#define E_MAIL_JUNK_FILTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass))
+#define E_IS_MAIL_JUNK_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_JUNK_FILTER))
+#define E_IS_MAIL_JUNK_FILTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_JUNK_FILTER))
+#define E_MAIL_JUNK_FILTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailJunkFilter EMailJunkFilter;
+typedef struct _EMailJunkFilterClass EMailJunkFilterClass;
+typedef struct _EMailJunkFilterPrivate EMailJunkFilterPrivate;
+
+struct _EMailJunkFilter {
+ EExtension parent;
+ EMailJunkFilterPrivate *priv;
+};
+
+struct _EMailJunkFilterClass {
+ EExtensionClass parent_class;
+
+ const gchar *filter_name;
+ const gchar *display_name;
+
+ gboolean (*available) (EMailJunkFilter *junk_filter);
+ GtkWidget * (*new_config_widget) (EMailJunkFilter *junk_filter);
+};
+
+GType e_mail_junk_filter_get_type (void) G_GNUC_CONST;
+gboolean e_mail_junk_filter_available (EMailJunkFilter *junk_filter);
+GtkWidget * e_mail_junk_filter_new_config_widget
+ (EMailJunkFilter *junk_filter);
+gint e_mail_junk_filter_compare (EMailJunkFilter *junk_filter_a,
+ EMailJunkFilter *junk_filter_b);
+
+G_END_DECLS
+
+#endif /* E_MAIL_JUNK_FILTER_H */
diff --git a/libemail-engine/e-mail-local.c b/libemail-engine/e-mail-local.c
new file mode 100644
index 0000000..1f33c08
--- /dev/null
+++ b/libemail-engine/e-mail-local.c
@@ -0,0 +1,157 @@
+/*
+ * e-mail-local.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-local.h"
+
+#include <glib/gi18n.h>
+
+#include "e-mail-folder-utils.h"
+
+#define CHECK_LOCAL_FOLDER_TYPE(type) \
+ ((type) < G_N_ELEMENTS (default_local_folders))
+
+/* The array elements correspond to EMailLocalFolder. */
+static struct {
+ const gchar *display_name;
+ CamelFolder *folder;
+ gchar *folder_uri;
+} default_local_folders[] = {
+ { N_("Inbox") },
+ { N_("Drafts") },
+ { N_("Outbox") },
+ { N_("Sent") },
+ { N_("Templates") },
+ { "Inbox" } /* "always local" inbox */
+};
+
+static CamelStore *local_store;
+static gboolean mail_local_initialized = FALSE;
+
+void
+e_mail_local_init (EMailSession *session,
+ const gchar *data_dir)
+{
+ CamelService *service;
+ CamelURL *url;
+ gchar *temp;
+ gint ii;
+ GError *error = NULL;
+
+ if (mail_local_initialized)
+ return;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (data_dir != NULL);
+
+ mail_local_initialized = TRUE;
+
+ url = camel_url_new ("maildir:", NULL);
+ temp = g_build_filename (data_dir, "local", NULL);
+ camel_url_set_path (url, temp);
+ g_free (temp);
+
+ temp = camel_url_to_string (url, 0);
+ service = camel_session_add_service (
+ CAMEL_SESSION (session), "local", temp,
+ CAMEL_PROVIDER_STORE, &error);
+ g_free (temp);
+
+ camel_service_set_display_name (service, _("On This Computer"));
+
+ /* Shouldn't need to worry about other mail applications
+ * altering files in our local mail store. */
+ g_object_set (service, "need-summary-check", FALSE, NULL);
+
+ if (error != NULL)
+ goto fail;
+
+ /* Populate the rest of the default_local_folders array. */
+ for (ii = 0; ii < G_N_ELEMENTS (default_local_folders); ii++) {
+ const gchar *display_name;
+
+ display_name = default_local_folders[ii].display_name;
+
+ default_local_folders[ii].folder_uri =
+ e_mail_folder_uri_build (
+ CAMEL_STORE (service), display_name);
+
+ /* FIXME camel_store_get_folder() may block. */
+ if (!strcmp (display_name, "Inbox"))
+ default_local_folders[ii].folder =
+ camel_store_get_inbox_folder_sync (
+ CAMEL_STORE (service), NULL, &error);
+ else
+ default_local_folders[ii].folder =
+ camel_store_get_folder_sync (
+ CAMEL_STORE (service), display_name,
+ CAMEL_STORE_FOLDER_CREATE, NULL, &error);
+
+ if (error != NULL) {
+ g_critical ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ camel_url_free (url);
+
+ local_store = g_object_ref (service);
+
+ return;
+
+fail:
+ g_critical (
+ "Could not initialize local store/folder: %s",
+ error->message);
+
+ g_error_free (error);
+ camel_url_free (url);
+}
+
+CamelFolder *
+e_mail_local_get_folder (EMailLocalFolder type)
+{
+ g_return_val_if_fail (mail_local_initialized, NULL);
+ g_return_val_if_fail (CHECK_LOCAL_FOLDER_TYPE (type), NULL);
+
+ return default_local_folders[type].folder;
+}
+
+const gchar *
+e_mail_local_get_folder_uri (EMailLocalFolder type)
+{
+ g_return_val_if_fail (mail_local_initialized, NULL);
+ g_return_val_if_fail (CHECK_LOCAL_FOLDER_TYPE (type), NULL);
+
+ return default_local_folders[type].folder_uri;
+}
+
+CamelStore *
+e_mail_local_get_store (void)
+{
+ g_return_val_if_fail (mail_local_initialized, NULL);
+ g_return_val_if_fail (CAMEL_IS_STORE (local_store), NULL);
+
+ return local_store;
+}
diff --git a/libemail-engine/e-mail-local.h b/libemail-engine/e-mail-local.h
new file mode 100644
index 0000000..d4b5bad
--- /dev/null
+++ b/libemail-engine/e-mail-local.h
@@ -0,0 +1,39 @@
+/*
+ * e-mail-local.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_LOCAL_H
+#define E_MAIL_LOCAL_H
+
+#include <camel/camel.h>
+#include <libemail-engine/e-mail-enums.h>
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+void e_mail_local_init (EMailSession *session,
+ const gchar *data_dir);
+CamelFolder * e_mail_local_get_folder (EMailLocalFolder type);
+const gchar * e_mail_local_get_folder_uri (EMailLocalFolder type);
+CamelStore * e_mail_local_get_store (void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_LOCAL_H */
diff --git a/libemail-engine/e-mail-session-utils.c b/libemail-engine/e-mail-session-utils.c
new file mode 100644
index 0000000..9a55891
--- /dev/null
+++ b/libemail-engine/e-mail-session-utils.c
@@ -0,0 +1,942 @@
+/*
+ * e-mail-session-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-session-utils.h"
+
+#include "e-mail-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "mail-tools.h"
+#include "e-mail-local.h"
+#include "e-mail-folder-utils.h"
+#include <libemail-utils/e-account-utils.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution Mail Data Server" PACKAGE_VERSION)
+
+/* FIXME: Do this after we move filter/ to eds */
+#define E_FILTER_SOURCE_OUTGOING "outgoing"/* performed on outgoing mail */
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ CamelFolder *sent_folder;
+
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+
+ CamelAddress *from;
+ CamelAddress *recipients;
+
+ CamelFilterDriver *driver;
+
+ GCancellable *cancellable;
+ gint io_priority;
+
+ /* X-Evolution headers */
+ struct _camel_header_raw *xev;
+
+ GPtrArray *post_to_uris;
+
+ gchar *folder_uri;
+ gchar *message_uid;
+ gchar *transport_uid;
+ gchar *sent_folder_uri;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->sent_folder != NULL)
+ g_object_unref (context->sent_folder);
+
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->info != NULL)
+ camel_message_info_free (context->info);
+
+ if (context->from != NULL)
+ g_object_unref (context->from);
+
+ if (context->recipients != NULL)
+ g_object_unref (context->recipients);
+
+ if (context->driver != NULL)
+ g_object_unref (context->driver);
+
+ if (context->cancellable != NULL) {
+ camel_operation_pop_message (context->cancellable);
+ g_object_unref (context->cancellable);
+ }
+
+ if (context->xev != NULL)
+ camel_header_raw_clear (&context->xev);
+
+ if (context->post_to_uris != NULL) {
+ g_ptr_array_foreach (
+ context->post_to_uris, (GFunc) g_free, NULL);
+ g_ptr_array_free (context->post_to_uris, TRUE);
+ }
+
+ g_free (context->folder_uri);
+ g_free (context->message_uid);
+ g_free (context->transport_uid);
+ g_free (context->sent_folder_uri);
+
+ g_slice_free (AsyncContext, context);
+}
+
+GQuark
+e_mail_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0)) {
+ const gchar *string = "e-mail-error-quark";
+ quark = g_quark_from_static_string (string);
+ }
+
+ return quark;
+}
+
+static void
+mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_session_handle_draft_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *header_name;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Draft-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Draft-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Draft
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL)
+ return TRUE;
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_draft_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_draft_headers);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_draft_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_draft_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_session_handle_source_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ CamelMessageFlags flags = 0;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *flag_string;
+ const gchar *header_name;
+ gboolean success;
+ guint length, ii;
+ gchar **tokens;
+ gchar *string;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Source-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Flags";
+ flag_string = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Source
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL || flag_string == NULL)
+ return TRUE;
+
+ /* Convert the flag string to CamelMessageFlags. */
+
+ string = g_strstrip (g_strdup (flag_string));
+ tokens = g_strsplit (string, " ", 0);
+ g_free (string);
+
+ /* If tokens is NULL, a length of 0 will skip the loop. */
+ length = (tokens != NULL) ? g_strv_length (tokens) : 0;
+
+ for (ii = 0; ii < length; ii++) {
+ /* Note: We're only checking for flags known to
+ * be used in X-Evolution-Source-Flags headers.
+ * Add more as needed. */
+ if (g_strcmp0 (tokens[ii], "ANSWERED") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED;
+ else if (g_strcmp0 (tokens[ii], "ANSWERED_ALL") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ else if (g_strcmp0 (tokens[ii], "FORWARDED") == 0)
+ flags |= CAMEL_MESSAGE_FORWARDED;
+ else if (g_strcmp0 (tokens[ii], "SEEN") == 0)
+ flags |= CAMEL_MESSAGE_SEEN;
+ else
+ g_warning (
+ "Unknown flag '%s' in %s",
+ tokens[ii], header_name);
+ }
+
+ g_strfreev (tokens);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid, flags, flags);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_source_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_source_headers);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_source_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_source_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_send_to_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ CamelFolder *local_sent_folder;
+ GString *error_messages;
+ gboolean copy_to_sent = TRUE;
+ guint ii;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* Send the message to all recipients. */
+ if (camel_address_length (context->recipients) > 0) {
+ CamelProvider *provider;
+ CamelService *service;
+ gboolean did_connect = FALSE;
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), context->transport_uid);
+
+ if (!CAMEL_IS_TRANSPORT (service)) {
+ g_simple_async_result_set_error (simple,
+ CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_URL_INVALID,
+ _("Cannot get transport for account '%s'"),
+ context->transport_uid);
+ return;
+ }
+
+ if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED) {
+ did_connect = TRUE;
+
+ /* XXX This API does not allow for cancellation. */
+ if (!em_utils_connect_service_sync (service, cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ provider = camel_service_get_provider (service);
+
+ if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
+ copy_to_sent = FALSE;
+
+ camel_transport_send_to_sync (
+ CAMEL_TRANSPORT (service),
+ context->message, context->from,
+ context->recipients, cancellable, &error);
+
+ if (did_connect)
+ em_utils_disconnect_service_sync (
+ service, error == NULL,
+ cancellable, error ? NULL : &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ /* Post the message to requested folders. */
+ for (ii = 0; ii < context->post_to_uris->len; ii++) {
+ CamelFolder *folder;
+ const gchar *folder_uri;
+
+ folder_uri = g_ptr_array_index (context->post_to_uris, ii);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ camel_folder_append_message_sync (
+ folder, context->message, context->info,
+ NULL, cancellable, &error);
+
+ g_object_unref (folder);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ /*** Post Processing ***/
+
+ /* This accumulates error messages during post-processing. */
+ error_messages = g_string_sized_new (256);
+
+ mail_tool_restore_xevolution_headers (context->message, context->xev);
+
+ /* Run filters on the outgoing message. */
+ if (context->driver != NULL) {
+ camel_filter_driver_filter_message (
+ context->driver, context->message, context->info,
+ NULL, NULL, NULL, "", cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error != NULL) {
+ g_string_append_printf (
+ error_messages,
+ _("Failed to apply outgoing filters: %s"),
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (!copy_to_sent)
+ goto cleanup;
+
+ /* Append the sent message to a Sent folder. */
+
+ local_sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+ /* Try to extract a CamelFolder from the Sent folder URI. */
+ if (context->sent_folder_uri != NULL) {
+ context->sent_folder = e_mail_session_uri_to_folder_sync (
+ session, context->sent_folder_uri, 0,
+ cancellable, &error);
+ if (error != NULL) {
+ g_warn_if_fail (context->sent_folder == NULL);
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ context->sent_folder_uri, error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* Fall back to the local Sent folder. */
+ if (context->sent_folder == NULL)
+ context->sent_folder = g_object_ref (local_sent_folder);
+
+ /* Append the message. */
+ camel_folder_append_message_sync (
+ context->sent_folder, context->message,
+ context->info, NULL, cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error == NULL)
+ goto cleanup;
+
+ /* If appending to a remote Sent folder failed,
+ * try appending to the local Sent folder. */
+ if (context->sent_folder != local_sent_folder) {
+ const gchar *description;
+
+ description = camel_folder_get_description (
+ context->sent_folder);
+
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ description, error->message);
+ g_clear_error (&error);
+
+ camel_folder_append_message_sync (
+ local_sent_folder, context->message,
+ context->info, NULL, cancellable, &error);
+ }
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ /* We can't even append to the local Sent folder?
+ * In that case just leave the message in Outbox. */
+ if (error != NULL) {
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to local 'Sent' folder: %s"),
+ error->message);
+ g_clear_error (&error);
+ goto exit;
+ }
+
+cleanup:
+
+ /* The send operation was successful; ignore cleanup errors. */
+
+ /* Mark the draft message for deletion, if present. */
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Set flags on the original source message, if present.
+ * Source message refers to the message being forwarded
+ * or replied to. */
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+exit:
+
+ /* If we were cancelled, disregard any other errors. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+
+ /* Stuff the accumulated error messages in a GError. */
+ } else if (error_messages->len > 0) {
+ g_simple_async_result_set_error (
+ simple, E_MAIL_ERROR,
+ E_MAIL_ERROR_POST_PROCESSING,
+ "%s", error_messages->str);
+ }
+
+ /* Synchronize the Sent folder. */
+ if (context->sent_folder != NULL)
+ camel_folder_synchronize_sync (
+ context->sent_folder, FALSE, cancellable, NULL);
+
+ g_string_free (error_messages, TRUE);
+}
+
+void
+e_mail_session_send_to (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder_func,
+ gpointer get_folder_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ CamelAddress *from;
+ CamelAddress *recipients;
+ CamelMedium *medium;
+ CamelMessageInfo *info;
+ EAccount *account = NULL;
+ GPtrArray *post_to_uris;
+ struct _camel_header_raw *xev;
+ struct _camel_header_raw *header;
+ const gchar *string;
+ const gchar *resent_from;
+ gchar *transport_uid = NULL;
+ gchar *sent_folder_uri = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ medium = CAMEL_MEDIUM (message);
+
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ xev = mail_tool_remove_xevolution_headers (message);
+
+ /* Extract directives from X-Evolution headers. */
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Account", NULL);
+ if (string != NULL) {
+ gchar *account_uid;
+
+ account_uid = g_strstrip (g_strdup (string));
+ account = e_get_account_by_uid (account_uid);
+ g_free (account_uid);
+ }
+
+ if (account != NULL) {
+ if (account->transport != NULL) {
+
+ /* XXX Transport UIDs are kludgy right now. We
+ * use the EAccount's regular UID and tack on
+ * "-transport". Will be better soon. */
+ transport_uid = g_strconcat (
+ account->uid, "-transport", NULL);
+
+ /* to reprompt password on sending if needed */
+ account->transport->get_password_canceled = FALSE;
+ }
+ sent_folder_uri = g_strdup (account->sent_folder_uri);
+ }
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Fcc", NULL);
+ if (sent_folder_uri == NULL && string != NULL)
+ sent_folder_uri = g_strstrip (g_strdup (string));
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Transport", NULL);
+ if (transport_uid == NULL && string != NULL)
+ transport_uid = g_strstrip (g_strdup (string));
+
+ post_to_uris = g_ptr_array_new ();
+ for (header = xev; header != NULL; header = header->next) {
+ gchar *folder_uri;
+
+ if (g_strcmp0 (header->name, "X-Evolution-PostTo") != 0)
+ continue;
+
+ folder_uri = g_strstrip (g_strdup (header->value));
+ g_ptr_array_add (post_to_uris, folder_uri);
+ }
+
+ /* Collect sender and recipients from headers. */
+
+ from = (CamelAddress *) camel_internet_address_new ();
+ recipients = (CamelAddress *) camel_internet_address_new ();
+ resent_from = camel_medium_get_header (medium, "Resent-From");
+
+ if (resent_from != NULL) {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ camel_address_decode (from, resent_from);
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ } else {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ addr = camel_mime_message_get_from (message);
+ camel_address_copy (from, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+ }
+
+ /* Miscellaneous preparations. */
+
+ info = camel_message_info_new (NULL);
+ camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+ /* The rest of the processing happens in a thread. */
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+ context->io_priority = io_priority;
+ context->from = from;
+ context->recipients = recipients;
+ context->message = g_object_ref (message);
+ context->info = info;
+ context->xev = xev;
+ context->post_to_uris = post_to_uris;
+ context->transport_uid = transport_uid;
+ context->sent_folder_uri = sent_folder_uri;
+
+ if (G_IS_CANCELLABLE (cancellable))
+ context->cancellable = g_object_ref (cancellable);
+
+ /* Failure here emits a runtime warning but is non-fatal. */
+ context->driver = camel_session_get_filter_driver (
+ CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, &error);
+ if (context->driver != NULL && get_folder_func)
+ camel_filter_driver_set_folder_func (
+ context->driver, get_folder_func, get_folder_data);
+ if (error != NULL) {
+ g_warn_if_fail (context->driver == NULL);
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ /* This gets popped in async_context_free(). */
+ camel_operation_push_message (
+ context->cancellable, _("Sending message"));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_send_to);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_send_to_thread,
+ context->io_priority,
+ context->cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_send_to_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_send_to), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_unsubscribe_folder_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_unsubscribe_folder_sync (
+ session, context->folder_uri, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_session_unsubscribe_folder_sync (EMailSession *session,
+ const gchar *folder_uri,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelStore *store = NULL;
+ gchar *folder_name = NULL;
+ const gchar *message;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+ success = e_mail_folder_uri_parse (
+ CAMEL_SESSION (session), folder_uri,
+ &store, &folder_name, error);
+
+ if (!success)
+ return FALSE;
+
+ message = _("Unsubscribing from folder '%s'");
+ camel_operation_push_message (cancellable, message, folder_name);
+
+ /* FIXME This should take our GCancellable. */
+ success =
+ em_utils_connect_service_sync (
+ CAMEL_SERVICE (store), cancellable, error) &&
+ camel_subscribable_unsubscribe_folder_sync (
+ CAMEL_SUBSCRIBABLE (store),
+ folder_name, cancellable, error);
+
+ camel_operation_pop_message (cancellable);
+
+ g_object_unref (store);
+ g_free (folder_name);
+
+ return success;
+}
+
+void
+e_mail_session_unsubscribe_folder (EMailSession *session,
+ const gchar *folder_uri,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (folder_uri != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->folder_uri = g_strdup (folder_uri);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_unsubscribe_folder);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_unsubscribe_folder_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_unsubscribe_folder_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_unsubscribe_folder), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/libemail-engine/e-mail-session-utils.h b/libemail-engine/e-mail-session-utils.h
new file mode 100644
index 0000000..2c92216
--- /dev/null
+++ b/libemail-engine/e-mail-session-utils.h
@@ -0,0 +1,97 @@
+/*
+ * e-mail-session-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_SESSION_UTILS_H
+#define E_MAIL_SESSION_UTILS_H
+
+/* High-level operations with Evolution-specific policies. */
+
+#include <libemail-engine/e-mail-session.h>
+
+#define E_MAIL_ERROR (e_mail_error_quark ())
+
+G_BEGIN_DECLS
+
+typedef enum {
+ E_MAIL_ERROR_POST_PROCESSING
+} EMailError;
+
+GQuark e_mail_error_quark (void) G_GNUC_CONST;
+gboolean e_mail_session_handle_draft_headers_sync
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_handle_draft_headers
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_handle_draft_headers_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_mail_session_handle_source_headers_sync
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_handle_source_headers
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_handle_source_headers_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+void e_mail_session_send_to (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder_func,
+ gpointer get_folder_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_send_to_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_mail_session_unsubscribe_folder_sync
+ (EMailSession *session,
+ const gchar *folder_uri,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_unsubscribe_folder
+ (EMailSession *session,
+ const gchar *folder_uri,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_unsubscribe_folder_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SESSION_UTILS_H */
diff --git a/libemail-engine/e-mail-session.c b/libemail-engine/e-mail-session.c
new file mode 100644
index 0000000..b8a9c37
--- /dev/null
+++ b/libemail-engine/e-mail-session.c
@@ -0,0 +1,1517 @@
+/*
+ * e-mail-session.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+/* mail-session.c: handles the session information and resource manipulation */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#ifdef HAVE_CANBERRA
+#include <canberra-gtk.h>
+#endif
+
+#include <libedataserver/e-flag.h>
+#include <libedataserver/e-proxy.h>
+#include <libebackend/e-extensible.h>
+#include <libedataserverui/e-passwords.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "libemail-utils/e-account-utils.h"
+#include "libemail-utils/mail-mt.h"
+#include "libemail-utils/gconf-bridge.h"
+
+#include "e-mail-junk-filter.h"
+#include "e-mail-local.h"
+#include "e-mail-session.h"
+#include "e-mail-folder-utils.h"
+#include "e-mail-utils.h"
+#include "mail-config.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+
+#define E_MAIL_SESSION_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
+
+static guint session_check_junk_notify_id;
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _EMailSessionPrivate {
+ MailFolderCache *folder_cache;
+
+ FILE *filter_logfile;
+ GHashTable *junk_filters;
+ EProxy *proxy;
+};
+
+struct _AsyncContext {
+ /* arguments */
+ CamelStoreGetFolderFlags flags;
+ gchar *uid;
+ gchar *uri;
+
+ /* results */
+ CamelFolder *folder;
+};
+
+enum {
+ PROP_0,
+ PROP_FOLDER_CACHE,
+ PROP_JUNK_FILTER_NAME
+};
+
+enum {
+ FLUSH_OUTBOX,
+ STORE_ADDED,
+ STORE_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gchar *mail_data_dir;
+static gchar *mail_cache_dir;
+static gchar *mail_config_dir;
+
+G_DEFINE_TYPE_WITH_CODE (
+ EMailSession,
+ e_mail_session,
+ CAMEL_TYPE_SESSION,
+ G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+/* Support for CamelSession.alert_user() *************************************/
+
+static GQueue user_message_queue = { NULL, NULL, 0 };
+
+struct _user_message_msg {
+ MailMsg base;
+
+ CamelSessionAlertType type;
+ gchar *prompt;
+ EFlag *done;
+
+ guint allow_cancel : 1;
+ guint result : 1;
+ guint ismain : 1;
+};
+
+static void user_message_exec (struct _user_message_msg *m,
+ GCancellable *cancellable,
+ GError **error);
+
+static void
+user_message_response_free (struct _user_message_msg *m)
+{
+
+ /* check for pendings */
+ if (!g_queue_is_empty (&user_message_queue)) {
+ GCancellable *cancellable;
+
+ m = g_queue_pop_head (&user_message_queue);
+ cancellable = m->base.cancellable;
+ user_message_exec (m, cancellable, &m->base.error);
+ mail_msg_unref (m);
+ }
+}
+
+/* clicked, send back the reply */
+static void
+user_message_response (struct _user_message_msg *m)
+{
+ /* if !allow_cancel, then we've already replied */
+ if (m->allow_cancel) {
+ m->result = TRUE; //If Accepted
+ e_flag_set (m->done);
+ }
+
+ user_message_response_free (m);
+}
+
+static void
+user_message_exec (struct _user_message_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *error_type;
+
+ if (!m->ismain) {
+ g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
+ return;
+ }
+
+ switch (m->type) {
+ case CAMEL_SESSION_ALERT_INFO:
+ error_type = m->allow_cancel ?
+ "mail:session-message-info-cancel" :
+ "mail:session-message-info";
+ break;
+ case CAMEL_SESSION_ALERT_WARNING:
+ error_type = m->allow_cancel ?
+ "mail:session-message-warning-cancel" :
+ "mail:session-message-warning";
+ break;
+ case CAMEL_SESSION_ALERT_ERROR:
+ error_type = m->allow_cancel ?
+ "mail:session-message-error-cancel" :
+ "mail:session-message-error";
+ break;
+ default:
+ error_type = NULL;
+ g_return_if_reached ();
+ }
+
+ /* XXX This is a case where we need to be able to construct
+ * custom EAlerts without a predefined XML definition. */
+ if (m->ismain) {
+ /* Use DBUS to raise dialogs in clients and reply back. For now say accept all */
+ user_message_response (m);
+ } else {
+ }
+}
+
+static void
+user_message_free (struct _user_message_msg *m)
+{
+ g_free (m->prompt);
+ e_flag_free (m->done);
+}
+
+static MailMsgInfo user_message_info = {
+ sizeof (struct _user_message_msg),
+ (MailMsgDescFunc) NULL,
+ (MailMsgExecFunc) user_message_exec,
+ (MailMsgDoneFunc) NULL,
+ (MailMsgFreeFunc) user_message_free
+};
+
+/* Support for CamelSession.get_filter_driver () *****************************/
+
+static CamelFolder *
+get_folder (CamelFilterDriver *d,
+ const gchar *uri,
+ gpointer user_data,
+ GError **error)
+{
+ EMailSession *session = E_MAIL_SESSION (user_data);
+
+ /* FIXME Not passing a GCancellable here. */
+ /* FIXME Need a camel_filter_driver_get_session(). */
+ return e_mail_session_uri_to_folder_sync (
+ session, uri, 0, NULL, error);
+}
+
+static CamelFilterDriver *
+main_get_filter_driver (CamelSession *session,
+ const gchar *type,
+ GError **error)
+{
+ CamelFilterDriver *driver;
+ GConfClient *client;
+ EMailSession *ms = (EMailSession *)session;
+
+ client = gconf_client_get_default ();
+
+ driver = camel_filter_driver_new (session);
+ camel_filter_driver_set_folder_func (driver, get_folder, session);
+
+ if (gconf_client_get_bool (client, "/apps/evolution/mail/filters/log", NULL)) {
+ if (ms->priv->filter_logfile == NULL) {
+ gchar *filename;
+
+ filename = gconf_client_get_string (
+ client, "/apps/evolution/mail/filters/logfile", NULL);
+ if (filename) {
+ ms->priv->filter_logfile = g_fopen (filename, "a+");
+ g_free (filename);
+ }
+ }
+
+ if (ms->priv->filter_logfile)
+ camel_filter_driver_set_logfile (driver, ms->priv->filter_logfile);
+ }
+
+ g_object_unref (client);
+
+ return driver;
+}
+
+/* Support for CamelSession.forward_to () ************************************/
+
+static guint preparing_flush = 0;
+
+static gboolean
+forward_to_flush_outbox_cb (EMailSession *session)
+{
+
+ preparing_flush = 0;
+
+ /* Connect to this and call mail_send in the main email client.*/
+ g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
+
+
+ return FALSE;
+}
+
+static void
+ms_forward_to_cb (CamelFolder *folder,
+ GAsyncResult *result,
+ EMailSession *session)
+{
+ GConfClient *client;
+
+ /* FIXME Poor error handling. */
+ if (!e_mail_folder_append_message_finish (folder, result, NULL, NULL))
+ return;
+
+ client = gconf_client_get_default ();
+
+ /* do not call mail send immediately, just pile them all in the outbox */
+ if (preparing_flush || gconf_client_get_bool (
+ client, "/apps/evolution/mail/filters/flush-outbox", NULL)) {
+ if (preparing_flush)
+ g_source_remove (preparing_flush);
+
+ preparing_flush = g_timeout_add_seconds (
+ 60, (GSourceFunc)
+ forward_to_flush_outbox_cb, session);
+ }
+
+ g_object_unref (client);
+}
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->folder != NULL)
+ g_object_unref (context->folder);
+
+ g_free (context->uid);
+ g_free (context->uri);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static gchar *
+mail_session_make_key (CamelService *service,
+ const gchar *item)
+{
+ gchar *key;
+
+ if (service != NULL)
+ key = camel_url_to_string (
+ camel_service_get_camel_url (service),
+ CAMEL_URL_HIDE_PARAMS);
+ else
+ key = g_strdup (item);
+
+ return key;
+}
+
+static void
+mail_session_check_junk_notify (GConfClient *gconf,
+ guint id,
+ GConfEntry *entry,
+ CamelSession *session)
+{
+ gchar *key;
+
+ g_return_if_fail (gconf_entry_get_key (entry) != NULL);
+ g_return_if_fail (gconf_entry_get_value (entry) != NULL);
+
+ key = strrchr (gconf_entry_get_key (entry), '/');
+ if (key) {
+ key++;
+ if (strcmp (key, "check_incoming") == 0)
+ camel_session_set_check_junk (
+ session, gconf_value_get_bool (
+ gconf_entry_get_value (entry)));
+ }
+}
+
+static const gchar *
+mail_session_get_junk_filter_name (EMailSession *session)
+{
+ CamelJunkFilter *junk_filter;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ /* XXX This property can be removed once Evolution moves to
+ * GSettings and can use transform functions when binding
+ * properties to settings. That's why this is private. */
+
+ g_hash_table_iter_init (&iter, session->priv->junk_filters);
+ junk_filter = camel_session_get_junk_filter (CAMEL_SESSION (session));
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (junk_filter == CAMEL_JUNK_FILTER (value))
+ return (const gchar *) key;
+ }
+
+ if (junk_filter != NULL)
+ g_warning (
+ "Camel is using a junk filter "
+ "unknown to Evolution of type %s",
+ G_OBJECT_TYPE_NAME (junk_filter));
+
+ return ""; /* GConfBridge doesn't like NULL strings */
+}
+
+static void
+mail_session_set_junk_filter_name (EMailSession *session,
+ const gchar *junk_filter_name)
+{
+ CamelJunkFilter *junk_filter = NULL;
+
+ /* XXX This property can be removed once Evolution moves to
+ * GSettings and can use transform functions when binding
+ * properties to settings. That's why this is private. */
+
+ /* An empty string is equivalent to a NULL string. */
+ if (junk_filter_name != NULL && *junk_filter_name == '\0')
+ junk_filter_name = NULL;
+
+ if (junk_filter_name != NULL) {
+ junk_filter = g_hash_table_lookup (
+ session->priv->junk_filters, junk_filter_name);
+ if (junk_filter != NULL) {
+ if (!e_mail_junk_filter_available (
+ E_MAIL_JUNK_FILTER (junk_filter)))
+ junk_filter = NULL;
+ } else {
+ g_warning (
+ "Unrecognized junk filter name "
+ "'%s' in GConf", junk_filter_name);
+ }
+ }
+
+ camel_session_set_junk_filter (CAMEL_SESSION (session), junk_filter);
+
+ /* XXX We emit the "notify" signal in mail_session_notify(). */
+}
+
+static void
+mail_session_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_JUNK_FILTER_NAME:
+ mail_session_set_junk_filter_name (
+ E_MAIL_SESSION (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_session_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_FOLDER_CACHE:
+ g_value_set_object (
+ value,
+ e_mail_session_get_folder_cache (
+ E_MAIL_SESSION (object)));
+ return;
+
+ case PROP_JUNK_FILTER_NAME:
+ g_value_set_string (
+ value,
+ mail_session_get_junk_filter_name (
+ E_MAIL_SESSION (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_session_dispose (GObject *object)
+{
+ EMailSessionPrivate *priv;
+
+ priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+ if (priv->folder_cache != NULL) {
+ g_object_unref (priv->folder_cache);
+ priv->folder_cache = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
+}
+
+static void
+mail_session_finalize (GObject *object)
+{
+ EMailSessionPrivate *priv;
+ GConfClient *client;
+
+ priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->junk_filters);
+ g_object_unref (priv->proxy);
+
+ client = gconf_client_get_default ();
+
+ if (session_check_junk_notify_id != 0) {
+ gconf_client_notify_remove (client, session_check_junk_notify_id);
+ session_check_junk_notify_id = 0;
+ }
+
+ g_object_unref (client);
+
+ g_free (mail_data_dir);
+ g_free (mail_config_dir);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object);
+}
+
+static void
+mail_session_notify (GObject *object,
+ GParamSpec *pspec)
+{
+ /* GObject does not implement this method; do not chain up. */
+
+ /* XXX Delete this once Evolution moves to GSettings and
+ * we're able to get rid of PROP_JUNK_FILTER_NAME. */
+ if (g_strcmp0 (pspec->name, "junk-filter") == 0)
+ g_object_notify (object, "junk-filter-name");
+}
+
+static void
+mail_session_constructed (GObject *object)
+{
+ EMailSessionPrivate *priv;
+ EExtensible *extensible;
+ GType extension_type;
+ GList *list, *iter;
+
+ priv = E_MAIL_SESSION_GET_PRIVATE (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
+
+ extensible = E_EXTENSIBLE (object);
+ e_extensible_load_extensions (extensible);
+
+ /* Add junk filter extensions to an internal hash table. */
+
+ extension_type = E_TYPE_MAIL_JUNK_FILTER;
+ list = e_extensible_list_extensions (extensible, extension_type);
+
+ for (iter = list; iter != NULL; iter = g_list_next (iter)) {
+ EMailJunkFilter *junk_filter;
+ EMailJunkFilterClass *class;
+
+ junk_filter = E_MAIL_JUNK_FILTER (iter->data);
+ class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
+
+ if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
+ g_warning (
+ "Skipping %s: Does not implement "
+ "CamelJunkFilterInterface",
+ G_OBJECT_TYPE_NAME (junk_filter));
+ continue;
+ }
+
+ if (class->filter_name == NULL) {
+ g_warning (
+ "Skipping %s: filter_name unset",
+ G_OBJECT_TYPE_NAME (junk_filter));
+ continue;
+ }
+
+ if (class->display_name == NULL) {
+ g_warning (
+ "Skipping %s: display_name unset",
+ G_OBJECT_TYPE_NAME (junk_filter));
+ continue;
+ }
+
+ /* No need to reference the EMailJunkFilter since
+ * EMailSession owns the reference to it already. */
+ g_hash_table_insert (
+ priv->junk_filters,
+ (gpointer) class->filter_name,
+ junk_filter);
+ }
+
+ g_list_free (list);
+
+ /* Bind the "/apps/evolution/mail/junk/default_plugin"
+ * GConf key to our "junk-filter-name" property. */
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/default_plugin",
+ object, "junk-filter-name");
+}
+
+static CamelService *
+mail_session_add_service (CamelSession *session,
+ const gchar *uid,
+ const gchar *url_string,
+ CamelProviderType type,
+ GError **error)
+{
+ CamelService *service;
+
+ /* Chain up to parents add_service() method. */
+ service = CAMEL_SESSION_CLASS (e_mail_session_parent_class)->
+ add_service (session, uid, url_string, type, error);
+
+ /* Initialize the CamelSettings object from CamelURL parameters.
+ * This is temporary; soon we'll read settings from key files. */
+
+ if (CAMEL_IS_SERVICE (service)) {
+ CamelSettings *settings;
+ CamelURL *url;
+
+ settings = camel_service_get_settings (service);
+ url = camel_service_get_camel_url (service);
+ camel_settings_load_from_url (settings, url);
+ }
+
+ return service;
+}
+
+static gchar *
+mail_session_get_password (CamelSession *session,
+ CamelService *service,
+ const gchar *prompt,
+ const gchar *item,
+ guint32 flags,
+ GError **error)
+{
+ EAccount *account = NULL;
+ const gchar *display_name = NULL;
+ const gchar *uid = NULL;
+ gchar *ret = NULL;
+
+ if (CAMEL_IS_SERVICE (service)) {
+ display_name = camel_service_get_display_name (service);
+ uid = camel_service_get_uid (service);
+ account = e_get_account_by_uid (uid);
+ }
+
+ if (!strcmp(item, "popb4smtp_uid")) {
+ /* not 100% mt safe, but should be ok */
+ ret = g_strdup ((account != NULL) ? account->uid : uid);
+ } else {
+ gchar *key = mail_session_make_key (service, item);
+ EAccountService *config_service = NULL;
+
+ ret = e_passwords_get_password (NULL, key);
+ if (ret == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
+ gboolean remember;
+
+ g_free (ret);
+ ret = NULL;
+
+ if (account != NULL) {
+ if (CAMEL_IS_STORE (service))
+ config_service = account->source;
+ if (CAMEL_IS_TRANSPORT (service))
+ config_service = account->transport;
+ }
+
+ remember = config_service ? config_service->save_passwd : FALSE;
+
+ if (!config_service || (config_service &&
+ !config_service->get_password_canceled)) {
+ guint32 eflags;
+ gchar *title;
+
+ if (flags & CAMEL_SESSION_PASSPHRASE) {
+ if (display_name != NULL)
+ title = g_strdup_printf (
+ _("Enter Passphrase for %s"),
+ display_name);
+ else
+ title = g_strdup (
+ _("Enter Passphrase"));
+ } else {
+ if (display_name != NULL)
+ title = g_strdup_printf (
+ _("Enter Password for %s"),
+ display_name);
+ else
+ title = g_strdup (
+ _("Enter Password"));
+ }
+ if ((flags & CAMEL_SESSION_PASSWORD_STATIC) != 0)
+ eflags = E_PASSWORDS_REMEMBER_NEVER;
+ else if (config_service == NULL)
+ eflags = E_PASSWORDS_REMEMBER_SESSION;
+ else
+ eflags = E_PASSWORDS_REMEMBER_FOREVER;
+
+ if (flags & CAMEL_SESSION_PASSWORD_REPROMPT)
+ eflags |= E_PASSWORDS_REPROMPT;
+
+ if (flags & CAMEL_SESSION_PASSWORD_SECRET)
+ eflags |= E_PASSWORDS_SECRET;
+
+ if (flags & CAMEL_SESSION_PASSPHRASE)
+ eflags |= E_PASSWORDS_PASSPHRASE;
+
+ /* HACK: breaks abstraction ...
+ * e_account_writable() doesn't use the
+ * EAccount, it also uses the same writable
+ * key for source and transport. */
+ if (!e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD))
+ eflags |= E_PASSWORDS_DISABLE_REMEMBER;
+
+ ret = e_passwords_ask_password (
+ title, NULL, key, prompt,
+ eflags, &remember, NULL);
+
+ if (!ret)
+ e_passwords_forget_password (NULL, key);
+
+ g_free (title);
+
+ if (ret && config_service) {
+ config_service->save_passwd = remember;
+ e_account_list_save (e_get_account_list ());
+ }
+
+ if (config_service)
+ config_service->get_password_canceled = ret == NULL;
+ }
+ }
+
+ g_free (key);
+ }
+
+ if (ret == NULL)
+ g_set_error (
+ error, G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ _("User canceled operation."));
+
+ return ret;
+}
+
+static gboolean
+mail_session_forget_password (CamelSession *session,
+ CamelService *service,
+ const gchar *item,
+ GError **error)
+{
+ gchar *key;
+
+ key = mail_session_make_key (service, item);
+
+ e_passwords_forget_password (NULL, key);
+
+ g_free (key);
+
+ return TRUE;
+}
+
+static gboolean
+mail_session_alert_user (CamelSession *session,
+ CamelSessionAlertType type,
+ const gchar *prompt,
+ gboolean cancel)
+{
+ struct _user_message_msg *m;
+ GCancellable *cancellable;
+ gboolean result = TRUE;
+
+ m = mail_msg_new (&user_message_info);
+ m->ismain = mail_in_main_thread ();
+ m->type = type;
+ m->prompt = g_strdup (prompt);
+ m->done = e_flag_new ();
+ m->allow_cancel = cancel;
+
+ if (cancel)
+ mail_msg_ref (m);
+
+ cancellable = m->base.cancellable;
+
+ if (m->ismain)
+ user_message_exec (m, cancellable, &m->base.error);
+ else
+ mail_msg_main_loop_push (m);
+
+ if (cancel) {
+ e_flag_wait (m->done);
+ result = m->result;
+ mail_msg_unref (m);
+ } else if (m->ismain)
+ mail_msg_unref (m);
+
+ return result;
+}
+
+static CamelFilterDriver *
+mail_session_get_filter_driver (CamelSession *session,
+ const gchar *type,
+ GError **error)
+{
+ return (CamelFilterDriver *) mail_call_main (
+ MAIL_CALL_p_ppp, (MailMainFunc) main_get_filter_driver,
+ session, type, error);
+}
+
+static gboolean
+mail_session_lookup_addressbook (CamelSession *session,
+ const gchar *name)
+{
+ CamelInternetAddress *addr;
+ gboolean ret;
+
+ if (!mail_config_get_lookup_book ())
+ return FALSE;
+
+ addr = camel_internet_address_new ();
+ camel_address_decode ((CamelAddress *) addr, name);
+ ret = em_utils_in_addressbook (
+ addr, mail_config_get_lookup_book_local_only ());
+ g_object_unref (addr);
+
+ return ret;
+}
+
+static gboolean
+mail_session_forward_to (CamelSession *session,
+ CamelFolder *folder,
+ CamelMimeMessage *message,
+ const gchar *address,
+ GError **error)
+{
+ EAccount *account;
+ CamelMimeMessage *forward;
+ CamelStream *mem;
+ CamelInternetAddress *addr;
+ CamelFolder *out_folder;
+ CamelMessageInfo *info;
+ CamelMedium *medium;
+ const gchar *header_name;
+ struct _camel_header_raw *xev;
+ gchar *subject;
+
+ g_return_val_if_fail (folder != NULL, FALSE);
+ g_return_val_if_fail (message != NULL, FALSE);
+ g_return_val_if_fail (address != NULL, FALSE);
+
+ if (!*address) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("No destination address provided, forward "
+ "of the message has been cancelled."));
+ return FALSE;
+ }
+
+ account = em_utils_guess_account_with_recipients (message, folder);
+ if (!account) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("No account found to use, forward of the "
+ "message has been cancelled."));
+ return FALSE;
+ }
+
+ forward = camel_mime_message_new ();
+
+ /* make copy of the message, because we are going to modify it */
+ mem = camel_stream_mem_new ();
+ camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message), mem, NULL, NULL);
+ g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
+ camel_data_wrapper_construct_from_stream_sync (
+ CAMEL_DATA_WRAPPER (forward), mem, NULL, NULL);
+ g_object_unref (mem);
+
+ /* clear previous recipients */
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_TO, NULL);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_CC, NULL);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_BCC, NULL);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_RESENT_TO, NULL);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_RESENT_CC, NULL);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_RESENT_BCC, NULL);
+
+ medium = CAMEL_MEDIUM (forward);
+
+ /* remove all delivery and notification headers */
+ header_name = "Disposition-Notification-To";
+ while (camel_medium_get_header (medium, header_name))
+ camel_medium_remove_header (medium, header_name);
+
+ header_name = "Delivered-To";
+ while (camel_medium_get_header (medium, header_name))
+ camel_medium_remove_header (medium, header_name);
+
+ /* remove any X-Evolution-* headers that may have been set */
+ xev = mail_tool_remove_xevolution_headers (forward);
+ camel_header_raw_clear (&xev);
+
+ /* from */
+ addr = camel_internet_address_new ();
+ camel_internet_address_add (
+ addr, account->id->name, account->id->address);
+ camel_mime_message_set_from (forward, addr);
+ g_object_unref (addr);
+
+ /* to */
+ addr = camel_internet_address_new ();
+ camel_address_decode (CAMEL_ADDRESS (addr), address);
+ camel_mime_message_set_recipients (
+ forward, CAMEL_RECIPIENT_TYPE_TO, addr);
+ g_object_unref (addr);
+
+ /* subject */
+ subject = mail_tool_generate_forward_subject (message);
+ camel_mime_message_set_subject (forward, subject);
+ g_free (subject);
+
+ /* and send it */
+ info = camel_message_info_new (NULL);
+ out_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+ camel_message_info_set_flags (
+ info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+
+ /* FIXME Pass a GCancellable. */
+ e_mail_folder_append_message (
+ out_folder, forward, info, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) ms_forward_to_cb, session);
+
+ camel_message_info_free (info);
+
+ return TRUE;
+}
+
+static void
+mail_session_get_socks_proxy (CamelSession *session,
+ const gchar *for_host,
+ gchar **host_ret,
+ gint *port_ret)
+{
+ EMailSession *mail_session;
+ gchar *uri;
+
+ g_return_if_fail (session != NULL);
+ g_return_if_fail (for_host != NULL);
+ g_return_if_fail (host_ret != NULL);
+ g_return_if_fail (port_ret != NULL);
+
+ mail_session = E_MAIL_SESSION (session);
+ g_return_if_fail (mail_session != NULL);
+ g_return_if_fail (mail_session->priv != NULL);
+
+ *host_ret = NULL;
+ *port_ret = 0;
+
+ uri = g_strconcat ("socks://", for_host, NULL);
+
+ if (e_proxy_require_proxy_for_uri (mail_session->priv->proxy, uri)) {
+ SoupURI *suri;
+
+ suri = e_proxy_peek_uri_for (mail_session->priv->proxy, uri);
+ if (suri) {
+ *host_ret = g_strdup (suri->host);
+ *port_ret = suri->port;
+ }
+ }
+
+ g_free (uri);
+}
+
+static void
+e_mail_session_class_init (EMailSessionClass *class)
+{
+ GObjectClass *object_class;
+ CamelSessionClass *session_class;
+
+ g_type_class_add_private (class, sizeof (EMailSessionPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = mail_session_set_property;
+ object_class->get_property = mail_session_get_property;
+ object_class->dispose = mail_session_dispose;
+ object_class->finalize = mail_session_finalize;
+ object_class->notify = mail_session_notify;
+ object_class->constructed = mail_session_constructed;
+
+ session_class = CAMEL_SESSION_CLASS (class);
+ session_class->add_service = mail_session_add_service;
+ session_class->get_password = mail_session_get_password;
+ session_class->forget_password = mail_session_forget_password;
+ session_class->alert_user = mail_session_alert_user;
+ session_class->get_filter_driver = mail_session_get_filter_driver;
+ session_class->lookup_addressbook = mail_session_lookup_addressbook;
+ session_class->forward_to = mail_session_forward_to;
+ session_class->get_socks_proxy = mail_session_get_socks_proxy;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FOLDER_CACHE,
+ g_param_spec_object (
+ "folder-cache",
+ NULL,
+ NULL,
+ MAIL_TYPE_FOLDER_CACHE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* XXX This property can be removed once Evolution moves to
+ * GSettings and can use transform functions when binding
+ * properties to settings. */
+ g_object_class_install_property (
+ object_class,
+ PROP_JUNK_FILTER_NAME,
+ g_param_spec_string (
+ "junk-filter-name",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EMailSession::flush-outbox
+ * @session: the email session
+ *
+ * Emitted if the send folder should be flushed.
+ **/
+ signals[FLUSH_OUTBOX] = g_signal_new (
+ "flush-outbox",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * EMailSession::store-added
+ * @session: the email session
+ * @store: the CamelStore
+ *
+ * Emitted when a store is added
+ **/
+ signals[STORE_ADDED] = g_signal_new (
+ "store-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_STORE);
+
+
+ /**
+ * EMailSession::store-removed
+ * @session: the email session
+ * @store: the CamelStore
+ *
+ * Emitted when a store is removed
+ **/
+ signals[STORE_REMOVED] = g_signal_new (
+ "store-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_STORE);
+
+
+}
+
+static void
+e_mail_session_init (EMailSession *session)
+{
+ GConfClient *client;
+
+ session->priv = E_MAIL_SESSION_GET_PRIVATE (session);
+ session->priv->folder_cache = mail_folder_cache_new ();
+ session->priv->junk_filters = g_hash_table_new (
+ (GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
+ session->priv->proxy = e_proxy_new ();
+
+ /* Initialize the EAccount setup. */
+ e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD);
+
+ client = gconf_client_get_default ();
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/junk",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ camel_session_set_check_junk (
+ CAMEL_SESSION (session), gconf_client_get_bool (
+ client, "/apps/evolution/mail/junk/check_incoming", NULL));
+ session_check_junk_notify_id = gconf_client_notify_add (
+ client, "/apps/evolution/mail/junk",
+ (GConfClientNotifyFunc) mail_session_check_junk_notify,
+ session, NULL, NULL);
+
+ mail_config_reload_junk_headers (session);
+
+ e_proxy_setup_proxy (session->priv->proxy);
+
+ g_object_unref (client);
+}
+
+EMailSession *
+e_mail_session_new (void)
+{
+ const gchar *user_data_dir;
+ const gchar *user_cache_dir;
+
+ user_data_dir = mail_session_get_data_dir ();
+ user_cache_dir = mail_session_get_cache_dir ();
+
+ return g_object_new (
+ E_TYPE_MAIL_SESSION,
+ "user-data-dir", user_data_dir,
+ "user-cache-dir", user_cache_dir,
+ NULL);
+}
+
+MailFolderCache *
+e_mail_session_get_folder_cache (EMailSession *session)
+{
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ return session->priv->folder_cache;
+}
+
+GList *
+e_mail_session_get_available_junk_filters (EMailSession *session)
+{
+ GList *list, *link;
+ GQueue trash = G_QUEUE_INIT;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ list = g_hash_table_get_values (session->priv->junk_filters);
+
+ /* Discard unavailable junk filters. (e.g. Junk filter
+ * requires Bogofilter but Bogofilter is not installed,
+ * hence the junk filter is unavailable.) */
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ EMailJunkFilter *junk_filter;
+
+ junk_filter = E_MAIL_JUNK_FILTER (link->data);
+ if (!e_mail_junk_filter_available (junk_filter))
+ g_queue_push_tail (&trash, link);
+ }
+
+ while ((link = g_queue_pop_head (&trash)) != NULL)
+ list = g_list_delete_link (list, link);
+
+ /* Sort the remaining junk filters by display name. */
+
+ return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare);
+}
+
+static void
+mail_session_get_inbox_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->folder = e_mail_session_get_inbox_sync (
+ session, context->uid, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+CamelFolder *
+e_mail_session_get_inbox_sync (EMailSession *session,
+ const gchar *service_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelService *service;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (service_uid != NULL, NULL);
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), service_uid);
+
+ if (!CAMEL_IS_STORE (service))
+ return NULL;
+
+ if (!em_utils_connect_service_sync (service, cancellable, error))
+ return NULL;
+
+ return camel_store_get_inbox_folder_sync (
+ CAMEL_STORE (service), cancellable, error);
+}
+
+void
+e_mail_session_get_inbox (EMailSession *session,
+ const gchar *service_uid,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (service_uid != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->uid = g_strdup (service_uid);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_get_inbox);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_get_inbox_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_get_inbox_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_get_inbox), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+ return g_object_ref (context->folder);
+}
+
+static void
+mail_session_get_trash_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->folder = e_mail_session_get_trash_sync (
+ session, context->uid, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+CamelFolder *
+e_mail_session_get_trash_sync (EMailSession *session,
+ const gchar *service_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelService *service;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (service_uid != NULL, NULL);
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), service_uid);
+
+ if (!CAMEL_IS_STORE (service))
+ return NULL;
+
+ if (!em_utils_connect_service_sync (service, cancellable, error))
+ return NULL;
+
+ return camel_store_get_trash_folder_sync (
+ CAMEL_STORE (service), cancellable, error);
+}
+
+void
+e_mail_session_get_trash (EMailSession *session,
+ const gchar *service_uid,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (service_uid != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->uid = g_strdup (service_uid);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_get_trash);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_get_trash_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_get_trash_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_get_trash), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+ return g_object_ref (context->folder);
+}
+
+static void
+mail_session_uri_to_folder_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->folder = e_mail_session_uri_to_folder_sync (
+ session, context->uri, context->flags,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+CamelFolder *
+e_mail_session_uri_to_folder_sync (EMailSession *session,
+ const gchar *folder_uri,
+ CamelStoreGetFolderFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelStore *store;
+ CamelFolder *folder;
+ gchar *folder_name;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (folder_uri != NULL, NULL);
+
+ success = e_mail_folder_uri_parse (
+ CAMEL_SESSION (session), folder_uri,
+ &store, &folder_name, error);
+
+ if (!success)
+ return NULL;
+
+ folder = camel_store_get_folder_sync (
+ store, folder_name, flags, cancellable, error);
+
+ if (folder != NULL) {
+ MailFolderCache *folder_cache;
+ folder_cache = e_mail_session_get_folder_cache (session);
+ mail_folder_cache_note_folder (folder_cache, folder);
+ }
+
+ g_free (folder_name);
+ g_object_unref (store);
+
+ return folder;
+}
+
+void
+e_mail_session_uri_to_folder (EMailSession *session,
+ const gchar *folder_uri,
+ CamelStoreGetFolderFlags flags,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (folder_uri != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->uri = g_strdup (folder_uri);
+ context->flags = flags;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_uri_to_folder);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_uri_to_folder_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+CamelFolder *
+e_mail_session_uri_to_folder_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_uri_to_folder), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
+
+ return g_object_ref (context->folder);
+}
+
+/******************************** Legacy API *********************************/
+
+void
+mail_session_flush_filter_log (EMailSession *session)
+{
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+ if (session->priv->filter_logfile)
+ fflush (session->priv->filter_logfile);
+}
+
+const gchar *
+mail_session_get_data_dir (void)
+{
+ if (G_UNLIKELY (mail_data_dir == NULL))
+ mail_data_dir = g_build_filename (
+ e_get_user_data_dir (), "mail", NULL);
+
+ return mail_data_dir;
+}
+
+const gchar *
+mail_session_get_cache_dir (void)
+{
+ if (G_UNLIKELY (mail_cache_dir == NULL))
+ mail_cache_dir = g_build_filename (
+ e_get_user_cache_dir (), "mail", NULL);
+
+ return mail_cache_dir;
+}
+
+const gchar *
+mail_session_get_config_dir (void)
+{
+ if (G_UNLIKELY (mail_config_dir == NULL))
+ mail_config_dir = g_build_filename (
+ e_get_user_config_dir (), "mail", NULL);
+
+ return mail_config_dir;
+}
+
diff --git a/libemail-engine/e-mail-session.h b/libemail-engine/e-mail-session.h
new file mode 100644
index 0000000..b82c6e9
--- /dev/null
+++ b/libemail-engine/e-mail-session.h
@@ -0,0 +1,124 @@
+/*
+ * e-mail-session.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_SESSION_H
+#define E_MAIL_SESSION_H
+
+#include <camel/camel.h>
+#include <libemail-engine/mail-folder-cache.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_SESSION \
+ (e_mail_session_get_type ())
+#define E_MAIL_SESSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_SESSION, EMailSession))
+#define E_MAIL_SESSION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_SESSION, EMailSessionClass))
+#define E_IS_MAIL_SESSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_SESSION))
+#define E_IS_MAIL_SESSION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_SESSION))
+#define E_MAIL_SESSION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_SESSION, EMailSessionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailSession EMailSession;
+typedef struct _EMailSessionClass EMailSessionClass;
+typedef struct _EMailSessionPrivate EMailSessionPrivate;
+
+struct _EMailSession {
+ CamelSession parent;
+ EMailSessionPrivate *priv;
+};
+
+struct _EMailSessionClass {
+ CamelSessionClass parent_class;
+};
+
+GType e_mail_session_get_type (void);
+EMailSession * e_mail_session_new (void);
+MailFolderCache *
+ e_mail_session_get_folder_cache (EMailSession *session);
+GList * e_mail_session_get_available_junk_filters
+ (EMailSession *session);
+CamelFolder * e_mail_session_get_inbox_sync (EMailSession *session,
+ const gchar *service_uid,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_get_inbox (EMailSession *session,
+ const gchar *service_uid,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelFolder * e_mail_session_get_inbox_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+CamelFolder * e_mail_session_get_trash_sync (EMailSession *session,
+ const gchar *service_uid,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_get_trash (EMailSession *session,
+ const gchar *service_uid,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelFolder * e_mail_session_get_trash_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+CamelFolder * e_mail_session_uri_to_folder_sync
+ (EMailSession *session,
+ const gchar *folder_uri,
+ CamelStoreGetFolderFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_uri_to_folder (EMailSession *session,
+ const gchar *folder_uri,
+ CamelStoreGetFolderFlags flags,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelFolder * e_mail_session_uri_to_folder_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+
+/*** Legacy API ***/
+
+void mail_session_flush_filter_log (EMailSession *session);
+const gchar * mail_session_get_data_dir (void);
+const gchar * mail_session_get_cache_dir (void);
+const gchar * mail_session_get_config_dir (void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SESSION_H */
diff --git a/libemail-engine/e-mail-store-utils.c b/libemail-engine/e-mail-store-utils.c
new file mode 100644
index 0000000..0d31525
--- /dev/null
+++ b/libemail-engine/e-mail-store-utils.c
@@ -0,0 +1,393 @@
+/*
+ * e-mail-store-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-utils.h"
+
+#include "e-mail-store-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ gchar *full_name;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ g_free (context->full_name);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_store_create_folder_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_store_create_folder_sync (
+ CAMEL_STORE (object), context->full_name,
+ cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_store_create_folder_sync (CamelStore *store,
+ const gchar *full_name,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolderInfo *folder_info;
+ gchar *copied_full_name;
+ gchar *display_name;
+ const gchar *parent;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
+ g_return_val_if_fail (full_name != NULL, FALSE);
+
+ copied_full_name = g_strdup (full_name);
+ display_name = strrchr (copied_full_name, '/');
+ if (display_name == NULL) {
+ display_name = copied_full_name;
+ parent = "";
+ } else {
+ *display_name++ = '\0';
+ parent = copied_full_name;
+ }
+
+ folder_info = camel_store_create_folder_sync (
+ store, parent, display_name, cancellable, error);
+
+ g_free (copied_full_name);
+
+ if (folder_info == NULL)
+ return FALSE;
+
+ if (CAMEL_IS_SUBSCRIBABLE (store))
+ success = camel_subscribable_subscribe_folder_sync (
+ CAMEL_SUBSCRIBABLE (store),
+ full_name, cancellable, error);
+
+ camel_store_free_folder_info (store, folder_info);
+
+ return success;
+}
+
+void
+e_mail_store_create_folder (CamelStore *store,
+ const gchar *full_name,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (full_name != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->full_name = g_strdup (full_name);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback, user_data,
+ e_mail_store_create_folder);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_store_create_folder_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_create_folder_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store),
+ e_mail_store_create_folder), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_go_offline_thread (GSimpleAsyncResult *simple,
+ CamelStore *store,
+ GCancellable *cancellable)
+{
+ CamelService *service;
+ gchar *service_name;
+ GError *error = NULL;
+
+ service = CAMEL_SERVICE (store);
+
+ service_name = camel_service_get_name (service, TRUE);
+ camel_operation_push_message (
+ cancellable, _("Disconnecting from '%s'"), service_name);
+ g_free (service_name);
+
+ if (CAMEL_IS_DISCO_STORE (store)) {
+ CamelDiscoStore *disco_store;
+
+ disco_store = CAMEL_DISCO_STORE (store);
+
+ if (camel_disco_store_can_work_offline (disco_store))
+ camel_disco_store_set_status (
+ disco_store, CAMEL_DISCO_STORE_OFFLINE,
+ cancellable, &error);
+ else
+ em_utils_disconnect_service_sync (service, TRUE, cancellable, &error);
+
+ } else if (CAMEL_IS_OFFLINE_STORE (store)) {
+ CamelOfflineStore *offline_store;
+
+ offline_store = CAMEL_OFFLINE_STORE (store);
+
+ camel_offline_store_set_online_sync (
+ offline_store, FALSE, cancellable, &error);
+
+ } else
+ em_utils_disconnect_service_sync (service, TRUE, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_offline (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ /* Cancel any pending connect first so the set_offline_op
+ * thread won't get queued behind a hung connect op. */
+ camel_service_cancel_connect (CAMEL_SERVICE (store));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback,
+ user_data, e_mail_store_go_offline);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_store_go_offline_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_offline_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store), e_mail_store_go_offline), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_go_online_thread (GSimpleAsyncResult *simple,
+ CamelStore *store,
+ GCancellable *cancellable)
+{
+ CamelService *service;
+ gchar *service_name;
+ GError *error = NULL;
+
+ service = CAMEL_SERVICE (store);
+
+ service_name = camel_service_get_name (service, TRUE);
+ camel_operation_push_message (
+ cancellable, _("Reconnecting to '%s'"), service_name);
+ g_free (service_name);
+
+ if (CAMEL_IS_DISCO_STORE (store))
+ camel_disco_store_set_status (
+ CAMEL_DISCO_STORE (store),
+ CAMEL_DISCO_STORE_ONLINE,
+ cancellable, &error);
+
+ else if (CAMEL_IS_OFFLINE_STORE (store))
+ camel_offline_store_set_online_sync (
+ CAMEL_OFFLINE_STORE (store),
+ TRUE, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_online (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback,
+ user_data, e_mail_store_go_online);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_store_go_online_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_online_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store), e_mail_store_go_online), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_prepare_for_offline_thread (GSimpleAsyncResult *simple,
+ CamelStore *store,
+ GCancellable *cancellable)
+{
+ CamelService *service;
+ gchar *service_name;
+ GError *error = NULL;
+
+ service = CAMEL_SERVICE (store);
+
+ service_name = camel_service_get_name (service, TRUE);
+ camel_operation_push_message (
+ cancellable, _("Preparing account '%s' for offline"),
+ service_name);
+ g_free (service_name);
+
+ if (CAMEL_IS_DISCO_STORE (store))
+ camel_disco_store_prepare_for_offline (
+ CAMEL_DISCO_STORE (store), cancellable, &error);
+
+ else if (CAMEL_IS_OFFLINE_STORE (store))
+ camel_offline_store_prepare_for_offline_sync (
+ CAMEL_OFFLINE_STORE (store), cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_prepare_for_offline (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback, user_data,
+ e_mail_store_prepare_for_offline);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_store_prepare_for_offline_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_prepare_for_offline_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store),
+ e_mail_store_prepare_for_offline), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/libemail-engine/e-mail-store-utils.h b/libemail-engine/e-mail-store-utils.h
new file mode 100644
index 0000000..de4484c
--- /dev/null
+++ b/libemail-engine/e-mail-store-utils.h
@@ -0,0 +1,74 @@
+/*
+ * e-mail-store-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_STORE_UTILS_H
+#define E_MAIL_STORE_UTILS_H
+
+/* CamelStore wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+gboolean e_mail_store_create_folder_sync (CamelStore *store,
+ const gchar *full_name,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_store_create_folder (CamelStore *store,
+ const gchar *full_name,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_create_folder_finish
+ (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+void e_mail_store_go_offline (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_go_offline_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+void e_mail_store_go_online (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_go_online_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+void e_mail_store_prepare_for_offline
+ (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_prepare_for_offline_finish
+ (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_STORE_UTILS_H */
diff --git a/libemail-engine/e-mail-store.c b/libemail-engine/e-mail-store.c
new file mode 100644
index 0000000..db5b94e
--- /dev/null
+++ b/libemail-engine/e-mail-store.c
@@ -0,0 +1,464 @@
+/*
+ * e-mail-store.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-store.h"
+
+#include <glib/gi18n.h>
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libedataserver/e-account-list.h>
+
+#include "libemail-utils/e-account-utils.h"
+
+#include "libemail-engine/e-mail-local.h"
+#include "libemail-engine/e-mail-utils.h"
+#include "libemail-engine/mail-folder-cache.h"
+#include "libemail-utils/mail-mt.h"
+#include "libemail-engine/mail-ops.h"
+
+typedef struct _StoreInfo StoreInfo;
+
+typedef void (*AddStoreCallback) (MailFolderCache *folder_cache,
+ CamelStore *store,
+ CamelFolderInfo *info,
+ StoreInfo *store_info);
+
+struct _StoreInfo {
+ gint ref_count;
+
+ CamelStore *store;
+
+ /* Hold a reference to keep them alive. */
+ CamelFolder *vtrash;
+ CamelFolder *vjunk;
+
+ AddStoreCallback callback;
+
+ guint removed : 1;
+};
+
+CamelStore *vfolder_store; /* XXX write a get () function for this */
+static GHashTable *store_table;
+
+static StoreInfo *
+store_info_new (CamelStore *store)
+{
+ StoreInfo *store_info;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+
+ store_info = g_slice_new0 (StoreInfo);
+ store_info->ref_count = 1;
+
+ store_info->store = g_object_ref (store);
+
+ /* If these are vfolders then they need to be opened now,
+ * otherwise they won't keep track of all folders. */
+ if (store->flags & CAMEL_STORE_VTRASH)
+ store_info->vtrash =
+ camel_store_get_trash_folder_sync (store, NULL, NULL);
+ if (store->flags & CAMEL_STORE_VJUNK)
+ store_info->vjunk =
+ camel_store_get_junk_folder_sync (store, NULL, NULL);
+
+ return store_info;
+}
+
+static StoreInfo *
+store_info_ref (StoreInfo *store_info)
+{
+ g_return_val_if_fail (store_info != NULL, store_info);
+ g_return_val_if_fail (store_info->ref_count > 0, store_info);
+
+ g_atomic_int_inc (&store_info->ref_count);
+
+ return store_info;
+}
+
+static void
+store_info_unref (StoreInfo *store_info)
+{
+ g_return_if_fail (store_info != NULL);
+ g_return_if_fail (store_info->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&store_info->ref_count)) {
+
+ g_object_unref (store_info->store);
+
+ if (store_info->vtrash != NULL)
+ g_object_unref (store_info->vtrash);
+
+ if (store_info->vjunk != NULL)
+ g_object_unref (store_info->vjunk);
+
+ g_slice_free (StoreInfo, store_info);
+ }
+}
+
+static void
+store_table_free (StoreInfo *store_info)
+{
+ store_info->removed = 1;
+ store_info_unref (store_info);
+}
+
+static gboolean
+mail_store_note_store_cb (MailFolderCache *folder_cache,
+ CamelStore *store,
+ CamelFolderInfo *info,
+ gpointer user_data)
+{
+ StoreInfo *store_info = user_data;
+
+ if (store_info->callback != NULL)
+ store_info->callback (
+ folder_cache, store, info, store_info);
+
+ if (!store_info->removed) {
+ /* This keeps message counters up-to-date. */
+ if (store_info->vtrash != NULL)
+ mail_folder_cache_note_folder (
+ folder_cache, store_info->vtrash);
+ if (store_info->vjunk != NULL)
+ mail_folder_cache_note_folder (
+ folder_cache, store_info->vjunk);
+ }
+
+ store_info_unref (store_info);
+
+ return TRUE;
+}
+
+static gboolean
+special_mail_store_is_enabled (CamelStore *store)
+{
+ CamelService *service;
+ const gchar *uid, *prop = NULL;
+
+ service = CAMEL_SERVICE (store);
+ g_return_val_if_fail (service, FALSE);
+
+ uid = camel_service_get_uid (service);
+ if (g_strcmp0 (uid, "local") == 0)
+ prop = "mail-enable-local-folders";
+ else if (g_strcmp0 (uid, "vfolder") == 0)
+ prop = "mail-enable-search-folders";
+
+ if (!prop)
+ return TRUE;
+
+
+ /* FIXME: Make this possible*/
+ //shell = e_shell_get_default ();
+ //shell_settings = e_shell_get_shell_settings (shell);
+
+ //return e_shell_settings_get_boolean (shell_settings, prop);
+ return FALSE;
+}
+
+static void
+mail_store_add (EMailSession *session,
+ CamelStore *store,
+ AddStoreCallback callback)
+{
+ MailFolderCache *folder_cache;
+ StoreInfo *store_info;
+
+ g_return_if_fail (store_table != NULL);
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ folder_cache = e_mail_session_get_folder_cache (session);
+
+ store_info = store_info_new (store);
+ store_info->callback = callback;
+
+ g_hash_table_insert (store_table, store, store_info);
+
+ if (special_mail_store_is_enabled (store)) {
+ /* Listen to this in evolution and add to the folder tree model. */
+ g_signal_emit_by_name (session, "store-added", store);
+ }
+
+ mail_folder_cache_note_store (
+ folder_cache, CAMEL_SESSION (session), store, NULL,
+ mail_store_note_store_cb, store_info_ref (store_info));
+}
+
+static void
+mail_store_add_local_done_cb (MailFolderCache *folder_cache,
+ CamelStore *store,
+ CamelFolderInfo *info,
+ StoreInfo *store_info)
+{
+ CamelFolder *folder;
+ gint ii;
+
+ for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
+ folder = e_mail_local_get_folder (ii);
+ if (folder == NULL)
+ continue;
+ mail_folder_cache_note_folder (folder_cache, folder);
+ }
+}
+
+static void
+mail_store_load_accounts (EMailSession *session,
+ const gchar *data_dir)
+{
+ CamelStore *local_store;
+ EAccountList *account_list;
+ EIterator *iter;
+
+ /* Add the local store. */
+
+ e_mail_local_init (session, data_dir);
+ local_store = e_mail_local_get_store ();
+
+ mail_store_add (
+ session, local_store, (AddStoreCallback)
+ mail_store_add_local_done_cb);
+
+ /* Add mail accounts.. */
+
+ account_list = e_get_account_list ();
+
+ for (iter = e_list_get_iterator ((EList *) account_list);
+ e_iterator_is_valid (iter); e_iterator_next (iter)) {
+ EAccount *account;
+
+ account = (EAccount *) e_iterator_get (iter);
+
+ if (!account->enabled)
+ continue;
+
+ e_mail_store_add_by_account (session, account);
+ }
+
+ g_object_unref (iter);
+}
+
+void
+e_mail_store_init (EMailSession *session,
+ const gchar *data_dir)
+{
+ static gboolean initialized = FALSE;
+
+ /* This function is idempotent because mail
+ * migration code may need to call it early. */
+ if (initialized)
+ return;
+
+ /* Initialize global variables. */
+
+ store_table = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) store_table_free);
+
+ mail_store_load_accounts (session, data_dir);
+
+ initialized = TRUE;
+}
+
+void
+e_mail_store_add (EMailSession *session,
+ CamelStore *store)
+{
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ mail_store_add (session, store, NULL);
+}
+
+CamelStore *
+e_mail_store_add_by_account (EMailSession *session,
+ EAccount *account)
+{
+ CamelService *service = NULL;
+ CamelProvider *provider = NULL;
+ CamelURL *url;
+ gboolean skip = FALSE, transport_only;
+ GError *error = NULL;
+
+ g_return_val_if_fail (E_IS_ACCOUNT (account), NULL);
+
+ /* check whether it's transport-only accounts */
+ transport_only = !account->source || !account->source->url || !*account->source->url;
+ if (transport_only)
+ goto handle_transport;
+
+ /* Load the service, but don't connect. Check its provider,
+ * and if this belongs in the folder tree model, add it. */
+
+ provider = camel_provider_get (account->source->url, &error);
+ if (provider == NULL) {
+ /* In case we do not have a provider here, we handle
+ * the special case of having multiple mail identities
+ * eg. a dummy account having just SMTP server defined */
+ goto handle_transport;
+ }
+
+ service = camel_session_add_service (
+ CAMEL_SESSION (session),
+ account->uid, account->source->url,
+ CAMEL_PROVIDER_STORE, &error);
+
+ camel_service_set_display_name (service, account->name);
+
+handle_transport:
+
+ if (account->transport) {
+ /* While we're at it, add the account's transport to the
+ * CamelSession. The transport's UID is a kludge for now.
+ * We take the EAccount's UID and tack on "-transport". */
+ gchar *transport_uid;
+ GError *transport_error = NULL;
+
+ transport_uid = g_strconcat (
+ account->uid, "-transport", NULL);
+
+ camel_session_add_service (
+ CAMEL_SESSION (session),
+ transport_uid, account->transport->url,
+ CAMEL_PROVIDER_TRANSPORT, &transport_error);
+
+ g_free (transport_uid);
+
+ if (transport_error) {
+ g_warning (
+ "%s: Failed to add transport service: %s",
+ G_STRFUNC, transport_error->message);
+ g_error_free (transport_error);
+ }
+ }
+
+ if (transport_only)
+ return NULL;
+
+ if (!CAMEL_IS_STORE (service))
+ goto fail;
+
+ /* Do not add local-delivery files,
+ * but make them ready for later use. */
+ url = camel_url_new (account->source->url, NULL);
+ if (url != NULL) {
+ skip = em_utils_is_local_delivery_mbox_file (url);
+ camel_url_free (url);
+ }
+
+ if (!skip && (provider->flags & CAMEL_PROVIDER_IS_STORAGE))
+ e_mail_store_add (session, CAMEL_STORE (service));
+
+ return CAMEL_STORE (service);
+
+fail:
+ /* FIXME: Show an error dialog. */
+ g_warning (
+ "Couldn't get service: %s: %s", account->name,
+ error ? error->message : "Not a CamelStore");
+ if (error)
+ g_error_free (error);
+
+ return NULL;
+}
+
+void
+e_mail_store_remove (EMailSession *session,
+ CamelStore *store)
+{
+ MailFolderCache *folder_cache;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (store_table != NULL);
+
+ /* Because the store table holds a reference to each store used
+ * as a key in it, none of them will ever be gc'ed, meaning any
+ * call to camel_session_get_{service,store} with the same URL
+ * will always return the same object. So this works. */
+
+ if (g_hash_table_lookup (store_table, store) == NULL)
+ return;
+
+ g_object_ref (store);
+
+ g_hash_table_remove (store_table, store);
+
+ folder_cache = e_mail_session_get_folder_cache (session);
+ mail_folder_cache_note_store_remove (folder_cache, store);
+
+ g_signal_emit_by_name (session, "store-removed", store);
+
+ mail_disconnect_store (store);
+
+ g_object_unref (store);
+}
+
+void
+e_mail_store_remove_by_account (EMailSession *session,
+ EAccount *account)
+{
+ CamelService *service;
+ CamelProvider *provider;
+ const gchar *uid;
+
+ g_return_if_fail (E_IS_ACCOUNT (account));
+
+ uid = account->uid;
+
+ service = camel_session_get_service (CAMEL_SESSION (session), uid);
+ g_return_if_fail (CAMEL_IS_STORE (service));
+
+ provider = camel_service_get_provider (service);
+ g_return_if_fail (provider != NULL);
+
+ if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE))
+ return;
+
+ e_mail_store_remove (session, CAMEL_STORE (service));
+}
+
+void
+e_mail_store_foreach (EMailSession *session,
+ GFunc func,
+ gpointer user_data)
+{
+ GList *list, *link;
+
+ /* XXX This is a silly convenience function.
+ * Could probably just get rid of it. */
+
+ g_return_if_fail (func != NULL);
+
+ list = camel_session_list_services (CAMEL_SESSION (session));
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ CamelService *service = CAMEL_SERVICE (link->data);
+
+ if (CAMEL_IS_STORE (service))
+ func (service, user_data);
+ }
+
+ g_list_free (list);
+}
diff --git a/libemail-engine/e-mail-store.h b/libemail-engine/e-mail-store.h
new file mode 100644
index 0000000..a60c252
--- /dev/null
+++ b/libemail-engine/e-mail-store.h
@@ -0,0 +1,47 @@
+/*
+ * e-mail-store.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_MAIL_STORE_H
+#define E_MAIL_STORE_H
+
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+void e_mail_store_init (EMailSession *session,
+ const gchar *data_dir);
+void e_mail_store_add (EMailSession *session,
+ CamelStore *store);
+CamelStore * e_mail_store_add_by_account (EMailSession *session,
+ EAccount *account);
+void e_mail_store_remove (EMailSession *session,
+ CamelStore *store);
+void e_mail_store_remove_by_account (EMailSession *session,
+ EAccount *account);
+void e_mail_store_foreach (EMailSession *session,
+ GFunc func,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* E_MAIL_STORE_H */
diff --git a/libemail-engine/e-mail-utils.c b/libemail-engine/e-mail-utils.c
new file mode 100644
index 0000000..27f492a
--- /dev/null
+++ b/libemail-engine/e-mail-utils.c
@@ -0,0 +1,1109 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan gnome org>
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+/* Work around namespace clobbage in <windows.h> */
+#define DATADIR windows_DATADIR
+#include <windows.h>
+#undef DATADIR
+#undef interface
+#endif
+
+#include <libebook/e-book-client.h>
+#include <libebook/e-book-query.h>
+
+
+#include <glib/gi18n.h>
+
+#include <gio/gio.h>
+
+#include "libemail-utils/mail-mt.h"
+#include "mail-tools.h"
+#include "e-mail-folder-utils.h"
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-flag.h>
+#include <libedataserver/e-proxy.h>
+#include "libemail-utils/e-account-utils.h"
+
+#include "e-mail-utils.h"
+#include "e-mail-local.h"
+
+#define d(x)
+
+/**
+ * em_utils_folder_is_templates:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Templates folder.
+ *
+ * Returns %TRUE if this is a Templates folder or %FALSE otherwise.
+ **/
+
+gboolean
+em_utils_folder_is_templates (CamelFolder *folder)
+{
+ CamelFolder *local_templates_folder;
+ CamelSession *session;
+ CamelStore *store;
+ EAccountList *account_list;
+ EIterator *iterator;
+ gchar *folder_uri;
+ gboolean is_templates = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ local_templates_folder =
+ e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_TEMPLATES);
+
+ if (folder == local_templates_folder)
+ return TRUE;
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+
+ store = camel_folder_get_parent_store (folder);
+ session = camel_service_get_session (CAMEL_SERVICE (store));
+
+ account_list = e_get_account_list ();
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (!is_templates && e_iterator_is_valid (iterator)) {
+ EAccount *account;
+
+ /* XXX EIterator misuses const. */
+ account = (EAccount *) e_iterator_get (iterator);
+
+ if (account->templates_folder_uri != NULL)
+ is_templates = e_mail_folder_uri_equal (
+ session, folder_uri,
+ account->templates_folder_uri);
+
+ e_iterator_next (iterator);
+ }
+
+ g_object_unref (iterator);
+ g_free (folder_uri);
+
+ return is_templates;
+}
+
+/**
+ * em_utils_folder_is_drafts:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Drafts folder.
+ *
+ * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_drafts (CamelFolder *folder)
+{
+ CamelFolder *local_drafts_folder;
+ CamelSession *session;
+ CamelStore *store;
+ EAccountList *account_list;
+ EIterator *iterator;
+ gchar *folder_uri;
+ gboolean is_drafts = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ local_drafts_folder =
+ e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_DRAFTS);
+
+ if (folder == local_drafts_folder)
+ return TRUE;
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+
+ store = camel_folder_get_parent_store (folder);
+ session = camel_service_get_session (CAMEL_SERVICE (store));
+
+ account_list = e_get_account_list ();
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (!is_drafts && e_iterator_is_valid (iterator)) {
+ EAccount *account;
+
+ /* XXX EIterator misuses const. */
+ account = (EAccount *) e_iterator_get (iterator);
+
+ if (account->drafts_folder_uri != NULL)
+ is_drafts = e_mail_folder_uri_equal (
+ session, folder_uri,
+ account->drafts_folder_uri);
+
+ e_iterator_next (iterator);
+ }
+
+ g_object_unref (iterator);
+ g_free (folder_uri);
+
+ return is_drafts;
+}
+
+/**
+ * em_utils_folder_is_sent:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is a Sent folder.
+ *
+ * Returns %TRUE if this is a Sent folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_sent (CamelFolder *folder)
+{
+ CamelFolder *local_sent_folder;
+ CamelSession *session;
+ CamelStore *store;
+ EAccountList *account_list;
+ EIterator *iterator;
+ gchar *folder_uri;
+ gboolean is_sent = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ local_sent_folder =
+ e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+ if (folder == local_sent_folder)
+ return TRUE;
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+
+ store = camel_folder_get_parent_store (folder);
+ session = camel_service_get_session (CAMEL_SERVICE (store));
+
+ account_list = e_get_account_list ();
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (!is_sent && e_iterator_is_valid (iterator)) {
+ EAccount *account;
+
+ /* XXX EIterator misuses const. */
+ account = (EAccount *) e_iterator_get (iterator);
+
+ if (account->sent_folder_uri != NULL)
+ is_sent = e_mail_folder_uri_equal (
+ session, folder_uri,
+ account->sent_folder_uri);
+
+ e_iterator_next (iterator);
+ }
+
+ g_object_unref (iterator);
+ g_free (folder_uri);
+
+ return is_sent;
+}
+
+/**
+ * em_utils_folder_is_outbox:
+ * @folder: a #CamelFolder
+ *
+ * Decides if @folder is an Outbox folder.
+ *
+ * Returns %TRUE if this is an Outbox folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_outbox (CamelFolder *folder)
+{
+ CamelFolder *local_outbox_folder;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ local_outbox_folder =
+ e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+
+ return (folder == local_outbox_folder);
+}
+
+
+/* ********************************************************************** */
+
+/* runs sync, in main thread */
+static gpointer
+emu_addr_setup (gpointer user_data)
+{
+ GError *err = NULL;
+ ESourceList **psource_list = user_data;
+
+ if (!e_book_client_get_sources (psource_list, &err))
+ g_error_free (err);
+
+ return NULL;
+}
+
+static void
+emu_addr_cancel_stop (gpointer data)
+{
+ gboolean *stop = data;
+
+ g_return_if_fail (stop != NULL);
+
+ *stop = TRUE;
+}
+
+static void
+emu_addr_cancel_cancellable (gpointer data)
+{
+ GCancellable *cancellable = data;
+
+ g_return_if_fail (cancellable != NULL);
+
+ g_cancellable_cancel (cancellable);
+}
+
+struct TryOpenEBookStruct {
+ GError **error;
+ EFlag *flag;
+ gboolean result;
+};
+
+static void
+try_open_book_client_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer closure)
+{
+ EBookClient *book_client = E_BOOK_CLIENT (source_object);
+ struct TryOpenEBookStruct *data = (struct TryOpenEBookStruct *) closure;
+ GError *error = NULL;
+
+ if (!data)
+ return;
+
+ e_client_open_finish (E_CLIENT (book_client), result, &error);
+
+ data->result = error == NULL;
+
+ if (!data->result) {
+ g_clear_error (data->error);
+ g_propagate_error (data->error, error);
+ }
+
+ e_flag_set (data->flag);
+}
+
+/*
+ * try_open_book_client:
+ * Tries to open address book asynchronously, but acts as synchronous.
+ * The advantage is it checks periodically whether the camel_operation
+ * has been canceled or not, and if so, then stops immediately, with
+ * result FALSE. Otherwise returns same as e_client_open()
+ */
+static gboolean
+try_open_book_client (EBookClient *book_client,
+ gboolean only_if_exists,
+ GCancellable *cancellable,
+ GError **error)
+{
+ struct TryOpenEBookStruct data;
+ gboolean canceled = FALSE;
+ EFlag *flag = e_flag_new ();
+
+ data.error = error;
+ data.flag = flag;
+ data.result = FALSE;
+
+ e_client_open (
+ E_CLIENT (book_client), only_if_exists,
+ cancellable, try_open_book_client_cb, &data);
+
+ while (canceled = camel_operation_cancel_check (NULL),
+ !canceled && !e_flag_is_set (flag)) {
+ GTimeVal wait;
+
+ g_get_current_time (&wait);
+ g_time_val_add (&wait, 250000); /* waits 250ms */
+
+ e_flag_timed_wait (flag, &wait);
+ }
+
+ if (canceled) {
+ g_cancellable_cancel (cancellable);
+
+ g_clear_error (error);
+ g_propagate_error (
+ error, e_client_error_create (
+ E_CLIENT_ERROR_CANCELLED, NULL));
+ }
+
+ e_flag_wait (flag);
+ e_flag_free (flag);
+
+ return data.result && (!error || !*error);
+}
+
+#define NOT_FOUND_BOOK (GINT_TO_POINTER (1))
+
+G_LOCK_DEFINE_STATIC (contact_cache);
+
+/* key is lowercased contact email; value is EBook pointer
+ * (just for comparison) where it comes from */
+static GHashTable *contact_cache = NULL;
+
+/* key is source ID; value is pointer to EBook */
+static GHashTable *emu_books_hash = NULL;
+
+/* key is source ID; value is same pointer as key; this is hash of
+ * broken books, which failed to open for some reason */
+static GHashTable *emu_broken_books_hash = NULL;
+
+static ESourceList *emu_books_source_list = NULL;
+
+static gboolean
+search_address_in_addressbooks (const gchar *address,
+ gboolean local_only,
+ gboolean (*check_contact) (EContact *contact,
+ gpointer user_data),
+ gpointer user_data)
+{
+ gboolean found = FALSE, stop = FALSE, found_any = FALSE;
+ gchar *lowercase_addr;
+ gpointer ptr;
+ EBookQuery *book_query;
+ gchar *query;
+ GSList *s, *g, *addr_sources = NULL;
+ GHook *hook_cancellable;
+ GCancellable *cancellable;
+
+ if (!address || !*address)
+ return FALSE;
+
+ G_LOCK (contact_cache);
+
+ if (!emu_books_source_list) {
+ mail_call_main (
+ MAIL_CALL_p_p, (MailMainFunc)
+ emu_addr_setup, &emu_books_source_list);
+ emu_books_hash = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, g_object_unref);
+ emu_broken_books_hash = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, NULL);
+ contact_cache = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, NULL);
+ }
+
+ if (!emu_books_source_list) {
+ G_UNLOCK (contact_cache);
+ return FALSE;
+ }
+
+ lowercase_addr = g_utf8_strdown (address, -1);
+ ptr = g_hash_table_lookup (contact_cache, lowercase_addr);
+ if (ptr != NULL && (check_contact == NULL || ptr == NOT_FOUND_BOOK)) {
+ g_free (lowercase_addr);
+ G_UNLOCK (contact_cache);
+ return ptr != NOT_FOUND_BOOK;
+ }
+
+ book_query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, address);
+ query = e_book_query_to_string (book_query);
+ e_book_query_unref (book_query);
+
+ for (g = e_source_list_peek_groups (emu_books_source_list);
+ g; g = g_slist_next (g)) {
+ ESourceGroup *group = g->data;
+
+ if (!group)
+ continue;
+
+ if (local_only && !(e_source_group_peek_base_uri (group) &&
+ g_str_has_prefix (
+ e_source_group_peek_base_uri (group), "local:")))
+ continue;
+
+ for (s = e_source_group_peek_sources (group); s; s = g_slist_next (s)) {
+ ESource *source = s->data;
+ const gchar *completion = e_source_get_property (source, "completion");
+
+ if (completion && g_ascii_strcasecmp (completion, "true") == 0) {
+ addr_sources = g_slist_prepend (addr_sources, g_object_ref (source));
+ }
+ }
+ }
+
+ cancellable = g_cancellable_new ();
+ hook_cancellable = mail_cancel_hook_add (emu_addr_cancel_cancellable, cancellable);
+
+ for (s = addr_sources; !stop && !found && s; s = g_slist_next (s)) {
+ ESource *source = s->data;
+ GSList *contacts;
+ EBookClient *book_client = NULL;
+ GHook *hook_stop;
+ gboolean cached_book = FALSE;
+ GError *err = NULL;
+
+ /* failed to load this book last time, skip it now */
+ if (g_hash_table_lookup (emu_broken_books_hash,
+ e_source_peek_uid (source)) != NULL) {
+ d(printf ("%s: skipping broken book '%s'\n",
+ G_STRFUNC, e_source_peek_name (source)));
+ continue;
+ }
+
+ d(printf(" checking '%s'\n", e_source_get_uri(source)));
+
+ hook_stop = mail_cancel_hook_add (emu_addr_cancel_stop, &stop);
+
+ book_client = g_hash_table_lookup (emu_books_hash, e_source_peek_uid (source));
+ if (!book_client) {
+ book_client = e_book_client_new (source, &err);
+
+ if (book_client == NULL) {
+ if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+ stop = TRUE;
+ } else if (err) {
+ gchar *source_uid;
+
+ source_uid = g_strdup (
+ e_source_peek_uid (source));
+
+ g_hash_table_insert (
+ emu_broken_books_hash,
+ source_uid, source_uid);
+
+ g_warning (
+ "%s: Unable to create addressbook '%s': %s",
+ G_STRFUNC,
+ e_source_peek_name (source),
+ err->message);
+ }
+ g_clear_error (&err);
+ } else if (!stop && !try_open_book_client (book_client, TRUE, cancellable, &err)) {
+ g_object_unref (book_client);
+ book_client = NULL;
+
+ if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+ stop = TRUE;
+ } else if (err) {
+ gchar *source_uid;
+
+ source_uid = g_strdup (
+ e_source_peek_uid (source));
+
+ g_hash_table_insert (
+ emu_broken_books_hash,
+ source_uid, source_uid);
+
+ g_warning (
+ "%s: Unable to open addressbook '%s': %s",
+ G_STRFUNC,
+ e_source_peek_name (source),
+ err->message);
+ }
+ g_clear_error (&err);
+ }
+ } else {
+ cached_book = TRUE;
+ }
+
+ if (book_client && !stop && e_book_client_get_contacts_sync (book_client, query, &contacts, cancellable, &err)) {
+ if (contacts != NULL) {
+ if (!found_any) {
+ g_hash_table_insert (contact_cache, g_strdup (lowercase_addr), book_client);
+ }
+ found_any = TRUE;
+
+ if (check_contact) {
+ GSList *l;
+
+ for (l = contacts; l && !found; l = l->next) {
+ EContact *contact = l->data;
+
+ found = check_contact (contact, user_data);
+ }
+ } else {
+ found = TRUE;
+ }
+
+ g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
+ g_slist_free (contacts);
+ }
+ } else if (book_client) {
+ stop = stop || (err &&
+ (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)));
+ if (err && !stop) {
+ gchar *source_uid = g_strdup (e_source_peek_uid (source));
+
+ g_hash_table_insert (emu_broken_books_hash, source_uid, source_uid);
+
+ g_warning (
+ "%s: Can't get contacts from '%s': %s",
+ G_STRFUNC, e_source_peek_name (source),
+ err->message);
+ }
+ g_clear_error (&err);
+ }
+
+ mail_cancel_hook_remove (hook_stop);
+
+ stop = stop || camel_operation_cancel_check (NULL);
+
+ if (stop && !cached_book && book_client) {
+ g_object_unref (book_client);
+ } else if (!stop && book_client && !cached_book) {
+ g_hash_table_insert (
+ emu_books_hash, g_strdup (
+ e_source_peek_uid (source)), book_client);
+ }
+ }
+
+ mail_cancel_hook_remove (hook_cancellable);
+ g_object_unref (cancellable);
+
+ g_slist_foreach (addr_sources, (GFunc) g_object_unref, NULL);
+ g_slist_free (addr_sources);
+
+ g_free (query);
+
+ if (!found_any) {
+ g_hash_table_insert (contact_cache, lowercase_addr, NOT_FOUND_BOOK);
+ lowercase_addr = NULL;
+ }
+
+ G_UNLOCK (contact_cache);
+
+ g_free (lowercase_addr);
+
+ return found_any;
+}
+
+gboolean
+em_utils_in_addressbook (CamelInternetAddress *iaddr,
+ gboolean local_only)
+{
+ const gchar *addr;
+
+ /* TODO: check all addresses? */
+ if (iaddr == NULL || !camel_internet_address_get (iaddr, 0, NULL, &addr))
+ return FALSE;
+
+ return search_address_in_addressbooks (addr, local_only, NULL, NULL);
+}
+
+static gboolean
+extract_photo_data (EContact *contact,
+ gpointer user_data)
+{
+ EContactPhoto **photo = user_data;
+
+ g_return_val_if_fail (contact != NULL, FALSE);
+ g_return_val_if_fail (user_data != NULL, FALSE);
+
+ *photo = e_contact_get (contact, E_CONTACT_PHOTO);
+ if (!*photo)
+ *photo = e_contact_get (contact, E_CONTACT_LOGO);
+
+ return *photo != NULL;
+}
+
+typedef struct _PhotoInfo {
+ gchar *address;
+ EContactPhoto *photo;
+} PhotoInfo;
+
+static void
+emu_free_photo_info (PhotoInfo *pi)
+{
+ if (!pi)
+ return;
+
+ if (pi->address)
+ g_free (pi->address);
+ if (pi->photo)
+ e_contact_photo_free (pi->photo);
+ g_free (pi);
+}
+
+G_LOCK_DEFINE_STATIC (photos_cache);
+static GSList *photos_cache = NULL; /* list of PhotoInfo-s */
+
+CamelMimePart *
+em_utils_contact_photo (CamelInternetAddress *cia,
+ gboolean local_only)
+{
+ const gchar *addr = NULL;
+ CamelMimePart *part = NULL;
+ EContactPhoto *photo = NULL;
+ GSList *p, *first_not_null = NULL;
+ gint count_not_null = 0;
+
+ if (cia == NULL || !camel_internet_address_get (cia, 0, NULL, &addr) || !addr) {
+ return NULL;
+ }
+
+ G_LOCK (photos_cache);
+
+ /* search a cache first */
+ for (p = photos_cache; p; p = p->next) {
+ PhotoInfo *pi = p->data;
+
+ if (!pi)
+ continue;
+
+ if (pi->photo) {
+ if (!first_not_null)
+ first_not_null = p;
+ count_not_null++;
+ }
+
+ if (g_ascii_strcasecmp (addr, pi->address) == 0) {
+ photo = pi->photo;
+ break;
+ }
+ }
+
+ /* !p means the address had not been found in the cache */
+ if (!p && search_address_in_addressbooks (
+ addr, local_only, extract_photo_data, &photo)) {
+ PhotoInfo *pi;
+
+ if (photo && photo->type != E_CONTACT_PHOTO_TYPE_INLINED) {
+ e_contact_photo_free (photo);
+ photo = NULL;
+ }
+
+ /* keep only up to 10 photos in memory */
+ if (photo && count_not_null >= 10 && first_not_null) {
+ pi = first_not_null->data;
+
+ photos_cache = g_slist_remove (photos_cache, pi);
+
+ emu_free_photo_info (pi);
+ }
+
+ pi = g_new0 (PhotoInfo, 1);
+ pi->address = g_strdup (addr);
+ pi->photo = photo;
+
+ photos_cache = g_slist_append (photos_cache, pi);
+ }
+
+ /* some photo found, use it */
+ if (photo) {
+ /* Form a mime part out of the photo */
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content (part,
+ (const gchar *) photo->data.inlined.data,
+ photo->data.inlined.length, "image/jpeg");
+ }
+
+ G_UNLOCK (photos_cache);
+
+ return part;
+}
+
+/* list of email addresses (strings) to remove from local cache of photos and
+ * contacts, but only if the photo doesn't exist or is an not-found contact */
+void
+emu_remove_from_mail_cache (const GSList *addresses)
+{
+ const GSList *a;
+ GSList *p;
+ CamelInternetAddress *cia;
+
+ cia = camel_internet_address_new ();
+
+ for (a = addresses; a; a = a->next) {
+ const gchar *addr = NULL;
+
+ if (!a->data)
+ continue;
+
+ if (camel_address_decode ((CamelAddress *) cia, a->data) != -1 &&
+ camel_internet_address_get (cia, 0, NULL, &addr) && addr) {
+ gchar *lowercase_addr = g_utf8_strdown (addr, -1);
+
+ G_LOCK (contact_cache);
+ if (g_hash_table_lookup (contact_cache, lowercase_addr) == NOT_FOUND_BOOK)
+ g_hash_table_remove (contact_cache, lowercase_addr);
+ G_UNLOCK (contact_cache);
+
+ g_free (lowercase_addr);
+
+ G_LOCK (photos_cache);
+ for (p = photos_cache; p; p = p->next) {
+ PhotoInfo *pi = p->data;
+
+ if (pi && !pi->photo && g_ascii_strcasecmp (pi->address, addr) == 0) {
+ photos_cache = g_slist_remove (photos_cache, pi);
+ emu_free_photo_info (pi);
+ break;
+ }
+ }
+ G_UNLOCK (photos_cache);
+ }
+ }
+
+ g_object_unref (cia);
+}
+
+void
+emu_remove_from_mail_cache_1 (const gchar *address)
+{
+ GSList *l;
+
+ g_return_if_fail (address != NULL);
+
+ l = g_slist_append (NULL, (gpointer) address);
+
+ emu_remove_from_mail_cache (l);
+
+ g_slist_free (l);
+}
+
+/* frees all data created by call of em_utils_in_addressbook() or
+ * em_utils_contact_photo() */
+void
+emu_free_mail_cache (void)
+{
+ G_LOCK (contact_cache);
+
+ if (emu_books_hash) {
+ g_hash_table_destroy (emu_books_hash);
+ emu_books_hash = NULL;
+ }
+
+ if (emu_broken_books_hash) {
+ g_hash_table_destroy (emu_broken_books_hash);
+ emu_broken_books_hash = NULL;
+ }
+
+ if (emu_books_source_list) {
+ g_object_unref (emu_books_source_list);
+ emu_books_source_list = NULL;
+ }
+
+ if (contact_cache) {
+ g_hash_table_destroy (contact_cache);
+ contact_cache = NULL;
+ }
+
+ G_UNLOCK (contact_cache);
+
+ G_LOCK (photos_cache);
+
+ g_slist_foreach (photos_cache, (GFunc) emu_free_photo_info, NULL);
+ g_slist_free (photos_cache);
+ photos_cache = NULL;
+
+ G_UNLOCK (photos_cache);
+}
+
+static EAccount *
+guess_account_from_folder (CamelFolder *folder)
+{
+ CamelStore *store;
+ const gchar *uid;
+
+ store = camel_folder_get_parent_store (folder);
+ uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+ return e_get_account_by_uid (uid);
+}
+
+static EAccount *
+guess_account_from_message (CamelMimeMessage *message)
+{
+ const gchar *uid;
+
+ uid = camel_mime_message_get_source (message);
+
+ return (uid != NULL) ? e_get_account_by_uid (uid) : NULL;
+}
+
+GHashTable *
+em_utils_generate_account_hash (void)
+{
+ GHashTable *account_hash;
+ EAccount *account, *def;
+ EAccountList *account_list;
+ EIterator *iterator;
+
+ account_list = e_get_account_list ();
+ account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+
+ def = e_get_default_account ();
+
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (e_iterator_is_valid (iterator)) {
+ account = (EAccount *) e_iterator_get (iterator);
+
+ if (account->id->address) {
+ EAccount *acnt;
+
+ /* Accounts with identical email addresses that are
+ * enabled take precedence over the accounts that
+ * aren't. If all accounts with matching email
+ * addresses are disabled, then the first one in
+ * the list takes precedence. The default account
+ * always takes precedence no matter what. */
+ acnt = g_hash_table_lookup (
+ account_hash, account->id->address);
+ if (acnt && acnt != def && !acnt->enabled && account->enabled) {
+ g_hash_table_remove (
+ account_hash, acnt->id->address);
+ acnt = NULL;
+ }
+
+ if (!acnt)
+ g_hash_table_insert (
+ account_hash, (gchar *)
+ account->id->address,
+ (gpointer) account);
+ }
+
+ e_iterator_next (iterator);
+ }
+
+ g_object_unref (iterator);
+
+ /* The default account has to be there if none
+ * of the enabled accounts are present. */
+ if (g_hash_table_size (account_hash) == 0 && def && def->id->address)
+ g_hash_table_insert (
+ account_hash, (gchar *)
+ def->id->address,
+ (gpointer) def);
+
+ return account_hash;
+}
+
+EAccount *
+em_utils_guess_account (CamelMimeMessage *message,
+ CamelFolder *folder)
+{
+ EAccount *account = NULL;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+ if (folder != NULL)
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ /* check for newsgroup header */
+ if (folder != NULL
+ && camel_medium_get_header (CAMEL_MEDIUM (message), "Newsgroups"))
+ account = guess_account_from_folder (folder);
+
+ /* check for source folder */
+ if (account == NULL && folder != NULL)
+ account = guess_account_from_folder (folder);
+
+ /* then message source */
+ if (account == NULL)
+ account = guess_account_from_message (message);
+
+ return account;
+}
+
+EAccount *
+em_utils_guess_account_with_recipients (CamelMimeMessage *message,
+ CamelFolder *folder)
+{
+ EAccount *account = NULL;
+ EAccountList *account_list;
+ GHashTable *recipients;
+ EIterator *iterator;
+ CamelInternetAddress *addr;
+ const gchar *type;
+ const gchar *key;
+
+ /* This policy is subject to debate and tweaking,
+ * but please also document the rational here. */
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+ /* Build a set of email addresses in which to test for membership.
+ * Only the keys matter here; the values just need to be non-NULL. */
+ recipients = g_hash_table_new (g_str_hash, g_str_equal);
+
+ type = CAMEL_RECIPIENT_TYPE_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ if (addr != NULL) {
+ gint index = 0;
+
+ while (camel_internet_address_get (addr, index++, NULL, &key))
+ g_hash_table_insert (
+ recipients, (gpointer) key,
+ GINT_TO_POINTER (1));
+ }
+
+ type = CAMEL_RECIPIENT_TYPE_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ if (addr != NULL) {
+ gint index = 0;
+
+ while (camel_internet_address_get (addr, index++, NULL, &key))
+ g_hash_table_insert (
+ recipients, (gpointer) key,
+ GINT_TO_POINTER (1));
+ }
+
+ /* First Preference: We were given a folder that maps to an
+ * enabled account, and that account's email address appears
+ * in the list of To: or Cc: recipients. */
+
+ if (folder != NULL)
+ account = guess_account_from_folder (folder);
+
+ if (account == NULL || !account->enabled)
+ goto second_preference;
+
+ if ((key = account->id->address) == NULL)
+ goto second_preference;
+
+ if (g_hash_table_lookup (recipients, key) != NULL)
+ goto exit;
+
+second_preference:
+
+ /* Second Preference: Choose any enabled account whose email
+ * address appears in the list to To: or Cc: recipients. */
+
+ account_list = e_get_account_list ();
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (e_iterator_is_valid (iterator)) {
+ account = (EAccount *) e_iterator_get (iterator);
+ e_iterator_next (iterator);
+
+ if (account == NULL || !account->enabled)
+ continue;
+
+ if ((key = account->id->address) == NULL)
+ continue;
+
+ if (g_hash_table_lookup (recipients, key) != NULL) {
+ g_object_unref (iterator);
+ goto exit;
+ }
+ }
+ g_object_unref (iterator);
+
+ /* Last Preference: Defer to em_utils_guess_account(). */
+ account = em_utils_guess_account (message, folder);
+
+exit:
+ g_hash_table_destroy (recipients);
+
+ return account;
+}
+
+static void
+cancel_service_connect_cb (GCancellable *cancellable,
+ CamelService *service)
+{
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ camel_service_cancel_connect (service);
+}
+
+gboolean
+em_utils_connect_service_sync (CamelService *service,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean res;
+ gulong handler_id = 0;
+
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+ if (cancellable != NULL)
+ handler_id = g_cancellable_connect (
+ cancellable,
+ G_CALLBACK (cancel_service_connect_cb),
+ service, NULL);
+
+ res = camel_service_connect_sync (service, error);
+
+ if (handler_id)
+ g_cancellable_disconnect (cancellable, handler_id);
+
+ return res;
+}
+
+gboolean
+em_utils_disconnect_service_sync (CamelService *service,
+ gboolean clean,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean res;
+ gulong handler_id = 0;
+
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+ if (cancellable != NULL)
+ handler_id = g_cancellable_connect (
+ cancellable,
+ G_CALLBACK (cancel_service_connect_cb),
+ service, NULL);
+
+ res = camel_service_disconnect_sync (service, clean, error);
+
+ if (handler_id)
+ g_cancellable_disconnect (cancellable, handler_id);
+
+ return res;
+}
+
+/**
+ * em_utils_uids_free:
+ * @uids: array of uids
+ *
+ * Frees the array of uids pointed to by @uids back to the system.
+ **/
+void
+em_utils_uids_free (GPtrArray *uids)
+{
+ gint i;
+
+ for (i = 0; i < uids->len; i++)
+ g_free (uids->pdata[i]);
+
+ g_ptr_array_free (uids, TRUE);
+}
+
+/* Returns TRUE if CamelURL points to a local mbox file. */
+gboolean
+em_utils_is_local_delivery_mbox_file (CamelURL *url)
+{
+ g_return_val_if_fail (url != NULL, FALSE);
+
+ return g_str_equal (url->protocol, "mbox") &&
+ (url->path != NULL) &&
+ g_file_test (url->path, G_FILE_TEST_EXISTS) &&
+ !g_file_test (url->path, G_FILE_TEST_IS_DIR);
+}
+
diff --git a/libemail-engine/e-mail-utils.h b/libemail-engine/e-mail-utils.h
new file mode 100644
index 0000000..62fdcf0
--- /dev/null
+++ b/libemail-engine/e-mail-utils.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Srinivasa Ragavan <sragavan gnome org>
+ *
+ *
+ */
+
+#ifndef __E_MAIL_UTILS_H__
+#define __E_MAIL_UTILS_H__
+
+#include <sys/types.h>
+#include <camel/camel.h>
+
+gboolean em_utils_folder_is_drafts (CamelFolder *folder);
+gboolean em_utils_folder_is_templates (CamelFolder *folder);
+gboolean em_utils_folder_is_sent (CamelFolder *folder);
+gboolean em_utils_folder_is_outbox (CamelFolder *folder);
+
+gboolean em_utils_in_addressbook (CamelInternetAddress *addr, gboolean local_only);
+CamelMimePart *em_utils_contact_photo (CamelInternetAddress *addr, gboolean local);
+
+GHashTable * em_utils_generate_account_hash (void);
+struct _EAccount *em_utils_guess_account (CamelMimeMessage *message, CamelFolder *folder);
+struct _EAccount *em_utils_guess_account_with_recipients (CamelMimeMessage *message, CamelFolder *folder);
+
+void emu_remove_from_mail_cache (const GSList *addresses);
+void emu_remove_from_mail_cache_1 (const gchar *address);
+void emu_free_mail_cache (void);
+
+gboolean em_utils_connect_service_sync (CamelService *service, GCancellable *cancellable, GError **error);
+gboolean em_utils_disconnect_service_sync (CamelService *service, gboolean clean, GCancellable *cancellable, GError **error);
+
+void em_utils_uids_free (GPtrArray *uids);
+gboolean em_utils_is_local_delivery_mbox_file (CamelURL *url);
+
+#endif
diff --git a/libemail-engine/libemail-engine.pc.in b/libemail-engine/libemail-engine.pc.in
new file mode 100644
index 0000000..006a9ce
--- /dev/null
+++ b/libemail-engine/libemail-engine.pc.in
@@ -0,0 +1,15 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+privincludedir= privincludedir@
+
+Name: libemail-utils
+Description: Client library for evolution mail
+Version: @VERSION@
+Requires: camel-1.2 libedataserver-1.2 gio-2.0
+Libs: -L${libdir} -lemail-engine
+Cflags: -I${privincludedir}
diff --git a/libemail-engine/mail-config.c b/libemail-engine/mail-config.c
new file mode 100644
index 0000000..47c147d
--- /dev/null
+++ b/libemail-engine/mail-config.c
@@ -0,0 +1,367 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj ximian com>
+ * Radek Doulik <rodo ximian com>
+ * Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include "libemail-utils/e-account-utils.h"
+#include "libemail-utils/e-signature-list.h"
+#include "libemail-utils/e-signature-utils.h"
+#include "libemail-engine/e-mail-folder-utils.h"
+
+#include <gconf/gconf-client.h>
+
+#include "e-mail-local.h"
+#include "mail-config.h"
+#include "mail-tools.h"
+
+typedef struct {
+ GSList *labels;
+
+ gboolean address_compress;
+ gint address_count;
+
+ GSList *jh_header;
+ gboolean jh_check;
+ gboolean book_lookup;
+ gboolean book_lookup_local_only;
+} MailConfig;
+
+extern gint camel_header_param_encode_filenames_in_rfc_2047;
+
+static MailConfig *config = NULL;
+
+static void
+gconf_outlook_filenames_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ const gchar *key;
+
+ g_return_if_fail (client != NULL);
+
+ key = "/apps/evolution/mail/composer/outlook_filenames";
+
+ /* pass option to the camel */
+ if (gconf_client_get_bool (client, key, NULL))
+ camel_header_param_encode_filenames_in_rfc_2047 = 1;
+ else
+ camel_header_param_encode_filenames_in_rfc_2047 = 0;
+}
+
+static void
+gconf_jh_headers_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ EMailSession *session)
+{
+ GSList *node;
+ GPtrArray *name, *value;
+
+ g_slist_foreach (config->jh_header, (GFunc) g_free, NULL);
+ g_slist_free (config->jh_header);
+
+ config->jh_header = gconf_client_get_list (
+ client, "/apps/evolution/mail/junk/custom_header",
+ GCONF_VALUE_STRING, NULL);
+
+ node = config->jh_header;
+ name = g_ptr_array_new ();
+ value = g_ptr_array_new ();
+ while (node && node->data) {
+ gchar **tok = g_strsplit (node->data, "=", 2);
+ g_ptr_array_add (name, g_strdup (tok[0]));
+ g_ptr_array_add (value, g_strdup (tok[1]));
+ node = node->next;
+ g_strfreev (tok);
+ }
+ camel_session_set_junk_headers (
+ CAMEL_SESSION (session),
+ (const gchar **) name->pdata,
+ (const gchar **) value->pdata, name->len);
+
+ g_ptr_array_foreach (name, (GFunc) g_free, NULL);
+ g_ptr_array_foreach (value, (GFunc) g_free, NULL);
+ g_ptr_array_free (name, TRUE);
+ g_ptr_array_free (value, TRUE);
+}
+
+static void
+gconf_jh_check_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ EMailSession *session)
+{
+ config->jh_check = gconf_client_get_bool (
+ client, "/apps/evolution/mail/junk/check_custom_header", NULL);
+ if (!config->jh_check) {
+ camel_session_set_junk_headers (
+ CAMEL_SESSION (session), NULL, NULL, 0);
+ } else {
+ gconf_jh_headers_changed (client, 0, NULL, session);
+ }
+}
+
+static void
+gconf_bool_value_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gboolean *save_location)
+{
+ GError *error = NULL;
+
+ *save_location = gconf_client_get_bool (client, entry->key, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+gconf_int_value_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gint *save_location)
+{
+ GError *error = NULL;
+
+ *save_location = gconf_client_get_int (client, entry->key, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+void
+mail_config_write (void)
+{
+ GConfClient *client;
+ EAccountList *account_list;
+ ESignatureList *signature_list;
+
+ if (!config)
+ return;
+
+ account_list = e_get_account_list ();
+ signature_list = e_get_signature_list ();
+
+ e_account_list_save (account_list);
+ e_signature_list_save (signature_list);
+
+ client = gconf_client_get_default ();
+ gconf_client_suggest_sync (client, NULL);
+ g_object_unref (client);
+}
+
+gint
+mail_config_get_address_count (void)
+{
+ if (!config->address_compress)
+ return -1;
+
+ return config->address_count;
+}
+
+/* timeout interval, in seconds, when to call server update */
+gint
+mail_config_get_sync_timeout (void)
+{
+ GConfClient *client;
+ gint res = 60;
+ GError *error = NULL;
+
+ client = gconf_client_get_default ();
+
+ res = gconf_client_get_int (
+ client, "/apps/evolution/mail/sync_interval", &error);
+
+ /* do not allow recheck sooner than every 30 seconds */
+ if (error || res == 0)
+ res = 60;
+ else if (res < 30)
+ res = 30;
+
+ if (error)
+ g_error_free (error);
+
+ return res;
+}
+
+gchar *
+mail_config_folder_to_cachename (CamelFolder *folder,
+ const gchar *prefix)
+{
+ gchar *folder_uri, *basename, *filename;
+ const gchar *config_dir;
+
+ config_dir = mail_session_get_config_dir ();
+
+ basename = g_build_filename (config_dir, "folders", NULL);
+ if (!g_file_test (basename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
+ /* create the folder if does not exist */
+ g_mkdir_with_parents (basename, 0700);
+ }
+ g_free (basename);
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+ e_filename_make_safe (folder_uri);
+ basename = g_strdup_printf ("%s%s", prefix, folder_uri);
+ filename = g_build_filename (config_dir, "folders", basename, NULL);
+ g_free (basename);
+ g_free (folder_uri);
+
+ return filename;
+}
+
+void
+mail_config_reload_junk_headers (EMailSession *session)
+{
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+ /* It automatically sets in the session */
+ if (config == NULL)
+ mail_config_init (session);
+ else {
+ GConfClient *client;
+
+ client = gconf_client_get_default ();
+ gconf_jh_check_changed (client, 0, NULL, session);
+ g_object_unref (client);
+ }
+}
+
+gboolean
+mail_config_get_lookup_book (void)
+{
+ g_return_val_if_fail (config != NULL, FALSE);
+
+ return config->book_lookup;
+}
+
+gboolean
+mail_config_get_lookup_book_local_only (void)
+{
+ g_return_val_if_fail (config != NULL, FALSE);
+
+ return config->book_lookup_local_only;
+}
+
+/* Config struct routines */
+void
+mail_config_init (EMailSession *session)
+{
+ GConfClient *client;
+ GConfClientNotifyFunc func;
+ const gchar *key;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+
+ if (config)
+ return;
+
+ config = g_new0 (MailConfig, 1);
+
+ client = gconf_client_get_default ();
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/prompts",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ /* Composer Configuration */
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/composer",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ key = "/apps/evolution/mail/composer/outlook_filenames";
+ func = (GConfClientNotifyFunc) gconf_outlook_filenames_changed;
+ gconf_outlook_filenames_changed (client, 0, NULL, NULL);
+ gconf_client_notify_add (client, key, func, NULL, NULL, NULL);
+
+ /* Display Configuration */
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/display",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ key = "/apps/evolution/mail/display/address_compress";
+ func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+ gconf_client_notify_add (
+ client, key, func,
+ &config->address_compress, NULL, NULL);
+ config->address_compress = gconf_client_get_bool (client, key, NULL);
+
+ key = "/apps/evolution/mail/display/address_count";
+ func = (GConfClientNotifyFunc) gconf_int_value_changed;
+ gconf_client_notify_add (
+ client, key, func,
+ &config->address_count, NULL, NULL);
+ config->address_count = gconf_client_get_int (client, key, NULL);
+
+ /* Font Configuration */
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/display/fonts",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ /* Junk Configuration */
+
+ gconf_client_add_dir (
+ client, "/apps/evolution/mail/junk",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ key = "/apps/evolution/mail/junk/check_custom_header";
+ func = (GConfClientNotifyFunc) gconf_jh_check_changed;
+ gconf_client_notify_add (client, key, func, session, NULL, NULL);
+ config->jh_check = gconf_client_get_bool (client, key, NULL);
+
+ key = "/apps/evolution/mail/junk/custom_header";
+ func = (GConfClientNotifyFunc) gconf_jh_headers_changed;
+ gconf_client_notify_add (client, key, func, session, NULL, NULL);
+
+ key = "/apps/evolution/mail/junk/lookup_addressbook";
+ func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+ gconf_client_notify_add (
+ client, key, func,
+ &config->book_lookup, NULL, NULL);
+ config->book_lookup = gconf_client_get_bool (client, key, NULL);
+
+ key = "/apps/evolution/mail/junk/lookup_addressbook_local_only";
+ func = (GConfClientNotifyFunc) gconf_bool_value_changed;
+ gconf_client_notify_add (
+ client, key, func,
+ &config->book_lookup_local_only, NULL, NULL);
+ config->book_lookup_local_only =
+ gconf_client_get_bool (client, key, NULL);
+
+ gconf_jh_check_changed (client, 0, NULL, session);
+
+ g_object_unref (client);
+}
diff --git a/libemail-engine/mail-config.h b/libemail-engine/mail-config.h
new file mode 100644
index 0000000..0a1c618
--- /dev/null
+++ b/libemail-engine/mail-config.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_CONFIG_H
+#define MAIL_CONFIG_H
+
+#include <libemail-engine/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+/* Configuration */
+void mail_config_init (EMailSession *session);
+void mail_config_write (void);
+
+/* General Accessor functions */
+
+gint mail_config_get_address_count (void);
+
+/* static utility functions */
+gchar * mail_config_folder_to_cachename (CamelFolder *folder,
+ const gchar *prefix);
+gint mail_config_get_sync_timeout (void);
+
+void mail_config_reload_junk_headers (EMailSession *session);
+gboolean mail_config_get_lookup_book (void);
+gboolean mail_config_get_lookup_book_local_only (void);
+
+G_END_DECLS
+
+#endif /* MAIL_CONFIG_H */
diff --git a/libemail-engine/mail-folder-cache.c b/libemail-engine/mail-folder-cache.c
new file mode 100644
index 0000000..07761a6
--- /dev/null
+++ b/libemail-engine/mail-folder-cache.c
@@ -0,0 +1,1424 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Peter Williams <peterw ximian com>
+ * Michael Zucchi <notzed ximian com>
+ * Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+/**
+ * SECTION: mail-folder-cache
+ * @short_description: Stores information about open folders
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libemail-utils/e-marshal.h>
+
+#include "libemail-utils/mail-mt.h"
+#include "mail-folder-cache.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "e-mail-utils.h"
+#include "e-mail-folder-utils.h"
+#include "e-mail-local.h"
+#include "e-mail-store-utils.h"
+#include "mail-config.h"
+
+#define w(x)
+#define d(x)
+
+/* This code is a mess, there is no reason it should be so complicated. */
+
+struct _MailFolderCachePrivate {
+ /* source id for the ping timeout callback */
+ guint ping_id;
+ /* Store to storeinfo table, active stores */
+ GHashTable *stores;
+ /* mutex to protect access to the stores hash */
+ GMutex *stores_mutex;
+ /* List of folder changes to be executed in gui thread */
+ GQueue updates;
+ /* idle source id for flushing all pending updates */
+ guint update_id;
+ /* hack for people who LIKE to have unsent count */
+ gint count_sent;
+ gint count_trash;
+};
+
+enum {
+ FOLDER_AVAILABLE,
+ FOLDER_UNAVAILABLE,
+ FOLDER_DELETED,
+ FOLDER_RENAMED,
+ FOLDER_UNREAD_UPDATED,
+ FOLDER_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _folder_info {
+ struct _store_info *store_info; /* 'parent' link */
+
+ gchar *full_name; /* full name of folder/folderinfo */
+
+ guint32 flags;
+ gboolean has_children;
+
+ gpointer folder; /* if known (weak pointer) */
+ guint timeout;
+};
+
+/* pending list of updates */
+struct _folder_update {
+ guint remove:1; /* removing from vfolders */
+ guint delete:1; /* deleting as well? */
+ guint add:1; /* add to vfolder */
+ guint unsub:1; /* unsubcribing? */
+ guint new; /* new mail arrived? */
+
+ gchar *full_name;
+ gchar *oldfull;
+
+ gint unread;
+ CamelStore *store;
+
+ /* for only one new message... */
+ gchar *msg_uid; /* ... its uid ... */
+ gchar *msg_sender; /* ... its sender ... */
+ gchar *msg_subject; /* ... and its subject. */
+};
+
+struct _store_info {
+ GHashTable *folders; /* by full_name */
+ CamelStore *store; /* the store for these folders */
+
+ /* Outstanding folderinfo requests */
+ GQueue folderinfo_updates;
+};
+
+G_DEFINE_TYPE (MailFolderCache, mail_folder_cache, G_TYPE_OBJECT)
+
+static void
+free_update (struct _folder_update *up)
+{
+ g_free (up->full_name);
+ if (up->store)
+ g_object_unref (up->store);
+ g_free (up->oldfull);
+ g_free (up->msg_uid);
+ g_free (up->msg_sender);
+ g_free (up->msg_subject);
+ g_free (up);
+}
+
+static gboolean
+flush_updates_idle_cb (MailFolderCache *self)
+{
+ struct _folder_update *up;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ while ((up = g_queue_pop_head (&self->priv->updates)) != NULL) {
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ if (up->remove) {
+ if (up->delete) {
+ g_signal_emit (
+ self, signals[FOLDER_DELETED], 0,
+ up->store, up->full_name);
+ } else
+ g_signal_emit (
+ self, signals[FOLDER_UNAVAILABLE], 0,
+ up->store, up->full_name);
+ } else {
+ if (up->oldfull && up->add) {
+ g_signal_emit (
+ self, signals[FOLDER_RENAMED], 0,
+ up->store, up->oldfull, up->full_name);
+ }
+
+ if (!up->oldfull && up->add)
+ g_signal_emit (
+ self, signals[FOLDER_AVAILABLE], 0,
+ up->store, up->full_name);
+ }
+
+ /* update unread counts */
+ g_signal_emit (self, signals[FOLDER_UNREAD_UPDATED], 0,
+ up->store, up->full_name, up->unread);
+
+ /* indicate that the folder has changed (new mail received, etc) */
+ if (up->store != NULL && up->full_name != NULL) {
+ g_signal_emit (
+ self, signals[FOLDER_CHANGED], 0, up->store,
+ up->full_name, up->new, up->msg_uid,
+ up->msg_sender, up->msg_subject);
+ }
+
+ if (CAMEL_IS_VEE_STORE (up->store) && !up->remove) {
+ /* Normally the vfolder store takes care of the
+ * folder_opened event itself, but we add folder to
+ * the noting system later, thus we do not know about
+ * search folders to update them in a tree, thus
+ * ensure their changes will be tracked correctly. */
+ CamelFolder *folder;
+
+ /* FIXME camel_store_get_folder_sync() may block. */
+ folder = camel_store_get_folder_sync (
+ up->store, up->full_name, 0, NULL, NULL);
+
+ if (folder) {
+ mail_folder_cache_note_folder (self, folder);
+ g_object_unref (folder);
+ }
+ }
+
+ free_update (up);
+
+ g_mutex_lock (self->priv->stores_mutex);
+ }
+ self->priv->update_id = 0;
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ return FALSE;
+}
+
+static void
+flush_updates (MailFolderCache *self)
+{
+ if (self->priv->update_id > 0)
+ return;
+
+ if (g_queue_is_empty (&self->priv->updates))
+ return;
+
+ self->priv->update_id = g_idle_add (
+ (GSourceFunc) flush_updates_idle_cb, self);
+}
+
+/* This is how unread counts work (and don't work):
+ *
+ * camel_folder_unread_message_count() only gives a correct answer if
+ * the store is paying attention to the folder. (Some stores always
+ * pay attention to all folders, but IMAP can only pay attention to
+ * one folder at a time.) But it doesn't have any way to know when
+ * it's lying, so it's only safe to call it when you know for sure
+ * that the store is paying attention to the folder, such as when it's
+ * just been created, or you get a folder_changed signal on it.
+ *
+ * camel_store_get_folder_info() always gives correct answers for the
+ * folders it checks, but it can also return -1 for a folder, meaning
+ * it didn't check, and so you should stick with your previous answer.
+ *
+ * update_1folder is called from three places: with info != NULL when
+ * the folder is created (or get_folder_info), with info == NULL when
+ * a folder changed event is emitted.
+ *
+ * So if info is NULL, camel_folder_unread_message_count is correct,
+ * and if it's not NULL and its unread_message_count isn't -1, then
+ * it's correct. */
+
+static void
+update_1folder (MailFolderCache *self,
+ struct _folder_info *mfi,
+ gint new,
+ const gchar *msg_uid,
+ const gchar *msg_sender,
+ const gchar *msg_subject,
+ CamelFolderInfo *info)
+{
+ struct _folder_update *up;
+ CamelFolder *folder;
+ gint unread = -1;
+ gint deleted;
+
+ folder = mfi->folder;
+ if (folder) {
+ gboolean folder_is_sent;
+ gboolean folder_is_drafts;
+ gboolean folder_is_outbox;
+ gboolean folder_is_vtrash;
+ gboolean special_case;
+
+ folder_is_sent = em_utils_folder_is_sent (folder);
+ folder_is_drafts = em_utils_folder_is_drafts (folder);
+ folder_is_outbox = em_utils_folder_is_outbox (folder);
+ folder_is_vtrash = CAMEL_IS_VTRASH_FOLDER (folder);
+
+ special_case =
+ (self->priv->count_trash && folder_is_vtrash) ||
+ (self->priv->count_sent && folder_is_sent) ||
+ folder_is_drafts || folder_is_outbox;
+
+ if (special_case) {
+ d(printf(" total count\n"));
+ unread = camel_folder_get_message_count (folder);
+ if (folder_is_drafts || folder_is_outbox) {
+ guint32 junked = 0;
+
+ if ((deleted = camel_folder_get_deleted_message_count (folder)) > 0)
+ unread -= deleted;
+
+ junked = folder->summary->junk_count;
+ if (junked > 0)
+ unread -= junked;
+ }
+ } else {
+ d(printf(" unread count\n"));
+ if (info)
+ unread = info->unread;
+ else
+ unread = camel_folder_get_unread_message_count (folder);
+ }
+ }
+
+ d(printf("folder updated: unread %d: '%s'\n", unread, mfi->full_name));
+
+ if (unread == -1)
+ return;
+
+ up = g_malloc0 (sizeof (*up));
+ up->full_name = g_strdup (mfi->full_name);
+ up->unread = unread;
+ up->new = new;
+ up->store = g_object_ref (mfi->store_info->store);
+ up->msg_uid = g_strdup (msg_uid);
+ up->msg_sender = g_strdup (msg_sender);
+ up->msg_subject = g_strdup (msg_subject);
+ g_queue_push_tail (&self->priv->updates, up);
+ flush_updates (self);
+}
+
+static void
+mail_sync_folder_done (CamelFolder *folder, gpointer data)
+{
+ /* We don't have to do anything here as of now */
+}
+
+static gboolean
+mail_folder_sync (struct _folder_info *mfi)
+{
+ mail_sync_folder (mfi->folder, mail_sync_folder_done, mfi);
+ mfi->timeout = 0;
+ return FALSE;
+}
+
+static void
+folder_changed_cb (CamelFolder *folder,
+ CamelFolderChangeInfo *changes,
+ MailFolderCache *self)
+{
+ static GHashTable *last_newmail_per_folder = NULL;
+ time_t latest_received, new_latest_received;
+ CamelFolder *local_drafts;
+ CamelFolder *local_outbox;
+ CamelFolder *local_sent;
+ CamelStore *parent_store;
+ CamelMessageInfo *info;
+ struct _store_info *si;
+ struct _folder_info *mfi;
+ const gchar *full_name;
+ gint new = 0;
+ gint i;
+ guint32 flags;
+ gchar *uid = NULL, *sender = NULL, *subject = NULL;
+ gboolean sync_changes = FALSE;
+
+ full_name = camel_folder_get_full_name (folder);
+ parent_store = camel_folder_get_parent_store (folder);
+
+ if (!last_newmail_per_folder)
+ last_newmail_per_folder = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ /* it's fine to hash them by folder pointer here */
+ latest_received = GPOINTER_TO_INT (
+ g_hash_table_lookup (last_newmail_per_folder, folder));
+ new_latest_received = latest_received;
+
+ local_drafts = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_DRAFTS);
+ local_outbox = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+ local_sent = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+ if (!CAMEL_IS_VEE_FOLDER (folder)
+ && folder != local_drafts
+ && folder != local_outbox
+ && folder != local_sent
+ && changes && (changes->uid_added->len > 0)) {
+ /* for each added message, check to see that it is
+ * brand new, not junk and not already deleted */
+ for (i = 0; i < changes->uid_added->len; i++) {
+ info = camel_folder_get_message_info (
+ folder, changes->uid_added->pdata[i]);
+ if (info) {
+ flags = camel_message_info_flags (info);
+ if (((flags & CAMEL_MESSAGE_SEEN) == 0) &&
+ ((flags & CAMEL_MESSAGE_JUNK) == 0) &&
+ ((flags & CAMEL_MESSAGE_DELETED) == 0) &&
+ (camel_message_info_date_received (info) > latest_received)) {
+ if (camel_message_info_date_received (info) > new_latest_received)
+ new_latest_received = camel_message_info_date_received (info);
+ new++;
+ if (new == 1) {
+ uid = g_strdup (camel_message_info_uid (info));
+ sender = g_strdup (camel_message_info_from (info));
+ subject = g_strdup (camel_message_info_subject (info));
+ } else {
+ g_free (uid);
+ g_free (sender);
+ g_free (subject);
+
+ uid = NULL;
+ sender = NULL;
+ subject = NULL;
+ }
+ }
+ camel_folder_free_message_info (folder, info);
+ }
+ }
+ } else if (!CAMEL_IS_VEE_FOLDER(folder)
+ && folder != local_drafts
+ && folder != local_outbox
+ && folder != local_sent
+ && changes && changes->uid_changed && changes->uid_changed->len > 0) {
+ /* We must sync this back. */
+ sync_changes = TRUE;
+ }
+
+ if (new > 0)
+ g_hash_table_insert (
+ last_newmail_per_folder, folder,
+ GINT_TO_POINTER (new_latest_received));
+
+ g_mutex_lock (self->priv->stores_mutex);
+ if (self->priv->stores != NULL
+ && (si = g_hash_table_lookup (self->priv->stores, parent_store)) != NULL
+ && (mfi = g_hash_table_lookup (si->folders, full_name)) != NULL
+ && mfi->folder == folder) {
+ update_1folder (self, mfi, new, uid, sender, subject, NULL);
+ if (sync_changes == TRUE) {
+ if (mfi->timeout) {
+ g_source_remove (mfi->timeout);
+ }
+ mfi->timeout = g_timeout_add_seconds (mail_config_get_sync_timeout (),
+ (GSourceFunc)mail_folder_sync,
+ mfi);
+
+ }
+ }
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ g_free (uid);
+ g_free (sender);
+ g_free (subject);
+}
+
+static void
+unset_folder_info (MailFolderCache *self,
+ struct _folder_info *mfi,
+ gint delete,
+ gint unsub)
+{
+ struct _folder_update *up;
+
+ d(printf("unset folderinfo '%s'\n", mfi->uri));
+
+ if (mfi->folder) {
+ CamelFolder *folder = mfi->folder;
+
+ g_signal_handlers_disconnect_by_func (
+ folder, folder_changed_cb, self);
+
+ g_object_remove_weak_pointer (
+ G_OBJECT (mfi->folder), &mfi->folder);
+ }
+
+ if ((mfi->flags & CAMEL_FOLDER_NOSELECT) == 0) {
+ up = g_malloc0 (sizeof (*up));
+
+ up->remove = TRUE;
+ up->delete = delete;
+ up->unsub = unsub;
+ up->store = g_object_ref (mfi->store_info->store);
+ up->full_name = g_strdup (mfi->full_name);
+
+ g_queue_push_tail (&self->priv->updates, up);
+ flush_updates (self);
+ }
+}
+
+static void
+free_folder_info (struct _folder_info *mfi)
+{
+ g_free (mfi->full_name);
+ g_source_remove (mfi->timeout);
+ g_free (mfi);
+}
+
+static void
+setup_folder (MailFolderCache *self,
+ CamelFolderInfo *fi,
+ struct _store_info *si)
+{
+ struct _folder_info *mfi;
+ struct _folder_update *up;
+
+ mfi = g_hash_table_lookup (si->folders, fi->full_name);
+ if (mfi) {
+ update_1folder (self, mfi, 0, NULL, NULL, NULL, fi);
+ } else {
+ mfi = g_malloc0 (sizeof (*mfi));
+ mfi->full_name = g_strdup (fi->full_name);
+ mfi->store_info = si;
+ mfi->flags = fi->flags;
+ mfi->has_children = fi->child != NULL;
+
+ g_hash_table_insert (si->folders, mfi->full_name, mfi);
+
+ up = g_malloc0 (sizeof (*up));
+ up->full_name = g_strdup (mfi->full_name);
+ up->unread = fi->unread;
+ up->store = g_object_ref (si->store);
+
+ if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
+ up->add = TRUE;
+
+ g_queue_push_tail (&self->priv->updates, up);
+ flush_updates (self);
+ }
+}
+
+static void
+create_folders (MailFolderCache *self,
+ CamelFolderInfo *fi,
+ struct _store_info *si)
+{
+ while (fi) {
+ setup_folder (self, fi, si);
+
+ if (fi->child)
+ create_folders (self, fi->child, si);
+
+ fi = fi->next;
+ }
+}
+
+static void
+store_folder_subscribed_cb (CamelStore *store,
+ CamelFolderInfo *info,
+ MailFolderCache *self)
+{
+ struct _store_info *si;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ si = g_hash_table_lookup (self->priv->stores, store);
+ if (si)
+ setup_folder (self, info, si);
+ g_mutex_unlock (self->priv->stores_mutex);
+}
+
+static void
+store_folder_created_cb (CamelStore *store,
+ CamelFolderInfo *info,
+ MailFolderCache *cache)
+{
+ /* We only want created events to do more work
+ * if we dont support subscriptions. */
+ if (!CAMEL_IS_SUBSCRIBABLE (store))
+ store_folder_subscribed_cb (store, info, cache);
+}
+
+static void
+store_folder_opened_cb (CamelStore *store,
+ CamelFolder *folder,
+ MailFolderCache *self)
+{
+ mail_folder_cache_note_folder (self, folder);
+}
+
+static void
+store_folder_unsubscribed_cb (CamelStore *store,
+ CamelFolderInfo *info,
+ MailFolderCache *self)
+{
+ struct _store_info *si;
+ struct _folder_info *mfi;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ si = g_hash_table_lookup (self->priv->stores, store);
+ if (si) {
+ mfi = g_hash_table_lookup (si->folders, info->full_name);
+ if (mfi) {
+ g_hash_table_remove (si->folders, mfi->full_name);
+ unset_folder_info (self, mfi, TRUE, TRUE);
+ free_folder_info (mfi);
+ }
+ }
+ g_mutex_unlock (self->priv->stores_mutex);
+}
+
+static void
+store_folder_deleted_cb (CamelStore *store,
+ CamelFolderInfo *info,
+ MailFolderCache *self)
+{
+ /* We only want deleted events to do more work
+ * if we dont support subscriptions. */
+ if (!CAMEL_IS_SUBSCRIBABLE (store))
+ store_folder_unsubscribed_cb (store, info, self);
+}
+
+static void
+rename_folders (MailFolderCache *self,
+ struct _store_info *si,
+ const gchar *oldbase,
+ const gchar *newbase,
+ CamelFolderInfo *fi)
+{
+ gchar *old, *olduri, *oldfile, *newuri, *newfile;
+ struct _folder_info *mfi;
+ struct _folder_update *up;
+ const gchar *config_dir;
+
+ up = g_malloc0 (sizeof (*up));
+
+ d(printf("oldbase '%s' newbase '%s' new '%s'\n", oldbase, newbase, fi->full_name));
+
+ /* Form what was the old name, and try and look it up */
+ old = g_strdup_printf("%s%s", oldbase, fi->full_name + strlen(newbase));
+ mfi = g_hash_table_lookup (si->folders, old);
+ if (mfi) {
+ up->oldfull = mfi->full_name;
+
+ /* Its a rename op */
+ g_hash_table_remove (si->folders, mfi->full_name);
+ mfi->full_name = g_strdup (fi->full_name);
+ mfi->flags = fi->flags;
+ mfi->has_children = fi->child != NULL;
+
+ g_hash_table_insert (si->folders, mfi->full_name, mfi);
+ } else {
+ /* Its a new op */
+ mfi = g_malloc0 (sizeof (*mfi));
+ mfi->full_name = g_strdup (fi->full_name);
+ mfi->store_info = si;
+ mfi->flags = fi->flags;
+ mfi->has_children = fi->child != NULL;
+
+ g_hash_table_insert (si->folders, mfi->full_name, mfi);
+ }
+
+ up->full_name = g_strdup (mfi->full_name);
+ up->unread = fi->unread==-1 ? 0 : fi->unread;
+ up->store = g_object_ref (si->store);
+
+ if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
+ up->add = TRUE;
+
+ g_queue_push_tail (&self->priv->updates, up);
+ flush_updates (self);
+#if 0
+ if (fi->sibling)
+ rename_folders (self, si, oldbase, newbase, fi->sibling, folders);
+ if (fi->child)
+ rename_folders (self, si, oldbase, newbase, fi->child, folders);
+#endif
+
+ /* rename the meta-data we maintain ourselves */
+ config_dir = mail_session_get_config_dir ();
+ olduri = e_mail_folder_uri_build (si->store, old);
+ e_filename_make_safe (olduri);
+ newuri = e_mail_folder_uri_build (si->store, fi->full_name);
+ e_filename_make_safe (newuri);
+ oldfile = g_strdup_printf("%s/custom_view-%s.xml", config_dir, olduri);
+ newfile = g_strdup_printf("%s/custom_view-%s.xml", config_dir, newuri);
+ g_rename (oldfile, newfile);
+ g_free (oldfile);
+ g_free (newfile);
+ oldfile = g_strdup_printf("%s/current_view-%s.xml", config_dir, olduri);
+ newfile = g_strdup_printf("%s/current_view-%s.xml", config_dir, newuri);
+ g_rename (oldfile, newfile);
+ g_free (oldfile);
+ g_free (newfile);
+ g_free (olduri);
+ g_free (newuri);
+
+ g_free (old);
+}
+
+static void
+get_folders (CamelFolderInfo *fi,
+ GPtrArray *folders)
+{
+ while (fi) {
+ g_ptr_array_add (folders, fi);
+
+ if (fi->child)
+ get_folders (fi->child, folders);
+
+ fi = fi->next;
+ }
+}
+
+static gint
+folder_cmp (gconstpointer ap,
+ gconstpointer bp)
+{
+ const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
+ const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];
+
+ return strcmp (a->full_name, b->full_name);
+}
+
+static void
+store_folder_renamed_cb (CamelStore *store,
+ const gchar *old_name,
+ CamelFolderInfo *info,
+ MailFolderCache *self)
+{
+ struct _store_info *si;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ si = g_hash_table_lookup (self->priv->stores, store);
+ if (si) {
+ GPtrArray *folders = g_ptr_array_new ();
+ CamelFolderInfo *top;
+ gint i;
+
+ /* Ok, so for some reason the folderinfo we have comes in all messed up from
+ * imap, should find out why ... this makes it workable */
+ get_folders (info, folders);
+ qsort (folders->pdata, folders->len, sizeof (folders->pdata[0]), folder_cmp);
+
+ top = folders->pdata[0];
+ for (i = 0; i < folders->len; i++) {
+ rename_folders (self, si, old_name, top->full_name, folders->pdata[i]);
+ }
+
+ g_ptr_array_free (folders, TRUE);
+
+ }
+ g_mutex_unlock (self->priv->stores_mutex);
+}
+
+struct _update_data {
+ NoteDoneFunc done;
+ gpointer data;
+ MailFolderCache *cache;
+ GCancellable *cancellable;
+};
+
+static void
+unset_folder_info_hash (gchar *path,
+ struct _folder_info *mfi,
+ gpointer data)
+{
+ MailFolderCache *self = (MailFolderCache *) data;
+ unset_folder_info (self, mfi, FALSE, FALSE);
+}
+
+static void
+free_folder_info_hash (gchar *path,
+ struct _folder_info *mfi,
+ gpointer data)
+{
+ free_folder_info (mfi);
+}
+
+static void
+update_folders (CamelStore *store,
+ GAsyncResult *result,
+ struct _update_data *ud)
+{
+ CamelFolderInfo *fi;
+ struct _store_info *si;
+ GError *error = NULL;
+
+ fi = camel_store_get_folder_info_finish (store, result, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_mutex_lock (ud->cache->priv->stores_mutex);
+ si = g_hash_table_lookup (ud->cache->priv->stores, store);
+ if (si && !g_cancellable_is_cancelled (ud->cancellable)) {
+ /* The 'si' is still there, so we can remove ourselves from
+ * its list. Or else its not, and we're on our own and free
+ * anyway. */
+ g_queue_remove (&si->folderinfo_updates, ud);
+
+ if (fi != NULL)
+ create_folders (ud->cache, fi, si);
+ }
+ g_mutex_unlock (ud->cache->priv->stores_mutex);
+
+ if (fi != NULL) {
+ gboolean free_fi = TRUE;
+
+ if (ud->done != NULL)
+ free_fi = ud->done (ud->cache, store, fi, ud->data);
+ if (free_fi)
+ camel_store_free_folder_info (store, fi);
+ }
+
+ if (ud->cancellable != NULL)
+ g_object_unref (ud->cancellable);
+
+ g_free (ud);
+}
+
+struct _ping_store_msg {
+ MailMsg base;
+ CamelStore *store;
+};
+
+static gchar *
+ping_store_desc (struct _ping_store_msg *m)
+{
+ gchar *service_name;
+ gchar *msg;
+
+ service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
+ msg = g_strdup_printf (_("Pinging %s"), service_name);
+ g_free (service_name);
+
+ return msg;
+}
+
+static void
+ping_store_exec (struct _ping_store_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelServiceConnectionStatus status;
+ CamelService *service;
+ gboolean online = FALSE;
+
+ service = CAMEL_SERVICE (m->store);
+ status = camel_service_get_connection_status (service);
+
+ if (status == CAMEL_SERVICE_CONNECTED) {
+ if (CAMEL_IS_DISCO_STORE (m->store) &&
+ camel_disco_store_status (
+ CAMEL_DISCO_STORE (m->store)) !=CAMEL_DISCO_STORE_OFFLINE)
+ online = TRUE;
+ else if (CAMEL_IS_OFFLINE_STORE (m->store) &&
+ camel_offline_store_get_online (
+ CAMEL_OFFLINE_STORE (m->store)))
+ online = TRUE;
+ }
+ if (online)
+ camel_store_noop_sync (m->store, cancellable, error);
+}
+
+static void
+ping_store_free (struct _ping_store_msg *m)
+{
+ g_object_unref (m->store);
+}
+
+static MailMsgInfo ping_store_info = {
+ sizeof (struct _ping_store_msg),
+ (MailMsgDescFunc) ping_store_desc,
+ (MailMsgExecFunc) ping_store_exec,
+ (MailMsgDoneFunc) NULL,
+ (MailMsgFreeFunc) ping_store_free
+};
+
+static void
+ping_store (CamelStore *store)
+{
+ CamelServiceConnectionStatus status;
+ CamelService *service;
+ struct _ping_store_msg *m;
+
+ service = CAMEL_SERVICE (store);
+ status = camel_service_get_connection_status (service);
+
+ if (status != CAMEL_SERVICE_CONNECTED)
+ return;
+
+ m = mail_msg_new (&ping_store_info);
+ m->store = g_object_ref (store);
+
+ mail_msg_slow_ordered_push (m);
+}
+
+static gboolean
+ping_cb (MailFolderCache *self)
+{
+ g_mutex_lock (self->priv->stores_mutex);
+
+ g_hash_table_foreach (self->priv->stores, (GHFunc) ping_store, NULL);
+
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ return TRUE;
+}
+
+static void
+store_go_online_cb (CamelStore *store,
+ GAsyncResult *result,
+ struct _update_data *ud)
+{
+ /* FIXME Not checking result for error. */
+
+ g_mutex_lock (ud->cache->priv->stores_mutex);
+
+ if (g_hash_table_lookup (ud->cache->priv->stores, store) != NULL &&
+ !g_cancellable_is_cancelled (ud->cancellable)) {
+ /* We're already in the store update list. */
+ camel_store_get_folder_info (
+ store, NULL,
+ CAMEL_STORE_FOLDER_INFO_FAST |
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+ G_PRIORITY_DEFAULT, ud->cancellable,
+ (GAsyncReadyCallback) update_folders, ud);
+ } else {
+ /* The store vanished, that means we were probably cancelled,
+ * or at any rate, need to clean ourselves up. */
+ if (ud->cancellable != NULL)
+ g_object_unref (ud->cancellable);
+ g_free (ud);
+ }
+
+ g_mutex_unlock (ud->cache->priv->stores_mutex);
+}
+
+struct _find_info {
+ const gchar *folder_uri;
+ struct _folder_info *fi;
+};
+
+static void
+storeinfo_find_folder_info (CamelStore *store,
+ struct _store_info *si,
+ struct _find_info *fi)
+{
+ gchar *folder_name;
+ gboolean success;
+
+ if (fi->fi != NULL)
+ return;
+
+ success = e_mail_folder_uri_parse (
+ camel_service_get_session (CAMEL_SERVICE (store)),
+ fi->folder_uri, NULL, &folder_name, NULL);
+
+ if (success) {
+ fi->fi = g_hash_table_lookup (si->folders, folder_name);
+ g_free (folder_name);
+ }
+}
+
+static void
+mail_folder_cache_finalize (GObject *object)
+{
+ MailFolderCache *cache = (MailFolderCache *) object;
+
+ g_hash_table_destroy (cache->priv->stores);
+ g_mutex_free (cache->priv->stores_mutex);
+
+ if (cache->priv->ping_id > 0) {
+ g_source_remove (cache->priv->ping_id);
+ cache->priv->ping_id = 0;
+ }
+
+ if (cache->priv->update_id > 0) {
+ g_source_remove (cache->priv->update_id);
+ cache->priv->update_id = 0;
+ }
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (mail_folder_cache_parent_class)->finalize (object);
+}
+
+static void
+mail_folder_cache_class_init (MailFolderCacheClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (MailFolderCachePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = mail_folder_cache_finalize;
+
+ /**
+ * MailFolderCache::folder-available
+ * @store: the #CamelStore containing the folder
+ * @folder_name: the name of the folder
+ *
+ * Emitted when a folder becomes available
+ **/
+ signals[FOLDER_AVAILABLE] = g_signal_new (
+ "folder-available",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_STORE,
+ G_TYPE_STRING);
+
+ /**
+ * MailFolderCache::folder-unavailable
+ * @store: the #CamelStore containing the folder
+ * @folder_name: the name of the folder
+ *
+ * Emitted when a folder becomes unavailable. This represents a
+ * transient condition. See MailFolderCache::folder-deleted to be
+ * notified when a folder is permanently removed.
+ **/
+ signals[FOLDER_UNAVAILABLE] = g_signal_new (
+ "folder-unavailable",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_STORE,
+ G_TYPE_STRING);
+
+ /**
+ * MailFolderCache::folder-deleted
+ * @store: the #CamelStore containing the folder
+ * @folder_name: the name of the folder
+ *
+ * Emitted when a folder is deleted
+ **/
+ signals[FOLDER_DELETED] = g_signal_new (
+ "folder-deleted",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_STORE,
+ G_TYPE_STRING);
+
+ /**
+ * MailFolderCache::folder-renamed
+ * @store: the #CamelStore containing the folder
+ * @old_folder_name: the old name of the folder
+ * @new_folder_name: the new name of the folder
+ *
+ * Emitted when a folder is renamed
+ **/
+ signals[FOLDER_RENAMED] = g_signal_new (
+ "folder-renamed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING_STRING,
+ G_TYPE_NONE, 3,
+ CAMEL_TYPE_STORE,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ /**
+ * MailFolderCache::folder-unread-updated
+ * @store: the #CamelStore containing the folder
+ * @name: the name of the folder
+ * @unread: the number of unread mails in the folder
+ *
+ * Emitted when a we receive an update to the unread count for a folder
+ **/
+ signals[FOLDER_UNREAD_UPDATED] =
+ g_signal_new ("folder-unread-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING_INT,
+ G_TYPE_NONE, 3,
+ CAMEL_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_INT);
+
+ /**
+ * MailFolderCache::folder-changed
+ * @store: the #CamelStore containing the folder
+ * @folder_fullname: the full name of the folder
+ * @new_messages: the number of new messages for the folder
+ * @msg_uid: uid of the new message, or NULL
+ * @msg_sender: sender of the new message, or NULL
+ * @msg_subject: subject of the new message, or NULL
+ *
+ * Emitted when a folder has changed. If @new_messages is not
+ * exactly 1, @msg_uid, @msg_sender, and @msg_subject will be NULL.
+ **/
+ signals[FOLDER_CHANGED] = g_signal_new (
+ "folder-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* struct offset */
+ NULL, NULL, /* accumulator */
+ e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING,
+ G_TYPE_NONE, 6,
+ CAMEL_TYPE_STORE,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+}
+
+static void
+mail_folder_cache_init (MailFolderCache *self)
+{
+ const gchar *buf;
+ guint timeout;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ self, MAIL_TYPE_FOLDER_CACHE, MailFolderCachePrivate);
+
+ /* initialize values */
+ self->priv->stores = g_hash_table_new (NULL, NULL);
+ self->priv->stores_mutex = g_mutex_new ();
+
+ g_queue_init (&self->priv->updates);
+ self->priv->count_sent = getenv("EVOLUTION_COUNT_SENT") != NULL;
+ self->priv->count_trash = getenv("EVOLUTION_COUNT_TRASH") != NULL;
+
+ buf = getenv ("EVOLUTION_PING_TIMEOUT");
+ timeout = buf ? strtoul (buf, NULL, 10) : 600;
+ self->priv->ping_id = g_timeout_add_seconds (
+ timeout, (GSourceFunc) ping_cb, self);
+}
+
+MailFolderCache *
+mail_folder_cache_new (void)
+{
+ return g_object_new (MAIL_TYPE_FOLDER_CACHE, NULL);
+}
+
+/**
+ * mail_folder_cache_note_store:
+ *
+ * Add a store whose folders should appear in the shell The folders are scanned
+ * from the store, and/or added at runtime via the folder_created event. The
+ * @done function returns if we can free folder info.
+ */
+void
+mail_folder_cache_note_store (MailFolderCache *self,
+ CamelSession *session,
+ CamelStore *store,
+ GCancellable *cancellable,
+ NoteDoneFunc done,
+ gpointer data)
+{
+ struct _store_info *si;
+ struct _update_data *ud;
+ gint hook = 0;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (mail_in_main_thread ());
+
+ g_mutex_lock (self->priv->stores_mutex);
+
+ printf("Loading: %s\n", camel_service_get_display_name ((CamelService *) store));
+ si = g_hash_table_lookup (self->priv->stores, store);
+ if (si == NULL) {
+ si = g_malloc0 (sizeof (*si));
+ si->folders = g_hash_table_new (g_str_hash, g_str_equal);
+ si->store = g_object_ref (store);
+ g_hash_table_insert (self->priv->stores, store, si);
+ g_queue_init (&si->folderinfo_updates);
+ hook = TRUE;
+ }
+
+ ud = g_malloc0 (sizeof (*ud));
+ ud->done = done;
+ ud->data = data;
+ ud->cache = self;
+
+ if (G_IS_CANCELLABLE (cancellable))
+ ud->cancellable = g_object_ref (cancellable);
+
+ /* We might get a race when setting up a store, such that it is
+ * still left in offline mode, after we've gone online. This
+ * catches and fixes it up when the shell opens us. */
+ if (CAMEL_IS_DISCO_STORE (store)) {
+ if (camel_session_get_online (session) &&
+ camel_disco_store_status (CAMEL_DISCO_STORE (store)) ==
+ CAMEL_DISCO_STORE_OFFLINE) {
+ e_mail_store_go_online (
+ store, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) store_go_online_cb, ud);
+ } else {
+ goto normal_setup;
+ }
+ } else if (CAMEL_IS_OFFLINE_STORE (store)) {
+ if (camel_session_get_online (session) &&
+ !camel_offline_store_get_online (
+ CAMEL_OFFLINE_STORE (store))) {
+ e_mail_store_go_online (
+ store, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) store_go_online_cb, ud);
+ } else {
+ goto normal_setup;
+ }
+ } else {
+ normal_setup:
+ camel_store_get_folder_info (
+ store, NULL,
+ CAMEL_STORE_FOLDER_INFO_FAST |
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) update_folders, ud);
+ }
+
+ g_queue_push_tail (&si->folderinfo_updates, ud);
+
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ /* there is potential for race here, but it is safe as we check
+ * for the store anyway */
+ if (hook) {
+ g_signal_connect (
+ store, "folder-opened",
+ G_CALLBACK (store_folder_opened_cb), self);
+ g_signal_connect (
+ store, "folder-created",
+ G_CALLBACK (store_folder_created_cb), self);
+ g_signal_connect (
+ store, "folder-deleted",
+ G_CALLBACK (store_folder_deleted_cb), self);
+ g_signal_connect (
+ store, "folder-renamed",
+ G_CALLBACK (store_folder_renamed_cb), self);
+ }
+
+ if (hook && CAMEL_IS_SUBSCRIBABLE (store)) {
+ g_signal_connect (
+ store, "folder-subscribed",
+ G_CALLBACK (store_folder_subscribed_cb), self);
+ g_signal_connect (
+ store, "folder-unsubscribed",
+ G_CALLBACK (store_folder_unsubscribed_cb), self);
+ }
+}
+
+/**
+ * mail_folder_cache_note_store_remove:
+ *
+ * Notify the cache that the specified @store can be removed from the cache
+ */
+void
+mail_folder_cache_note_store_remove (MailFolderCache *self,
+ CamelStore *store)
+{
+ struct _store_info *si;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ if (self->priv->stores == NULL)
+ return;
+
+ d(printf("store removed!!\n"));
+ g_mutex_lock (self->priv->stores_mutex);
+ si = g_hash_table_lookup (self->priv->stores, store);
+ if (si) {
+ GList *link;
+
+ g_hash_table_remove (self->priv->stores, store);
+
+ g_signal_handlers_disconnect_matched (
+ store, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, self);
+
+ g_hash_table_foreach (
+ si->folders, (GHFunc)
+ unset_folder_info_hash, self);
+
+ link = g_queue_peek_head_link (&si->folderinfo_updates);
+
+ while (link != NULL) {
+ struct _update_data *ud = link->data;
+ g_cancellable_cancel (ud->cancellable);
+ link = g_list_next (link);
+ }
+
+ g_object_unref (si->store);
+ g_hash_table_foreach (si->folders, (GHFunc) free_folder_info_hash, NULL);
+ g_hash_table_destroy (si->folders);
+ g_free (si);
+ }
+
+ g_mutex_unlock (self->priv->stores_mutex);
+}
+
+/**
+ * mail_folder_cache_note_folder:
+ *
+ * When a folder has been opened, notify it for watching. The folder must have
+ * already been created on the store (which has already been noted) before the
+ * folder can be opened
+ */
+void
+mail_folder_cache_note_folder (MailFolderCache *self,
+ CamelFolder *folder)
+{
+ CamelStore *parent_store;
+ struct _store_info *si;
+ struct _folder_info *mfi;
+ const gchar *full_name;
+
+ full_name = camel_folder_get_full_name (folder);
+ parent_store = camel_folder_get_parent_store (folder);
+
+ g_mutex_lock (self->priv->stores_mutex);
+ if (self->priv->stores == NULL
+ || (si = g_hash_table_lookup (self->priv->stores, parent_store)) == NULL
+ || (mfi = g_hash_table_lookup (si->folders, full_name)) == NULL) {
+ w(g_warning("Noting folder before store initialised"));
+ g_mutex_unlock (self->priv->stores_mutex);
+ return;
+ }
+
+ /* dont do anything if we already have this */
+ if (mfi->folder == folder) {
+ g_mutex_unlock (self->priv->stores_mutex);
+ return;
+ }
+
+ mfi->folder = folder;
+
+ g_object_add_weak_pointer (G_OBJECT (folder), &mfi->folder);
+
+ update_1folder (self, mfi, 0, NULL, NULL, NULL, NULL);
+
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ g_signal_connect (
+ folder, "changed",
+ G_CALLBACK (folder_changed_cb), self);
+}
+
+/**
+ * mail_folder_cache_get_folder_from_uri:
+ *
+ * Gets the #CamelFolder for the supplied @uri.
+ *
+ * Returns: %TRUE if the URI is available, folderp is set to a reffed
+ * folder if the folder has also already been opened
+ */
+gboolean
+mail_folder_cache_get_folder_from_uri (MailFolderCache *self,
+ const gchar *uri,
+ CamelFolder **folderp)
+{
+ struct _find_info fi = { uri, NULL };
+
+ if (self->priv->stores == NULL)
+ return FALSE;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ g_hash_table_foreach (
+ self->priv->stores, (GHFunc)
+ storeinfo_find_folder_info, &fi);
+ if (folderp) {
+ if (fi.fi && fi.fi->folder)
+ *folderp = g_object_ref (fi.fi->folder);
+ else
+ *folderp = NULL;
+ }
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ return fi.fi != NULL;
+}
+
+gboolean
+mail_folder_cache_get_folder_info_flags (MailFolderCache *self,
+ CamelFolder *folder,
+ CamelFolderInfoFlags *flags)
+{
+ struct _find_info fi = { NULL, NULL };
+ gchar *folder_uri;
+
+ if (self->priv->stores == NULL)
+ return FALSE;
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+ fi.folder_uri = folder_uri;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ g_hash_table_foreach (
+ self->priv->stores, (GHFunc)
+ storeinfo_find_folder_info, &fi);
+ if (flags) {
+ if (fi.fi)
+ *flags = fi.fi->flags;
+ else
+ *flags = 0;
+ }
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ g_free (folder_uri);
+
+ return fi.fi != NULL;
+}
+
+/* Returns whether folder 'folder' has children based on folder_info->child property.
+ * If not found returns FALSE and sets 'found' to FALSE, if not NULL. */
+gboolean
+mail_folder_cache_get_folder_has_children (MailFolderCache *self,
+ CamelFolder *folder,
+ gboolean *found)
+{
+ struct _find_info fi = { NULL, NULL };
+ gchar *folder_uri;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (folder != NULL, FALSE);
+
+ if (self->priv->stores == NULL)
+ return FALSE;
+
+ folder_uri = e_mail_folder_uri_from_folder (folder);
+ fi.folder_uri = folder_uri;
+
+ g_mutex_lock (self->priv->stores_mutex);
+ g_hash_table_foreach (
+ self->priv->stores, (GHFunc)
+ storeinfo_find_folder_info, &fi);
+ if (found)
+ *found = fi.fi != NULL;
+ g_mutex_unlock (self->priv->stores_mutex);
+
+ g_free (folder_uri);
+
+ return fi.fi != NULL && fi.fi->has_children;
+}
diff --git a/libemail-engine/mail-folder-cache.h b/libemail-engine/mail-folder-cache.h
new file mode 100644
index 0000000..681c6ef
--- /dev/null
+++ b/libemail-engine/mail-folder-cache.h
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Peter Williams <peterw ximian com>
+ * Michael Zucchi <notzed ximian com>
+ * Jonathon Jongsma <jonathon jongsma collabora co uk>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2009 Intel Corporation
+ *
+ */
+
+#ifndef MAIL_FOLDER_CACHE_H
+#define MAIL_FOLDER_CACHE_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define MAIL_TYPE_FOLDER_CACHE \
+ (mail_folder_cache_get_type ())
+#define MAIL_FOLDER_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCache))
+#define MAIL_FOLDER_CACHE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), MAIL_TYPE_FOLDER_CACHE, MailFolderCacheClass))
+#define MAIL_IS_FOLDER_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), MAIL_TYPE_FOLDER_CACHE))
+#define MAIL_IS_FOLDER_CACHE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), MAIL_TYPE_FOLDER_CACHE))
+#define MAIL_FOLDER_CACHE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCacheClass))
+
+G_BEGIN_DECLS
+
+typedef struct _MailFolderCache MailFolderCache;
+typedef struct _MailFolderCacheClass MailFolderCacheClass;
+typedef struct _MailFolderCachePrivate MailFolderCachePrivate;
+
+/**
+ * NoteDoneFunc:
+ *
+ * The signature of a function to be registered as a callback for
+ * mail_folder_cache_note_store()
+ */
+typedef gboolean (*NoteDoneFunc) (MailFolderCache *cache,
+ CamelStore *store,
+ CamelFolderInfo *info,
+ gpointer data);
+
+/**
+ * MailFolderCache:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ */
+struct _MailFolderCache {
+ GObject parent;
+ MailFolderCachePrivate *priv;
+};
+
+struct _MailFolderCacheClass {
+ GObjectClass parent_class;
+};
+
+GType mail_folder_cache_get_type (void) G_GNUC_CONST;
+MailFolderCache *
+ mail_folder_cache_new (void);
+void mail_folder_cache_note_store (MailFolderCache *self,
+ CamelSession *session,
+ CamelStore *store,
+ GCancellable *cancellable,
+ NoteDoneFunc done,
+ gpointer data);
+void mail_folder_cache_note_store_remove
+ (MailFolderCache *self,
+ CamelStore *store);
+void mail_folder_cache_note_folder (MailFolderCache *self,
+ CamelFolder *folder);
+gboolean mail_folder_cache_get_folder_from_uri
+ (MailFolderCache *self,
+ const gchar *uri,
+ CamelFolder **folderp);
+gboolean mail_folder_cache_get_folder_info_flags
+ (MailFolderCache *self,
+ CamelFolder *folder,
+ CamelFolderInfoFlags *flags);
+
+gboolean mail_folder_cache_get_folder_has_children
+ (MailFolderCache *self,
+ CamelFolder *folder,
+ gboolean *found);
+
+G_END_DECLS
+
+#endif /* MAIL_FOLDER_CACHE_H */
diff --git a/libemail-engine/mail-ops.c b/libemail-engine/mail-ops.c
new file mode 100644
index 0000000..a12a63a
--- /dev/null
+++ b/libemail-engine/mail-ops.c
@@ -0,0 +1,1680 @@
+/*
+ * mail-ops.c: callbacks for the mail toolbar/menus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Dan Winship <danw ximian com>
+ * Jeffrey Stedfast <fejj ximian com>
+ * Peter Williams <peterw ximian com>
+ * Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include "libemail-utils/e-account-utils.h"
+
+#include "e-mail-utils.h"
+#include "libemail-utils/mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "e-mail-session-utils.h"
+#include "e-mail-local.h"
+
+#define w(x)
+#define d(x)
+
+/* XXX Make this a preprocessor definition. */
+const gchar *x_mailer = "Evolution Mail Data Server" PACKAGE_VERSION;
+
+/* used for both just filtering a folder + uid's, and for filtering a whole folder */
+/* used both for fetching mail, and for filtering mail */
+struct _filter_mail_msg {
+ MailMsg base;
+
+ EMailSession *session;
+ CamelFolder *source_folder; /* where they come from */
+ GPtrArray *source_uids; /* uids to copy, or NULL == copy all */
+ CamelUIDCache *cache; /* UID cache if we are to cache the uids, NULL otherwise */
+ CamelFilterDriver *driver;
+ gint delete; /* delete messages after filtering them? */
+ CamelFolder *destination; /* default destination for any messages, NULL for none */
+};
+
+/* since fetching also filters, we subclass the data here */
+struct _fetch_mail_msg {
+ struct _filter_mail_msg fmsg;
+
+ CamelStore *store;
+ GCancellable *cancellable; /* we have our own cancellation
+ * struct, the other should be empty */
+ gint keep; /* keep on server? */
+
+ void (*done)(gpointer data);
+ gpointer data;
+};
+
+static gchar *
+em_filter_folder_element_desc (struct _filter_mail_msg *m)
+{
+ return g_strdup (_("Filtering Selected Messages"));
+}
+
+/* filter a folder, or a subset thereof, uses source_folder/source_uids */
+/* this is shared with fetch_mail */
+static void
+em_filter_folder_element_exec (struct _filter_mail_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ GPtrArray *uids, *folder_uids = NULL;
+
+ folder = m->source_folder;
+
+ if (folder == NULL || camel_folder_get_message_count (folder) == 0)
+ return;
+
+ if (m->destination) {
+ camel_folder_freeze (m->destination);
+ camel_filter_driver_set_default_folder (m->driver, m->destination);
+ }
+
+ camel_folder_freeze (folder);
+
+ if (m->source_uids)
+ uids = m->source_uids;
+ else
+ folder_uids = uids = camel_folder_get_uids (folder);
+
+ camel_filter_driver_filter_folder (
+ m->driver, folder, m->cache, uids, m->delete,
+ cancellable, error);
+ camel_filter_driver_flush (m->driver, error);
+
+ if (folder_uids)
+ camel_folder_free_uids (folder, folder_uids);
+
+ /* sync our source folder */
+ if (!m->cache)
+ camel_folder_synchronize_sync (
+ folder, FALSE, cancellable, error);
+ camel_folder_thaw (folder);
+
+ if (m->destination)
+ camel_folder_thaw (m->destination);
+
+ /* this may thaw/unref source folders, do it here so we dont do
+ * it in the main thread see also fetch_mail_fetch () below */
+ g_object_unref (m->driver);
+ m->driver = NULL;
+}
+
+static void
+em_filter_folder_element_done (struct _filter_mail_msg *m)
+{
+}
+
+static void
+em_filter_folder_element_free (struct _filter_mail_msg *m)
+{
+ mail_session_flush_filter_log (m->session);
+
+ if (m->session)
+ g_object_unref (m->session);
+
+ if (m->source_folder)
+ g_object_unref (m->source_folder);
+
+ if (m->source_uids)
+ em_utils_uids_free (m->source_uids);
+
+ if (m->destination)
+ g_object_unref (m->destination);
+
+ if (m->driver)
+ g_object_unref (m->driver);
+}
+
+static MailMsgInfo em_filter_folder_element_info = {
+ sizeof (struct _filter_mail_msg),
+ (MailMsgDescFunc) em_filter_folder_element_desc,
+ (MailMsgExecFunc) em_filter_folder_element_exec,
+ (MailMsgDoneFunc) em_filter_folder_element_done,
+ (MailMsgFreeFunc) em_filter_folder_element_free
+};
+
+void
+mail_filter_folder (EMailSession *session,
+ CamelFolder *source_folder,
+ GPtrArray *uids,
+ const gchar *type,
+ gboolean notify)
+{
+ struct _filter_mail_msg *m;
+
+ m = mail_msg_new (&em_filter_folder_element_info);
+ m->session = g_object_ref (session);
+ m->source_folder = g_object_ref (source_folder);
+ m->source_uids = uids;
+ m->cache = NULL;
+ m->delete = FALSE;
+
+ m->driver = camel_session_get_filter_driver (
+ CAMEL_SESSION (session), type, NULL);
+
+ if (!notify) {
+ /* FIXME: have a #define NOTIFY_FILTER_NAME macro? */
+ /* the filter name has to stay in sync with mail-session::get_filter_driver */
+ camel_filter_driver_remove_rule_by_name (m->driver, "new-mail-notification");
+ }
+
+ mail_msg_unordered_push (m);
+}
+
+/* ********************************************************************** */
+
+static gchar *
+fetch_mail_desc (struct _fetch_mail_msg *m)
+{
+ return g_strdup (_("Fetching Mail"));
+}
+
+static void
+fetch_mail_exec (struct _fetch_mail_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ struct _filter_mail_msg *fm = (struct _filter_mail_msg *) m;
+ CamelFolder *folder = NULL;
+ CamelURL *url;
+ const gchar *uid;
+ gboolean is_local_delivery = FALSE;
+ gint i;
+
+ fm->destination = e_mail_local_get_folder (
+ E_MAIL_LOCAL_FOLDER_LOCAL_INBOX);
+ if (fm->destination == NULL)
+ goto fail;
+ g_object_ref (fm->destination);
+
+ url = camel_service_get_camel_url (CAMEL_SERVICE (m->store));
+ is_local_delivery = em_utils_is_local_delivery_mbox_file (url);
+ if (is_local_delivery) {
+ gchar *path;
+ gchar *url_string;
+
+ path = mail_tool_do_movemail (m->store, error);
+ url_string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+
+ if (path && (!error || !*error)) {
+ camel_folder_freeze (fm->destination);
+ camel_filter_driver_set_default_folder (
+ fm->driver, fm->destination);
+ camel_filter_driver_filter_mbox (
+ fm->driver, path, url_string,
+ cancellable, error);
+ camel_folder_thaw (fm->destination);
+
+ if (!error || !*error)
+ g_unlink (path);
+ }
+
+ g_free (path);
+ g_free (url_string);
+ } else {
+ uid = camel_service_get_uid (CAMEL_SERVICE (m->store));
+
+ folder = fm->source_folder =
+ e_mail_session_get_inbox_sync (
+ fm->session, uid, cancellable, error);
+ }
+
+ if (folder != NULL) {
+ /* This handles 'keep on server' stuff, if we have any new
+ * uid's to copy across, we need to copy them to a new array
+ * 'cause of the way fetch_mail_free works. */
+ CamelUIDCache *cache = NULL;
+ CamelStore *parent_store;
+ CamelService *service;
+ const gchar *data_dir;
+ gchar *cachename;
+
+ parent_store = camel_folder_get_parent_store (folder);
+
+ service = CAMEL_SERVICE (parent_store);
+ data_dir = camel_service_get_user_data_dir (service);
+
+ cachename = g_build_filename (data_dir, "uid-cache", NULL);
+ cache = camel_uid_cache_new (cachename);
+ g_free (cachename);
+
+ if (cache) {
+ GPtrArray *folder_uids, *cache_uids, *uids;
+
+ folder_uids = camel_folder_get_uids (folder);
+ cache_uids = camel_uid_cache_get_new_uids (cache, folder_uids);
+ if (cache_uids) {
+ /* need to copy this, sigh */
+ fm->source_uids = uids = g_ptr_array_new ();
+ g_ptr_array_set_size (uids, cache_uids->len);
+ for (i = 0; i < cache_uids->len; i++)
+ uids->pdata[i] = g_strdup (cache_uids->pdata[i]);
+ camel_uid_cache_free_uids (cache_uids);
+
+ fm->cache = cache;
+ em_filter_folder_element_exec (fm, cancellable, error);
+
+ /* need to uncancel so writes/etc. don't fail */
+ if (g_cancellable_is_cancelled (m->cancellable))
+ g_cancellable_reset (m->cancellable);
+
+ /* save the cache of uids that we've just downloaded */
+ camel_uid_cache_save (cache);
+ }
+
+ if (fm->delete && (!error || !*error)) {
+ /* not keep on server - just delete all
+ * the actual messages on the server */
+ for (i = 0; i < folder_uids->len; i++) {
+ camel_folder_delete_message (
+ folder, folder_uids->pdata[i]);
+ }
+ }
+
+ if ((fm->delete || cache_uids) && (!error || !*error)) {
+ /* expunge messages (downloaded so far) */
+ /* FIXME Not passing a GCancellable or GError here. */
+ camel_folder_synchronize_sync (
+ folder, fm->delete, NULL, NULL);
+ }
+
+ camel_uid_cache_destroy (cache);
+ camel_folder_free_uids (folder, folder_uids);
+ } else {
+ em_filter_folder_element_exec (fm, cancellable, error);
+ }
+
+ /* we unref the source folder here since we
+ * may now block in finalize (we try to
+ * disconnect cleanly) */
+ g_object_unref (fm->source_folder);
+ fm->source_folder = NULL;
+ }
+
+fail:
+ /* we unref this here as it may have more work to do (syncing
+ * folders and whatnot) before we are really done */
+ /* should this be cancellable too? (i.e. above unregister above) */
+ if (fm->driver) {
+ g_object_unref (fm->driver);
+ fm->driver = NULL;
+ }
+
+ /* also disconnect if not a local delivery mbox;
+ * there is no need to keep the connection alive forever */
+ if (!is_local_delivery)
+ em_utils_disconnect_service_sync (
+ CAMEL_SERVICE (m->store), TRUE, cancellable, NULL);
+}
+
+static void
+fetch_mail_done (struct _fetch_mail_msg *m)
+{
+ if (m->done)
+ m->done (m->data);
+}
+
+static void
+fetch_mail_free (struct _fetch_mail_msg *m)
+{
+ if (m->store != NULL)
+ g_object_unref (m->store);
+
+ if (m->cancellable != NULL)
+ g_object_unref (m->cancellable);
+
+ em_filter_folder_element_free ((struct _filter_mail_msg *) m);
+}
+
+static MailMsgInfo fetch_mail_info = {
+ sizeof (struct _fetch_mail_msg),
+ (MailMsgDescFunc) fetch_mail_desc,
+ (MailMsgExecFunc) fetch_mail_exec,
+ (MailMsgDoneFunc) fetch_mail_done,
+ (MailMsgFreeFunc) fetch_mail_free
+};
+
+/* ouch, a 'do everything' interface ... */
+void
+mail_fetch_mail (CamelStore *store,
+ gint keep,
+ const gchar *type,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder,
+ gpointer get_data,
+ CamelFilterStatusFunc *status,
+ gpointer status_data,
+ void (*done)(gpointer data),
+ gpointer data)
+{
+ struct _fetch_mail_msg *m;
+ struct _filter_mail_msg *fm;
+ CamelSession *session;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ session = camel_service_get_session (CAMEL_SERVICE (store));
+
+ m = mail_msg_new (&fetch_mail_info);
+ fm = (struct _filter_mail_msg *) m;
+ fm->session = g_object_ref (session);
+ m->store = g_object_ref (store);
+ fm->delete = !keep;
+ fm->cache = NULL;
+ if (cancellable)
+ m->cancellable = g_object_ref (cancellable);
+ m->done = done;
+ m->data = data;
+
+ fm->driver = camel_session_get_filter_driver (session, type, NULL);
+ camel_filter_driver_set_folder_func (fm->driver, get_folder, get_data);
+ if (status)
+ camel_filter_driver_set_status_func (fm->driver, status, status_data);
+
+ mail_msg_unordered_push (m);
+}
+
+static gchar *
+escape_percent_sign (const gchar *str)
+{
+ GString *res;
+
+ if (!str)
+ return NULL;
+
+ res = g_string_sized_new (strlen (str));
+ while (*str) {
+ if (*str == '%') {
+ g_string_append (res, "%%");
+ } else {
+ g_string_append_c (res, *str);
+ }
+
+ str++;
+ }
+
+ return g_string_free (res, FALSE);
+}
+
+/* ********************************************************************** */
+/* sending stuff */
+/* ** SEND MAIL *********************************************************** */
+
+static const gchar *normal_recipients[] = {
+ CAMEL_RECIPIENT_TYPE_TO,
+ CAMEL_RECIPIENT_TYPE_CC,
+ CAMEL_RECIPIENT_TYPE_BCC
+};
+
+static const gchar *resent_recipients[] = {
+ CAMEL_RECIPIENT_TYPE_RESENT_TO,
+ CAMEL_RECIPIENT_TYPE_RESENT_CC,
+ CAMEL_RECIPIENT_TYPE_RESENT_BCC
+};
+
+struct _send_queue_msg {
+ MailMsg base;
+
+ EMailSession *session;
+ CamelFolder *queue;
+ CamelTransport *transport;
+
+ CamelFilterDriver *driver;
+
+ /* we use camelfilterstatusfunc, even though its not the filter doing it */
+ CamelFilterStatusFunc *status;
+ gpointer status_data;
+
+ void (*done)(gpointer data);
+ gpointer data;
+};
+
+static void report_status (struct _send_queue_msg *m,
+ enum camel_filter_status_t status,
+ gint pc,
+ const gchar *desc,
+ ...);
+
+/* send 1 message to a specific transport */
+static void
+mail_send_message (struct _send_queue_msg *m,
+ CamelFolder *queue,
+ const gchar *uid,
+ CamelTransport *transport,
+ CamelFilterDriver *driver,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EAccount *account = NULL;
+ EMailSession *session;
+ const CamelInternetAddress *iaddr;
+ CamelAddress *from, *recipients;
+ CamelMessageInfo *info = NULL;
+ CamelProvider *provider;
+ gchar *transport_uid = NULL;
+ gchar *sent_folder_uri = NULL;
+ const gchar *resent_from, *tmp;
+ CamelFolder *folder = NULL;
+ GString *err = NULL;
+ struct _camel_header_raw *xev, *header;
+ CamelMimeMessage *message;
+ gint i;
+ GError *local_error = NULL;
+
+ message = camel_folder_get_message_sync (
+ queue, uid, cancellable, error);
+ if (!message)
+ return;
+
+ camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer", x_mailer);
+
+ err = g_string_new ("");
+ xev = mail_tool_remove_xevolution_headers (message);
+
+ session = m->session;
+
+ tmp = camel_header_raw_find (&xev, "X-Evolution-Account", NULL);
+ if (tmp != NULL) {
+ gchar *name;
+
+ name = g_strstrip (g_strdup (tmp));
+ if ((account = e_get_account_by_uid (name))
+ /* 'old' x-evolution-account stored the name, how silly */
+ || (account = e_get_account_by_name (name))) {
+ if (account->transport) {
+ CamelService *service;
+ gchar *transport_uid;
+
+ transport_uid = g_strconcat (
+ account->uid, "-transport", NULL);
+ service = camel_session_get_service (
+ CAMEL_SESSION (session),
+ transport_uid);
+ g_free (transport_uid);
+
+ if (CAMEL_IS_TRANSPORT (service))
+ transport = CAMEL_TRANSPORT (service);
+ }
+
+ sent_folder_uri = g_strdup (account->sent_folder_uri);
+ }
+ g_free (name);
+ }
+
+ if (!account) {
+ /* default back to these headers */
+ tmp = camel_header_raw_find(&xev, "X-Evolution-Transport", NULL);
+ if (tmp)
+ transport_uid = g_strstrip (g_strdup (tmp));
+
+ tmp = camel_header_raw_find(&xev, "X-Evolution-Fcc", NULL);
+ if (tmp)
+ sent_folder_uri = g_strstrip (g_strdup (tmp));
+ }
+
+ if (transport != NULL) {
+ CamelURL *url;
+ gchar *url_string;
+ gchar *escaped;
+
+ url = camel_service_get_camel_url (CAMEL_SERVICE (transport));
+ url_string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+ escaped = escape_percent_sign (url_string);
+
+ /* Let the dialog know the right account it is using. */
+ report_status (m, CAMEL_FILTER_STATUS_ACTION, 0, escaped);
+
+ g_free (escaped);
+ g_free (url_string);
+ }
+
+ /* Check for email sending */
+ from = (CamelAddress *) camel_internet_address_new ();
+ resent_from = camel_medium_get_header (CAMEL_MEDIUM (message), "Resent-From");
+ if (resent_from) {
+ camel_address_decode (from, resent_from);
+ } else {
+ iaddr = camel_mime_message_get_from (message);
+ camel_address_copy (from, CAMEL_ADDRESS (iaddr));
+ }
+
+ recipients = (CamelAddress *) camel_internet_address_new ();
+ for (i = 0; i < 3; i++) {
+ const gchar *type;
+
+ type = resent_from ? resent_recipients[i] : normal_recipients[i];
+ iaddr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (iaddr));
+ }
+
+ if (camel_address_length (recipients) > 0) {
+ if (!em_utils_connect_service_sync (
+ CAMEL_SERVICE (transport), cancellable, error))
+ goto exit;
+
+ if (!camel_transport_send_to_sync (
+ transport, message, from,
+ recipients, cancellable, error))
+ goto exit;
+ }
+
+ /* Now check for posting, failures are ignored */
+ info = camel_message_info_new (NULL);
+ camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+ for (header = xev; header; header = header->next) {
+ gchar *uri;
+
+ if (strcmp(header->name, "X-Evolution-PostTo") != 0)
+ continue;
+
+ /* TODO: don't lose errors */
+
+ uri = g_strstrip (g_strdup (header->value));
+ /* FIXME Not passing a GCancellable or GError here. */
+ folder = e_mail_session_uri_to_folder_sync (
+ session, uri, 0, NULL, NULL);
+ if (folder) {
+ /* FIXME Not passing a GCancellable or GError here. */
+ camel_folder_append_message_sync (
+ folder, message, info, NULL, NULL, NULL);
+ g_object_unref (folder);
+ folder = NULL;
+ }
+ g_free (uri);
+ }
+
+ /* post process */
+ mail_tool_restore_xevolution_headers (message, xev);
+
+ if (driver) {
+ camel_filter_driver_filter_message (
+ driver, message, info, NULL, NULL,
+ NULL, "", cancellable, &local_error);
+
+ if (local_error != NULL) {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ /* sending mail, filtering failed */
+ g_string_append_printf (
+ err, _("Failed to apply outgoing filters: %s"),
+ local_error->message);
+
+ g_clear_error (&local_error);
+ }
+ }
+
+ provider = camel_service_get_provider (CAMEL_SERVICE (transport));
+
+ if (provider == NULL
+ || !(provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)) {
+ GError *local_error = NULL;
+
+ if (sent_folder_uri) {
+ folder = e_mail_session_uri_to_folder_sync (
+ session, sent_folder_uri, 0,
+ cancellable, &local_error);
+ if (folder == NULL) {
+ g_string_append_printf (
+ err, _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ sent_folder_uri,
+ local_error ?
+ local_error->message :
+ _("Unknown error"));
+ if (local_error)
+ g_clear_error (&local_error);
+ }
+ }
+
+ if (!folder) {
+ folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+ g_object_ref (folder);
+ }
+
+ if (!camel_folder_append_message_sync (
+ folder, message, info,
+ NULL, cancellable, &local_error)) {
+
+ CamelFolder *sent_folder;
+
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+ if (folder != sent_folder) {
+ const gchar *description;
+
+ description = camel_folder_get_description (folder);
+ if (err->len)
+ g_string_append(err, "\n\n");
+ g_string_append_printf (
+ err, _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ description, local_error->message);
+ g_object_ref (sent_folder);
+ g_object_unref (folder);
+ folder = sent_folder;
+
+ g_clear_error (&local_error);
+ camel_folder_append_message_sync (
+ folder, message, info,
+ NULL, cancellable, &local_error);
+ }
+
+ if (local_error != NULL) {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (err->len)
+ g_string_append(err, "\n\n");
+ g_string_append_printf (
+ err, _("Failed to append to local 'Sent' folder: %s"),
+ local_error->message);
+ }
+ }
+ }
+
+ if (local_error == NULL) {
+ /* Mark the draft message for deletion, if present. */
+ e_mail_session_handle_draft_headers_sync (
+ session, message, cancellable, &local_error);
+ if (local_error != NULL) {
+ g_warning ("%s: Failed to handle draft headers: %s", G_STRFUNC, local_error->message);
+ g_clear_error (&local_error);
+ }
+
+ /* Set flags on the original source message, if present.
+ * Source message refers to the message being forwarded
+ * or replied to. */
+ e_mail_session_handle_source_headers_sync (
+ session, message, cancellable, &local_error);
+ if (local_error != NULL) {
+ g_warning ("%s: Failed to handle source headers: %s", G_STRFUNC, local_error->message);
+ g_clear_error (&local_error);
+ }
+ }
+
+ if (local_error == NULL) {
+ camel_folder_set_message_flags (
+ queue, uid, CAMEL_MESSAGE_DELETED |
+ CAMEL_MESSAGE_SEEN, ~0);
+ /* Sync it to disk, since if it crashes in between,
+ * we keep sending it again on next start. */
+ /* FIXME Not passing a GCancellable or GError here. */
+ camel_folder_synchronize_sync (queue, FALSE, NULL, NULL);
+ }
+
+ if (err->len) {
+ /* set the culmulative exception report */
+ g_set_error (
+ &local_error, CAMEL_ERROR,
+ CAMEL_ERROR_GENERIC, "%s", err->str);
+ }
+
+exit:
+ if (local_error != NULL)
+ g_propagate_error (error, local_error);
+
+ /* FIXME Not passing a GCancellable or GError here. */
+ if (folder) {
+ camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
+ g_object_unref (folder);
+ }
+ if (info)
+ camel_message_info_free (info);
+ g_object_unref (recipients);
+ g_object_unref (from);
+ g_free (sent_folder_uri);
+ g_free (transport_uid);
+ camel_header_raw_clear (&xev);
+ g_string_free (err, TRUE);
+ g_object_unref (message);
+}
+
+/* ** SEND MAIL QUEUE ***************************************************** */
+
+static void
+report_status (struct _send_queue_msg *m,
+ enum camel_filter_status_t status,
+ gint pc,
+ const gchar *desc,
+ ...)
+{
+ va_list ap;
+ gchar *str;
+
+ if (m->status) {
+ va_start (ap, desc);
+ str = g_strdup_vprintf (desc, ap);
+ va_end (ap);
+ m->status (m->driver, status, pc, str, m->status_data);
+ g_free (str);
+ }
+}
+
+static void
+send_queue_exec (struct _send_queue_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *sent_folder;
+ GPtrArray *uids, *send_uids = NULL;
+ gint i, j;
+ GError *local_error = NULL;
+
+ d(printf("sending queue\n"));
+
+ sent_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_SENT);
+
+ if (!(uids = camel_folder_get_uids (m->queue)))
+ return;
+
+ send_uids = g_ptr_array_sized_new (uids->len);
+ for (i = 0, j = 0; i < uids->len; i++) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_get_message_info (m->queue, uids->pdata[i]);
+ if (info) {
+ if ((camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED) == 0)
+ send_uids->pdata[j++] = uids->pdata[i];
+ camel_folder_free_message_info (m->queue, info);
+ }
+ }
+
+ send_uids->len = j;
+ if (send_uids->len == 0) {
+ /* nothing to send */
+ camel_folder_free_uids (m->queue, uids);
+ g_ptr_array_free (send_uids, TRUE);
+ return;
+ }
+
+ camel_operation_push_message (cancellable, _("Sending message"));
+
+ /* NB: This code somewhat abuses the 'exception' stuff. Apart from
+ * fatal problems, it is also used as a mechanism to accumualte
+ * warning messages and present them back to the user. */
+
+ for (i = 0, j = 0; i < send_uids->len; i++) {
+ gint pc = (100 * i) / send_uids->len;
+
+ report_status (
+ m, CAMEL_FILTER_STATUS_START, pc,
+ _("Sending message %d of %d"), i+1,
+ send_uids->len);
+
+ camel_operation_progress (
+ cancellable, (i + 1) * 100 / send_uids->len);
+
+ mail_send_message (
+ m, m->queue, send_uids->pdata[i], m->transport,
+ m->driver, cancellable, &local_error);
+ if (local_error != NULL) {
+ if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ /* merge exceptions into one */
+ if (m->base.error != NULL) {
+ gchar *old_message;
+
+ old_message = g_strdup (
+ m->base.error->message);
+ g_clear_error (&m->base.error);
+ g_set_error (
+ &m->base.error, CAMEL_ERROR,
+ CAMEL_ERROR_GENERIC,
+ "%s\n\n%s", old_message,
+ local_error->message);
+ g_free (old_message);
+
+ g_clear_error (&local_error);
+ } else {
+ g_propagate_error (&m->base.error, local_error);
+ local_error = NULL;
+ }
+
+ /* keep track of the number of failures */
+ j++;
+ } else {
+ /* transfer the USER_CANCEL error to the
+ * async op exception and then break */
+ g_propagate_error (&m->base.error, local_error);
+ local_error = NULL;
+ break;
+ }
+ }
+ }
+
+ j += (send_uids->len - i);
+
+ if (j > 0)
+ report_status (
+ m, CAMEL_FILTER_STATUS_END, 100,
+ _("Failed to send %d of %d messages"),
+ j, send_uids->len);
+ else if (g_error_matches (
+ m->base.error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Canceled."));
+ else
+ report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Complete."));
+
+ if (m->driver) {
+ g_object_unref (m->driver);
+ m->driver = NULL;
+ }
+
+ camel_folder_free_uids (m->queue, uids);
+ g_ptr_array_free (send_uids, TRUE);
+
+ /* FIXME Not passing a GCancellable or GError here. */
+ if (j <= 0 && m->base.error == NULL)
+ camel_folder_synchronize_sync (m->queue, TRUE, NULL, NULL);
+
+ /* FIXME Not passing a GCancellable or GError here. */
+ if (sent_folder)
+ camel_folder_synchronize_sync (sent_folder, FALSE, NULL, NULL);
+
+ camel_operation_pop_message (cancellable);
+}
+
+static void
+send_queue_done (struct _send_queue_msg *m)
+{
+ if (m->done)
+ m->done (m->data);
+}
+
+static gchar *
+send_queue_desc (struct _send_queue_msg *m)
+{
+ return g_strdup (_("Sending message"));
+}
+
+static void
+send_queue_free (struct _send_queue_msg *m)
+{
+ if (m->session != NULL)
+ g_object_unref (m->session);
+ if (m->driver != NULL)
+ g_object_unref (m->driver);
+ if (m->transport != NULL)
+ g_object_unref (m->transport);
+ g_object_unref (m->queue);
+}
+
+static MailMsgInfo send_queue_info = {
+ sizeof (struct _send_queue_msg),
+ (MailMsgDescFunc) send_queue_desc,
+ (MailMsgExecFunc) send_queue_exec,
+ (MailMsgDoneFunc) send_queue_done,
+ (MailMsgFreeFunc) send_queue_free
+};
+
+/* same interface as fetch_mail, just 'cause i'm lazy today
+ * (and we need to run it from the same spot?) */
+void
+mail_send_queue (EMailSession *session,
+ CamelFolder *queue,
+ CamelTransport *transport,
+ const gchar *type,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder,
+ gpointer get_data,
+ CamelFilterStatusFunc *status,
+ gpointer status_data,
+ void (*done)(gpointer data),
+ gpointer data)
+{
+ struct _send_queue_msg *m;
+
+
+ m = mail_msg_new (&send_queue_info);
+ m->session = g_object_ref (session);
+ m->queue = g_object_ref (queue);
+ m->transport = g_object_ref (transport);
+ if (G_IS_CANCELLABLE (cancellable))
+ m->base.cancellable = cancellable;
+ m->status = status;
+ m->status_data = status_data;
+ m->done = done;
+ m->data = data;
+
+ m->driver = camel_session_get_filter_driver (
+ CAMEL_SESSION (session), type, NULL);
+ camel_filter_driver_set_folder_func (m->driver, get_folder, get_data);
+
+ mail_msg_unordered_push (m);
+}
+
+/* ** TRANSFER MESSAGES **************************************************** */
+
+struct _transfer_msg {
+ MailMsg base;
+
+ EMailSession *session;
+ CamelFolder *source;
+ GPtrArray *uids;
+ gboolean delete;
+ gchar *dest_uri;
+ guint32 dest_flags;
+
+ void (*done)(gboolean ok, gpointer data);
+ gpointer data;
+};
+
+static gchar *
+transfer_messages_desc (struct _transfer_msg *m)
+{
+ return g_strdup_printf (
+ m->delete ?
+ _("Moving messages to '%s'") :
+ _("Copying messages to '%s'"),
+ m->dest_uri);
+
+}
+
+static void
+transfer_messages_exec (struct _transfer_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *dest;
+
+ dest = e_mail_session_uri_to_folder_sync (
+ m->session, m->dest_uri, m->dest_flags,
+ cancellable, error);
+ if (dest == NULL)
+ return;
+
+ if (dest == m->source) {
+ g_object_unref (dest);
+ /* no-op */
+ return;
+ }
+
+ camel_folder_freeze (m->source);
+ camel_folder_freeze (dest);
+
+ camel_folder_transfer_messages_to_sync (
+ m->source, m->uids, dest, m->delete, NULL,
+ cancellable, error);
+
+ /* make sure all deleted messages are marked as seen */
+
+ if (m->delete) {
+ gint i;
+
+ for (i = 0; i < m->uids->len; i++)
+ camel_folder_set_message_flags (
+ m->source, m->uids->pdata[i],
+ CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+ }
+
+ camel_folder_thaw (m->source);
+ camel_folder_thaw (dest);
+
+ /* FIXME Not passing a GCancellable or GError here. */
+ camel_folder_synchronize_sync (dest, FALSE, NULL, NULL);
+ g_object_unref (dest);
+}
+
+static void
+transfer_messages_done (struct _transfer_msg *m)
+{
+ if (m->done)
+ m->done (m->base.error == NULL, m->data);
+}
+
+static void
+transfer_messages_free (struct _transfer_msg *m)
+{
+ g_object_unref (m->session);
+ g_object_unref (m->source);
+ g_free (m->dest_uri);
+ em_utils_uids_free (m->uids);
+}
+
+static MailMsgInfo transfer_messages_info = {
+ sizeof (struct _transfer_msg),
+ (MailMsgDescFunc) transfer_messages_desc,
+ (MailMsgExecFunc) transfer_messages_exec,
+ (MailMsgDoneFunc) transfer_messages_done,
+ (MailMsgFreeFunc) transfer_messages_free
+};
+
+void
+mail_transfer_messages (EMailSession *session,
+ CamelFolder *source,
+ GPtrArray *uids,
+ gboolean delete_from_source,
+ const gchar *dest_uri,
+ guint32 dest_flags,
+ void (*done) (gboolean ok,
+ gpointer data),
+ gpointer data)
+{
+ struct _transfer_msg *m;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (source));
+ g_return_if_fail (uids != NULL);
+ g_return_if_fail (dest_uri != NULL);
+
+ m = mail_msg_new (&transfer_messages_info);
+ m->session = g_object_ref (session);
+ m->source = g_object_ref (source);
+ m->uids = uids;
+ m->delete = delete_from_source;
+ m->dest_uri = g_strdup (dest_uri);
+ m->dest_flags = dest_flags;
+ m->done = done;
+ m->data = data;
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ** SYNC FOLDER ********************************************************* */
+
+struct _sync_folder_msg {
+ MailMsg base;
+
+ EMailSession *session;
+ CamelFolder *folder;
+ void (*done) (CamelFolder *folder, gpointer data);
+ gpointer data;
+};
+
+static gchar *
+sync_folder_desc (struct _sync_folder_msg *m)
+{
+ return g_strdup_printf (_("Storing folder '%s'"),
+ camel_folder_get_full_name (m->folder));
+}
+
+static void
+sync_folder_exec (struct _sync_folder_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ camel_folder_synchronize_sync (
+ m->folder, FALSE, cancellable, error);
+}
+
+static void
+sync_folder_done (struct _sync_folder_msg *m)
+{
+ if (m->done)
+ m->done (m->folder, m->data);
+}
+
+static void
+sync_folder_free (struct _sync_folder_msg *m)
+{
+ if (m->session)
+ g_object_unref (m->session);
+
+ if (m->folder)
+ g_object_unref (m->folder);
+}
+
+static MailMsgInfo sync_folder_info = {
+ sizeof (struct _sync_folder_msg),
+ (MailMsgDescFunc) sync_folder_desc,
+ (MailMsgExecFunc) sync_folder_exec,
+ (MailMsgDoneFunc) sync_folder_done,
+ (MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_sync_folder (CamelFolder *folder,
+ void (*done) (CamelFolder *folder,
+ gpointer data),
+ gpointer data)
+{
+ struct _sync_folder_msg *m;
+
+ m = mail_msg_new (&sync_folder_info);
+ m->folder = g_object_ref (folder);
+ m->data = data;
+ m->done = done;
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ** SYNC STORE ********************************************************* */
+
+struct _sync_store_msg {
+ MailMsg base;
+
+ CamelStore *store;
+ gint expunge;
+ void (*done) (CamelStore *store, gpointer data);
+ gpointer data;
+};
+
+static gchar *
+sync_store_desc (struct _sync_store_msg *m)
+{
+ CamelURL *url;
+ gchar *uri, *res;
+
+ url = camel_service_get_camel_url (CAMEL_SERVICE (m->store));
+ uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
+
+ res = g_strdup_printf (m->expunge
+ ?_("Expunging and storing account '%s'")
+ :_("Storing account '%s'"),
+ uri);
+ g_free (uri);
+
+ return res;
+}
+
+static void
+sync_store_exec (struct _sync_store_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ camel_store_synchronize_sync (
+ m->store, m->expunge,
+ cancellable, error);
+}
+
+static void
+sync_store_done (struct _sync_store_msg *m)
+{
+ if (m->done)
+ m->done (m->store, m->data);
+}
+
+static void
+sync_store_free (struct _sync_store_msg *m)
+{
+ g_object_unref (m->store);
+}
+
+static MailMsgInfo sync_store_info = {
+ sizeof (struct _sync_store_msg),
+ (MailMsgDescFunc) sync_store_desc,
+ (MailMsgExecFunc) sync_store_exec,
+ (MailMsgDoneFunc) sync_store_done,
+ (MailMsgFreeFunc) sync_store_free
+};
+
+void
+mail_sync_store (CamelStore *store,
+ gint expunge,
+ void (*done) (CamelStore *store,
+ gpointer data),
+ gpointer data)
+{
+ struct _sync_store_msg *m;
+
+ m = mail_msg_new (&sync_store_info);
+ m->store = g_object_ref (store);
+ m->expunge = expunge;
+ m->data = data;
+ m->done = done;
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+static gchar *
+refresh_folder_desc (struct _sync_folder_msg *m)
+{
+ return g_strdup_printf (
+ _("Refreshing folder '%s'"),
+ camel_folder_get_full_name (m->folder));
+}
+
+static void
+refresh_folder_exec (struct _sync_folder_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ camel_folder_refresh_info_sync (
+ m->folder, cancellable, error);
+}
+
+/* we just use the sync stuff where we can, since it would be the same */
+static MailMsgInfo refresh_folder_info = {
+ sizeof (struct _sync_folder_msg),
+ (MailMsgDescFunc) refresh_folder_desc,
+ (MailMsgExecFunc) refresh_folder_exec,
+ (MailMsgDoneFunc) sync_folder_done,
+ (MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_refresh_folder (CamelFolder *folder,
+ void (*done) (CamelFolder *folder,
+ gpointer data),
+ gpointer data)
+{
+ struct _sync_folder_msg *m;
+
+ m = mail_msg_new (&refresh_folder_info);
+ m->folder = g_object_ref (folder);
+ m->data = data;
+ m->done = done;
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+static gboolean
+folder_is_from_source_uid (CamelFolder *folder,
+ const gchar *source_uid)
+{
+ CamelStore *store;
+ const gchar *uid;
+
+ store = camel_folder_get_parent_store (folder);
+ uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+ return (g_strcmp0 (uid, source_uid) == 0);
+}
+
+/* This is because pop3 accounts are hidden under local Inbox,
+ * thus whenever an expunge is done on a local trash or Inbox,
+ * then also all active pop3 accounts should be expunged. */
+static gboolean
+expunge_pop3_stores (CamelFolder *expunging,
+ EMailSession *session,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *expunging_uids;
+ GPtrArray *uids;
+ EAccount *account;
+ EIterator *iter;
+ gboolean success = TRUE;
+ guint ii;
+
+ uids = camel_folder_get_uids (expunging);
+
+ if (uids == NULL)
+ return TRUE;
+
+ expunging_uids = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ for (ii = 0; ii < uids->len; ii++) {
+ CamelMessageInfo *info;
+ CamelMessageFlags flags = 0;
+ CamelMimeMessage *message;
+ const gchar *pop3_uid;
+ const gchar *source_uid;
+
+ info = camel_folder_get_message_info (
+ expunging, uids->pdata[ii]);
+
+ if (info != NULL) {
+ flags = camel_message_info_flags (info);
+ camel_folder_free_message_info (expunging, info);
+ }
+
+ /* Only interested in deleted messages. */
+ if ((flags & CAMEL_MESSAGE_DELETED) == 0)
+ continue;
+
+ /* because the UID in the local store doesn't
+ * match with the UID in the pop3 store */
+ message = camel_folder_get_message_sync (
+ expunging, uids->pdata[ii], cancellable, NULL);
+
+ if (message == NULL)
+ continue;
+
+ pop3_uid = camel_medium_get_header (
+ CAMEL_MEDIUM (message), "X-Evolution-POP3-UID");
+ source_uid = camel_mime_message_get_source (message);
+
+ if (pop3_uid != NULL)
+ g_hash_table_insert (
+ expunging_uids,
+ g_strstrip (g_strdup (pop3_uid)),
+ g_strstrip (g_strdup (source_uid)));
+
+ g_object_unref (message);
+ }
+
+ camel_folder_free_uids (expunging, uids);
+ uids = NULL;
+
+ if (g_hash_table_size (expunging_uids) == 0) {
+ g_hash_table_destroy (expunging_uids);
+ return TRUE;
+ }
+
+ for (iter = e_list_get_iterator ((EList *) e_get_account_list ());
+ e_iterator_is_valid (iter); e_iterator_next (iter)) {
+ account = (EAccount *) e_iterator_get (iter);
+
+ if (account->enabled &&
+ account->source && account->source->url &&
+ g_str_has_prefix (account->source->url, "pop://")) {
+ CamelFolder *folder;
+ gboolean any_found = FALSE;
+
+ folder = e_mail_session_get_inbox_sync (
+ session, account->uid, cancellable, error);
+
+ /* Abort the loop on error. */
+ if (folder == NULL) {
+ success = FALSE;
+ break;
+ }
+
+ uids = camel_folder_get_uids (folder);
+ if (uids) {
+ for (ii = 0; ii < uids->len; ii++) {
+ /* ensure the ID is from this account,
+ * as it's generated by evolution */
+ const gchar *source_uid;
+
+ source_uid = g_hash_table_lookup (
+ expunging_uids, uids->pdata[ii]);
+ if (folder_is_from_source_uid (folder, source_uid)) {
+ any_found = TRUE;
+ camel_folder_delete_message (folder, uids->pdata[ii]);
+ }
+ }
+ camel_folder_free_uids (folder, uids);
+ }
+
+ if (any_found)
+ success = camel_folder_synchronize_sync (folder, TRUE, cancellable, error);
+
+ g_object_unref (folder);
+
+ /* Abort the loop on error. */
+ if (!success)
+ break;
+ }
+ }
+
+ if (iter)
+ g_object_unref (iter);
+
+ g_hash_table_destroy (expunging_uids);
+
+ return success;
+}
+
+static gchar *
+expunge_folder_desc (struct _sync_folder_msg *m)
+{
+ return g_strdup_printf (
+ _("Expunging folder '%s'"),
+ camel_folder_get_full_name (m->folder));
+}
+
+static void
+expunge_folder_exec (struct _sync_folder_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *local_inbox;
+ CamelStore *local_store;
+ CamelStore *parent_store;
+ gboolean is_local_inbox_or_trash;
+ gboolean success = TRUE;
+
+ local_inbox = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_INBOX);
+ is_local_inbox_or_trash = (m->folder == local_inbox);
+
+ local_store = e_mail_local_get_store ();
+ parent_store = camel_folder_get_parent_store (m->folder);
+
+ if (!is_local_inbox_or_trash && local_store == parent_store) {
+ CamelFolder *trash;
+
+ trash = camel_store_get_trash_folder_sync (
+ parent_store, cancellable, error);
+
+ if (trash == NULL)
+ return;
+
+ is_local_inbox_or_trash = (m->folder == trash);
+
+ g_object_unref (trash);
+ }
+
+ /* do this before expunge, to know which messages will be expunged */
+ if (is_local_inbox_or_trash)
+ success = expunge_pop3_stores (
+ m->folder, m->session, cancellable, error);
+
+ if (success)
+ camel_folder_expunge_sync (m->folder, cancellable, error);
+}
+
+/* we just use the sync stuff where we can, since it would be the same */
+static MailMsgInfo expunge_folder_info = {
+ sizeof (struct _sync_folder_msg),
+ (MailMsgDescFunc) expunge_folder_desc,
+ (MailMsgExecFunc) expunge_folder_exec,
+ (MailMsgDoneFunc) sync_folder_done,
+ (MailMsgFreeFunc) sync_folder_free
+};
+
+void
+mail_expunge_folder (EMailSession *session,
+ CamelFolder *folder)
+{
+ struct _sync_folder_msg *m;
+
+ m = mail_msg_new (&expunge_folder_info);
+ m->session = g_object_ref (session);
+ m->folder = g_object_ref (folder);
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ******************************************************************************** */
+
+struct _empty_trash_msg {
+ MailMsg base;
+
+ EMailSession *session;
+ CamelStore *store;
+};
+
+static gchar *
+empty_trash_desc (struct _empty_trash_msg *m)
+{
+ CamelService *service;
+ const gchar *display_name;
+
+ service = CAMEL_SERVICE (m->store);
+ display_name = camel_service_get_display_name (service);
+
+ return g_strdup_printf (
+ _("Emptying trash in '%s'"), display_name);
+}
+
+static void
+empty_trash_exec (struct _empty_trash_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelService *service;
+ CamelFolder *trash;
+ const gchar *uid;
+ gboolean success = TRUE;
+
+ service = CAMEL_SERVICE (m->store);
+ uid = camel_service_get_uid (service);
+
+ if (!em_utils_connect_service_sync (service, cancellable, error))
+ return;
+
+ trash = camel_store_get_trash_folder_sync (
+ m->store, cancellable, error);
+
+ if (trash == NULL)
+ return;
+
+ /* do this before expunge, to know which messages will be expunged */
+ if (g_strcmp0 (uid, "local") == 0)
+ success = expunge_pop3_stores (
+ trash, m->session, cancellable, error);
+
+ if (success)
+ camel_folder_expunge_sync (trash, cancellable, error);
+
+ g_object_unref (trash);
+}
+
+static void
+empty_trash_done (struct _empty_trash_msg *m)
+{
+}
+
+static void
+empty_trash_free (struct _empty_trash_msg *m)
+{
+ if (m->session)
+ g_object_unref (m->session);
+ if (m->store)
+ g_object_unref (m->store);
+}
+
+static MailMsgInfo empty_trash_info = {
+ sizeof (struct _empty_trash_msg),
+ (MailMsgDescFunc) empty_trash_desc,
+ (MailMsgExecFunc) empty_trash_exec,
+ (MailMsgDoneFunc) empty_trash_done,
+ (MailMsgFreeFunc) empty_trash_free
+};
+
+void
+mail_empty_trash (EMailSession *session,
+ CamelStore *store)
+{
+ struct _empty_trash_msg *m;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ m = mail_msg_new (&empty_trash_info);
+ m->session = g_object_ref (session);
+ m->store = g_object_ref (store);
+
+ mail_msg_slow_ordered_push (m);
+}
+
+/* ** Execute Shell Command ************************************************ */
+
+void
+mail_execute_shell_command (CamelFilterDriver *driver,
+ gint argc,
+ gchar **argv,
+ gpointer data)
+{
+ if (argc <= 0)
+ return;
+
+ g_spawn_async (NULL, argv, NULL, 0, NULL, data, NULL, NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct _disconnect_msg {
+ MailMsg base;
+
+ CamelStore *store;
+};
+
+static gchar *
+disconnect_service_desc (struct _disconnect_msg *m)
+{
+ gchar *name, *res;
+
+ name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
+ res = g_strdup_printf (_("Disconnecting %s"), name ? name : "");
+ g_free (name);
+
+ return res;
+}
+
+static void
+disconnect_service_exec (struct _disconnect_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ em_utils_disconnect_service_sync (
+ CAMEL_SERVICE (m->store), TRUE, cancellable, error);
+}
+
+static void
+disconnect_service_free (struct _disconnect_msg *m)
+{
+ g_object_unref (m->store);
+}
+
+static MailMsgInfo disconnect_service_info = {
+ sizeof (struct _disconnect_msg),
+ (MailMsgDescFunc) disconnect_service_desc,
+ (MailMsgExecFunc) disconnect_service_exec,
+ (MailMsgDoneFunc) NULL,
+ (MailMsgFreeFunc) disconnect_service_free
+};
+
+gint
+mail_disconnect_store (CamelStore *store)
+{
+ struct _disconnect_msg *m;
+ gint id;
+
+ g_return_val_if_fail (store != NULL, -1);
+
+ m = mail_msg_new (&disconnect_service_info);
+ m->store = g_object_ref (store);
+
+ id = m->base.seq;
+ mail_msg_unordered_push (m);
+
+ return id;
+}
+
diff --git a/libemail-engine/mail-ops.h b/libemail-engine/mail-ops.h
new file mode 100644
index 0000000..63d8528
--- /dev/null
+++ b/libemail-engine/mail-ops.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Peter Williams <peterw ximian com>
+ * Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_OPS_H
+#define MAIL_OPS_H
+
+G_BEGIN_DECLS
+
+#include <camel/camel.h>
+#include <libemail-engine/e-mail-session.h>
+
+#include <libemail-utils/mail-mt.h>
+
+void mail_transfer_messages (EMailSession *session,
+ CamelFolder *source,
+ GPtrArray *uids,
+ gboolean delete_from_source,
+ const gchar *dest_uri,
+ guint32 dest_flags,
+ void (*done) (gboolean ok, gpointer data),
+ gpointer data);
+
+void mail_sync_folder (CamelFolder *folder,
+ void (*done) (CamelFolder *folder, gpointer data), gpointer data);
+
+void mail_sync_store (CamelStore *store, gint expunge,
+ void (*done) (CamelStore *store, gpointer data), gpointer data);
+
+void mail_refresh_folder (CamelFolder *folder,
+ void (*done) (CamelFolder *folder, gpointer data),
+ gpointer data);
+
+void mail_expunge_folder (EMailSession *session,
+ CamelFolder *folder);
+
+void mail_empty_trash (EMailSession *session,
+ CamelStore *store);
+
+/* transfer (copy/move) a folder */
+void mail_xfer_folder (const gchar *src_uri, const gchar *dest_uri, gboolean remove_source,
+ void (*done) (gchar *src_uri, gchar *dest_uri, gboolean remove_source,
+ CamelFolder *folder, gpointer data),
+ gpointer data);
+
+/* yeah so this is messy, but it does a lot, maybe i can consolidate all user_data's to be the one */
+void mail_send_queue (EMailSession *session,
+ CamelFolder *queue,
+ CamelTransport *transport,
+ const gchar *type,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder,
+ gpointer get_data,
+ CamelFilterStatusFunc *status,
+ gpointer status_data,
+ void (*done)(gpointer data),
+ gpointer data);
+
+void mail_fetch_mail (CamelStore *store,
+ gint keep,
+ const gchar *type,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder,
+ gpointer get_data,
+ CamelFilterStatusFunc *status,
+ gpointer status_data,
+ void (*done)(gpointer data),
+ gpointer data);
+
+void mail_filter_folder (EMailSession *session,
+ CamelFolder *source_folder,
+ GPtrArray *uids,
+ const gchar *type,
+ gboolean notify);
+
+/* filter driver execute shell command async callback */
+void mail_execute_shell_command (CamelFilterDriver *driver, gint argc, gchar **argv, gpointer data);
+
+gint mail_disconnect_store (CamelStore *store);
+
+G_END_DECLS
+
+#endif /* MAIL_OPS_H */
diff --git a/libemail-engine/mail-tools.c b/libemail-engine/mail-tools.c
new file mode 100644
index 0000000..135df53
--- /dev/null
+++ b/libemail-engine/mail-tools.c
@@ -0,0 +1,234 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Dan Winship <danw ximian com>
+ * Peter Williams <peterw ximian com>
+ * Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <glib/gi18n.h>
+
+#include "e-mail-session.h"
+#include "mail-folder-cache.h"
+#include "mail-tools.h"
+
+/* **************************************** */
+
+#ifndef G_OS_WIN32
+
+static gchar *
+mail_tool_get_local_movemail_path (CamelStore *store,
+ GError **error)
+{
+ const gchar *uid;
+ guchar *safe_uid, *c;
+ const gchar *data_dir;
+ gchar *path, *full;
+ struct stat st;
+
+ uid = camel_service_get_uid (CAMEL_SERVICE (store));
+ safe_uid = (guchar *) g_strdup ((const gchar *) uid);
+ for (c = safe_uid; *c; c++)
+ if (strchr("/:;=|%&#!*^()\\, ", *c) || !isprint((gint) *c))
+ *c = '_';
+
+ data_dir = mail_session_get_data_dir ();
+ path = g_build_filename (data_dir, "spool", NULL);
+
+ if (g_stat (path, &st) == -1 && g_mkdir_with_parents (path, 0700) == -1) {
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Could not create spool directory '%s': %s"),
+ path, g_strerror (errno));
+ g_free (path);
+ return NULL;
+ }
+
+ full = g_strdup_printf("%s/movemail.%s", path, safe_uid);
+ g_free (path);
+ g_free (safe_uid);
+
+ return full;
+}
+
+#endif
+
+gchar *
+mail_tool_do_movemail (CamelStore *store,
+ GError **error)
+{
+#ifndef G_OS_WIN32
+ gchar *dest_path;
+ struct stat sb;
+ CamelURL *url;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+
+ url = camel_service_get_camel_url (CAMEL_SERVICE (store));
+
+ if (strcmp (url->protocol, "mbox") != 0) {
+ /* This is really only an internal error anyway */
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_URL_INVALID,
+ _("Trying to movemail a non-mbox source '%s'"),
+ camel_service_get_uid (CAMEL_SERVICE (store)));
+ return NULL;
+ }
+
+ /* Set up our destination. */
+ dest_path = mail_tool_get_local_movemail_path (store, error);
+ if (dest_path == NULL)
+ return NULL;
+
+ /* Movemail from source (source_url) to dest_path */
+ success = camel_movemail (url->path, dest_path, error) != -1;
+
+ if (g_stat (dest_path, &sb) < 0 || sb.st_size == 0) {
+ g_unlink (dest_path); /* Clean up the movemail.foo file. */
+ g_free (dest_path);
+ return NULL;
+ }
+
+ if (!success) {
+ g_free (dest_path);
+ return NULL;
+ }
+
+ return dest_path;
+#else
+ /* Unclear yet whether camel-movemail etc makes any sense on
+ * Win32, at least it is not ported yet.
+ */
+ g_warning("%s: Not implemented", __FUNCTION__);
+ return NULL;
+#endif
+}
+
+gchar *
+mail_tool_generate_forward_subject (CamelMimeMessage *msg)
+{
+ const gchar *subject;
+ gchar *fwd_subj;
+ const gint max_subject_length = 1024;
+
+ subject = camel_mime_message_get_subject (msg);
+
+ if (subject && *subject) {
+ /* Truncate insanely long subjects */
+ if (strlen (subject) < max_subject_length) {
+ fwd_subj = g_strdup_printf ("[Fwd: %s]", subject);
+ } else {
+ /* We can't use %.*s because it depends on the
+ * locale being C/POSIX or UTF-8 to work correctly
+ * in glibc. */
+ fwd_subj = g_malloc (max_subject_length + 11);
+ memcpy (fwd_subj, "[Fwd: ", 6);
+ memcpy (fwd_subj + 6, subject, max_subject_length);
+ memcpy (fwd_subj + 6 + max_subject_length, "...]", 5);
+ }
+ } else {
+ const CamelInternetAddress *from;
+ gchar *fromstr;
+
+ from = camel_mime_message_get_from (msg);
+ if (from) {
+ fromstr = camel_address_format (CAMEL_ADDRESS (from));
+ fwd_subj = g_strdup_printf ("[Fwd: %s]", fromstr);
+ g_free (fromstr);
+ } else
+ fwd_subj = g_strdup ("[Fwd: No Subject]");
+ }
+
+ return fwd_subj;
+}
+
+struct _camel_header_raw *
+mail_tool_remove_xevolution_headers (CamelMimeMessage *message)
+{
+ struct _camel_header_raw *scan, *list = NULL;
+
+ for (scan = ((CamelMimePart *) message)->headers; scan; scan = scan->next)
+ if (!strncmp(scan->name, "X-Evolution", 11))
+ camel_header_raw_append (&list, scan->name, scan->value, scan->offset);
+
+ for (scan = list; scan; scan = scan->next)
+ camel_medium_remove_header ((CamelMedium *) message, scan->name);
+
+ return list;
+}
+
+void
+mail_tool_restore_xevolution_headers (CamelMimeMessage *message,
+ struct _camel_header_raw *xev)
+{
+ CamelMedium *medium;
+
+ medium = CAMEL_MEDIUM (message);
+
+ for (; xev; xev = xev->next)
+ camel_medium_add_header (medium, xev->name, xev->value);
+}
+
+CamelMimePart *
+mail_tool_make_message_attachment (CamelMimeMessage *message)
+{
+ CamelMimePart *part;
+ const gchar *subject;
+ struct _camel_header_raw *xev;
+ gchar *desc;
+
+ subject = camel_mime_message_get_subject (message);
+ if (subject)
+ desc = g_strdup_printf (_("Forwarded message - %s"), subject);
+ else
+ desc = g_strdup (_("Forwarded message"));
+
+ /* rip off the X-Evolution headers */
+ xev = mail_tool_remove_xevolution_headers (message);
+ camel_header_raw_clear (&xev);
+
+ /* remove Bcc headers */
+ camel_medium_remove_header (CAMEL_MEDIUM (message), "Bcc");
+
+ part = camel_mime_part_new ();
+ camel_mime_part_set_disposition (part, "inline");
+ camel_mime_part_set_description (part, desc);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (message));
+ camel_mime_part_set_content_type (part, "message/rfc822");
+ g_free (desc);
+
+ return part;
+}
diff --git a/libemail-engine/mail-tools.h b/libemail-engine/mail-tools.h
new file mode 100644
index 0000000..94b19c0
--- /dev/null
+++ b/libemail-engine/mail-tools.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Peter Williams <peterw ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef MAIL_TOOLS_H
+#define MAIL_TOOLS_H
+
+#include <camel/camel.h>
+
+/* Does a camel_movemail into the local movemail folder
+ * and returns the path to the new movemail folder that was created. which shoudl be freed later */
+gchar *mail_tool_do_movemail (CamelStore *store, GError **error);
+
+struct _camel_header_raw *mail_tool_remove_xevolution_headers (CamelMimeMessage *message);
+void mail_tool_restore_xevolution_headers (CamelMimeMessage *message, struct _camel_header_raw *);
+
+/* Generates the subject for a message forwarding @msg */
+gchar *mail_tool_generate_forward_subject (CamelMimeMessage *msg);
+
+/* Make a message into an attachment */
+CamelMimePart *mail_tool_make_message_attachment (CamelMimeMessage *message);
+
+#endif
diff --git a/libemail-utils/Makefile.am b/libemail-utils/Makefile.am
new file mode 100644
index 0000000..6bc3164
--- /dev/null
+++ b/libemail-utils/Makefile.am
@@ -0,0 +1,57 @@
+
+lib_LTLIBRARIES = libemail-utils.la
+
+AM_CFLAGS = -I. -I$(top_builddir) @GNOME_PLATFORM_CFLAGS@ -Wall -Werror -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\""
+AM_LDFLAGS = @GNOME_PLATFORM_LIBS@
+
+include $(top_srcdir)/glib-gen.mak
+glib_enum_prefix=e
+MARSHAL_GENERATED = e-marshal.c e-marshal.h
+
+libemail_utils_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(EMAIL_UTILS_CFLAGS) \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"
+
+libemail_utils_la_LIBADD = \
+ $(CAMEL_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(EMAIL_UTILS_LIBS)
+
+libemail_utils_la_LDFLAGS =
+
+libemail_utils_la_SOURCES = \
+ e-marshal.c \
+ e-account-utils.c \
+ mail-mt.c \
+ e-signature.c \
+ e-signature-list.c \
+ e-signature-utils.c \
+ gconf-bridge.c
+
+
+libmailutilsincludedir = $(privincludedir)/libemail-utils
+libmailutilsinclude_HEADERS = \
+ e-marshal.h \
+ e-account-utils.h \
+ mail-mt.h \
+ e-signature.h \
+ e-signature-list.h \
+ e-signature-utils.h \
+ gconf-bridge.h
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libemail-utils.pc
+
+BUILT_SOURCES = $(MARSHAL_GENERATED)
+
+EXTRA_DIST = e-marshal.list
+CLEANFILES = $(BUILT_SOURCES)
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+dist-hook:
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
diff --git a/libemail-utils/e-account-utils.c b/libemail-utils/e-account-utils.c
new file mode 100644
index 0000000..6e64d45
--- /dev/null
+++ b/libemail-utils/e-account-utils.c
@@ -0,0 +1,252 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+/**
+ * SECTION: e-account-utils
+ * @include: e-util/e-account-utils.h
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-account-utils.h"
+
+#include <string.h>
+#include <gconf/gconf-client.h>
+
+static EAccountList *global_account_list;
+
+static gboolean
+account_has_transport_url (EAccount *account)
+{
+ return (account != NULL) &&
+ (account->enabled) &&
+ (account->transport != NULL) &&
+ (account->transport->url != NULL) &&
+ (account->transport->url[0] != '\0');
+}
+
+/**
+ * e_get_account_list:
+ *
+ * Returns the global #EAccountList.
+ *
+ * Returns: the global #EAccountList
+ **/
+EAccountList *
+e_get_account_list (void)
+{
+ if (G_UNLIKELY (global_account_list == NULL)) {
+ GConfClient *client;
+
+ client = gconf_client_get_default ();
+ global_account_list = e_account_list_new (client);
+ g_object_unref (client);
+ }
+
+ g_return_val_if_fail (global_account_list != NULL, NULL);
+
+ return global_account_list;
+}
+
+/**
+ * e_get_default_account:
+ *
+ * Returns the #EAccount marked as the default mail account.
+ *
+ * Returns: the default #EAccount
+ **/
+EAccount *
+e_get_default_account (void)
+{
+ EAccountList *account_list;
+ const EAccount *account;
+
+ account_list = e_get_account_list ();
+ account = e_account_list_get_default (account_list);
+
+ /* XXX EAccountList misuses const. */
+ return (EAccount *) account;
+}
+
+/**
+ * e_set_default_account:
+ * @account: an #EAccount
+ *
+ * Marks @account as the default mail account.
+ **/
+void
+e_set_default_account (EAccount *account)
+{
+ EAccountList *account_list;
+
+ g_return_if_fail (E_IS_ACCOUNT (account));
+
+ account_list = e_get_account_list ();
+ e_account_list_set_default (account_list, account);
+}
+
+/**
+ * e_get_account_by_name:
+ * @name: a mail account name
+ *
+ * Returns the #EAccount with the given name, or %NULL if no such
+ * account exists.
+ *
+ * Returns: an #EAccount having the given account name, or %NULL
+ **/
+EAccount *
+e_get_account_by_name (const gchar *name)
+{
+ EAccountList *account_list;
+ const EAccount *account;
+ e_account_find_t find;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ find = E_ACCOUNT_FIND_NAME;
+ account_list = e_get_account_list ();
+ account = e_account_list_find (account_list, find, name);
+
+ /* XXX EAccountList misuses const. */
+ return (EAccount *) account;
+}
+
+/**
+ * e_get_account_by_uid:
+ * @uid: a mail account UID
+ *
+ * Returns the #EAccount corresponding to the given unique identity (UID),
+ * or %NULL if no such account exists. The @uid can refer to an #EAccount
+ * UID, a #CamelStore UID, or even a #CamelTransport UID.
+ *
+ * Returns: the corresponding #EAccount, or %NULL
+ **/
+EAccount *
+e_get_account_by_uid (const gchar *uid)
+{
+ EAccountList *account_list;
+ const EAccount *account;
+ e_account_find_t find;
+ gchar *account_uid;
+
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ /* EAccounts have the following invariant:
+ *
+ * CamelStore UID == EAccount UID
+ * CamelTransport UID == EAccount UID + "-transport"
+ *
+ * Therefore we can detect CamelTransport UIDs and convert them.
+ */
+ if (g_str_has_suffix (uid, "-transport"))
+ account_uid = g_strndup (uid, strlen (uid) - 10);
+ else
+ account_uid = g_strdup (uid);
+
+ find = E_ACCOUNT_FIND_UID;
+ account_list = e_get_account_list ();
+ account = e_account_list_find (account_list, find, account_uid);
+
+ g_free (account_uid);
+
+ /* XXX EAccountList misuses const. */
+ return (EAccount *) account;
+}
+
+/**
+ * e_get_any_enabled_account:
+ *
+ * Returns the default mail account if it's enabled, otherwise the first
+ * enabled mail account in the global #EAccountList, or finally %NULL if
+ * all mail accounts are disabled or none exist.
+ *
+ * Returns: an enabled #EAccount, or %NULL if there are none
+ **/
+EAccount *
+e_get_any_enabled_account (void)
+{
+ EAccount *account;
+ EAccountList *account_list;
+ EIterator *iter;
+
+ account = e_get_default_account ();
+ if (account != NULL && account->enabled)
+ return account;
+
+ account = NULL;
+
+ account_list = e_get_account_list ();
+ iter = e_list_get_iterator (E_LIST (account_list));
+
+ while (e_iterator_is_valid (iter) && account == NULL) {
+ EAccount *candidate;
+
+ /* XXX EIterator misuses const. */
+ candidate = (EAccount *) e_iterator_get (iter);
+
+ if (candidate->enabled)
+ account = candidate;
+ else
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ return account;
+}
+
+/**
+ * e_get_default_transport:
+ *
+ * Returns transport information for the default account if it's enabled and
+ * has transport information, or else from the first enabled mail account in
+ * the global #EAccountList that has transport information, or finally %NULL
+ * if no transport information could be found.
+ *
+ * Returns: an #EAccount with transport info, or %NULL
+ **/
+EAccount *
+e_get_default_transport (void)
+{
+ EAccountList *account_list;
+ EAccount *account;
+ EIterator *iterator;
+
+ account = e_get_default_account ();
+ if (account_has_transport_url (account))
+ return account;
+
+ account_list = e_get_account_list ();
+ iterator = e_list_get_iterator (E_LIST (account_list));
+
+ while (e_iterator_is_valid (iterator)) {
+ /* XXX EIterator misuses const. */
+ account = (EAccount *) e_iterator_get (iterator);
+ if (account_has_transport_url (account)) {
+ g_object_unref (iterator);
+ return account;
+ }
+ e_iterator_next (iterator);
+ }
+
+ g_object_unref (iterator);
+
+ return NULL;
+}
+
diff --git a/libemail-utils/e-account-utils.h b/libemail-utils/e-account-utils.h
new file mode 100644
index 0000000..d7dbd28
--- /dev/null
+++ b/libemail-utils/e-account-utils.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_ACCOUNT_UTILS_H
+#define E_ACCOUNT_UTILS_H
+
+#include <camel/camel.h>
+#include <libedataserver/e-account.h>
+#include <libedataserver/e-account-list.h>
+
+G_BEGIN_DECLS
+
+EAccountList * e_get_account_list (void);
+EAccount * e_get_default_account (void);
+void e_set_default_account (EAccount *account);
+EAccount * e_get_account_by_name (const gchar *name);
+EAccount * e_get_account_by_uid (const gchar *uid);
+EAccount * e_get_any_enabled_account (void);
+EAccount * e_get_default_transport (void);
+
+G_END_DECLS
+
+#endif /* E_ACCOUNT_UTILS_H */
diff --git a/libemail-utils/e-marshal.c b/libemail-utils/e-marshal.c
new file mode 100644
index 0000000..0c7c621
--- /dev/null
+++ b/libemail-utils/e-marshal.c
@@ -0,0 +1,2492 @@
+#include "e-marshal.h"
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:BOXED (e-marshal.list:1) */
+void
+e_marshal_BOOLEAN__BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:BOXED,STRING (e-marshal.list:2) */
+void
+e_marshal_BOOLEAN__BOXED_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__BOXED_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__BOXED_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:BOXED,POINTER,POINTER (e-marshal.list:3) */
+void
+e_marshal_BOOLEAN__BOXED_POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__BOXED_POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,INT,BOXED (e-marshal.list:4) */
+void
+e_marshal_BOOLEAN__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__INT_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_boxed (param_values + 3),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,INT,OBJECT,INT,INT,UINT (e-marshal.list:5) */
+void
+e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gint arg_4,
+ gint arg_5,
+ guint arg_6,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 7);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ g_marshal_value_peek_int (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ g_marshal_value_peek_uint (param_values + 6),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,POINTER,INT,BOXED (e-marshal.list:6) */
+void
+e_marshal_BOOLEAN__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__INT_POINTER_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__INT_POINTER_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_boxed (param_values + 4),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT (e-marshal.list:7) */
+void
+e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gint arg_5,
+ gint arg_6,
+ guint arg_7,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 8);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_object (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ g_marshal_value_peek_int (param_values + 6),
+ g_marshal_value_peek_uint (param_values + 7),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:NONE (e-marshal.list:8) */
+void
+e_marshal_BOOLEAN__VOID (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__VOID) (gpointer data1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__VOID callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 1);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__VOID) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:OBJECT (e-marshal.list:9) */
+void
+e_marshal_BOOLEAN__OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:10) */
+void
+e_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gdouble arg_3,
+ gboolean arg_4,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_double (param_values + 3),
+ g_marshal_value_peek_boolean (param_values + 4),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER (e-marshal.list:11) */
+void
+e_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,BOOLEAN,POINTER (e-marshal.list:12) */
+void
+e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_BOOLEAN_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gboolean arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_BOOLEAN_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_BOOLEAN_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_boolean (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,POINTER (e-marshal.list:13) */
+void
+e_marshal_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:POINTER,POINTER,POINTER,POINTER (e-marshal.list:14) */
+void
+e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER_POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer arg_4,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER_POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ g_marshal_value_peek_pointer (param_values + 4),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:STRING (e-marshal.list:15) */
+void
+e_marshal_BOOLEAN__STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* BOOLEAN:STRING,INT (e-marshal.list:16) */
+void
+e_marshal_BOOLEAN__STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__STRING_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+/* DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:17) */
+void
+e_marshal_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gdouble (*GMarshalFunc_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gdouble arg_3,
+ gboolean arg_4,
+ gpointer data2);
+ register GMarshalFunc_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gdouble v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_double (param_values + 3),
+ g_marshal_value_peek_boolean (param_values + 4),
+ data2);
+
+ g_value_set_double (return_value, v_return);
+}
+
+/* INT:BOXED (e-marshal.list:18) */
+void
+e_marshal_INT__BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__BOXED) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_INT__BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT (e-marshal.list:19) */
+void
+e_marshal_INT__INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__INT) (gpointer data1,
+ gint arg_1,
+ gpointer data2);
+ register GMarshalFunc_INT__INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__INT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT,INT,BOXED (e-marshal.list:20) */
+void
+e_marshal_INT__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__INT_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_INT__INT_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_boxed (param_values + 3),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* INT:INT,POINTER,INT,BOXED (e-marshal.list:21) */
+void
+e_marshal_INT__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__INT_POINTER_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer data2);
+ register GMarshalFunc_INT__INT_POINTER_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_boxed (param_values + 4),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* INT:OBJECT,BOXED (e-marshal.list:22) */
+void
+e_marshal_INT__OBJECT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__OBJECT_BOXED) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_INT__OBJECT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__OBJECT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_boxed (param_values + 2),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* INT:POINTER (e-marshal.list:23) */
+void
+e_marshal_INT__POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gint (*GMarshalFunc_INT__POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_INT__POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gint v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_INT__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ data2);
+
+ g_value_set_int (return_value, v_return);
+}
+
+/* NONE:BOXED,INT (e-marshal.list:24) */
+void
+e_marshal_VOID__BOXED_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOXED_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOXED_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOXED_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* NONE:ENUM,OBJECT,OBJECT (e-marshal.list:25) */
+void
+e_marshal_VOID__ENUM_OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__ENUM_OBJECT_OBJECT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__ENUM_OBJECT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__ENUM_OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_enum (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ data2);
+}
+
+/* NONE:INT,INT (e-marshal.list:26) */
+void
+e_marshal_VOID__INT_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* NONE:INT,INT,BOXED (e-marshal.list:27) */
+void
+e_marshal_VOID__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_boxed (param_values + 3),
+ data2);
+}
+
+/* NONE:INT,INT,OBJECT (e-marshal.list:28) */
+void
+e_marshal_VOID__INT_INT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ data2);
+}
+
+/* NONE:INT,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:29) */
+void
+e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_BOXED_UINT_UINT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer arg_4,
+ guint arg_5,
+ guint arg_6,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT_OBJECT_BOXED_UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 7);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT_OBJECT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ g_marshal_value_peek_boxed (param_values + 4),
+ g_marshal_value_peek_uint (param_values + 5),
+ g_marshal_value_peek_uint (param_values + 6),
+ data2);
+}
+
+/* NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:30) */
+void
+e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gint arg_4,
+ gint arg_5,
+ gpointer arg_6,
+ guint arg_7,
+ guint arg_8,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 9);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ g_marshal_value_peek_int (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ g_marshal_value_peek_boxed (param_values + 6),
+ g_marshal_value_peek_uint (param_values + 7),
+ g_marshal_value_peek_uint (param_values + 8),
+ data2);
+}
+
+/* NONE:INT,INT,OBJECT,UINT (e-marshal.list:31) */
+void
+e_marshal_VOID__INT_INT_OBJECT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT_OBJECT_UINT) (gpointer data1,
+ gint arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ guint arg_4,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_INT_OBJECT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT_OBJECT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ g_marshal_value_peek_uint (param_values + 4),
+ data2);
+}
+
+/* NONE:INT,OBJECT (e-marshal.list:32) */
+void
+e_marshal_VOID__INT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_OBJECT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ data2);
+}
+
+/* NONE:INT,POINTER (e-marshal.list:33) */
+void
+e_marshal_VOID__INT_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+
+/* NONE:INT,POINTER,INT,BOXED (e-marshal.list:34) */
+void
+e_marshal_VOID__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_BOXED) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER_INT_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER_INT_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_boxed (param_values + 4),
+ data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT (e-marshal.list:35) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_object (param_values + 4),
+ data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:36) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer arg_5,
+ guint arg_6,
+ guint arg_7,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 8);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_object (param_values + 4),
+ g_marshal_value_peek_boxed (param_values + 5),
+ g_marshal_value_peek_uint (param_values + 6),
+ g_marshal_value_peek_uint (param_values + 7),
+ data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:37) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gint arg_5,
+ gint arg_6,
+ gpointer arg_7,
+ guint arg_8,
+ guint arg_9,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 10);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_object (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ g_marshal_value_peek_int (param_values + 6),
+ g_marshal_value_peek_boxed (param_values + 7),
+ g_marshal_value_peek_uint (param_values + 8),
+ g_marshal_value_peek_uint (param_values + 9),
+ data2);
+}
+
+/* NONE:INT,POINTER,INT,OBJECT,UINT (e-marshal.list:38) */
+void
+e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_UINT) (gpointer data1,
+ gint arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ guint arg_5,
+ gpointer data2);
+ register GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_POINTER_INT_OBJECT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_object (param_values + 4),
+ g_marshal_value_peek_uint (param_values + 5),
+ data2);
+}
+
+/* NONE:LONG,LONG (e-marshal.list:39) */
+void
+e_marshal_VOID__LONG_LONG (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__LONG_LONG) (gpointer data1,
+ glong arg_1,
+ glong arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__LONG_LONG callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__LONG_LONG) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_long (param_values + 1),
+ g_marshal_value_peek_long (param_values + 2),
+ data2);
+}
+
+/* NONE:OBJECT,BOOLEAN (e-marshal.list:40) */
+void
+e_marshal_VOID__OBJECT_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_BOOLEAN) (gpointer data1,
+ gpointer arg_1,
+ gboolean arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_BOOLEAN callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_boolean (param_values + 2),
+ data2);
+}
+
+/* NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:41) */
+void
+e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gdouble arg_3,
+ gboolean arg_4,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_double (param_values + 3),
+ g_marshal_value_peek_boolean (param_values + 4),
+ data2);
+}
+
+/* NONE:OBJECT,OBJECT (e-marshal.list:42) */
+void
+e_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ data2);
+}
+
+/* NONE:OBJECT,STRING (e-marshal.list:43) */
+void
+e_marshal_VOID__OBJECT_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ data2);
+}
+
+/* NONE:OBJECT,STRING,INT (e-marshal.list:44) */
+void
+e_marshal_VOID__OBJECT_STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_STRING_INT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_STRING_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ data2);
+}
+
+/* NONE:OBJECT,STRING,INT,STRING,STRING,STRING (e-marshal.list:45) */
+void
+e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_STRING_INT_STRING_STRING_STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer arg_4,
+ gpointer arg_5,
+ gpointer arg_6,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_STRING_INT_STRING_STRING_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 7);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_STRING_INT_STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_string (param_values + 4),
+ g_marshal_value_peek_string (param_values + 5),
+ g_marshal_value_peek_string (param_values + 6),
+ data2);
+}
+
+/* NONE:OBJECT,STRING,STRING (e-marshal.list:46) */
+void
+e_marshal_VOID__OBJECT_STRING_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_STRING_STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_STRING_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ g_marshal_value_peek_string (param_values + 3),
+ data2);
+}
+
+/* NONE:OBJECT,STRING,UINT (e-marshal.list:47) */
+void
+e_marshal_VOID__OBJECT_STRING_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_STRING_UINT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ guint arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_STRING_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_STRING_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ g_marshal_value_peek_uint (param_values + 3),
+ data2);
+}
+
+/* NONE:POINTER,INT (e-marshal.list:48) */
+void
+e_marshal_VOID__POINTER_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* NONE:POINTER,INT,INT,INT,INT (e-marshal.list:49) */
+void
+e_marshal_VOID__POINTER_INT_INT_INT_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_INT_INT_INT_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gint arg_3,
+ gint arg_4,
+ gint arg_5,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_INT_INT_INT_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 6);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_int (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ data2);
+}
+
+/* NONE:POINTER,INT,OBJECT (e-marshal.list:50) */
+void
+e_marshal_VOID__POINTER_INT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_INT_OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_INT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_INT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_object (param_values + 3),
+ data2);
+}
+
+/* NONE:POINTER,OBJECT (e-marshal.list:51) */
+void
+e_marshal_VOID__POINTER_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ data2);
+}
+
+/* NONE:POINTER,POINTER (e-marshal.list:52) */
+void
+e_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+
+/* NONE:POINTER,POINTER,INT (e-marshal.list:53) */
+void
+e_marshal_VOID__POINTER_POINTER_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_POINTER_INT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gint arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_POINTER_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_POINTER_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ data2);
+}
+
+/* NONE:STRING,DOUBLE (e-marshal.list:54) */
+void
+e_marshal_VOID__STRING_DOUBLE (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_DOUBLE) (gpointer data1,
+ gpointer arg_1,
+ gdouble arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_DOUBLE callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ data2);
+}
+
+/* NONE:STRING,INT (e-marshal.list:55) */
+void
+e_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* NONE:STRING,INT,INT (e-marshal.list:56) */
+void
+e_marshal_VOID__STRING_INT_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gint arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_INT_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ data2);
+}
+
+/* NONE:STRING,POINTER,POINTER (e-marshal.list:57) */
+void
+e_marshal_VOID__STRING_POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ data2);
+}
+
+/* NONE:STRING,STRING (e-marshal.list:58) */
+void
+e_marshal_VOID__STRING_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ data2);
+}
+
+/* NONE:UINT,STRING (e-marshal.list:59) */
+void
+e_marshal_VOID__UINT_STRING (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_STRING) (gpointer data1,
+ guint arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_STRING callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_STRING) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_string (param_values + 2),
+ data2);
+}
+
+/* STRING:NONE (e-marshal.list:60) */
+void
+e_marshal_STRING__VOID (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gchar* (*GMarshalFunc_STRING__VOID) (gpointer data1,
+ gpointer data2);
+ register GMarshalFunc_STRING__VOID callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gchar* v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 1);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_STRING__VOID) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ data2);
+
+ g_value_take_string (return_value, v_return);
+}
+
diff --git a/libemail-utils/e-marshal.h b/libemail-utils/e-marshal.h
new file mode 100644
index 0000000..92c89d2
--- /dev/null
+++ b/libemail-utils/e-marshal.h
@@ -0,0 +1,530 @@
+
+#ifndef __e_marshal_MARSHAL_H__
+#define __e_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* BOOLEAN:BOXED (e-marshal.list:1) */
+extern void e_marshal_BOOLEAN__BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:BOXED,STRING (e-marshal.list:2) */
+extern void e_marshal_BOOLEAN__BOXED_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:BOXED,POINTER,POINTER (e-marshal.list:3) */
+extern void e_marshal_BOOLEAN__BOXED_POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:INT,INT,BOXED (e-marshal.list:4) */
+extern void e_marshal_BOOLEAN__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:INT,INT,OBJECT,INT,INT,UINT (e-marshal.list:5) */
+extern void e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:INT,POINTER,INT,BOXED (e-marshal.list:6) */
+extern void e_marshal_BOOLEAN__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT (e-marshal.list:7) */
+extern void e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:NONE (e-marshal.list:8) */
+extern void e_marshal_BOOLEAN__VOID (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_BOOLEAN__NONE e_marshal_BOOLEAN__VOID
+
+/* BOOLEAN:OBJECT (e-marshal.list:9) */
+extern void e_marshal_BOOLEAN__OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:10) */
+extern void e_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:POINTER (e-marshal.list:11) */
+extern void e_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:POINTER,BOOLEAN,POINTER (e-marshal.list:12) */
+extern void e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:POINTER,POINTER (e-marshal.list:13) */
+extern void e_marshal_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:POINTER,POINTER,POINTER,POINTER (e-marshal.list:14) */
+extern void e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:STRING (e-marshal.list:15) */
+extern void e_marshal_BOOLEAN__STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN:STRING,INT (e-marshal.list:16) */
+extern void e_marshal_BOOLEAN__STRING_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:17) */
+extern void e_marshal_DOUBLE__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:BOXED (e-marshal.list:18) */
+extern void e_marshal_INT__BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:INT (e-marshal.list:19) */
+extern void e_marshal_INT__INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:INT,INT,BOXED (e-marshal.list:20) */
+extern void e_marshal_INT__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:INT,POINTER,INT,BOXED (e-marshal.list:21) */
+extern void e_marshal_INT__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:OBJECT,BOXED (e-marshal.list:22) */
+extern void e_marshal_INT__OBJECT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* INT:POINTER (e-marshal.list:23) */
+extern void e_marshal_INT__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* NONE:BOXED,INT (e-marshal.list:24) */
+extern void e_marshal_VOID__BOXED_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__BOXED_INT e_marshal_VOID__BOXED_INT
+
+/* NONE:ENUM,OBJECT,OBJECT (e-marshal.list:25) */
+extern void e_marshal_VOID__ENUM_OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__ENUM_OBJECT_OBJECT e_marshal_VOID__ENUM_OBJECT_OBJECT
+
+/* NONE:INT,INT (e-marshal.list:26) */
+extern void e_marshal_VOID__INT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT e_marshal_VOID__INT_INT
+
+/* NONE:INT,INT,BOXED (e-marshal.list:27) */
+extern void e_marshal_VOID__INT_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT_BOXED e_marshal_VOID__INT_INT_BOXED
+
+/* NONE:INT,INT,OBJECT (e-marshal.list:28) */
+extern void e_marshal_VOID__INT_INT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT e_marshal_VOID__INT_INT_OBJECT
+
+/* NONE:INT,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:29) */
+extern void e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_BOXED_UINT_UINT e_marshal_VOID__INT_INT_OBJECT_BOXED_UINT_UINT
+
+/* NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:30) */
+extern void e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT e_marshal_VOID__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT
+
+/* NONE:INT,INT,OBJECT,UINT (e-marshal.list:31) */
+extern void e_marshal_VOID__INT_INT_OBJECT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_INT_OBJECT_UINT e_marshal_VOID__INT_INT_OBJECT_UINT
+
+/* NONE:INT,OBJECT (e-marshal.list:32) */
+extern void e_marshal_VOID__INT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_OBJECT e_marshal_VOID__INT_OBJECT
+
+/* NONE:INT,POINTER (e-marshal.list:33) */
+extern void e_marshal_VOID__INT_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER e_marshal_VOID__INT_POINTER
+
+/* NONE:INT,POINTER,INT,BOXED (e-marshal.list:34) */
+extern void e_marshal_VOID__INT_POINTER_INT_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_BOXED e_marshal_VOID__INT_POINTER_INT_BOXED
+
+/* NONE:INT,POINTER,INT,OBJECT (e-marshal.list:35) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT e_marshal_VOID__INT_POINTER_INT_OBJECT
+
+/* NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT (e-marshal.list:36) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT
+
+/* NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT (e-marshal.list:37) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT
+
+/* NONE:INT,POINTER,INT,OBJECT,UINT (e-marshal.list:38) */
+extern void e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__INT_POINTER_INT_OBJECT_UINT e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT
+
+/* NONE:LONG,LONG (e-marshal.list:39) */
+extern void e_marshal_VOID__LONG_LONG (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__LONG_LONG e_marshal_VOID__LONG_LONG
+
+/* NONE:OBJECT,BOOLEAN (e-marshal.list:40) */
+extern void e_marshal_VOID__OBJECT_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_BOOLEAN e_marshal_VOID__OBJECT_BOOLEAN
+
+/* NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN (e-marshal.list:41) */
+extern void e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_DOUBLE_DOUBLE_BOOLEAN e_marshal_VOID__OBJECT_DOUBLE_DOUBLE_BOOLEAN
+
+/* NONE:OBJECT,OBJECT (e-marshal.list:42) */
+extern void e_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_OBJECT e_marshal_VOID__OBJECT_OBJECT
+
+/* NONE:OBJECT,STRING (e-marshal.list:43) */
+extern void e_marshal_VOID__OBJECT_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_STRING e_marshal_VOID__OBJECT_STRING
+
+/* NONE:OBJECT,STRING,INT (e-marshal.list:44) */
+extern void e_marshal_VOID__OBJECT_STRING_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_INT e_marshal_VOID__OBJECT_STRING_INT
+
+/* NONE:OBJECT,STRING,INT,STRING,STRING,STRING (e-marshal.list:45) */
+extern void e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_INT_STRING_STRING_STRING e_marshal_VOID__OBJECT_STRING_INT_STRING_STRING_STRING
+
+/* NONE:OBJECT,STRING,STRING (e-marshal.list:46) */
+extern void e_marshal_VOID__OBJECT_STRING_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_STRING e_marshal_VOID__OBJECT_STRING_STRING
+
+/* NONE:OBJECT,STRING,UINT (e-marshal.list:47) */
+extern void e_marshal_VOID__OBJECT_STRING_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__OBJECT_STRING_UINT e_marshal_VOID__OBJECT_STRING_UINT
+
+/* NONE:POINTER,INT (e-marshal.list:48) */
+extern void e_marshal_VOID__POINTER_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_INT e_marshal_VOID__POINTER_INT
+
+/* NONE:POINTER,INT,INT,INT,INT (e-marshal.list:49) */
+extern void e_marshal_VOID__POINTER_INT_INT_INT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_INT_INT_INT_INT e_marshal_VOID__POINTER_INT_INT_INT_INT
+
+/* NONE:POINTER,INT,OBJECT (e-marshal.list:50) */
+extern void e_marshal_VOID__POINTER_INT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_INT_OBJECT e_marshal_VOID__POINTER_INT_OBJECT
+
+/* NONE:POINTER,OBJECT (e-marshal.list:51) */
+extern void e_marshal_VOID__POINTER_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_OBJECT e_marshal_VOID__POINTER_OBJECT
+
+/* NONE:POINTER,POINTER (e-marshal.list:52) */
+extern void e_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_POINTER e_marshal_VOID__POINTER_POINTER
+
+/* NONE:POINTER,POINTER,INT (e-marshal.list:53) */
+extern void e_marshal_VOID__POINTER_POINTER_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__POINTER_POINTER_INT e_marshal_VOID__POINTER_POINTER_INT
+
+/* NONE:STRING,DOUBLE (e-marshal.list:54) */
+extern void e_marshal_VOID__STRING_DOUBLE (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__STRING_DOUBLE e_marshal_VOID__STRING_DOUBLE
+
+/* NONE:STRING,INT (e-marshal.list:55) */
+extern void e_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__STRING_INT e_marshal_VOID__STRING_INT
+
+/* NONE:STRING,INT,INT (e-marshal.list:56) */
+extern void e_marshal_VOID__STRING_INT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__STRING_INT_INT e_marshal_VOID__STRING_INT_INT
+
+/* NONE:STRING,POINTER,POINTER (e-marshal.list:57) */
+extern void e_marshal_VOID__STRING_POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__STRING_POINTER_POINTER e_marshal_VOID__STRING_POINTER_POINTER
+
+/* NONE:STRING,STRING (e-marshal.list:58) */
+extern void e_marshal_VOID__STRING_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__STRING_STRING e_marshal_VOID__STRING_STRING
+
+/* NONE:UINT,STRING (e-marshal.list:59) */
+extern void e_marshal_VOID__UINT_STRING (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_NONE__UINT_STRING e_marshal_VOID__UINT_STRING
+
+/* STRING:NONE (e-marshal.list:60) */
+extern void e_marshal_STRING__VOID (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+#define e_marshal_STRING__NONE e_marshal_STRING__VOID
+
+G_END_DECLS
+
+#endif /* __e_marshal_MARSHAL_H__ */
+
diff --git a/libemail-utils/e-marshal.list b/libemail-utils/e-marshal.list
new file mode 100644
index 0000000..c42078b
--- /dev/null
+++ b/libemail-utils/e-marshal.list
@@ -0,0 +1,60 @@
+BOOLEAN:BOXED
+BOOLEAN:BOXED,STRING
+BOOLEAN:BOXED,POINTER,POINTER
+BOOLEAN:INT,INT,BOXED
+BOOLEAN:INT,INT,OBJECT,INT,INT,UINT
+BOOLEAN:INT,POINTER,INT,BOXED
+BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT
+BOOLEAN:NONE
+BOOLEAN:OBJECT
+BOOLEAN:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+BOOLEAN:POINTER
+BOOLEAN:POINTER,BOOLEAN,POINTER
+BOOLEAN:POINTER,POINTER
+BOOLEAN:POINTER,POINTER,POINTER,POINTER
+BOOLEAN:STRING
+BOOLEAN:STRING,INT
+DOUBLE:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+INT:BOXED
+INT:INT
+INT:INT,INT,BOXED
+INT:INT,POINTER,INT,BOXED
+INT:OBJECT,BOXED
+INT:POINTER
+NONE:BOXED,INT
+NONE:ENUM,OBJECT,OBJECT
+NONE:INT,INT
+NONE:INT,INT,BOXED
+NONE:INT,INT,OBJECT
+NONE:INT,INT,OBJECT,BOXED,UINT,UINT
+NONE:INT,INT,OBJECT,INT,INT,BOXED,UINT,UINT
+NONE:INT,INT,OBJECT,UINT
+NONE:INT,OBJECT
+NONE:INT,POINTER
+NONE:INT,POINTER,INT,BOXED
+NONE:INT,POINTER,INT,OBJECT
+NONE:INT,POINTER,INT,OBJECT,BOXED,UINT,UINT
+NONE:INT,POINTER,INT,OBJECT,INT,INT,BOXED,UINT,UINT
+NONE:INT,POINTER,INT,OBJECT,UINT
+NONE:LONG,LONG
+NONE:OBJECT,BOOLEAN
+NONE:OBJECT,DOUBLE,DOUBLE,BOOLEAN
+NONE:OBJECT,OBJECT
+NONE:OBJECT,STRING
+NONE:OBJECT,STRING,INT
+NONE:OBJECT,STRING,INT,STRING,STRING,STRING
+NONE:OBJECT,STRING,STRING
+NONE:OBJECT,STRING,UINT
+NONE:POINTER,INT
+NONE:POINTER,INT,INT,INT,INT
+NONE:POINTER,INT,OBJECT
+NONE:POINTER,OBJECT
+NONE:POINTER,POINTER
+NONE:POINTER,POINTER,INT
+NONE:STRING,DOUBLE
+NONE:STRING,INT
+NONE:STRING,INT,INT
+NONE:STRING,POINTER,POINTER
+NONE:STRING,STRING
+NONE:UINT,STRING
+STRING:NONE
diff --git a/libemail-utils/e-signature-list.c b/libemail-utils/e-signature-list.c
new file mode 100644
index 0000000..1f03cd3
--- /dev/null
+++ b/libemail-utils/e-signature-list.c
@@ -0,0 +1,494 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signature-list.h"
+
+#include <string.h>
+
+#include <libedataserver/e-uid.h>
+
+struct _ESignatureListPrivate {
+ GConfClient *gconf;
+ guint notify_id;
+ gboolean resave;
+};
+
+enum {
+ SIGNATURE_ADDED,
+ SIGNATURE_CHANGED,
+ SIGNATURE_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (
+ ESignatureList,
+ e_signature_list,
+ E_TYPE_LIST)
+
+static void
+e_signature_list_dispose (GObject *object)
+{
+ ESignatureList *list = (ESignatureList *) object;
+
+ if (list->priv->gconf) {
+ if (list->priv->notify_id != 0)
+ gconf_client_notify_remove (
+ list->priv->gconf, list->priv->notify_id);
+ g_object_unref (list->priv->gconf);
+ list->priv->gconf = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_signature_list_parent_class)->dispose (object);
+}
+
+static void
+e_signature_list_class_init (ESignatureListClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ESignatureListPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = e_signature_list_dispose;
+
+ signals[SIGNATURE_ADDED] = g_signal_new (
+ "signature-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ESignatureListClass, signature_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_SIGNATURE);
+
+ signals[SIGNATURE_CHANGED] = g_signal_new (
+ "signature-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ESignatureListClass, signature_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_SIGNATURE);
+
+ signals[SIGNATURE_REMOVED] = g_signal_new (
+ "signature-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ESignatureListClass, signature_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_SIGNATURE);
+}
+
+static void
+e_signature_list_init (ESignatureList *signature_list)
+{
+ signature_list->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ signature_list, E_TYPE_SIGNATURE_LIST, ESignatureListPrivate);
+}
+
+static GSList *
+add_autogen (ESignatureList *list,
+ GSList *new_sigs)
+{
+ ESignature *autogen;
+
+ autogen = e_signature_new ();
+ e_signature_set_autogenerated (autogen, TRUE);
+
+ e_list_append (E_LIST (list), autogen);
+
+ return g_slist_prepend (new_sigs, autogen);
+}
+
+static void
+gconf_signatures_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ ESignatureList *signature_list = user_data;
+ GSList *list, *l, *n, *new_sigs = NULL;
+ gboolean have_autogen = FALSE;
+ gboolean resave = FALSE;
+ ESignature *signature;
+ EList *old_sigs;
+ EIterator *iter;
+ gboolean found;
+ gchar *uid;
+
+ old_sigs = e_list_duplicate (E_LIST (signature_list));
+
+ list = gconf_client_get_list (
+ client, "/apps/evolution/mail/signatures",
+ GCONF_VALUE_STRING, NULL);
+ for (l = list; l; l = l->next) {
+ found = FALSE;
+ if ((uid = e_signature_uid_from_xml (l->data))) {
+ /* See if this is an existing signature */
+ iter = e_list_get_iterator (old_sigs);
+ while (e_iterator_is_valid (iter)) {
+ const gchar *signature_uid;
+
+ signature = (ESignature *) e_iterator_get (iter);
+ signature_uid = e_signature_get_uid (signature);
+ if (!strcmp (signature_uid, uid)) {
+ /* The signature still exists, so remove
+ * it from "old_sigs" and update it. */
+ found = TRUE;
+ e_iterator_delete (iter);
+ if (e_signature_set_from_xml (
+ signature, l->data))
+ g_signal_emit (
+ signature_list,
+ signals[SIGNATURE_CHANGED],
+ 0, signature);
+
+ have_autogen |=
+ e_signature_get_autogenerated (
+ signature);
+
+ break;
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+ }
+
+ if (!found) {
+ resave = TRUE;
+
+ /* Must be a new signature */
+ signature = e_signature_new_from_xml (l->data);
+ if (signature) {
+ have_autogen |=
+ e_signature_get_autogenerated (signature);
+
+ e_list_append (E_LIST (signature_list), signature);
+ new_sigs = g_slist_prepend (new_sigs, signature);
+ }
+ }
+
+ g_free (uid);
+ }
+
+ if (!have_autogen) {
+ new_sigs = add_autogen (signature_list, new_sigs);
+ resave = TRUE;
+ }
+
+ if (new_sigs != NULL) {
+ /* Now emit signals for each added signature. */
+ l = g_slist_reverse (new_sigs);
+ while (l != NULL) {
+ n = l->next;
+ signature = l->data;
+ g_signal_emit (
+ signature_list,
+ signals[SIGNATURE_ADDED], 0,
+ signature);
+ g_object_unref (signature);
+ g_slist_free_1 (l);
+ l = n;
+ }
+ }
+
+ /* Anything left in old_sigs must have been deleted */
+ iter = e_list_get_iterator (old_sigs);
+ while (e_iterator_is_valid (iter)) {
+ signature = (ESignature *) e_iterator_get (iter);
+ e_list_remove (E_LIST (signature_list), signature);
+ g_signal_emit (
+ signature_list, signals[SIGNATURE_REMOVED], 0,
+ signature);
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+ g_object_unref (old_sigs);
+
+ signature_list->priv->resave = resave;
+}
+
+static gpointer
+copy_func (gconstpointer data,
+ gpointer closure)
+{
+ GObject *object = (GObject *) data;
+
+ g_object_ref (object);
+
+ return object;
+}
+
+static void
+free_func (gpointer data,
+ gpointer closure)
+{
+ g_object_unref (data);
+}
+
+/**
+ * e_signature_list_new:
+ * @gconf: a #GConfClient
+ *
+ * Reads the list of signaturess from @gconf and listens for changes.
+ * Will emit #signature_added, #signature_changed, and #signature_removed
+ * signals according to notifications from GConf.
+ *
+ * You can modify the list using e_list_append(), e_list_remove(), and
+ * e_iterator_delete(). After adding, removing, or changing accounts,
+ * you must call e_signature_list_save() to push the changes back to
+ * GConf.
+ *
+ * Return value: the list of signatures
+ **/
+ESignatureList *
+e_signature_list_new (GConfClient *gconf)
+{
+ ESignatureList *signature_list;
+
+ g_return_val_if_fail (GCONF_IS_CLIENT (gconf), NULL);
+
+ signature_list = g_object_new (E_TYPE_SIGNATURE_LIST, NULL);
+ e_signature_list_construct (signature_list, gconf);
+
+ return signature_list;
+}
+
+void
+e_signature_list_construct (ESignatureList *signature_list,
+ GConfClient *gconf)
+{
+ g_return_if_fail (GCONF_IS_CLIENT (gconf));
+
+ e_list_construct (E_LIST (signature_list), copy_func, free_func, NULL);
+ signature_list->priv->gconf = gconf;
+ g_object_ref (gconf);
+
+ gconf_client_add_dir (signature_list->priv->gconf,
+ "/apps/evolution/mail/signatures",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ signature_list->priv->notify_id =
+ gconf_client_notify_add (signature_list->priv->gconf,
+ "/apps/evolution/mail/signatures",
+ gconf_signatures_changed, signature_list,
+ NULL, NULL);
+
+ gconf_signatures_changed (signature_list->priv->gconf,
+ signature_list->priv->notify_id,
+ NULL, signature_list);
+
+ if (signature_list->priv->resave) {
+ e_signature_list_save (signature_list);
+ signature_list->priv->resave = FALSE;
+ }
+}
+
+/**
+ * e_signature_list_save:
+ * @signature_list: an #ESignatureList
+ *
+ * Saves @signature_list to GConf. Signals will be emitted for changes.
+ **/
+void
+e_signature_list_save (ESignatureList *signature_list)
+{
+ GSList *list = NULL;
+ ESignature *signature;
+ EIterator *iter;
+ gchar *xmlbuf;
+
+ for (iter = e_list_get_iterator (E_LIST (signature_list));
+ e_iterator_is_valid (iter);
+ e_iterator_next (iter)) {
+ signature = (ESignature *) e_iterator_get (iter);
+
+ if ((xmlbuf = e_signature_to_xml (signature)))
+ list = g_slist_append (list, xmlbuf);
+ }
+
+ g_object_unref (iter);
+
+ gconf_client_set_list (signature_list->priv->gconf,
+ "/apps/evolution/mail/signatures",
+ GCONF_VALUE_STRING, list, NULL);
+
+ while (list) {
+ g_free (list->data);
+ list = g_slist_remove (list, list->data);
+ }
+
+ gconf_client_suggest_sync (signature_list->priv->gconf, NULL);
+}
+
+/**
+ * e_signature_list_add:
+ * @signature_list: signature list
+ * @signature: signature to add
+ *
+ * Add an signature to the signature list. Will emit the signature-changed
+ * event.
+ **/
+void
+e_signature_list_add (ESignatureList *signature_list,
+ ESignature *signature)
+{
+ e_list_append ((EList *) signature_list, signature);
+ g_signal_emit (signature_list, signals[SIGNATURE_ADDED], 0, signature);
+}
+
+/**
+ * e_signature_list_change:
+ * @signature_list: signature list
+ * @signature: signature to change
+ *
+ * Signal that the details of an signature have changed.
+ **/
+void
+e_signature_list_change (ESignatureList *signature_list,
+ ESignature *signature)
+{
+ /* maybe the signature should do this itself ... */
+ g_signal_emit (signature_list, signals[SIGNATURE_CHANGED], 0, signature);
+}
+
+/**
+ * e_signature_list_remove:
+ * @signature_list: signature list
+ * @signature: signature
+ *
+ * Remove an signature from the signature list, and emit the
+ * signature-removed signal. If the signature was the default signature,
+ * then reset the default to the first signature.
+ **/
+void
+e_signature_list_remove (ESignatureList *signature_list,
+ ESignature *signature)
+{
+ /* not sure if need to ref but no harm */
+ g_object_ref (signature);
+ e_list_remove ((EList *) signature_list, signature);
+ g_signal_emit (signature_list, signals[SIGNATURE_REMOVED], 0, signature);
+ g_object_unref (signature);
+}
+
+/**
+ * e_signature_list_find_by_name:
+ * @signature_list: an #ESignatureList
+ * @name: the signature name to find
+ *
+ * Searches @signature_list for the given signature name.
+ *
+ * Returns: the matching signature or %NULL if it doesn't exist
+ **/
+ESignature *
+e_signature_list_find_by_name (ESignatureList *signature_list,
+ const gchar *signature_name)
+{
+ ESignature *signature = NULL;
+ EIterator *it;
+
+ g_return_val_if_fail (E_IS_SIGNATURE_LIST (signature_list), NULL);
+
+ /* this could use a callback for more flexibility ...
+ * ... but this makes the common cases easier */
+
+ if (signature_name == NULL)
+ return NULL;
+
+ for (it = e_list_get_iterator (E_LIST (signature_list));
+ e_iterator_is_valid (it);
+ e_iterator_next (it)) {
+ const gchar *value;
+
+ /* XXX EIterator misuses const. */
+ signature = (ESignature *) e_iterator_get (it);
+ value = e_signature_get_name (signature);
+
+ if (g_strcmp0 (value, signature_name) == 0)
+ break;
+
+ signature = NULL;
+ }
+
+ g_object_unref (it);
+
+ return signature;
+}
+
+/**
+ * e_signature_list_find_by_uid:
+ * @signature_list: an #ESignatureList
+ * @name: the signature UID to find
+ *
+ * Searches @signature_list for the given signature UID.
+ *
+ * Returns: the matching signature or %NULL if it doesn't exist
+ **/
+ESignature *
+e_signature_list_find_by_uid (ESignatureList *signature_list,
+ const gchar *signature_uid)
+{
+ ESignature *signature = NULL;
+ EIterator *it;
+
+ g_return_val_if_fail (E_IS_SIGNATURE_LIST (signature_list), NULL);
+
+ /* this could use a callback for more flexibility ...
+ * ... but this makes the common cases easier */
+
+ if (signature_uid == NULL)
+ return NULL;
+
+ for (it = e_list_get_iterator (E_LIST (signature_list));
+ e_iterator_is_valid (it);
+ e_iterator_next (it)) {
+ const gchar *value = NULL;
+
+ /* XXX EIterator misuses const. */
+ signature = (ESignature *) e_iterator_get (it);
+ value = e_signature_get_uid (signature);
+
+ if (g_strcmp0 (value, signature_uid) == 0)
+ break;
+
+ signature = NULL;
+ }
+
+ g_object_unref (it);
+
+ return signature;
+}
diff --git a/libemail-utils/e-signature-list.h b/libemail-utils/e-signature-list.h
new file mode 100644
index 0000000..4e929c8
--- /dev/null
+++ b/libemail-utils/e-signature-list.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_LIST_H
+#define E_SIGNATURE_LIST_H
+
+#include <libedataserver/e-list.h>
+#include "libemail-utils/e-signature.h"
+
+#include <gconf/gconf-client.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_LIST \
+ (e_signature_list_get_type ())
+#define E_SIGNATURE_LIST(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SIGNATURE_LIST, ESignatureList))
+#define E_SIGNATURE_LIST_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SIGNATURE_LIST, ESignatureListClass))
+#define E_IS_SIGNATURE_LIST(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SIGNATURE_LIST))
+#define E_IS_SIGNATURE_LIST_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SIGNATURE_LIST))
+#define E_SIGNATURE_LIST_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SIGNATURE_LIST, ESignatureListClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureList ESignatureList;
+typedef struct _ESignatureListClass ESignatureListClass;
+typedef struct _ESignatureListPrivate ESignatureListPrivate;
+
+struct _ESignatureList {
+ EList parent;
+ ESignatureListPrivate *priv;
+};
+
+struct _ESignatureListClass {
+ EListClass parent_class;
+
+ /* Signals */
+ void (*signature_added) (ESignatureList *signature_list,
+ ESignature *signature);
+ void (*signature_changed) (ESignatureList *signature_list,
+ ESignature *signature);
+ void (*signature_removed) (ESignatureList *signature_list,
+ ESignature *signature);
+};
+
+GType e_signature_list_get_type (void);
+ESignatureList *e_signature_list_new (GConfClient *client);
+void e_signature_list_construct (ESignatureList *signature_list,
+ GConfClient *client);
+void e_signature_list_save (ESignatureList *signature_list);
+void e_signature_list_add (ESignatureList *signature_list,
+ ESignature *signature);
+void e_signature_list_change (ESignatureList *signature_list,
+ ESignature *signature);
+void e_signature_list_remove (ESignatureList *signature_list,
+ ESignature *signature);
+ESignature * e_signature_list_find_by_name (ESignatureList *signature_list,
+ const gchar *signature_name);
+ESignature * e_signature_list_find_by_uid (ESignatureList *signature_list,
+ const gchar *signature_uid);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_LIST_H */
diff --git a/libemail-utils/e-signature-utils.c b/libemail-utils/e-signature-utils.c
new file mode 100644
index 0000000..a7eea3e
--- /dev/null
+++ b/libemail-utils/e-signature-utils.c
@@ -0,0 +1,342 @@
+/*
+ * e-signature-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signature-utils.h"
+
+#include <errno.h>
+#include <camel/camel.h>
+#include <glib/gstdio.h>
+#include <gconf/gconf-client.h>
+
+#ifndef G_OS_WIN32
+#include <sys/wait.h>
+#endif
+
+#include <libedataserver/e-data-server-util.h>
+
+static ESignatureList *global_signature_list;
+
+ESignatureList *
+e_get_signature_list (void)
+{
+ if (G_UNLIKELY (global_signature_list == NULL)) {
+ GConfClient *client;
+
+ client = gconf_client_get_default ();
+ global_signature_list = e_signature_list_new (client);
+ g_object_unref (client);
+ }
+
+ g_return_val_if_fail (global_signature_list != NULL, NULL);
+
+ return global_signature_list;
+}
+
+ESignature *
+e_get_signature_by_name (const gchar *name)
+{
+ ESignatureList *signature_list;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ signature_list = e_get_signature_list ();
+
+ return e_signature_list_find_by_name (signature_list, name);
+}
+
+ESignature *
+e_get_signature_by_uid (const gchar *uid)
+{
+ ESignatureList *signature_list;
+
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ signature_list = e_get_signature_list ();
+
+ return e_signature_list_find_by_uid (signature_list, uid);
+}
+
+gchar *
+e_create_signature_file (GError **error)
+{
+ const gchar *data_dir;
+ gchar basename[32];
+ gchar *filename;
+ gchar *pathname;
+ gint32 ii;
+
+ data_dir = e_get_user_data_dir ();
+ pathname = g_build_filename (data_dir, "signatures", NULL);
+ filename = NULL;
+
+ if (g_mkdir_with_parents (pathname, 0700) < 0) {
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s: %s", pathname, g_strerror (errno));
+ g_free (pathname);
+ return NULL;
+ }
+
+ for (ii = 0; ii < G_MAXINT32; ii++) {
+
+ g_snprintf (
+ basename, sizeof (basename),
+ "signature-%" G_GINT32_FORMAT, ii);
+
+ g_free (filename);
+ filename = g_build_filename (pathname, basename, NULL);
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ gint fd;
+
+ fd = g_creat (filename, 0600);
+ if (fd >= 0) {
+ close (fd);
+ break;
+ }
+
+ /* If we failed once we're probably going
+ * to continue failing, so just give up. */
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s: %s", filename, g_strerror (errno));
+ g_free (filename);
+ filename = NULL;
+ break;
+ }
+ }
+
+ /* If there are actually G_MAXINT32 signature files, the
+ * most recent signature file we be overwritten. Sorry. */
+
+ return filename;
+}
+
+gchar *
+e_read_signature_file (ESignature *signature,
+ gboolean convert_to_html,
+ GError **error)
+{
+ CamelStream *input_stream;
+ CamelStream *output_stream;
+ GByteArray *buffer;
+ const gchar *filename;
+ gboolean is_html;
+ gchar *content;
+ gsize length;
+ gint fd;
+
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+ filename = e_signature_get_filename (signature);
+ is_html = e_signature_get_is_html (signature);
+
+ fd = g_open (filename, O_RDONLY, 0);
+ if (fd < 0) {
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s: %s", filename, g_strerror (errno));
+ return NULL;
+ }
+
+ input_stream = camel_stream_fs_new_with_fd (fd);
+
+ if (!is_html && convert_to_html) {
+ CamelStream *filtered_stream;
+ CamelMimeFilter *filter;
+ gint32 flags;
+
+ filtered_stream =
+ camel_stream_filter_new (input_stream);
+ g_object_unref (input_stream);
+
+ flags =
+ CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
+ filter = camel_mime_filter_tohtml_new (flags, 0);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream), filter);
+ g_object_unref (filter);
+
+ input_stream = filtered_stream;
+ }
+
+ buffer = g_byte_array_new ();
+ output_stream = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (
+ CAMEL_STREAM_MEM (output_stream), buffer);
+ camel_stream_write_to_stream (input_stream, output_stream, NULL, NULL);
+ g_object_unref (output_stream);
+ g_object_unref (input_stream);
+
+ /* Make sure the buffer is nul-terminated. */
+ length = (gsize) buffer->len;
+ g_byte_array_append (buffer, (guint8 *) "", 1);
+ content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+ /* Signatures are saved as UTF-8, but we still need to check that
+ * the signature is valid UTF-8 because the user may be opening
+ * a signature file that is in his/her locale character set. If
+ * it's not in UTF-8 then try converting from the current locale. */
+ if (!g_utf8_validate (content, length, NULL)) {
+ gchar *utf8;
+
+ utf8 = g_locale_to_utf8 (content, length, NULL, NULL, error);
+ g_free (content);
+ content = utf8;
+ }
+
+ return content;
+}
+
+gchar *
+e_run_signature_script (const gchar *filename)
+{
+ /* FIXME Make this cross-platform, prefer GLib functions over
+ * POSIX, and report errors via GError instead of dumping
+ * messages to the terminal where users won't see them. */
+
+#ifndef G_OS_WIN32
+ gint in_fds[2];
+ pid_t pid;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ if (pipe (in_fds) == -1) {
+ g_warning (
+ "Failed to create pipe to '%s': %s",
+ filename, g_strerror (errno));
+ return NULL;
+ }
+
+ pid = fork ();
+
+ /* Child Process */
+ if (pid == 0) {
+ gint maxfd, ii;
+
+ close (in_fds[0]);
+ if (dup2 (in_fds[1], STDOUT_FILENO) < 0)
+ _exit (255);
+ close (in_fds[1]);
+
+ setsid ();
+
+ maxfd = sysconf (_SC_OPEN_MAX);
+ for (ii = 3; ii < maxfd; ii++) {
+ if (ii == STDIN_FILENO)
+ continue;
+ if (ii == STDOUT_FILENO)
+ continue;
+ if (ii == STDERR_FILENO)
+ continue;
+ fcntl (ii, F_SETFD, FD_CLOEXEC);
+ }
+
+ execlp ("/bin/sh", "/bin/sh", "-c", filename, NULL);
+
+ g_warning (
+ "Could not execute '%s': %s",
+ filename, g_strerror (errno));
+
+ _exit (255);
+
+ /* Parent Process */
+ } else if (pid > 0) {
+ CamelStream *output_stream;
+ CamelStream *input_stream;
+ GByteArray *buffer;
+ gchar *content;
+ gsize length;
+ gint result;
+ gint status;
+
+ close (in_fds[1]);
+
+ buffer = g_byte_array_new ();
+ output_stream = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (
+ CAMEL_STREAM_MEM (output_stream), buffer);
+
+ input_stream = camel_stream_fs_new_with_fd (in_fds[0]);
+ camel_stream_write_to_stream (
+ input_stream, output_stream, NULL, NULL);
+ g_object_unref (input_stream);
+
+ g_object_unref (output_stream);
+
+ /* Make sure the buffer is nul-terminated. */
+ length = (gsize) buffer->len;
+ g_byte_array_append (buffer, (guchar *) "", 1);
+ content = (gchar *) g_byte_array_free (buffer, FALSE);
+
+ /* Signature scripts are supposed to generate UTF-8 content,
+ * but because users are known to never read the manual, we
+ * try to do our best if the content isn't valid UTF-8 by
+ * assuming that the content is in the user's locale
+ * character set. */
+ if (!g_utf8_validate (content, length, NULL)) {
+ gchar *utf8;
+
+ /* XXX Should pass a GError here. */
+ utf8 = g_locale_to_utf8 (
+ content, length, NULL, NULL, NULL);
+ g_free (content);
+ content = utf8;
+ }
+
+ /* Wait for the script process to terminate. */
+ result = waitpid (pid, &status, 0);
+
+ if (result == -1 && errno == EINTR) {
+ /* Child process is hanging... */
+ kill (pid, SIGTERM);
+ sleep (1);
+ result = waitpid (pid, &status, WNOHANG);
+ if (result == 0) {
+ /* ...still hanging, set phasers to KILL. */
+ kill (pid, SIGKILL);
+ sleep (1);
+ waitpid (pid, &status, WNOHANG);
+ }
+ }
+
+ return content;
+
+ /* Forking Failed */
+ } else {
+ g_warning (
+ "Failed to create child process '%s': %s",
+ filename, g_strerror (errno));
+ close (in_fds[0]);
+ close (in_fds[1]);
+ }
+#endif
+
+ return NULL;
+}
diff --git a/libemail-utils/e-signature-utils.h b/libemail-utils/e-signature-utils.h
new file mode 100644
index 0000000..a642a13
--- /dev/null
+++ b/libemail-utils/e-signature-utils.h
@@ -0,0 +1,40 @@
+/*
+ * e-signature-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_SIGNATURE_UTILS_H
+#define E_SIGNATURE_UTILS_H
+
+#include <gtk/gtk.h>
+#include <libemail-utils/e-signature.h>
+#include <libemail-utils/e-signature-list.h>
+
+G_BEGIN_DECLS
+
+ESignatureList *e_get_signature_list (void);
+ESignature * e_get_signature_by_name (const gchar *name);
+ESignature * e_get_signature_by_uid (const gchar *uid);
+gchar * e_create_signature_file (GError **error);
+gchar * e_read_signature_file (ESignature *signature,
+ gboolean convert_to_html,
+ GError **error);
+gchar * e_run_signature_script (const gchar *filename);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_UTILS_H */
diff --git a/libemail-utils/e-signature.c b/libemail-utils/e-signature.c
new file mode 100644
index 0000000..185378d
--- /dev/null
+++ b/libemail-utils/e-signature.c
@@ -0,0 +1,747 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include <glib/gi18n-lib.h>
+#include <gconf/gconf-client.h>
+
+#include <libedataserver/e-uid.h>
+#include <libedataserver/e-data-server-util.h>
+
+#include "e-signature.h"
+
+struct _ESignaturePrivate {
+ gchar *filename;
+ gchar *name;
+ gchar *uid;
+
+ gboolean autogenerated;
+ gboolean is_html;
+ gboolean is_script;
+};
+
+enum {
+ PROP_0,
+ PROP_AUTOGENERATED,
+ PROP_FILENAME,
+ PROP_IS_HTML,
+ PROP_IS_SCRIPT,
+ PROP_NAME,
+ PROP_UID
+};
+
+G_DEFINE_TYPE (
+ ESignature,
+ e_signature,
+ G_TYPE_OBJECT)
+
+static gboolean
+xml_set_bool (xmlNodePtr node,
+ const gchar *name,
+ gboolean *val)
+{
+ gboolean v_boolean;
+ gchar *buf;
+
+ if ((buf = (gchar *) xmlGetProp (node, (xmlChar *) name))) {
+ v_boolean = (!strcmp (buf, "true") || !strcmp (buf, "yes"));
+ xmlFree (buf);
+
+ if (v_boolean != *val) {
+ *val = v_boolean;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+xml_set_prop (xmlNodePtr node,
+ const gchar *name,
+ gchar **val)
+{
+ gchar *buf, *new_val;
+
+ buf = (gchar *) xmlGetProp (node, (xmlChar *) name);
+ new_val = g_strdup (buf);
+ xmlFree (buf);
+
+ /* We can use strcmp here whether the value is UTF8 or
+ * not, since we only care if the bytes changed.
+ */
+ if (!*val || strcmp (*val, new_val)) {
+ g_free (*val);
+ *val = new_val;
+ return TRUE;
+ } else {
+ g_free (new_val);
+ return FALSE;
+ }
+}
+
+static gboolean
+xml_set_content (xmlNodePtr node,
+ gchar **val)
+{
+ gchar *buf, *new_val;
+
+ buf = (gchar *) xmlNodeGetContent (node);
+ new_val = g_strdup (buf);
+ xmlFree (buf);
+
+ /* We can use strcmp here whether the value is UTF8 or
+ * not, since we only care if the bytes changed. */
+ if (!*val || strcmp (*val, new_val)) {
+ g_free (*val);
+ *val = new_val;
+ return TRUE;
+ } else {
+ g_free (new_val);
+ return FALSE;
+ }
+}
+
+static void
+signature_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_AUTOGENERATED:
+ e_signature_set_autogenerated (
+ E_SIGNATURE (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_FILENAME:
+ e_signature_set_filename (
+ E_SIGNATURE (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_IS_HTML:
+ e_signature_set_is_html (
+ E_SIGNATURE (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_IS_SCRIPT:
+ e_signature_set_is_script (
+ E_SIGNATURE (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_NAME:
+ e_signature_set_name (
+ E_SIGNATURE (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_UID:
+ e_signature_set_uid (
+ E_SIGNATURE (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_AUTOGENERATED:
+ g_value_set_boolean (
+ value, e_signature_get_autogenerated (
+ E_SIGNATURE (object)));
+ return;
+
+ case PROP_FILENAME:
+ g_value_set_string (
+ value, e_signature_get_filename (
+ E_SIGNATURE (object)));
+ return;
+
+ case PROP_IS_HTML:
+ g_value_set_boolean (
+ value, e_signature_get_is_html (
+ E_SIGNATURE (object)));
+ return;
+
+ case PROP_IS_SCRIPT:
+ g_value_set_boolean (
+ value, e_signature_get_is_script (
+ E_SIGNATURE (object)));
+ return;
+
+ case PROP_NAME:
+ g_value_set_string (
+ value, e_signature_get_name (
+ E_SIGNATURE (object)));
+ return;
+
+ case PROP_UID:
+ g_value_set_string (
+ value, e_signature_get_uid (
+ E_SIGNATURE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_finalize (GObject *object)
+{
+ ESignaturePrivate *priv;
+
+ priv = E_SIGNATURE (object)->priv;
+
+ g_free (priv->filename);
+ g_free (priv->name);
+ g_free (priv->uid);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_signature_parent_class)->finalize (object);
+}
+
+static void
+e_signature_class_init (ESignatureClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ESignaturePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = signature_set_property;
+ object_class->get_property = signature_get_property;
+ object_class->finalize = signature_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_AUTOGENERATED,
+ g_param_spec_boolean (
+ "autogenerated",
+ "Autogenerated",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FILENAME,
+ g_param_spec_string (
+ "filename",
+ "Filename",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_IS_HTML,
+ g_param_spec_boolean (
+ "is-html",
+ "Is HTML",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_IS_SCRIPT,
+ g_param_spec_boolean (
+ "is-script",
+ "Is Script",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_NAME,
+ g_param_spec_string (
+ "name",
+ "Name",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_UID,
+ g_param_spec_string (
+ "uid",
+ "UID",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_signature_init (ESignature *signature)
+{
+ signature->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ signature, E_TYPE_SIGNATURE, ESignaturePrivate);
+}
+
+/**
+ * e_signature_new:
+ *
+ * Returns a new signature which can be filled in and
+ * added to an #ESignatureList.
+ *
+ * Returns: a new #ESignature
+ **/
+ESignature *
+e_signature_new (void)
+{
+ ESignature *signature;
+
+ signature = g_object_new (E_TYPE_SIGNATURE, NULL);
+ signature->priv->uid = e_uid_new ();
+
+ return signature;
+}
+
+/**
+ * e_signature_new_from_xml:
+ * @xml: an XML signature description
+ *
+ * Return value: a new #ESignature based on the data in @xml, or %NULL
+ * if @xml could not be parsed as valid signature data.
+ **/
+ESignature *
+e_signature_new_from_xml (const gchar *xml)
+{
+ ESignature *signature;
+
+ signature = g_object_new (E_TYPE_SIGNATURE, NULL);
+
+ if (!e_signature_set_from_xml (signature, xml)) {
+ g_object_unref (signature);
+ return NULL;
+ }
+
+ return signature;
+}
+
+/**
+ * e_signature_uid_from_xml:
+ * @xml: an XML signature description
+ *
+ * Return value: the permanent UID of the signature described by @xml
+ * (or %NULL if @xml could not be parsed or did not contain a uid).
+ * The caller must free this string.
+ **/
+gchar *
+e_signature_uid_from_xml (const gchar *xml)
+{
+ xmlNodePtr node;
+ xmlDocPtr doc;
+ gchar *uid = NULL;
+
+ if (!(doc = xmlParseDoc ((xmlChar *) xml)))
+ return NULL;
+
+ node = doc->children;
+ if (strcmp ((gchar *)node->name, "signature") != 0) {
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+
+ xml_set_prop (node, "uid", &uid);
+ xmlFreeDoc (doc);
+
+ return uid;
+}
+
+/**
+ * e_signature_set_from_xml:
+ * @signature: an #ESignature
+ * @xml: an XML signature description.
+ *
+ * Changes @signature to match @xml.
+ *
+ * Returns: %TRUE if the signature was loaded or %FALSE otherwise
+ **/
+gboolean
+e_signature_set_from_xml (ESignature *signature,
+ const gchar *xml)
+{
+ gboolean changed = FALSE;
+ xmlNodePtr node, cur;
+ xmlDocPtr doc;
+ gboolean bool;
+ gchar *buf;
+
+ if (!(doc = xmlParseDoc ((xmlChar *) xml)))
+ return FALSE;
+
+ node = doc->children;
+ if (strcmp ((gchar *)node->name, "signature") != 0) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ buf = NULL;
+ xml_set_prop (node, "uid", &buf);
+
+ if (buf && *buf) {
+ g_free (signature->priv->uid);
+ signature->priv->uid = buf;
+ }
+
+ changed |= xml_set_prop (node, "name", &signature->priv->name);
+ changed |= xml_set_bool (node, "auto", &signature->priv->autogenerated);
+
+ if (e_signature_get_autogenerated (signature)) {
+ xmlFreeDoc (doc);
+
+ return changed;
+ }
+
+ buf = NULL;
+ xml_set_prop (node, "format", &buf);
+ if (buf && !strcmp (buf, "text/html"))
+ bool = TRUE;
+ else
+ bool = FALSE;
+ g_free (buf);
+
+ if (e_signature_get_is_html (signature) != bool) {
+ e_signature_set_is_html (signature, bool);
+ changed = TRUE;
+ }
+
+ cur = node->children;
+ while (cur) {
+ if (!strcmp ((gchar *)cur->name, "filename")) {
+ changed |= xml_set_content (
+ cur, &signature->priv->filename);
+ changed |= xml_set_bool (
+ cur, "script", &signature->priv->is_script);
+ break;
+ } else if (!strcmp ((gchar *)cur->name, "script")) {
+ /* this is for handling 1.4 signature script definitions */
+ changed |= xml_set_content (
+ cur, &signature->priv->filename);
+ if (!e_signature_get_is_script (signature)) {
+ e_signature_set_is_script (signature, TRUE);
+ changed = TRUE;
+ }
+ break;
+ }
+ cur = cur->next;
+ }
+
+ /* If the signature is not a script, replace the directory
+ * part with the current signatures directory. This makes
+ * moving the signatures directory transparent. */
+ if (!e_signature_get_is_script (signature)) {
+ const gchar *user_data_dir;
+ gchar *basename;
+ gchar *filename;
+
+ user_data_dir = e_get_user_data_dir ();
+
+ filename = signature->priv->filename;
+ basename = g_path_get_basename (filename);
+ signature->priv->filename = g_build_filename (
+ user_data_dir, "signatures", basename, NULL);
+ g_free (basename);
+ g_free (filename);
+ }
+
+ xmlFreeDoc (doc);
+
+ return changed;
+}
+
+/**
+ * e_signature_to_xml:
+ * @signature: an #ESignature
+ *
+ * Return value: an XML representation of @signature, which the caller
+ * must free.
+ **/
+gchar *
+e_signature_to_xml (ESignature *signature)
+{
+ xmlChar *xmlbuf;
+ gchar *tmp;
+ xmlNodePtr root, node;
+ xmlDocPtr doc;
+ const gchar *string;
+ gint n;
+
+ doc = xmlNewDoc ((xmlChar *) "1.0");
+
+ root = xmlNewDocNode (doc, NULL, (xmlChar *) "signature", NULL);
+ xmlDocSetRootElement (doc, root);
+
+ string = e_signature_get_name (signature);
+ xmlSetProp (root, (xmlChar *) "name", (xmlChar *) string);
+
+ string = e_signature_get_uid (signature);
+ xmlSetProp (root, (xmlChar *) "uid", (xmlChar *) string);
+
+ if (e_signature_get_autogenerated (signature))
+ string = "true";
+ else
+ string = "false";
+ xmlSetProp (root, (xmlChar *) "auto", (xmlChar *) string);
+
+ if (!e_signature_get_autogenerated (signature)) {
+ if (e_signature_get_is_html (signature))
+ string = "text/html";
+ else
+ string = "text/plain";
+ xmlSetProp (root, (xmlChar *) "format", (xmlChar *) string);
+
+ string = e_signature_get_filename (signature);
+ if (string != NULL) {
+
+ /* For scripts we save the full filename,
+ * for normal signatures just the basename. */
+ if (e_signature_get_is_script (signature)) {
+ node = xmlNewTextChild (
+ root, NULL, (xmlChar *) "filename",
+ (xmlChar *) string);
+ xmlSetProp (
+ node, (xmlChar *) "script",
+ (xmlChar *) "true");
+ } else {
+ gchar *basename;
+
+ basename = g_path_get_basename (string);
+ node = xmlNewTextChild (
+ root, NULL, (xmlChar *) "filename",
+ (xmlChar *) basename);
+ g_free (basename);
+ }
+ }
+ } else {
+ /* this is to make Evolution-1.4 and older 1.5 versions happy */
+ xmlSetProp (root, (xmlChar *) "format", (xmlChar *) "text/html");
+ }
+
+ xmlDocDumpMemory (doc, &xmlbuf, &n);
+ xmlFreeDoc (doc);
+
+ /* remap to glib memory */
+ tmp = g_malloc (n + 1);
+ memcpy (tmp, xmlbuf, n);
+ tmp[n] = '\0';
+ xmlFree (xmlbuf);
+
+ return tmp;
+}
+
+gboolean
+e_signature_is_equal (ESignature *signature1,
+ ESignature *signature2)
+{
+ const gchar *uid1;
+ const gchar *uid2;
+
+ g_return_val_if_fail (E_IS_SIGNATURE (signature1), FALSE);
+ g_return_val_if_fail (E_IS_SIGNATURE (signature2), FALSE);
+
+ /* XXX Simply compares the UIDs. Not fool-proof. */
+ uid1 = e_signature_get_uid (signature1);
+ uid2 = e_signature_get_uid (signature2);
+
+ return (g_strcmp0 (uid1, uid2) == 0);
+}
+
+gboolean
+e_signature_get_autogenerated (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+ return signature->priv->autogenerated;
+}
+
+void
+e_signature_set_autogenerated (ESignature *signature,
+ gboolean autogenerated)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ if (signature->priv->autogenerated == autogenerated)
+ return;
+
+ signature->priv->autogenerated = autogenerated;
+
+ /* Autogenerated flags overrides several properties. */
+ g_object_freeze_notify (G_OBJECT (signature));
+ g_object_notify (G_OBJECT (signature), "autogenerated");
+ g_object_notify (G_OBJECT (signature), "filename");
+ g_object_notify (G_OBJECT (signature), "is-html");
+ g_object_notify (G_OBJECT (signature), "is-script");
+ g_object_notify (G_OBJECT (signature), "name");
+ g_object_thaw_notify (G_OBJECT (signature));
+}
+
+const gchar *
+e_signature_get_filename (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+ /* Autogenerated flags overrides the filename property. */
+ if (e_signature_get_autogenerated (signature))
+ return NULL;
+
+ return signature->priv->filename;
+}
+
+void
+e_signature_set_filename (ESignature *signature,
+ const gchar *filename)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ g_free (signature->priv->filename);
+ signature->priv->filename = g_strdup (filename);
+
+ g_object_notify (G_OBJECT (signature), "filename");
+}
+
+gboolean
+e_signature_get_is_html (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+ /* Autogenerated flag overrides the is-html property. */
+ if (e_signature_get_autogenerated (signature))
+ return FALSE;
+
+ return signature->priv->is_html;
+}
+
+void
+e_signature_set_is_html (ESignature *signature,
+ gboolean is_html)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ if (signature->priv->is_html == is_html)
+ return;
+
+ signature->priv->is_html = is_html;
+
+ g_object_notify (G_OBJECT (signature), "is-html");
+}
+
+gboolean
+e_signature_get_is_script (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+ /* Autogenerated flags overrides the is-script property. */
+ if (e_signature_get_autogenerated (signature))
+ return FALSE;
+
+ return signature->priv->is_script;
+}
+
+void
+e_signature_set_is_script (ESignature *signature,
+ gboolean is_script)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ if (signature->priv->is_script == is_script)
+ return;
+
+ signature->priv->is_script = is_script;
+
+ g_object_notify (G_OBJECT (signature), "is-script");
+}
+
+const gchar *
+e_signature_get_name (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+ /* Autogenerated flag overrides the name property. */
+ if (e_signature_get_autogenerated (signature))
+ return _("Autogenerated");
+
+ return signature->priv->name;
+}
+
+void
+e_signature_set_name (ESignature *signature,
+ const gchar *name)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ g_free (signature->priv->name);
+ signature->priv->name = g_strdup (name);
+
+ g_object_notify (G_OBJECT (signature), "name");
+}
+
+const gchar *
+e_signature_get_uid (ESignature *signature)
+{
+ g_return_val_if_fail (E_IS_SIGNATURE (signature), NULL);
+
+ return signature->priv->uid;
+}
+
+void
+e_signature_set_uid (ESignature *signature,
+ const gchar *uid)
+{
+ g_return_if_fail (E_IS_SIGNATURE (signature));
+
+ g_free (signature->priv->uid);
+
+ if (uid == NULL)
+ signature->priv->uid = e_uid_new ();
+ else
+ signature->priv->uid = g_strdup (uid);
+
+ g_object_notify (G_OBJECT (signature), "uid");
+}
diff --git a/libemail-utils/e-signature.h b/libemail-utils/e-signature.h
new file mode 100644
index 0000000..fad1faf
--- /dev/null
+++ b/libemail-utils/e-signature.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_H
+#define E_SIGNATURE_H
+
+#include <glib-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE \
+ (e_signature_get_type ())
+#define E_SIGNATURE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SIGNATURE, ESignature))
+#define E_SIGNATURE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SIGNATURE, ESignatureClass))
+#define E_IS_SIGNATURE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SIGNATURE))
+#define E_IS_SIGNATURE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SIGNATURE))
+#define E_SIGNATURE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SIGNATURE, ESignatureClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignature ESignature;
+typedef struct _ESignatureClass ESignatureClass;
+typedef struct _ESignaturePrivate ESignaturePrivate;
+
+struct _ESignature {
+ GObject parent;
+ ESignaturePrivate *priv;
+};
+
+struct _ESignatureClass {
+ GObjectClass parent_class;
+};
+
+GType e_signature_get_type (void);
+ESignature * e_signature_new (void);
+ESignature * e_signature_new_from_xml (const gchar *xml);
+gchar * e_signature_uid_from_xml (const gchar *xml);
+gboolean e_signature_set_from_xml (ESignature *signature,
+ const gchar *xml);
+gchar * e_signature_to_xml (ESignature *signature);
+gboolean e_signature_is_equal (ESignature *signature1,
+ ESignature *signature2);
+gboolean e_signature_get_autogenerated (ESignature *signature);
+void e_signature_set_autogenerated (ESignature *signature,
+ gboolean autogenerated);
+const gchar * e_signature_get_filename (ESignature *signature);
+void e_signature_set_filename (ESignature *signature,
+ const gchar *filename);
+gboolean e_signature_get_is_html (ESignature *signature);
+void e_signature_set_is_html (ESignature *signature,
+ gboolean is_html);
+gboolean e_signature_get_is_script (ESignature *signature);
+void e_signature_set_is_script (ESignature *signature,
+ gboolean is_script);
+const gchar * e_signature_get_name (ESignature *signature);
+void e_signature_set_name (ESignature *signature,
+ const gchar *name);
+const gchar * e_signature_get_uid (ESignature *signature);
+void e_signature_set_uid (ESignature *signature,
+ const gchar *uid);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_H */
diff --git a/libemail-utils/gconf-bridge.c b/libemail-utils/gconf-bridge.c
new file mode 100644
index 0000000..3de48b6
--- /dev/null
+++ b/libemail-utils/gconf-bridge.c
@@ -0,0 +1,1364 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "gconf-bridge.h"
+
+struct _GConfBridge {
+ GConfClient *client;
+
+ GHashTable *bindings;
+};
+
+/* The data structures for the different kinds of bindings */
+typedef enum {
+ BINDING_PROP,
+ BINDING_WINDOW,
+ BINDING_LIST_STORE
+} BindingType;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ gboolean delayed_mode;
+
+ gchar *key;
+ guint val_notify_id;
+ GSList *val_changes; /* List of changes made to GConf value,
+ that have not received change notification
+ yet. */
+
+ GObject *object;
+ GParamSpec *prop;
+ gulong prop_notify_id;
+
+ guint sync_timeout_id; /* Used in delayed mode */
+} PropBinding;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ gboolean bind_size;
+ gboolean bind_pos;
+
+ gchar *key_prefix;
+
+ GtkWindow *window;
+ gulong configure_event_id;
+ gulong window_state_event_id;
+ gulong unmap_id;
+ guint sync_timeout_id;
+} WindowBinding;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ gchar *key;
+ guint val_notify_id;
+ GSList *val_changes; /* List of changes made to GConf value,
+ that have not received change notification
+ yet. */
+
+ GtkListStore *list_store;
+ guint row_inserted_id;
+ guint row_changed_id;
+ guint row_deleted_id;
+ guint rows_reordered_id;
+
+ guint sync_idle_id;
+} ListStoreBinding;
+
+/* Some trickery to be able to treat the data structures generically */
+typedef union {
+ BindingType type;
+
+ PropBinding prop_binding;
+ WindowBinding window_binding;
+ ListStoreBinding list_store_binding;
+} Binding;
+
+/* Function prototypes */
+static void
+unbind (Binding *binding);
+
+static GConfBridge *bridge = NULL; /* Global GConfBridge object */
+
+/* Free up all resources allocated by the GConfBridge. Called on exit. */
+static void
+destroy_bridge (void)
+{
+ g_hash_table_destroy (bridge->bindings);
+ g_object_unref (bridge->client);
+
+ g_free (bridge);
+}
+
+/**
+ * gconf_bridge_get
+ *
+ * Returns the #GConfBridge. This is a singleton object.
+ *
+ * Return value: The #GConfBridge.
+ **/
+GConfBridge *
+gconf_bridge_get (void)
+{
+ if (bridge)
+ return bridge;
+
+ gconf_bridge_install_default_error_handler ();
+
+ bridge = g_new (GConfBridge, 1);
+
+ bridge->client = gconf_client_get_default ();
+ bridge->bindings = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) unbind);
+
+ g_atexit (destroy_bridge);
+
+ return bridge;
+}
+
+/**
+ * gconf_bridge_get_client
+ * @bridge: A #GConfBridge
+ *
+ * Returns the #GConfClient used by @bridge. This is the same #GConfClient
+ * as returned by gconf_client_get_default().
+ *
+ * Return value: A #GConfClient.
+ **/
+GConfClient *
+gconf_bridge_get_client (GConfBridge *bridge)
+{
+ g_return_val_if_fail (bridge != NULL, NULL);
+
+ return bridge->client;
+}
+
+/* Generate an ID for a new binding */
+static guint
+new_id (void)
+{
+ static guint id_counter = 0;
+
+ id_counter++;
+
+ return id_counter;
+}
+
+/*
+ * Property bindings
+ */
+
+/* Syncs a value from GConf to an object property */
+static void
+prop_binding_sync_pref_to_prop (PropBinding *binding,
+ GConfValue *pref_value)
+{
+ GValue src_value, value;
+
+ /* Make sure we don't enter an infinite synchronizing loop */
+ g_signal_handler_block (binding->object, binding->prop_notify_id);
+
+ memset (&src_value, 0, sizeof (GValue));
+
+ /* First, convert GConfValue to GValue */
+ switch (pref_value->type) {
+ case GCONF_VALUE_STRING:
+ g_value_init (&src_value, G_TYPE_STRING);
+ g_value_set_string (&src_value,
+ gconf_value_get_string (pref_value));
+ break;
+ case GCONF_VALUE_INT:
+ g_value_init (&src_value, G_TYPE_INT);
+ g_value_set_int (&src_value,
+ gconf_value_get_int (pref_value));
+ break;
+ case GCONF_VALUE_BOOL:
+ g_value_init (&src_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&src_value,
+ gconf_value_get_bool (pref_value));
+ break;
+ case GCONF_VALUE_FLOAT:
+ g_value_init (&src_value, G_TYPE_FLOAT);
+ g_value_set_float (&src_value,
+ gconf_value_get_float (pref_value));
+ break;
+ default:
+ g_warning ("prop_binding_sync_pref_to_prop: Unhandled value "
+ "type '%d'.\n", pref_value->type);
+
+ return;
+ }
+
+ /* Then convert to the type expected by the object, if necessary */
+ memset (&value, 0, sizeof (GValue));
+ g_value_init (&value,
+ G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+
+ if (src_value.g_type != value.g_type) {
+ if (!g_value_transform (&src_value, &value)) {
+ g_warning ("prop_binding_sync_pref_to_prop: Failed to "
+ "transform a \"%s\" to a \"%s\".",
+ g_type_name (src_value.g_type),
+ g_type_name (value.g_type));
+
+ goto done;
+ }
+
+ g_object_set_property (binding->object,
+ binding->prop->name, &value);
+ } else {
+ g_object_set_property (binding->object,
+ binding->prop->name, &src_value);
+ }
+
+done:
+ g_value_unset (&src_value);
+ g_value_unset (&value);
+
+ g_signal_handler_unblock (binding->object, binding->prop_notify_id);
+}
+
+/* Syncs an object property to GConf */
+static void
+prop_binding_sync_prop_to_pref (PropBinding *binding)
+{
+ GValue value;
+ GConfValue *gconf_value;
+
+ memset (&value, 0, sizeof (GValue));
+
+ g_value_init (&value,
+ G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+ g_object_get_property (binding->object,
+ binding->prop->name,
+ &value);
+
+ switch (value.g_type) {
+ case G_TYPE_STRING:
+ gconf_value = gconf_value_new (GCONF_VALUE_STRING);
+ gconf_value_set_string (gconf_value,
+ g_value_get_string (&value));
+ break;
+ case G_TYPE_INT:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_int (&value));
+ break;
+ case G_TYPE_UINT:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uint (&value));
+ break;
+ case G_TYPE_LONG:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_long (&value));
+ break;
+ case G_TYPE_ULONG:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_ulong (&value));
+ break;
+ case G_TYPE_INT64:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_int64 (&value));
+ break;
+ case G_TYPE_UINT64:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uint64 (&value));
+ break;
+ case G_TYPE_CHAR:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_char (&value));
+ break;
+ case G_TYPE_UCHAR:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uchar (&value));
+ break;
+ case G_TYPE_ENUM:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_enum (&value));
+ break;
+ case G_TYPE_BOOLEAN:
+ gconf_value = gconf_value_new (GCONF_VALUE_BOOL);
+ gconf_value_set_bool (gconf_value,
+ g_value_get_boolean (&value));
+ break;
+ case G_TYPE_DOUBLE:
+ gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+#ifdef HAVE_CORBA_GCONF
+ /* FIXME we cast to a gfloat explicitly as CORBA GConf
+ * uses doubles in its API, but treats them as floats
+ * when transporting them over CORBA. See #322837 */
+ gconf_value_set_float (gconf_value,
+ (gfloat) g_value_get_double (&value));
+#else
+ gconf_value_set_float (gconf_value,
+ g_value_get_double (&value));
+#endif
+ break;
+ case G_TYPE_FLOAT:
+ gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+ gconf_value_set_float (gconf_value,
+ g_value_get_float (&value));
+ break;
+ default:
+ if (g_type_is_a (value.g_type, G_TYPE_ENUM)) {
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_enum (&value));
+ } else {
+ g_warning ("prop_binding_sync_prop_to_pref: "
+ "Unhandled value type '%s'.\n",
+ g_type_name (value.g_type));
+
+ goto done;
+ }
+
+ break;
+ }
+
+ /* Set to GConf */
+ gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+ /* Store until change notification comes in, so that we are able
+ * to ignore it */
+ binding->val_changes = g_slist_append (binding->val_changes,
+ gconf_value);
+
+done:
+ g_value_unset (&value);
+}
+
+/* Called when a GConf value bound to an object property has changed */
+static void
+prop_binding_pref_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ GConfValue *gconf_value;
+ PropBinding *binding;
+ GSList *l;
+
+ gconf_value = gconf_entry_get_value (entry);
+ if (!gconf_value)
+ return; /* NULL means that the value has been unset */
+
+ binding = (PropBinding *) user_data;
+
+ /* Check that this notification is not caused by sync_prop_to_pref() */
+ l = g_slist_find_custom (binding->val_changes,
+ gconf_value,
+ (GCompareFunc) gconf_value_compare);
+ if (l) {
+ gconf_value_free (l->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, l);
+
+ return;
+ }
+
+ prop_binding_sync_pref_to_prop (binding, gconf_value);
+}
+
+/* Performs a scheduled prop-to-pref sync for a prop binding in
+ * delay mode */
+static gboolean
+prop_binding_perform_scheduled_sync (PropBinding *binding)
+{
+ prop_binding_sync_prop_to_pref (binding);
+
+ binding->sync_timeout_id = 0;
+
+ g_object_unref (binding->object);
+
+ return FALSE;
+}
+
+#define PROP_BINDING_SYNC_DELAY 100 /* Delay for bindings with "delayed"
+ set to TRUE, in ms */
+
+/* Called when an object property has changed */
+static void
+prop_binding_prop_changed (GObject *object,
+ GParamSpec *param_spec,
+ PropBinding *binding)
+{
+ if (binding->delayed_mode) {
+ /* Just schedule a sync */
+ if (binding->sync_timeout_id == 0) {
+ /* We keep a reference on the object as long as
+ * we haven't synced yet to make sure we don't
+ * lose any data */
+ g_object_ref (binding->object);
+
+ binding->sync_timeout_id =
+ g_timeout_add
+ (PROP_BINDING_SYNC_DELAY,
+ (GSourceFunc)
+ prop_binding_perform_scheduled_sync,
+ binding);
+ }
+ } else {
+ /* Directly sync */
+ prop_binding_sync_prop_to_pref (binding);
+ }
+}
+
+/* Called when an object is destroyed */
+static void
+prop_binding_object_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ PropBinding *binding;
+
+ binding = (PropBinding *) user_data;
+ binding->object = NULL; /* Don't do anything with the object
+ * at unbind() */
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_property_full
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ * @delayed_sync: TRUE if there should be a delay between property changes
+ * and syncs to GConf. Set to TRUE when binding to a rapidly-changing
+ * property, for example the "value" property on a #GtkAdjustment.
+ *
+ * Binds @key to @prop, causing them to have the same value at all times.
+ *
+ * The types of @key and @prop should be compatible. Floats and doubles, and
+ * ints, uints, longs, unlongs, int64s, uint64s, chars, uchars and enums
+ * can be matched up. Booleans and strings can only be matched to their
+ * respective types.
+ *
+ * On calling this function the current value of @key will be set to @prop.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_property_full (GConfBridge *bridge,
+ const gchar *key,
+ GObject *object,
+ const gchar *prop,
+ gboolean delayed_sync)
+{
+ GParamSpec *pspec;
+ PropBinding *binding;
+ gchar *signal;
+ GConfValue *val;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (G_IS_OBJECT (object), 0);
+ g_return_val_if_fail (prop != NULL, 0);
+
+ /* First, try to fetch the propertys GParamSpec off the object */
+ pspec = g_object_class_find_property
+ (G_OBJECT_GET_CLASS (object), prop);
+ if (G_UNLIKELY (pspec == NULL)) {
+ g_warning ("gconf_bridge_bind_property_full: A property \"%s\" "
+ "was not found. Please make sure you are passing "
+ "the right property name.", prop);
+
+ return 0;
+ }
+
+ /* GParamSpec found: All good, create new binding. */
+ binding = g_new (PropBinding, 1);
+
+ binding->type = BINDING_PROP;
+ binding->id = new_id ();
+ binding->delayed_mode = delayed_sync;
+ binding->val_changes = NULL;
+ binding->key = g_strdup (key);
+ binding->object = object;
+ binding->prop = pspec;
+ binding->sync_timeout_id = 0;
+
+ /* Watch GConf key */
+ binding->val_notify_id =
+ gconf_client_notify_add (bridge->client, key,
+ prop_binding_pref_changed,
+ binding, NULL, NULL);
+
+ /* Connect to property change notifications */
+ signal = g_strconcat ("notify::", prop, NULL);
+ binding->prop_notify_id =
+ g_signal_connect (object, signal,
+ G_CALLBACK (prop_binding_prop_changed),
+ binding);
+ g_free (signal);
+
+ /* Sync object to value from GConf, if set */
+ val = gconf_client_get (bridge->client, key, NULL);
+ if (val) {
+ prop_binding_sync_pref_to_prop (binding, val);
+ gconf_value_free (val);
+ }
+
+ /* Handle case where watched object gets destroyed */
+ g_object_weak_ref (object,
+ prop_binding_object_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+static void
+prop_binding_block_cb (gpointer hkey,
+ PropBinding *binding,
+ const gchar *key)
+{
+ g_return_if_fail (binding != NULL);
+ g_return_if_fail (key != NULL);
+
+ if (binding->type == BINDING_PROP && binding->key &&
+ g_ascii_strcasecmp (binding->key, key) == 0)
+ g_signal_handler_block (
+ binding->object, binding->prop_notify_id);
+}
+
+static void
+prop_binding_unblock_cb (gpointer hkey,
+ PropBinding *binding,
+ const gchar *key)
+{
+ g_return_if_fail (binding != NULL);
+ g_return_if_fail (key != NULL);
+
+ if (binding->type == BINDING_PROP && binding->key &&
+ g_ascii_strcasecmp (binding->key, key) == 0)
+ g_signal_handler_unblock (
+ binding->object, binding->prop_notify_id);
+}
+
+void
+gconf_bridge_block_property_bindings (GConfBridge *bridge,
+ const gchar *key)
+{
+ g_return_if_fail (bridge != NULL);
+ g_return_if_fail (key != NULL);
+
+ g_hash_table_foreach (
+ bridge->bindings, (GHFunc)
+ prop_binding_block_cb, (gpointer) key);
+}
+
+void
+gconf_bridge_unblock_property_bindings (GConfBridge *bridge,
+ const gchar *key)
+{
+ g_return_if_fail (bridge != NULL);
+ g_return_if_fail (key != NULL);
+
+ g_hash_table_foreach (
+ bridge->bindings, (GHFunc)
+ prop_binding_unblock_cb, (gpointer) key);
+}
+
+/* Unbinds a property binding */
+static void
+prop_binding_unbind (PropBinding *binding)
+{
+ if (binding->delayed_mode && binding->sync_timeout_id > 0) {
+ /* Perform any scheduled syncs */
+ g_source_remove (binding->sync_timeout_id);
+
+ /* The object will still be around as we have
+ * a reference */
+ prop_binding_perform_scheduled_sync (binding);
+ }
+
+ gconf_client_notify_remove (bridge->client,
+ binding->val_notify_id);
+ g_free (binding->key);
+
+ while (binding->val_changes) {
+ gconf_value_free (binding->val_changes->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, binding->val_changes);
+ }
+
+ /* The object might have been destroyed .. */
+ if (binding->object) {
+ g_signal_handler_disconnect (binding->object,
+ binding->prop_notify_id);
+
+ g_object_weak_unref (binding->object,
+ prop_binding_object_destroyed, binding);
+ }
+}
+
+/*
+ * Window bindings
+ */
+
+/* Performs a scheduled dimensions-to-prefs sync for a window binding */
+static gboolean
+window_binding_perform_scheduled_sync (WindowBinding *binding)
+{
+ if (binding->bind_size) {
+ gint width, height;
+ gchar *key;
+ GdkWindowState state;
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (GTK_WIDGET (binding->window));
+ state = gdk_window_get_state (window);
+
+ if (state & GDK_WINDOW_STATE_MAXIMIZED) {
+ key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+ gconf_client_set_bool (bridge->client, key, TRUE, NULL);
+ g_free (key);
+ } else {
+ gtk_window_get_size (binding->window, &width, &height);
+
+ key = g_strconcat (binding->key_prefix, "_width", NULL);
+ gconf_client_set_int (bridge->client, key, width, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_height", NULL);
+ gconf_client_set_int (bridge->client, key, height, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+ gconf_client_set_bool (bridge->client, key, FALSE, NULL);
+ g_free (key);
+ }
+ }
+
+ if (binding->bind_pos) {
+ gint x, y;
+ gchar *key;
+
+ gtk_window_get_position (binding->window, &x, &y);
+
+ key = g_strconcat (binding->key_prefix, "_x", NULL);
+ gconf_client_set_int (bridge->client, key, x, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_y", NULL);
+ gconf_client_set_int (bridge->client, key, y, NULL);
+ g_free (key);
+ }
+
+ binding->sync_timeout_id = 0;
+
+ return FALSE;
+}
+
+#define WINDOW_BINDING_SYNC_DELAY 1 /* Delay before syncing new window
+ dimensions to GConf, in s */
+
+/* Called when the window han been resized or moved */
+static gboolean
+window_binding_configure_event_cb (GtkWindow *window,
+ GdkEventConfigure *event,
+ WindowBinding *binding)
+{
+ /* re-postpone by cancel of the previous request */
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ /* Schedule a sync */
+ binding->sync_timeout_id = g_timeout_add_seconds (WINDOW_BINDING_SYNC_DELAY,
+ (GSourceFunc) window_binding_perform_scheduled_sync,
+ binding);
+
+ return FALSE;
+}
+
+/* Called when the window state is being changed */
+static gboolean
+window_binding_state_event_cb (GtkWindow *window,
+ GdkEventWindowState *event,
+ WindowBinding *binding)
+{
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ if (event
+ && (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0
+ && (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) == 0) {
+ /* the window was restored from a maximized state; make sure its
+ * width and height is the one user stored before maximization */
+ gint width, height;
+
+ width = GPOINTER_TO_INT (g_object_get_data (
+ G_OBJECT (window), "binding-premax-width"));
+ height = GPOINTER_TO_INT (g_object_get_data (
+ G_OBJECT (window), "binding-premax-height"));
+
+ if (width && height) {
+ gtk_window_resize (window, width, height);
+
+ /* Do this only once, as it is restored
+ * after loading maximized state. */
+ g_object_set_data (
+ G_OBJECT (window),
+ "binding-premax-width", NULL);
+ g_object_set_data (
+ G_OBJECT (window),
+ "binding-premax-height", NULL);
+ }
+ }
+
+ window_binding_perform_scheduled_sync (binding);
+
+ return FALSE;
+}
+
+/* Called when the window is being unmapped */
+static gboolean
+window_binding_unmap_cb (GtkWindow *window,
+ WindowBinding *binding)
+{
+ /* Force sync */
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ /* XXX It's too late to record the window position.
+ * gtk_window_get_position() will report (0, 0). */
+ binding->bind_pos = FALSE;
+
+ window_binding_perform_scheduled_sync (binding);
+
+ return FALSE;
+}
+
+/* Called when a window is destroyed */
+static void
+window_binding_window_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ WindowBinding *binding;
+
+ binding = (WindowBinding *) user_data;
+ binding->window = NULL; /* Don't do anything with the window
+ * at unbind() */
+
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_window
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ * @bind_size: TRUE to bind the size of @window
+ * @bind_pos: TRUE to bind the position of @window
+ *
+ * On calling this function @window will be resized to the values
+ * specified by "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height"
+ * and maximixed if "@key_prefix<!-- -->_maximized is TRUE if
+ * @bind_size is TRUE, and moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y" if @bind_pos is TRUE.
+ * The respective GConf values will be updated when the window is resized
+ * and/or moved.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_window (GConfBridge *bridge,
+ const gchar *key_prefix,
+ GtkWindow *window,
+ gboolean bind_size,
+ gboolean bind_pos)
+{
+ WindowBinding *binding;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key_prefix != NULL, 0);
+ g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+ /* Create new binding. */
+ binding = g_new (WindowBinding, 1);
+
+ binding->type = BINDING_WINDOW;
+ binding->id = new_id ();
+ binding->bind_size = bind_size;
+ binding->bind_pos = bind_pos;
+ binding->key_prefix = g_strdup (key_prefix);
+ binding->window = window;
+ binding->sync_timeout_id = 0;
+
+ /* Set up GConf keys & sync window to GConf values */
+ if (bind_size) {
+ gchar *key;
+ GConfValue *width_val, *height_val, *maximized_val;
+
+ key = g_strconcat (key_prefix, "_width", NULL);
+ width_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_height", NULL);
+ height_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_maximized", NULL);
+ maximized_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ if (width_val && height_val) {
+ gtk_window_resize (window,
+ gconf_value_get_int (width_val),
+ gconf_value_get_int (height_val));
+
+ gconf_value_free (width_val);
+ gconf_value_free (height_val);
+ } else if (width_val) {
+ gconf_value_free (width_val);
+ } else if (height_val) {
+ gconf_value_free (height_val);
+ }
+
+ if (maximized_val) {
+ if (gconf_value_get_bool (maximized_val)) {
+ /* Maximize is not done immediately, but to
+ * count with proper window size, resize it
+ * before. The previous size is restored
+ * after the maximization is changed,
+ * in window_binding_state_event_cb(). */
+ gint width = 0, height = 0;
+ GdkScreen *screen;
+
+ gtk_window_get_size (window, &width, &height);
+ g_object_set_data (
+ G_OBJECT (window),
+ "binding-premax-width",
+ GINT_TO_POINTER (width));
+ g_object_set_data (
+ G_OBJECT (window),
+ "binding-premax-height",
+ GINT_TO_POINTER (height));
+
+ screen = gtk_window_get_screen (window);
+ gtk_window_resize (window,
+ gdk_screen_get_width (screen),
+ gdk_screen_get_height (screen));
+
+ gtk_window_maximize (window);
+ }
+ gconf_value_free (maximized_val);
+ }
+ }
+
+ if (bind_pos) {
+ gchar *key;
+ GConfValue *x_val, *y_val;
+
+ key = g_strconcat (key_prefix, "_x", NULL);
+ x_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_y", NULL);
+ y_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ if (x_val && y_val) {
+ gtk_window_move (window,
+ gconf_value_get_int (x_val),
+ gconf_value_get_int (y_val));
+
+ gconf_value_free (x_val);
+ gconf_value_free (y_val);
+ } else if (x_val) {
+ gconf_value_free (x_val);
+ } else if (y_val) {
+ gconf_value_free (y_val);
+ }
+ }
+
+ /* Connect to window size change notifications */
+ binding->configure_event_id =
+ g_signal_connect (window,
+ "configure-event",
+ G_CALLBACK
+ (window_binding_configure_event_cb),
+ binding);
+
+ binding->window_state_event_id =
+ g_signal_connect (window,
+ "window_state_event",
+ G_CALLBACK
+ (window_binding_state_event_cb),
+ binding);
+ binding->unmap_id =
+ g_signal_connect (window,
+ "unmap",
+ G_CALLBACK (window_binding_unmap_cb),
+ binding);
+
+ /* Handle case where window gets destroyed */
+ g_object_weak_ref (G_OBJECT (window),
+ window_binding_window_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+/* Unbinds a window binding */
+static void
+window_binding_unbind (WindowBinding *binding)
+{
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ g_free (binding->key_prefix);
+
+ /* The window might have been destroyed .. */
+ if (binding->window) {
+ g_signal_handler_disconnect (binding->window,
+ binding->configure_event_id);
+ g_signal_handler_disconnect (binding->window,
+ binding->window_state_event_id);
+ g_signal_handler_disconnect (binding->window,
+ binding->unmap_id);
+
+ g_object_weak_unref (G_OBJECT (binding->window),
+ window_binding_window_destroyed, binding);
+ }
+}
+
+/*
+ * List store bindings
+ */
+
+/* Fills a GtkListStore with the string list from @value */
+static void
+list_store_binding_sync_pref_to_store (ListStoreBinding *binding,
+ GConfValue *value)
+{
+ GSList *list, *l;
+ GtkTreeIter iter;
+
+ /* Make sure we don't enter an infinite synchronizing loop */
+ g_signal_handler_block (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_block (binding->list_store,
+ binding->row_deleted_id);
+
+ gtk_list_store_clear (binding->list_store);
+
+ list = gconf_value_get_list (value);
+ for (l = list; l; l = l->next) {
+ GConfValue *l_value;
+ const gchar *string;
+
+ l_value = (GConfValue *) l->data;
+ string = gconf_value_get_string (l_value);
+
+ gtk_list_store_insert_with_values (binding->list_store,
+ &iter, -1,
+ 0, string,
+ -1);
+ }
+
+ g_signal_handler_unblock (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_unblock (binding->list_store,
+ binding->row_deleted_id);
+}
+
+/* Sets a GConf value to the contents of a GtkListStore */
+static gboolean
+list_store_binding_sync_store_to_pref (ListStoreBinding *binding)
+{
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ GSList *list;
+ gint res;
+ GConfValue *gconf_value;
+
+ tree_model = GTK_TREE_MODEL (binding->list_store);
+
+ /* Build list */
+ list = NULL;
+ res = gtk_tree_model_get_iter_first (tree_model, &iter);
+ while (res) {
+ gchar *string;
+ GConfValue *tmp_value;
+
+ gtk_tree_model_get (tree_model, &iter,
+ 0, &string, -1);
+
+ tmp_value = gconf_value_new (GCONF_VALUE_STRING);
+ gconf_value_set_string (tmp_value, string);
+
+ list = g_slist_append (list, tmp_value);
+
+ res = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+
+ /* Create value */
+ gconf_value = gconf_value_new (GCONF_VALUE_LIST);
+ gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING);
+ gconf_value_set_list_nocopy (gconf_value, list);
+
+ /* Set */
+ gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+ /* Store until change notification comes in, so that we are able
+ * to ignore it */
+ binding->val_changes = g_slist_append (binding->val_changes,
+ gconf_value);
+
+ binding->sync_idle_id = 0;
+
+ g_object_unref (binding->list_store);
+
+ return FALSE;
+}
+
+/* Pref changed: sync */
+static void
+list_store_binding_pref_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ GConfValue *gconf_value;
+ ListStoreBinding *binding;
+ GSList *l;
+
+ gconf_value = gconf_entry_get_value (entry);
+ if (!gconf_value)
+ return; /* NULL means that the value has been unset */
+
+ binding = (ListStoreBinding *) user_data;
+
+ /* Check that this notification is not caused by
+ * sync_store_to_pref() */
+ l = g_slist_find_custom (binding->val_changes,
+ gconf_value,
+ (GCompareFunc) gconf_value_compare);
+ if (l) {
+ gconf_value_free (l->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, l);
+
+ return;
+ }
+
+ list_store_binding_sync_pref_to_store (binding, gconf_value);
+}
+
+/* Called when an object is destroyed */
+static void
+list_store_binding_store_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ ListStoreBinding *binding;
+
+ binding = (ListStoreBinding *) user_data;
+ binding->list_store = NULL; /* Don't do anything with the store
+ * at unbind() */
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/* List store changed: Sync */
+static void
+list_store_binding_store_changed_cb (ListStoreBinding *binding)
+{
+ if (binding->sync_idle_id == 0) {
+ g_object_ref (binding->list_store);
+
+ binding->sync_idle_id = g_idle_add
+ ((GSourceFunc) list_store_binding_sync_store_to_pref,
+ binding);
+ }
+}
+
+/**
+ * gconf_bridge_bind_string_list_store
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @list_store: A #GtkListStore
+ *
+ * On calling this function single string column #GtkListStore @list_store
+ * will be kept synchronized with the GConf string list value pointed to by
+ * @key. On calling this function @list_store will be populated with the
+ * strings specified by the value of @key.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+ const gchar *key,
+ GtkListStore *list_store)
+{
+ GtkTreeModel *tree_model;
+ gboolean have_one_column, is_string_column;
+ ListStoreBinding *binding;
+ GConfValue *val;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), 0);
+
+ /* Check list store suitability */
+ tree_model = GTK_TREE_MODEL (list_store);
+ have_one_column = (gtk_tree_model_get_n_columns (tree_model) == 1);
+ is_string_column = (gtk_tree_model_get_column_type
+ (tree_model, 0) == G_TYPE_STRING);
+ if (G_UNLIKELY (!have_one_column || !is_string_column)) {
+ g_warning ("gconf_bridge_bind_string_list_store: Only "
+ "GtkListStores with exactly one string column are "
+ "supported.");
+
+ return 0;
+ }
+
+ /* Create new binding. */
+ binding = g_new (ListStoreBinding, 1);
+
+ binding->type = BINDING_LIST_STORE;
+ binding->id = new_id ();
+ binding->key = g_strdup (key);
+ binding->val_changes = NULL;
+ binding->list_store = list_store;
+ binding->sync_idle_id = 0;
+
+ /* Watch GConf key */
+ binding->val_notify_id =
+ gconf_client_notify_add (bridge->client, key,
+ list_store_binding_pref_changed,
+ binding, NULL, NULL);
+
+ /* Connect to ListStore change notifications */
+ binding->row_inserted_id =
+ g_signal_connect_swapped (list_store, "row-inserted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->row_changed_id =
+ g_signal_connect_swapped (list_store, "row-changed",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->row_deleted_id =
+ g_signal_connect_swapped (list_store, "row-deleted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->rows_reordered_id =
+ g_signal_connect_swapped (list_store, "rows-reordered",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+
+ /* Sync object to value from GConf, if set */
+ val = gconf_client_get (bridge->client, key, NULL);
+ if (val) {
+ list_store_binding_sync_pref_to_store (binding, val);
+ gconf_value_free (val);
+ }
+
+ /* Handle case where watched object gets destroyed */
+ g_object_weak_ref (G_OBJECT (list_store),
+ list_store_binding_store_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+/* Unbinds a list store binding */
+static void
+list_store_binding_unbind (ListStoreBinding *binding)
+{
+ /* Perform any scheduled syncs */
+ if (binding->sync_idle_id > 0) {
+ g_source_remove (binding->sync_idle_id);
+
+ /* The store will still be around as we added a reference */
+ list_store_binding_sync_store_to_pref (binding);
+ }
+
+ g_free (binding->key);
+
+ while (binding->val_changes) {
+ gconf_value_free (binding->val_changes->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, binding->val_changes);
+ }
+
+ /* The store might have been destroyed .. */
+ if (binding->list_store) {
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_changed_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_deleted_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->rows_reordered_id);
+
+ g_object_weak_unref (G_OBJECT (binding->list_store),
+ list_store_binding_store_destroyed,
+ binding);
+ }
+}
+
+/*
+ * Generic unbinding
+ */
+
+/* Unbinds a binding */
+static void
+unbind (Binding *binding)
+{
+ /* Call specialized unbinding function */
+ switch (binding->type) {
+ case BINDING_PROP:
+ prop_binding_unbind ((PropBinding *) binding);
+ break;
+ case BINDING_WINDOW:
+ window_binding_unbind ((WindowBinding *) binding);
+ break;
+ case BINDING_LIST_STORE:
+ list_store_binding_unbind ((ListStoreBinding *) binding);
+ break;
+ default:
+ g_warning ("Unknown binding type '%d'\n", binding->type);
+ break;
+ }
+
+ g_free (binding);
+}
+
+/**
+ * gconf_bridge_unbind
+ * @bridge: A #GConfBridge
+ * @binding_id: The ID of the binding to be removed
+ *
+ * Removes the binding with ID @binding_id.
+ **/
+void
+gconf_bridge_unbind (GConfBridge *bridge,
+ guint binding_id)
+{
+ g_return_if_fail (bridge != NULL);
+ g_return_if_fail (binding_id > 0);
+
+ /* This will trigger the hash tables value destruction
+ * function, which will take care of further cleanup */
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding_id));
+}
+
+/*
+ * Error handling
+ */
+
+/* This is the same dialog as used in eel */
+static void
+error_handler (GConfClient *client,
+ GError *error)
+{
+ static gboolean shown_dialog = FALSE;
+
+ g_warning ("GConf error:\n %s", error->message);
+
+ if (!shown_dialog) {
+ gchar *message;
+ GtkWidget *dlg;
+
+ message = g_strdup_printf (_("GConf error: %s"),
+ error->message);
+ dlg = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ message);
+ g_free (message);
+
+ gtk_message_dialog_format_secondary_text
+ (GTK_MESSAGE_DIALOG (dlg),
+ _("All further errors shown only on terminal."));
+ gtk_window_set_title (GTK_WINDOW (dlg), "");
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+
+ gtk_widget_destroy (dlg);
+
+ shown_dialog = TRUE;
+ }
+}
+
+/**
+ * gconf_bridge_install_default_error_handler
+ *
+ * Sets up the default error handler. Any unhandled GConf errors will
+ * automatically be handled by presenting the user an error dialog.
+ **/
+void
+gconf_bridge_install_default_error_handler (void)
+{
+ gconf_client_set_global_default_error_handler (error_handler);
+}
diff --git a/libemail-utils/gconf-bridge.h b/libemail-utils/gconf-bridge.h
new file mode 100644
index 0000000..371f117
--- /dev/null
+++ b/libemail-utils/gconf-bridge.h
@@ -0,0 +1,134 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GCONF_BRIDGE_H__
+#define __GCONF_BRIDGE_H__
+
+#include <gconf/gconf-client.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void gconf_bridge_install_default_error_handler (void);
+
+typedef struct _GConfBridge GConfBridge;
+
+GConfBridge *gconf_bridge_get (void);
+
+GConfClient *gconf_bridge_get_client (GConfBridge *bridge);
+
+guint gconf_bridge_bind_property_full (GConfBridge *bridge,
+ const gchar *key,
+ GObject *object,
+ const gchar *prop,
+ gboolean delayed_sync);
+
+/**
+ * gconf_bridge_bind_property
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop without delays, causing them to have the same value at all times. See
+ * #gconf_bridge_bind_property_full for more details.
+ *
+ **/
+#define gconf_bridge_bind_property(bridge, key, object, prop) \
+ gconf_bridge_bind_property_full ((bridge), (key), \
+ (object), (prop), FALSE)
+
+/**
+ * gconf_bridge_bind_property_delayed
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop with a delay, causing them to have the same value at all
+ * times. See #gconf_bridge_bind_property_full for more details.
+ **/
+#define gconf_bridge_bind_property_delayed(bridge, key, object, prop) \
+ gconf_bridge_bind_property_full ((bridge), (key), \
+ (object), (prop), TRUE)
+
+/**
+ * gconf_bridge_block_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be blocked
+ *
+ * Blocks property bindings for @key. To unblock it call #gconf_bridge_unblock_property_bindings.
+ **/
+void gconf_bridge_block_property_bindings (GConfBridge *bridge, const gchar *key);
+
+/**
+ * gconf_bridge_unblock_property_bindings
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be unblocked
+ *
+ * Unblocks property bindings for @key, these should be previously blocked with #gconf_bridge_unblock_property_bindings.
+ **/
+void gconf_bridge_unblock_property_bindings (GConfBridge *bridge, const gchar *key);
+
+guint gconf_bridge_bind_window (GConfBridge *bridge,
+ const gchar *key_prefix,
+ GtkWindow *window,
+ gboolean bind_size,
+ gboolean bind_pos);
+
+/**
+ * gconf_bridge_bind_window_size
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be resized to the values specified by
+ * "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height". The respective
+ * GConf values will be updated when the window is resized. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_size(bridge, key_prefix, window) \
+ gconf_bridge_bind_window ((bridge), (key_prefix), (window), TRUE, FALSE)
+
+/**
+ * gconf_bridge_bind_window_pos
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y". The respective GConf
+ * values will be updated when the window is moved. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_pos(bridge, key_prefix, window) \
+ gconf_bridge_bind_window ((bridge), (key_prefix), (window), FALSE, TRUE)
+
+guint gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+ const gchar *key,
+ GtkListStore *list_store);
+
+void gconf_bridge_unbind (GConfBridge *bridge,
+ guint binding_id);
+
+G_END_DECLS
+
+#endif /* __GCONF_BRIDGE_H__ */
diff --git a/libemail-utils/libemail-utils.pc.in b/libemail-utils/libemail-utils.pc.in
new file mode 100644
index 0000000..384147b
--- /dev/null
+++ b/libemail-utils/libemail-utils.pc.in
@@ -0,0 +1,15 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+privincludedir= privincludedir@
+
+Name: libemail-utils
+Description: Client library for evolution mail
+Version: @VERSION@
+Requires: camel-1.2 libedataserver-1.2 gio-2.0
+Libs: -L${libdir} -lemail-utils
+Cflags: -I${privincludedir}
diff --git a/libemail-utils/mail-mt.c b/libemail-utils/mail-mt.c
new file mode 100644
index 0000000..4c3ee34
--- /dev/null
+++ b/libemail-utils/mail-mt.c
@@ -0,0 +1,639 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/e-flag.h>
+
+#include "mail-mt.h"
+
+/*#define MALLOC_CHECK*/
+#define d(x)
+
+/* XXX This is a dirty hack on a dirty hack. We really need
+ * to rework or get rid of the functions that use this. */
+const gchar *shell_builtin_backend = "mail";
+
+static guint mail_msg_seq; /* sequence number of each message */
+
+/* Table of active messages. Must hold mail_msg_lock to access. */
+static GHashTable *mail_msg_active_table;
+static GMutex *mail_msg_lock;
+static GCond *mail_msg_cond;
+
+static MailMsgCreateActivityFunc create_activity = NULL;
+static MailMsgSubmitActivityFunc submit_acitivity = NULL;
+static MailMsgFreeActivityFunc free_activity = NULL;
+static MailMsgCompleteActivityFunc complete_activity = NULL;
+static MailMsgAlertErrorFunc alert_error = NULL;
+static MailMsgCancelActivityFunc cancel_activity = NULL;
+
+void mail_msg_register_activities (MailMsgCreateActivityFunc acreate,
+ MailMsgSubmitActivityFunc asubmit,
+ MailMsgFreeActivityFunc freeact,
+ MailMsgCompleteActivityFunc comp_act,
+ MailMsgCancelActivityFunc cancel_act,
+ MailMsgAlertErrorFunc ealert)
+{
+ /* This is a utter hack to keep EActivity out of EDS and still let Evolution do EActivity */
+ create_activity = acreate;
+ submit_acitivity = asubmit;
+ free_activity = freeact;
+ complete_activity = comp_act;
+ cancel_activity = cancel_act;
+ alert_error = ealert;
+}
+
+static void
+mail_msg_cancelled (CamelOperation *operation,
+ gpointer user_data)
+{
+ mail_msg_cancel (GPOINTER_TO_UINT (user_data));
+}
+
+
+static gboolean
+mail_msg_submit (CamelOperation *cancellable)
+{
+
+ if (submit_acitivity)
+ submit_acitivity ((GCancellable *)cancellable);
+ return FALSE;
+}
+
+gpointer
+mail_msg_new (MailMsgInfo *info)
+{
+ MailMsg *msg;
+
+ g_mutex_lock (mail_msg_lock);
+
+ msg = g_slice_alloc0 (info->size);
+ msg->info = info;
+ msg->ref_count = 1;
+ msg->seq = mail_msg_seq++;
+
+ msg->cancellable = camel_operation_new ();
+
+ if (create_activity)
+ create_activity (msg->cancellable);
+
+ g_signal_connect (
+ msg->cancellable, "cancelled",
+ G_CALLBACK (mail_msg_cancelled),
+ GINT_TO_POINTER (msg->seq));
+
+ g_hash_table_insert (
+ mail_msg_active_table, GINT_TO_POINTER (msg->seq), msg);
+
+ d(printf("New message %p\n", msg));
+
+ g_mutex_unlock (mail_msg_lock);
+
+ return msg;
+}
+
+#ifdef MALLOC_CHECK
+#include <mcheck.h>
+
+static void
+checkmem (gpointer p)
+{
+ if (p) {
+ gint status = mprobe (p);
+
+ switch (status) {
+ case MCHECK_HEAD:
+ printf("Memory underrun at %p\n", p);
+ abort ();
+ case MCHECK_TAIL:
+ printf("Memory overrun at %p\n", p);
+ abort ();
+ case MCHECK_FREE:
+ printf("Double free %p\n", p);
+ abort ();
+ }
+ }
+}
+#endif
+
+static gboolean
+mail_msg_free (MailMsg *mail_msg)
+{
+ /* This is an idle callback. */
+
+ if (free_activity)
+ free_activity (mail_msg->cancellable);
+
+ if (mail_msg->cancellable != NULL)
+ g_object_unref (mail_msg->cancellable);
+
+ if (mail_msg->error != NULL)
+ g_error_free (mail_msg->error);
+
+ g_slice_free1 (mail_msg->info->size, mail_msg);
+
+ return FALSE;
+}
+
+gpointer
+mail_msg_ref (gpointer msg)
+{
+ MailMsg *mail_msg = msg;
+
+ g_return_val_if_fail (mail_msg != NULL, msg);
+ g_return_val_if_fail (mail_msg->ref_count > 0, msg);
+
+ g_atomic_int_inc (&mail_msg->ref_count);
+
+ return msg;
+}
+
+void
+mail_msg_unref (gpointer msg)
+{
+ MailMsg *mail_msg = msg;
+
+ g_return_if_fail (mail_msg != NULL);
+ g_return_if_fail (mail_msg->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&mail_msg->ref_count)) {
+
+#ifdef MALLOC_CHECK
+ checkmem (mail_msg);
+ checkmem (mail_msg->cancel);
+ checkmem (mail_msg->priv);
+#endif
+ d(printf("Free message %p\n", msg));
+
+ if (mail_msg->info->free)
+ mail_msg->info->free (mail_msg);
+
+ g_mutex_lock (mail_msg_lock);
+
+ g_hash_table_remove (
+ mail_msg_active_table,
+ GINT_TO_POINTER (mail_msg->seq));
+ g_cond_broadcast (mail_msg_cond);
+
+ g_mutex_unlock (mail_msg_lock);
+
+ /* Destroy the message from an idle callback
+ * so we know we're in the main loop thread. */
+ g_idle_add ((GSourceFunc) mail_msg_free, mail_msg);
+ }
+}
+
+void
+mail_msg_check_error (gpointer msg)
+{
+ MailMsg *m = msg;
+
+#ifdef MALLOC_CHECK
+ checkmem (m);
+ checkmem (m->cancel);
+ checkmem (m->priv);
+#endif
+
+ if (m->error == NULL)
+ return;
+
+ if (complete_activity)
+ complete_activity (m->cancellable);
+
+ if (g_error_matches (m->error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ if (cancel_activity)
+ cancel_activity (m->cancellable);
+ return;
+ }
+
+ /* XXX Hmm, no explanation of why this is needed. It looks like
+ * a lame hack and will be removed at some point, if only to
+ * reintroduce whatever issue made this necessary so we can
+ * document it the source code this time. */
+ if (g_error_matches (m->error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_UID))
+ return;
+
+ /* FIXME: Submit an error on the dbus */
+ if (alert_error) {
+ char *what;
+
+ if (m->info->desc && (what = m->info->desc (m))) {
+ alert_error (what, m->error->message);
+ g_free (what);
+ } else
+ alert_error (NULL, m->error->message);
+ }
+}
+
+void
+mail_msg_cancel (guint msgid)
+{
+ MailMsg *msg;
+ GCancellable *cancellable = NULL;
+
+ g_mutex_lock (mail_msg_lock);
+
+ msg = g_hash_table_lookup (
+ mail_msg_active_table, GINT_TO_POINTER (msgid));
+
+ /* Hold a reference to the GCancellable so it doesn't finalize
+ * itself on us between unlocking the mutex and cancelling. */
+ if (msg != NULL) {
+ cancellable = msg->cancellable;
+ if (g_cancellable_is_cancelled (cancellable))
+ cancellable = NULL;
+ else
+ g_object_ref (cancellable);
+ }
+
+ g_mutex_unlock (mail_msg_lock);
+
+ if (cancellable != NULL) {
+ camel_operation_cancel (CAMEL_OPERATION (cancellable));
+ g_object_unref (cancellable);
+ }
+}
+
+gboolean
+mail_msg_active (void)
+{
+ gboolean active;
+
+ g_mutex_lock (mail_msg_lock);
+ active = g_hash_table_size (mail_msg_active_table) > 0;
+ g_mutex_unlock (mail_msg_lock);
+
+ return active;
+}
+
+/* **************************************** */
+
+static GHookList cancel_hook_list;
+
+GHook *
+mail_cancel_hook_add (GHookFunc func,
+ gpointer data)
+{
+ GHook *hook;
+
+ g_mutex_lock (mail_msg_lock);
+
+ if (!cancel_hook_list.is_setup)
+ g_hook_list_init (&cancel_hook_list, sizeof (GHook));
+
+ hook = g_hook_alloc (&cancel_hook_list);
+ hook->func = func;
+ hook->data = data;
+
+ g_hook_append (&cancel_hook_list, hook);
+
+ g_mutex_unlock (mail_msg_lock);
+
+ return hook;
+}
+
+void
+mail_cancel_hook_remove (GHook *hook)
+{
+ g_mutex_lock (mail_msg_lock);
+
+ g_return_if_fail (cancel_hook_list.is_setup);
+ g_hook_destroy_link (&cancel_hook_list, hook);
+
+ g_mutex_unlock (mail_msg_lock);
+}
+
+void
+mail_cancel_all (void)
+{
+ camel_operation_cancel (NULL);
+
+ g_mutex_lock (mail_msg_lock);
+
+ if (cancel_hook_list.is_setup)
+ g_hook_list_invoke (&cancel_hook_list, FALSE);
+
+ g_mutex_unlock (mail_msg_lock);
+}
+
+static guint idle_source_id = 0;
+G_LOCK_DEFINE_STATIC (idle_source_id);
+static GAsyncQueue *main_loop_queue = NULL;
+static GAsyncQueue *msg_reply_queue = NULL;
+static GThread *main_thread = NULL;
+
+static gboolean
+mail_msg_idle_cb (void)
+{
+ MailMsg *msg;
+
+ g_return_val_if_fail (main_loop_queue != NULL, FALSE);
+ g_return_val_if_fail (msg_reply_queue != NULL, FALSE);
+
+ G_LOCK (idle_source_id);
+ idle_source_id = 0;
+ G_UNLOCK (idle_source_id);
+ /* check the main loop queue */
+ while ((msg = g_async_queue_try_pop (main_loop_queue)) != NULL) {
+ GCancellable *cancellable;
+
+ cancellable = msg->cancellable;
+
+ g_idle_add_full (
+ G_PRIORITY_DEFAULT,
+ (GSourceFunc) mail_msg_submit,
+ g_object_ref (msg->cancellable),
+ (GDestroyNotify) g_object_unref);
+ if (msg->info->exec != NULL)
+ msg->info->exec (msg, cancellable, &msg->error);
+ if (msg->info->done != NULL)
+ msg->info->done (msg);
+ mail_msg_unref (msg);
+ }
+
+ /* check the reply queue */
+ while ((msg = g_async_queue_try_pop (msg_reply_queue)) != NULL) {
+ if (msg->info->done != NULL)
+ msg->info->done (msg);
+ mail_msg_check_error (msg);
+ mail_msg_unref (msg);
+ }
+ return FALSE;
+}
+
+static void
+mail_msg_proxy (MailMsg *msg)
+{
+ GCancellable *cancellable;
+
+ cancellable = msg->cancellable;
+
+ if (msg->info->desc != NULL) {
+ gchar *text = msg->info->desc (msg);
+ camel_operation_push_message (cancellable, "%s", text);
+ g_free (text);
+ }
+
+ g_idle_add_full (
+ G_PRIORITY_DEFAULT,
+ (GSourceFunc) mail_msg_submit,
+ g_object_ref (msg->cancellable),
+ (GDestroyNotify) g_object_unref);
+
+ if (msg->info->exec != NULL)
+ msg->info->exec (msg, cancellable, &msg->error);
+
+ if (msg->info->desc != NULL)
+ camel_operation_pop_message (cancellable);
+
+ g_async_queue_push (msg_reply_queue, msg);
+
+ G_LOCK (idle_source_id);
+ if (idle_source_id == 0)
+ idle_source_id = g_idle_add (
+ (GSourceFunc) mail_msg_idle_cb, NULL);
+ G_UNLOCK (idle_source_id);
+}
+
+void
+mail_msg_init (void)
+{
+ mail_msg_lock = g_mutex_new ();
+ mail_msg_cond = g_cond_new ();
+
+ main_loop_queue = g_async_queue_new ();
+ msg_reply_queue = g_async_queue_new ();
+
+ mail_msg_active_table = g_hash_table_new (NULL, NULL);
+ main_thread = g_thread_self ();
+}
+
+static gint
+mail_msg_compare (const MailMsg *msg1,
+ const MailMsg *msg2)
+{
+ gint priority1 = msg1->priority;
+ gint priority2 = msg2->priority;
+
+ if (priority1 == priority2)
+ return 0;
+
+ return (priority1 < priority2) ? 1 : -1;
+}
+
+static gpointer
+create_thread_pool (gpointer data)
+{
+ GThreadPool *thread_pool;
+ gint max_threads = GPOINTER_TO_INT (data);
+
+ /* once created, run forever */
+ thread_pool = g_thread_pool_new (
+ (GFunc) mail_msg_proxy, NULL, max_threads, FALSE, NULL);
+ g_thread_pool_set_sort_function (
+ thread_pool, (GCompareDataFunc) mail_msg_compare, NULL);
+
+ return thread_pool;
+}
+
+void
+mail_msg_main_loop_push (gpointer msg)
+{
+ g_async_queue_push_sorted (main_loop_queue, msg,
+ (GCompareDataFunc) mail_msg_compare, NULL);
+
+ G_LOCK (idle_source_id);
+ if (idle_source_id == 0)
+ idle_source_id = g_idle_add (
+ (GSourceFunc) mail_msg_idle_cb, NULL);
+ G_UNLOCK (idle_source_id);
+}
+
+void
+mail_msg_unordered_push (gpointer msg)
+{
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (10));
+
+ g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+void
+mail_msg_fast_ordered_push (gpointer msg)
+{
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1));
+
+ g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+void
+mail_msg_slow_ordered_push (gpointer msg)
+{
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1));
+
+ g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL);
+}
+
+gboolean
+mail_in_main_thread (void)
+{
+ return (g_thread_self () == main_thread);
+}
+
+/* ********************************************************************** */
+
+struct _call_msg {
+ MailMsg base;
+
+ mail_call_t type;
+ MailMainFunc func;
+ gpointer ret;
+ va_list ap;
+ EFlag *done;
+};
+
+static void
+do_call (struct _call_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gpointer p1, *p2, *p3, *p4, *p5;
+ gint i1;
+ va_list ap;
+
+ G_VA_COPY (ap, m->ap);
+
+ switch (m->type) {
+ case MAIL_CALL_p_p:
+ p1 = va_arg (ap, gpointer );
+ m->ret = m->func (p1);
+ break;
+ case MAIL_CALL_p_pp:
+ p1 = va_arg (ap, gpointer );
+ p2 = va_arg (ap, gpointer );
+ m->ret = m->func (p1, p2);
+ break;
+ case MAIL_CALL_p_ppp:
+ p1 = va_arg (ap, gpointer );
+ p2 = va_arg (ap, gpointer );
+ p3 = va_arg (ap, gpointer );
+ m->ret = m->func (p1, p2, p3);
+ break;
+ case MAIL_CALL_p_pppp:
+ p1 = va_arg (ap, gpointer );
+ p2 = va_arg (ap, gpointer );
+ p3 = va_arg (ap, gpointer );
+ p4 = va_arg (ap, gpointer );
+ m->ret = m->func (p1, p2, p3, p4);
+ break;
+ case MAIL_CALL_p_ppppp:
+ p1 = va_arg (ap, gpointer );
+ p2 = va_arg (ap, gpointer );
+ p3 = va_arg (ap, gpointer );
+ p4 = va_arg (ap, gpointer );
+ p5 = va_arg (ap, gpointer );
+ m->ret = m->func (p1, p2, p3, p4, p5);
+ break;
+ case MAIL_CALL_p_ppippp:
+ p1 = va_arg (ap, gpointer );
+ p2 = va_arg (ap, gpointer );
+ i1 = va_arg (ap, gint);
+ p3 = va_arg (ap, gpointer );
+ p4 = va_arg (ap, gpointer );
+ p5 = va_arg (ap, gpointer );
+ m->ret = m->func (p1, p2, i1, p3, p4, p5);
+ break;
+ }
+
+ if (g_cancellable_is_cancelled (cancellable)) {
+ if (cancel_activity)
+ cancel_activity (cancellable);
+ } else {
+ if (complete_activity)
+ complete_activity (cancellable);
+ }
+
+ if (m->done != NULL)
+ e_flag_set (m->done);
+}
+
+static MailMsgInfo mail_call_info = {
+ sizeof (struct _call_msg),
+ (MailMsgDescFunc) NULL,
+ (MailMsgExecFunc) do_call,
+ (MailMsgDoneFunc) NULL,
+ (MailMsgFreeFunc) NULL
+};
+
+gpointer
+mail_call_main (mail_call_t type,
+ MailMainFunc func,
+ ...)
+{
+ GCancellable *cancellable;
+ struct _call_msg *m;
+ gpointer ret;
+ va_list ap;
+
+ va_start (ap, func);
+
+ m = mail_msg_new (&mail_call_info);
+ m->type = type;
+ m->func = func;
+ G_VA_COPY (m->ap, ap);
+
+ cancellable = m->base.cancellable;
+
+ if (mail_in_main_thread ())
+ do_call (m, cancellable, &m->base.error);
+ else {
+ mail_msg_ref (m);
+ m->done = e_flag_new ();
+ mail_msg_main_loop_push (m);
+ e_flag_wait (m->done);
+ e_flag_free (m->done);
+ }
+
+ va_end (ap);
+
+ ret = m->ret;
+ mail_msg_unref (m);
+
+ return ret;
+}
+
+void
+mail_mt_set_backend (gchar *backend)
+{
+ shell_builtin_backend = backend;
+}
+
diff --git a/libemail-utils/mail-mt.h b/libemail-utils/mail-mt.h
new file mode 100644
index 0000000..edae135
--- /dev/null
+++ b/libemail-utils/mail-mt.h
@@ -0,0 +1,116 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _MAIL_MT
+#define _MAIL_MT
+
+#include <camel/camel.h>
+
+typedef struct _MailMsg MailMsg;
+typedef struct _MailMsgInfo MailMsgInfo;
+
+typedef gchar * (*MailMsgDescFunc) (MailMsg *msg);
+typedef void (*MailMsgExecFunc) (MailMsg *msg,
+ GCancellable *cancellable,
+ GError **error);
+typedef void (*MailMsgDoneFunc) (MailMsg *msg);
+typedef void (*MailMsgFreeFunc) (MailMsg *msg);
+typedef void (*MailMsgDispatchFunc) (gpointer msg);
+
+
+typedef void (*MailMsgCreateActivityFunc) (GCancellable *cancellable);
+typedef void (*MailMsgSubmitActivityFunc) (GCancellable *cancellable);
+typedef void (*MailMsgFreeActivityFunc) (GCancellable *cancellable);
+typedef void (*MailMsgCompleteActivityFunc) (GCancellable *cancellable);
+typedef void (*MailMsgCancelActivityFunc) (GCancellable *cancellable);
+typedef void (*MailMsgAlertErrorFunc) (const char *what, const char *message);
+
+struct _MailMsg {
+ MailMsgInfo *info;
+ volatile gint ref_count;
+ guint seq; /* seq number for synchronisation */
+ gint priority; /* priority (default = 0) */
+ GCancellable *cancellable;
+ GError *error; /* up to the caller to use this */
+};
+
+struct _MailMsgInfo {
+ gsize size;
+ MailMsgDescFunc desc;
+ MailMsgExecFunc exec;
+ MailMsgDoneFunc done;
+ MailMsgFreeFunc free;
+};
+
+/* setup ports */
+void mail_msg_init (void);
+void mail_msg_register_activities (MailMsgCreateActivityFunc,
+ MailMsgSubmitActivityFunc,
+ MailMsgFreeActivityFunc,
+ MailMsgCompleteActivityFunc,
+ MailMsgCancelActivityFunc,
+ MailMsgAlertErrorFunc);
+
+gboolean mail_in_main_thread (void);
+
+/* allocate a new message */
+gpointer mail_msg_new (MailMsgInfo *info);
+gpointer mail_msg_ref (gpointer msg);
+void mail_msg_unref (gpointer msg);
+void mail_msg_check_error (gpointer msg);
+void mail_msg_cancel (guint msgid);
+gboolean mail_msg_active (void);
+
+/* dispatch a message */
+void mail_msg_main_loop_push (gpointer msg);
+void mail_msg_unordered_push (gpointer msg);
+void mail_msg_fast_ordered_push (gpointer msg);
+void mail_msg_slow_ordered_push (gpointer msg);
+
+/* To implement the stop button */
+GHook * mail_cancel_hook_add (GHookFunc func, gpointer data);
+void mail_cancel_hook_remove (GHook *hook);
+void mail_cancel_all (void);
+
+/* request a string/password */
+gchar *mail_get_password (CamelService *service, const gchar *prompt,
+ gboolean secret, gboolean *cache);
+
+void mail_mt_set_backend (gchar *backend);
+
+/* Call a function in the GUI thread, wait for it to return, type is
+ * the marshaller to use. FIXME This thing is horrible, please put
+ * it out of its misery. */
+typedef enum {
+ MAIL_CALL_p_p,
+ MAIL_CALL_p_pp,
+ MAIL_CALL_p_ppp,
+ MAIL_CALL_p_pppp,
+ MAIL_CALL_p_ppppp,
+ MAIL_CALL_p_ppippp
+} mail_call_t;
+
+typedef gpointer (*MailMainFunc)();
+
+gpointer mail_call_main (mail_call_t type, MailMainFunc func, ...);
+
+#endif /* _MAIL_MT */
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 8deeeaa..3d4fc61 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -198,6 +198,8 @@ SMIME_LIBS = \
endif
libevolution_mail_la_LIBADD = \
+ $(top_builddir)/libemail-utils/libemail-utils.la \
+ $(top_builddir)/libemail-engine/libemail-engine.la \
$(top_builddir)/e-util/libeutil.la \
$(top_builddir)/shell/libeshell.la \
$(top_builddir)/composer/libcomposer.la \
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
index c4c39bf..6805d17 100644
--- a/modules/mail/Makefile.am
+++ b/modules/mail/Makefile.am
@@ -53,6 +53,8 @@ libevolution_module_mail_la_SOURCES = \
em-network-prefs.h
libevolution_module_mail_la_LIBADD = \
+ $(top_builddir)/libemail-utils/libemail-utils.la \
+ $(top_builddir)/libemail-engine/libemail-engine.la \
$(top_builddir)/e-util/libeutil.la \
$(top_builddir)/em-format/libemformat.la \
$(top_builddir)/filter/libfilter.la \
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 9f6e658..afae3c5 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -60,7 +60,6 @@ libeshell_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"evolution-shell\" \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
- $(EMAIL_UTILS_CFLAGS) \
$(EGG_SMCLIENT_CFLAGS) \
$(GTKHTML_CFLAGS) \
$(CLUTTER_CFLAGS)
@@ -91,13 +90,13 @@ libeshell_la_SOURCES = \
libeshell_la_LDFLAGS = $(NO_UNDEFINED)
libeshell_la_LIBADD = \
+ $(top_builddir)/libemail-utils/libemail-utils.la \
$(top_builddir)/e-util/libeutil.la \
$(top_builddir)/filter/libfilter.la \
$(top_builddir)/smclient/libeggsmclient.la \
$(top_builddir)/widgets/misc/libemiscwidgets.la \
$(top_builddir)/widgets/menus/libmenus.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
- $(EMAIL_UTILS_LIBS) \
$(GNOME_PLATFORM_LIBS) \
$(EGG_SMCLIENT_LIBS) \
$(CLUTTER_LIBS)
@@ -145,11 +144,11 @@ evolution_LDADD = \
$(top_builddir)/widgets/e-timezone-dialog/libetimezonedialog.la \
$(top_builddir)/widgets/menus/libmenus.la \
$(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(top_builddir)/libemail-utils/libemail-utils.la \
$(top_builddir)/e-util/libeutil.la \
$(top_builddir)/filter/libfilter.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
$(GNOME_PLATFORM_LIBS) \
- $(EMAIL_UTILS_LIBS) \
$(CLUTTER_LIBS) \
$(EVOLUTIONICON)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]