aboutsummaryrefslogtreecommitdiffstats
path: root/libemail-utils
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-01-18 00:07:19 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-01-19 12:48:47 +0800
commit61ae36351b24cc676f60483d576706bf827f2987 (patch)
treec55d9e000efd47fa14865fad2defa79b5ed61ffd /libemail-utils
parent37644b9d257369c5c158121ca4807cafbe844595 (diff)
downloadgsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.gz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.bz2
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.lz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.xz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.zst
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.zip
Introduce libemail-engine and libemail-utils.
These libraries are bound for E-D-S so they live at the lowest layer of Evolution for now -- even libeutil can link to them (but please don't). This is the first step toward moving mail handing to a D-Bus service.
Diffstat (limited to 'libemail-utils')
-rw-r--r--libemail-utils/Makefile.am44
-rw-r--r--libemail-utils/e-account-utils.c252
-rw-r--r--libemail-utils/e-account-utils.h37
-rw-r--r--libemail-utils/e-signature-list.c498
-rw-r--r--libemail-utils/e-signature-list.h91
-rw-r--r--libemail-utils/e-signature-utils.c336
-rw-r--r--libemail-utils/e-signature-utils.h40
-rw-r--r--libemail-utils/e-signature.c746
-rw-r--r--libemail-utils/e-signature.h90
-rw-r--r--libemail-utils/libemail-utils.pc.in15
-rw-r--r--libemail-utils/mail-mt.c639
-rw-r--r--libemail-utils/mail-mt.h116
12 files changed, 2904 insertions, 0 deletions
diff --git a/libemail-utils/Makefile.am b/libemail-utils/Makefile.am
new file mode 100644
index 0000000000..a2aea9aa93
--- /dev/null
+++ b/libemail-utils/Makefile.am
@@ -0,0 +1,44 @@
+NULL =
+
+lib_LTLIBRARIES = libemail-utils.la
+
+libemail_utils_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(NULL)
+
+libmailutilsincludedir = $(privincludedir)/libemail-utils
+libmailutilsinclude_HEADERS = \
+ e-account-utils.h \
+ e-signature-list.h \
+ e-signature-utils.h \
+ e-signature.h \
+ mail-mt.h \
+ $(NULL)
+
+libemail_utils_la_SOURCES = \
+ $(libmailutilsinclude_HEADERS) \
+ e-account-utils.c \
+ e-signature-list.c \
+ e-signature-utils.c \
+ e-signature.c \
+ mail-mt.c \
+ $(NULL)
+
+libemail_utils_la_LDFLAGS = $(NO_UNDEFINED)
+
+libemail_utils_la_LIBADD = \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(NULL)
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libemail-utils.pc
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/libemail-utils/e-account-utils.c b/libemail-utils/e-account-utils.c
new file mode 100644
index 0000000000..6e64d45747
--- /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 0000000000..d7dbd283fd
--- /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-signature-list.c b/libemail-utils/e-signature-list.c
new file mode 100644
index 0000000000..afc1d9eefd
--- /dev/null
+++ b/libemail-utils/e-signature-list.c
@@ -0,0 +1,498 @@
+/*
+ *
+ * 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);
+ }
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+
+ 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:
+ *
+ * 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 (void)
+{
+ ESignatureList *signature_list;
+ GConfClient *client;
+
+ signature_list = g_object_new (E_TYPE_SIGNATURE_LIST, NULL);
+
+ client = gconf_client_get_default ();
+ e_signature_list_construct (signature_list, client);
+ g_object_unref (client);
+
+ 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 0000000000..ebcb4b28e2
--- /dev/null
+++ b/libemail-utils/e-signature-list.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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 <gconf/gconf-client.h>
+#include <libedataserver/e-list.h>
+#include <libemail-utils/e-signature.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 (void);
+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 0000000000..ca46f053db
--- /dev/null
+++ b/libemail-utils/e-signature-utils.c
@@ -0,0 +1,336 @@
+/*
+ * 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>
+
+#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))
+ global_signature_list = e_signature_list_new ();
+
+ 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 0000000000..a642a136f1
--- /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 0000000000..1b49389a82
--- /dev/null
+++ b/libemail-utils/e-signature.c
@@ -0,0 +1,746 @@
+/*
+ *
+ * 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 <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 0000000000..fad1faffa3
--- /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/libemail-utils.pc.in b/libemail-utils/libemail-utils.pc.in
new file mode 100644
index 0000000000..384147b793
--- /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 0000000000..e932bd6e93
--- /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 (m->cancellable, what, m->error->message);
+ g_free (what);
+ } else
+ alert_error (m->cancellable, 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) {
+ g_cancellable_cancel (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_all ();
+
+ 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 0000000000..52da408f70
--- /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) (GCancellable *cancellable, 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 */