diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2012-01-18 00:07:19 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2012-01-19 12:48:47 +0800 |
commit | 61ae36351b24cc676f60483d576706bf827f2987 (patch) | |
tree | c55d9e000efd47fa14865fad2defa79b5ed61ffd /libemail-utils | |
parent | 37644b9d257369c5c158121ca4807cafbe844595 (diff) | |
download | gsoc2013-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.am | 44 | ||||
-rw-r--r-- | libemail-utils/e-account-utils.c | 252 | ||||
-rw-r--r-- | libemail-utils/e-account-utils.h | 37 | ||||
-rw-r--r-- | libemail-utils/e-signature-list.c | 498 | ||||
-rw-r--r-- | libemail-utils/e-signature-list.h | 91 | ||||
-rw-r--r-- | libemail-utils/e-signature-utils.c | 336 | ||||
-rw-r--r-- | libemail-utils/e-signature-utils.h | 40 | ||||
-rw-r--r-- | libemail-utils/e-signature.c | 746 | ||||
-rw-r--r-- | libemail-utils/e-signature.h | 90 | ||||
-rw-r--r-- | libemail-utils/libemail-utils.pc.in | 15 | ||||
-rw-r--r-- | libemail-utils/mail-mt.c | 639 | ||||
-rw-r--r-- | libemail-utils/mail-mt.h | 116 |
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 */ |