aboutsummaryrefslogtreecommitdiffstats
path: root/mail/em-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/em-utils.c')
-rw-r--r--mail/em-utils.c2327
1 files changed, 2327 insertions, 0 deletions
diff --git a/mail/em-utils.c b/mail/em-utils.c
new file mode 100644
index 0000000000..07de855e0f
--- /dev/null
+++ b/mail/em-utils.c
@@ -0,0 +1,2327 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-url-scanner.h>
+
+#include <filter/filter-editor.h>
+
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "mail-config.h"
+#include "mail-config-druid.h"
+#include "message-tag-followup.h"
+
+#include <e-util/e-mktemp.h>
+#include <e-util/e-dialog-utils.h>
+
+#include "em-utils.h"
+#include "em-composer-utils.h"
+#include "em-format-quote.h"
+
+static EAccount *guess_account (CamelMimeMessage *message);
+
+/**
+ * em_utils_prompt_user:
+ * @parent: parent window
+ * @def: default response
+ * @again: continue prompting the user in the future
+ * @fmt: prompt format
+ * @Varargs: varargs
+ *
+ * Convenience function to query the user with a Yes/No dialog and a
+ * "Don't show this dialog again" checkbox. If the user checks that
+ * checkbox, then @again is set to %FALSE, otherwise it is set to
+ * %TRUE.
+ *
+ * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
+ **/
+gboolean
+em_utils_prompt_user (GtkWindow *parent, int def, gboolean *again, const char *fmt, ...)
+{
+ GtkWidget *mbox, *check = NULL;
+ va_list ap;
+ int button;
+ char *str;
+
+ va_start (ap, fmt);
+ str = g_strdup_vprintf (fmt, ap);
+ va_end (ap);
+ mbox = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ "%s", str);
+ g_free (str);
+ gtk_dialog_set_default_response ((GtkDialog *) mbox, def);
+ if (again) {
+ check = gtk_check_button_new_with_label (_("Don't show this message again."));
+ gtk_box_pack_start ((GtkBox *)((GtkDialog *) mbox)->vbox, check, TRUE, TRUE, 10);
+ gtk_widget_show (check);
+ }
+
+ button = gtk_dialog_run ((GtkDialog *) mbox);
+ if (again)
+ *again = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
+ gtk_widget_destroy (mbox);
+
+ return button == GTK_RESPONSE_YES;
+}
+
+
+/**
+ * em_utils_uids_copy:
+ * @uids: array of uids
+ *
+ * Duplicates the array of uids held by @uids into a new
+ * GPtrArray. Use em_utils_uids_free() to free the resultant uid
+ * array.
+ *
+ * Returns a duplicate copy of @uids.
+ **/
+GPtrArray *
+em_utils_uids_copy (GPtrArray *uids)
+{
+ GPtrArray *copy;
+ int i;
+
+ copy = g_ptr_array_new ();
+ g_ptr_array_set_size (copy, uids->len);
+
+ for (i = 0; i < uids->len; i++)
+ copy->pdata[i] = g_strdup (uids->pdata[i]);
+
+ return copy;
+}
+
+/**
+ * em_utils_uids_free:
+ * @uids: array of uids
+ *
+ * Frees the array of uids pointed to by @uids back to the system.
+ **/
+void
+em_utils_uids_free (GPtrArray *uids)
+{
+ int i;
+
+ for (i = 0; i < uids->len; i++)
+ g_free (uids->pdata[i]);
+
+ g_ptr_array_free (uids, TRUE);
+}
+
+static void
+druid_destroy_cb (gpointer user_data, GObject *deadbeef)
+{
+ gtk_main_quit ();
+}
+
+/**
+ * em_utils_configure_account:
+ * @parent: parent window for the druid to be a child of.
+ *
+ * Displays a druid allowing the user to configure an account. If
+ * @parent is non-NULL, then the druid will be created as a child
+ * window of @parent's toplevel window.
+ *
+ * Returns %TRUE if an account has been configured or %FALSE
+ * otherwise.
+ **/
+gboolean
+em_utils_configure_account (GtkWidget *parent)
+{
+ MailConfigDruid *druid;
+
+ druid = mail_config_druid_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) druid, parent);
+
+ g_object_weak_ref ((GObject *) druid, (GWeakNotify) druid_destroy_cb, NULL);
+ gtk_widget_show ((GtkWidget *) druid);
+ gtk_grab_add ((GtkWidget *) druid);
+ gtk_main ();
+
+ return mail_config_is_configured ();
+}
+
+/**
+ * em_utils_check_user_can_send_mail:
+ * @parent: parent window for the druid to be a child of.
+ *
+ * If no accounts have been configured, the user will be given a
+ * chance to configure an account. In the case that no accounts are
+ * configured, a druid will be created. If @parent is non-NULL, then
+ * the druid will be created as a child window of @parent's toplevel
+ * window.
+ *
+ * Returns %TRUE if the user has an account configured (to send mail)
+ * or %FALSE otherwise.
+ **/
+gboolean
+em_utils_check_user_can_send_mail (GtkWidget *parent)
+{
+ EAccount *account;
+
+ if (!mail_config_is_configured ()) {
+ if (!em_utils_configure_account (parent))
+ return FALSE;
+ }
+
+ if (!(account = mail_config_get_default_account ()))
+ return FALSE;
+
+ /* Check for a transport */
+ if (!account->transport->url)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Editing Filters/vFolders... */
+
+static GtkWidget *filter_editor = NULL;
+
+static void
+filter_editor_response (GtkWidget *dialog, int button, gpointer user_data)
+{
+ extern char *evolution_dir;
+ FilterContext *fc;
+
+ if (button == GTK_RESPONSE_ACCEPT) {
+ char *user;
+
+ fc = g_object_get_data ((GObject *) dialog, "context");
+ user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ rule_context_save ((RuleContext *) fc, user);
+ g_free (user);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ filter_editor = NULL;
+}
+
+static const char *filter_source_names[] = {
+ "incoming",
+ "outgoing",
+ NULL,
+};
+
+/**
+ * em_utils_edit_filters:
+ * @parent: parent window
+ *
+ * Opens or raises the filters editor dialog so that the user may edit
+ * his/her filters. If @parent is non-NULL, then the dialog will be
+ * created as a child window of @parent's toplevel window.
+ **/
+void
+em_utils_edit_filters (GtkWidget *parent)
+{
+ extern char *evolution_dir;
+ char *user, *system;
+ FilterContext *fc;
+
+ if (filter_editor) {
+ gdk_window_raise (GTK_WIDGET (filter_editor)->window);
+ return;
+ }
+
+ fc = filter_context_new ();
+ user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
+ rule_context_load ((RuleContext *) fc, system, user);
+ g_free (user);
+
+ if (((RuleContext *) fc)->error) {
+ e_notice (parent, GTK_MESSAGE_ERROR,
+ _("Error loading filter information:\n%s"),
+ ((RuleContext *) fc)->error);
+ return;
+ }
+
+ filter_editor = (GtkWidget *) filter_editor_new (fc, filter_source_names);
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) filter_editor, parent);
+
+ gtk_window_set_title (GTK_WINDOW (filter_editor), _("Filters"));
+ g_object_set_data_full ((GObject *) filter_editor, "context", fc, (GtkDestroyNotify) g_object_unref);
+ g_signal_connect (filter_editor, "response", G_CALLBACK (filter_editor_response), NULL);
+ gtk_widget_show (GTK_WIDGET (filter_editor));
+}
+
+/* Composing messages... */
+
+static EMsgComposer *
+create_new_composer (GtkWidget *parent)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ return composer;
+}
+
+/**
+ * em_utils_compose_new_message:
+ * @parent: parent window
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window.
+ **/
+void
+em_utils_compose_new_message (GtkWidget *parent)
+{
+ GtkWidget *composer;
+
+ composer = (GtkWidget *) create_new_composer (parent);
+
+ gtk_widget_show (composer);
+}
+
+/**
+ * em_utils_compose_new_message_with_mailto:
+ * @parent: parent window
+ * @url: mailto url
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window. If @url is non-NULL, the composer fields will be filled in
+ * according to the values in the mailto url.
+ **/
+void
+em_utils_compose_new_message_with_mailto (GtkWidget *parent, const char *url)
+{
+ EMsgComposer *composer;
+
+ if (url != NULL)
+ composer = e_msg_composer_new_from_url (url);
+ else
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ gtk_widget_show ((GtkWidget *) composer);
+}
+
+/**
+ * em_utils_post_to_url:
+ * @parent: parent window
+ * @url: mailto url
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window. If @url is non-NULL, the composer will default to posting
+ * mail to the folder specified by @url.
+ **/
+void
+em_utils_post_to_url (GtkWidget *parent, const char *url)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new_post ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ if (url != NULL)
+ e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show ((GtkWidget *) composer);
+}
+
+/* Editing messages... */
+
+static void
+edit_message (GtkWidget *parent, CamelMimeMessage *message, CamelFolder *drafts, const char *uid)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new_with_message (message);
+ em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid);
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
+
+/**
+ * em_utils_edit_message:
+ * @parent: parent window
+ * @message: message to edit
+ *
+ * Opens a composer filled in with the headers/mime-parts/etc of
+ * @message.
+ **/
+void
+em_utils_edit_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ edit_message (parent, message, NULL, NULL);
+}
+
+static void
+edit_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, void *user_data)
+{
+ int i;
+
+ if (msgs == NULL)
+ return;
+
+ for (i = 0; i < msgs->len; i++) {
+ camel_medium_remove_header (CAMEL_MEDIUM (msgs->pdata[i]), "X-Mailer");
+
+ edit_message ((GtkWidget *) user_data, msgs->pdata[i], folder, uids->pdata[i]);
+ }
+}
+
+/**
+ * em_utils_edit_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to edit
+ * @uids: uids of messages to edit
+ *
+ * Opens a composer for each message to be edited.
+ **/
+void
+em_utils_edit_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, edit_messages, parent);
+}
+
+/* Forwarding messages... */
+
+static void
+forward_attached (CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, char *subject, void *user_data)
+{
+ EMsgComposer *composer;
+
+ if (part == NULL)
+ return;
+
+ composer = create_new_composer ((GtkWidget *) user_data);
+ e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject);
+ e_msg_composer_attach (composer, part);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
+
+/**
+ * em_utils_forward_attached:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * If there is more than a single message in @uids, a multipart/digest
+ * will be constructed and attached to a new composer window preset
+ * with the appropriate header defaults for forwarding the first
+ * message in the list. If only one message is to be forwarded, it is
+ * forwarded as a simple message/rfc822 attachment.
+ **/
+void
+em_utils_forward_attached (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_build_attachment (folder, uids, forward_attached, parent);
+}
+
+static void
+forward_non_attached (GtkWidget *parent, GPtrArray *messages, int style)
+{
+ CamelMimeMessage *message;
+ CamelDataWrapper *wrapper;
+ EMsgComposer *composer;
+ char *subject, *text;
+ int i;
+ guint32 flags;
+
+ if (messages->len == 0)
+ return;
+
+ flags = EM_FORMAT_QUOTE_HEADERS;
+ if (style == MAIL_CONFIG_FORWARD_QUOTED)
+ flags |= EM_FORMAT_QUOTE_CITE;
+
+ for (i = 0; i < messages->len; i++) {
+ message = messages->pdata[i];
+ subject = mail_tool_generate_forward_subject (message);
+
+ text = em_utils_message_to_html(message, _("-------- Forwarded Message --------"), flags);
+
+ if (text) {
+ composer = create_new_composer (parent);
+ e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject);
+ e_msg_composer_set_body_text (composer, text);
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+ if (CAMEL_IS_MULTIPART (wrapper))
+ e_msg_composer_add_message_attachments (composer, message, FALSE);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+
+ g_free (text);
+ }
+
+ g_free (subject);
+ }
+}
+
+static void
+forward_inline (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data)
+{
+ forward_non_attached ((GtkWidget *) user_data, messages, MAIL_CONFIG_FORWARD_INLINE);
+}
+
+/**
+ * em_utils_forward_inline:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards each message in the 'inline' form, each in its own composer window.
+ **/
+void
+em_utils_forward_inline (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, forward_inline, parent);
+}
+
+static void
+forward_quoted (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data)
+{
+ forward_non_attached ((GtkWidget *) user_data, messages, MAIL_CONFIG_FORWARD_QUOTED);
+}
+
+/**
+ * em_utils_forward_quoted:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards each message in the 'quoted' form (each line starting with
+ * a "> "), each in its own composer window.
+ **/
+void
+em_utils_forward_quoted (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, forward_quoted, parent);
+}
+
+/**
+ * em_utils_forward_message:
+ * @parent: parent window
+ * @message: message to be forwarded
+ *
+ * Forwards a message in the user's configured default style.
+ **/
+void
+em_utils_forward_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ GPtrArray *messages;
+ CamelMimePart *part;
+ GConfClient *gconf;
+ char *subject;
+ int mode;
+
+ messages = g_ptr_array_new ();
+ g_ptr_array_add (messages, message);
+
+ gconf = mail_config_get_gconf_client ();
+ mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL);
+
+ switch (mode) {
+ case MAIL_CONFIG_FORWARD_ATTACHED:
+ default:
+ part = mail_tool_make_message_attachment (message);
+
+ subject = mail_tool_generate_forward_subject (message);
+
+ forward_attached (NULL, messages, part, subject, parent);
+ camel_object_unref (part);
+ g_free (subject);
+ break;
+ case MAIL_CONFIG_FORWARD_INLINE:
+ forward_non_attached(parent, messages, MAIL_CONFIG_FORWARD_INLINE);
+ break;
+ case MAIL_CONFIG_FORWARD_QUOTED:
+ forward_non_attached(parent, messages, MAIL_CONFIG_FORWARD_QUOTED);
+ break;
+ }
+
+ g_ptr_array_free (messages, TRUE);
+}
+
+/**
+ * em_utils_forward_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards a group of messages in the user's configured default
+ * style.
+ **/
+void
+em_utils_forward_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ GConfClient *gconf;
+ int mode;
+
+ gconf = mail_config_get_gconf_client ();
+ mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL);
+
+ switch (mode) {
+ case MAIL_CONFIG_FORWARD_ATTACHED:
+ default:
+ em_utils_forward_attached (parent, folder, uids);
+ break;
+ case MAIL_CONFIG_FORWARD_INLINE:
+ em_utils_forward_inline (parent, folder, uids);
+ break;
+ case MAIL_CONFIG_FORWARD_QUOTED:
+ em_utils_forward_quoted (parent, folder, uids);
+ break;
+ }
+}
+
+/* Redirecting messages... */
+
+static EMsgComposer *
+redirect_get_composer (GtkWidget *parent, CamelMimeMessage *message)
+{
+ EMsgComposer *composer;
+ EAccount *account;
+
+ /* QMail will refuse to send a message if it finds one of
+ it's Delivered-To headers in the message, so remove all
+ Delivered-To headers. Fixes bug #23635. */
+ while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-To"))
+ camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-To");
+
+ account = guess_account (message);
+
+ composer = e_msg_composer_new_redirect (message, account ? account->name : NULL);
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ return composer;
+}
+
+/**
+ * em_utils_redirect_message:
+ * @parent: parent window
+ * @message: message to redirect
+ *
+ * Opens a composer to redirect @message (Note: only headers will be
+ * editable). Adds Resent-From/Resent-To/etc headers.
+ **/
+void
+em_utils_redirect_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ EMsgComposer *composer;
+ CamelDataWrapper *wrapper;
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ composer = redirect_get_composer (parent, message);
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+ if (CAMEL_IS_MULTIPART (wrapper))
+ e_msg_composer_add_message_attachments (composer, message, FALSE);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+}
+
+static void
+redirect_msg (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ if (message == NULL)
+ return;
+
+ em_utils_redirect_message ((GtkWidget *) user_data, message);
+}
+
+/**
+ * em_utils_redirect_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to be redirected
+ * @uid: uid of message to be redirected
+ *
+ * Opens a composer to redirect the message (Note: only headers will
+ * be editable). Adds Resent-From/Resent-To/etc headers.
+ **/
+void
+em_utils_redirect_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ mail_get_message (folder, uid, redirect_msg, parent, mail_thread_new);
+}
+
+/* Replying to messages... */
+
+static GHashTable *
+generate_account_hash (void)
+{
+ GHashTable *account_hash;
+ EAccount *account, *def;
+ EAccountList *accounts;
+ EIterator *iter;
+
+ accounts = mail_config_get_accounts ();
+ account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+
+ /* add the default account to the hash first */
+ if ((def = mail_config_get_default_account ())) {
+ if (def->id->address)
+ g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def);
+ }
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ account = (EAccount *) e_iterator_get (iter);
+
+ if (account->id->address) {
+ EAccount *acnt;
+
+ /* Accounts with identical email addresses that are enabled
+ * take precedence over the accounts that aren't. If all
+ * accounts with matching email addresses are disabled, then
+ * the first one in the list takes precedence. The default
+ * account always takes precedence no matter what.
+ */
+ acnt = g_hash_table_lookup (account_hash, account->id->address);
+ if (acnt && acnt != def && !acnt->enabled && account->enabled) {
+ g_hash_table_remove (account_hash, acnt->id->address);
+ acnt = NULL;
+ }
+
+ if (!acnt)
+ g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account);
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ return account_hash;
+}
+
+static EDestination **
+em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
+{
+ EDestination *dest, **destv;
+ int n, i, j;
+
+ if (iaddr == NULL)
+ return NULL;
+
+ if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0)
+ return NULL;
+
+ destv = g_malloc (sizeof (EDestination *) * (n + 1));
+ for (i = 0, j = 0; i < n; i++) {
+ const char *name, *addr;
+
+ if (camel_internet_address_get (iaddr, i, &name, &addr)) {
+ dest = e_destination_new ();
+ e_destination_set_name (dest, name);
+ e_destination_set_email (dest, addr);
+
+ destv[j++] = dest;
+ }
+ }
+
+ if (j == 0) {
+ g_free (destv);
+ return NULL;
+ }
+
+ destv[j] = NULL;
+
+ return destv;
+}
+
+static EMsgComposer *
+reply_get_composer (GtkWidget *parent, CamelMimeMessage *message, EAccount *account,
+ CamelInternetAddress *to, CamelInternetAddress *cc)
+{
+ const char *message_id, *references;
+ EDestination **tov, **ccv;
+ EMsgComposer *composer;
+ char *subject;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+ g_return_val_if_fail (to == NULL || CAMEL_IS_INTERNET_ADDRESS (to), NULL);
+ g_return_val_if_fail (cc == NULL || CAMEL_IS_INTERNET_ADDRESS (cc), NULL);
+
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ /* construct the tov/ccv */
+ tov = em_utils_camel_address_to_destination (to);
+ ccv = em_utils_camel_address_to_destination (cc);
+
+ /* Set the subject of the new message. */
+ if ((subject = (char *) camel_mime_message_get_subject (message))) {
+ if (strncasecmp (subject, "Re: ", 4) != 0)
+ subject = g_strdup_printf ("Re: %s", subject);
+ else
+ subject = g_strdup (subject);
+ } else {
+ subject = g_strdup ("");
+ }
+
+ e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, ccv, NULL, subject);
+
+ g_free (subject);
+
+ /* Add In-Reply-To and References. */
+ message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id");
+ references = camel_medium_get_header (CAMEL_MEDIUM (message), "References");
+ if (message_id) {
+ char *reply_refs;
+
+ e_msg_composer_add_header (composer, "In-Reply-To", message_id);
+
+ if (references)
+ reply_refs = g_strdup_printf ("%s %s", references, message_id);
+ else
+ reply_refs = g_strdup (message_id);
+
+ e_msg_composer_add_header (composer, "References", reply_refs);
+ g_free (reply_refs);
+ } else if (references) {
+ e_msg_composer_add_header (composer, "References", references);
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+
+ return composer;
+}
+
+static EAccount *
+guess_account (CamelMimeMessage *message)
+{
+ const CamelInternetAddress *to, *cc;
+ GHashTable *account_hash;
+ EAccount *account = NULL;
+ const char *addr;
+ int i;
+
+ to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
+ cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
+
+ if (to == NULL && cc == NULL)
+ return NULL;
+
+ account_hash = generate_account_hash ();
+
+ if (to) {
+ for (i = 0; camel_internet_address_get (to, i, NULL, &addr); i++) {
+ account = g_hash_table_lookup (account_hash, addr);
+ if (account)
+ goto found;
+ }
+ }
+
+ if (cc) {
+ for (i = 0; camel_internet_address_get (cc, i, NULL, &addr); i++) {
+ account = g_hash_table_lookup (account_hash, addr);
+ if (account)
+ goto found;
+ }
+ }
+
+ found:
+
+ g_hash_table_destroy (account_hash);
+
+ return account;
+}
+
+static void
+get_reply_sender (CamelMimeMessage *message, CamelInternetAddress **to)
+{
+ const CamelInternetAddress *reply_to;
+ const char *name, *addr;
+ int i;
+
+ reply_to = camel_mime_message_get_reply_to (message);
+ if (!reply_to)
+ reply_to = camel_mime_message_get_from (message);
+
+ if (reply_to) {
+ *to = camel_internet_address_new ();
+
+ for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++)
+ camel_internet_address_add (*to, name, addr);
+ }
+}
+
+static gboolean
+get_reply_list (CamelMimeMessage *message, CamelInternetAddress **to)
+{
+ const char *header, *p;
+ char *addr;
+
+ /* Examples:
+ *
+ * List-Post: <mailto:list@host.com>
+ * List-Post: <mailto:moderator@host.com?subject=list%20posting>
+ * List-Post: NO (posting not allowed on this list)
+ */
+ if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post")))
+ return FALSE;
+
+ while (*header == ' ' || *header == '\t')
+ header++;
+
+ /* check for NO */
+ if (!strncasecmp (header, "NO", 2))
+ return FALSE;
+
+ /* Search for the first mailto angle-bracket enclosed URL.
+ * (See rfc2369, Section 2, paragraph 3 for details) */
+ if (!(header = camel_strstrcase (header, "<mailto:")))
+ return FALSE;
+
+ header += 8;
+
+ p = header;
+ while (*p && !strchr ("?>", *p))
+ p++;
+
+ addr = g_strndup (header, p - header);
+
+ *to = camel_internet_address_new ();
+ camel_internet_address_add (*to, NULL, addr);
+
+ g_free (addr);
+
+ return TRUE;
+}
+
+static void
+concat_unique_addrs (CamelInternetAddress *dest, const CamelInternetAddress *src, GHashTable *rcpt_hash)
+{
+ const char *name, *addr;
+ int i;
+
+ for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) {
+ if (!g_hash_table_lookup (rcpt_hash, addr)) {
+ camel_internet_address_add (dest, name, addr);
+ g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1));
+ }
+ }
+}
+
+static void
+get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to, CamelInternetAddress **cc)
+{
+ const CamelInternetAddress *reply_to, *to_addrs, *cc_addrs;
+ const char *name, *addr;
+ GHashTable *rcpt_hash;
+ int i;
+
+ rcpt_hash = generate_account_hash ();
+
+ reply_to = camel_mime_message_get_reply_to (message);
+ if (!reply_to)
+ reply_to = camel_mime_message_get_from (message);
+
+ to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
+ cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
+
+ *to = camel_internet_address_new ();
+ *cc = camel_internet_address_new ();
+
+ if (reply_to) {
+ for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) {
+ /* ignore references to the Reply-To address in the To and Cc lists */
+ if (addr && !g_hash_table_lookup (rcpt_hash, addr)) {
+ /* In the case that we are doing a Reply-To-All, we do not want
+ to include the user's email address because replying to oneself
+ is kinda silly. */
+
+ camel_internet_address_add (*to, name, addr);
+ g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1));
+ }
+ }
+ }
+
+ concat_unique_addrs (*cc, to_addrs, rcpt_hash);
+ concat_unique_addrs (*cc, cc_addrs, rcpt_hash);
+
+ /* promote the first Cc: address to To: if To: is empty */
+ if (camel_address_length ((CamelAddress *) *to) == 0 && camel_address_length ((CamelAddress *) *cc) > 0) {
+ camel_internet_address_get (*cc, 0, &name, &addr);
+ camel_internet_address_add (*to, name, addr);
+ camel_address_remove ((CamelAddress *) *cc, 0);
+ }
+
+ g_hash_table_destroy (rcpt_hash);
+}
+
+static void
+composer_set_body (EMsgComposer *composer, CamelMimeMessage *message)
+{
+ const CamelInternetAddress *sender;
+ char *text, *credits, format[256];
+ const char *name, *addr;
+ CamelMimePart *part;
+ GConfClient *gconf;
+ time_t date;
+ int date_offset;
+
+ gconf = mail_config_get_gconf_client ();
+
+ switch (gconf_client_get_int (gconf, "/apps/evolution/mail/format/reply_style", NULL)) {
+ case MAIL_CONFIG_REPLY_DO_NOT_QUOTE:
+ /* do nothing */
+ break;
+ case MAIL_CONFIG_REPLY_ATTACH:
+ /* attach the original message as an attachment */
+ part = mail_tool_make_message_attachment (message);
+ e_msg_composer_attach (composer, part);
+ camel_object_unref (part);
+ break;
+ case MAIL_CONFIG_REPLY_QUOTED:
+ default:
+ /* do what any sane user would want when replying... */
+ sender = camel_mime_message_get_from (message);
+ if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) {
+ camel_internet_address_get (sender, 0, &name, &addr);
+ } else {
+ name = _("an unknown sender");
+ }
+
+ date = camel_mime_message_get_date(message, &date_offset);
+ /* Convert to UTC */
+ date += (date_offset / 100) * 60 * 60;
+ date += (date_offset % 100) * 60;
+
+ /* translators: attribution string used when quoting messages,
+ it must contain a single single %%+05d followed by a single '%%s' */
+ e_utf8_strftime(format, sizeof(format), _("On %a, %Y-%m-%d at %H:%M %%+05d, %%s wrote:"), gmtime(&date));
+ credits = g_strdup_printf(format, date_offset, name && *name ? name : addr);
+ text = em_utils_message_to_html(message, credits, EM_FORMAT_QUOTE_CITE);
+ g_free (credits);
+ e_msg_composer_set_body_text(composer, text);
+ g_free (text);
+ break;
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+}
+
+/**
+ * em_utils_reply_to_message:
+ * @parent: parent window
+ * @message: message to reply to
+ * @mode: reply mode
+ *
+ * Creates a new composer ready to reply to @message.
+ **/
+void
+em_utils_reply_to_message (GtkWidget *parent, CamelMimeMessage *message, int mode)
+{
+ CamelInternetAddress *to = NULL, *cc = NULL;
+ EMsgComposer *composer;
+ EAccount *account;
+
+ account = guess_account (message);
+
+ switch (mode) {
+ case REPLY_MODE_SENDER:
+ get_reply_sender (message, &to);
+ break;
+ case REPLY_MODE_LIST:
+ if (get_reply_list (message, &to))
+ break;
+ case REPLY_MODE_ALL:
+ get_reply_all (message, &to, &cc);
+ break;
+ }
+
+ composer = reply_get_composer (parent, message, account, to, cc);
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ if (cc != NULL)
+ camel_object_unref (cc);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+struct rtm_t {
+ GtkWidget *parent;
+ int mode;
+};
+
+static void
+reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ CamelInternetAddress *to = NULL, *cc = NULL;
+ struct rtm_t *rtm = user_data;
+ EMsgComposer *composer;
+ EAccount *account;
+ guint32 flags;
+
+ account = guess_account (message);
+ flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
+
+ switch (rtm->mode) {
+ case REPLY_MODE_SENDER:
+ get_reply_sender (message, &to);
+ break;
+ case REPLY_MODE_LIST:
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ if (get_reply_list (message, &to))
+ break;
+ case REPLY_MODE_ALL:
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ get_reply_all (message, &to, &cc);
+ break;
+ }
+
+ composer = reply_get_composer (rtm->parent, message, account, to, cc);
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ if (cc != NULL)
+ camel_object_unref (cc);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+/**
+ * em_utils_reply_to_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to reply to
+ * @uid: message uid
+ * @mode: reply mode
+ *
+ * Creates a new composer ready to reply to the message referenced by
+ * @folder and @uid.
+ **/
+void
+em_utils_reply_to_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid, int mode)
+{
+ struct rtm_t *rtm;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ rtm = g_malloc (sizeof (struct rtm_t));
+ rtm->parent = parent;
+ rtm->mode = mode;
+
+ mail_get_message (folder, uid, reply_to_message, rtm, mail_thread_new);
+}
+
+/* Posting replies... */
+
+static void
+post_reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ /* FIXME: would be nice if this shared more code with reply_get_composer() */
+ const char *message_id, *references;
+ CamelInternetAddress *to = NULL;
+ GtkWidget *parent = user_data;
+ EDestination **tov = NULL;
+ EMsgComposer *composer;
+ char *subject, *url;
+ EAccount *account;
+ guint32 flags;
+
+ account = guess_account (message);
+ flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
+
+ get_reply_sender (message, &to);
+
+ composer = e_msg_composer_new_post ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ /* construct the tov/ccv */
+ tov = em_utils_camel_address_to_destination (to);
+
+ /* Set the subject of the new message. */
+ if ((subject = (char *) camel_mime_message_get_subject (message))) {
+ if (strncasecmp (subject, "Re: ", 4) != 0)
+ subject = g_strdup_printf ("Re: %s", subject);
+ else
+ subject = g_strdup (subject);
+ } else {
+ subject = g_strdup ("");
+ }
+
+ e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, NULL, NULL, subject);
+
+ g_free (subject);
+
+ url = mail_tools_folder_to_url (folder);
+ e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) composer->hdrs, url);
+ g_free (url);
+
+ /* Add In-Reply-To and References. */
+ message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id");
+ references = camel_medium_get_header (CAMEL_MEDIUM (message), "References");
+ if (message_id) {
+ char *reply_refs;
+
+ e_msg_composer_add_header (composer, "In-Reply-To", message_id);
+
+ if (references)
+ reply_refs = g_strdup_printf ("%s %s", references, message_id);
+ else
+ reply_refs = g_strdup (message_id);
+
+ e_msg_composer_add_header (composer, "References", reply_refs);
+ g_free (reply_refs);
+ } else if (references) {
+ e_msg_composer_add_header (composer, "References", references);
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+/**
+ * em_utils_post_reply_to_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to reply to
+ * @uid: message uid
+ * @mode: reply mode
+ *
+ * Creates a new composer (post mode) ready to reply to the message
+ * referenced by @folder and @uid.
+ **/
+void
+em_utils_post_reply_to_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ mail_get_message (folder, uid, post_reply_to_message, parent, mail_thread_new);
+}
+
+/* Saving messages... */
+
+static GtkFileSelection *
+emu_get_save_filesel(GtkWidget *parent, const char *title, const char *name)
+{
+ GtkFileSelection *filesel;
+ char *gdir, *mname = NULL, *filename;
+ const char *realname, *dir;
+ GConfClient *gconf;
+
+ filesel = (GtkFileSelection *)gtk_file_selection_new(title);
+ if (parent)
+ e_dialog_set_transient_for((GtkWindow *)filesel, parent);
+
+ gconf = gconf_client_get_default();
+ dir = gdir = gconf_client_get_string(gconf, "/apps/evolution/mail/save_dir", NULL);
+ g_object_unref(gconf);
+ if (dir == NULL)
+ dir = g_get_home_dir();
+
+ if (name && name[0]) {
+ realname = mname = g_strdup(name);
+ e_filename_make_safe(mname);
+ } else {
+ realname = "/";
+ }
+
+ filename = g_build_filename(dir, realname, NULL);
+ gtk_file_selection_set_filename(filesel, filename);
+ g_free(filename);
+ g_free(mname);
+ g_free (gdir);
+
+ return filesel;
+}
+
+static void
+emu_update_save_path(const char *filename)
+{
+ char *dir = g_path_get_dirname(filename);
+ GConfClient *gconf = gconf_client_get_default();
+
+ gconf_client_set_string(gconf, "/apps/evolution/mail/save_dir", dir, NULL);
+ g_object_unref(gconf);
+ g_free(dir);
+}
+
+static gboolean
+emu_can_save(GtkWindow *parent, const char *path)
+{
+ struct stat st;
+
+ if (path[0] == 0)
+ return FALSE;
+
+ /* make sure we can actually save to it... */
+ if (stat (path, &st) != -1 && !S_ISREG (st.st_mode))
+ return FALSE;
+
+ if (access (path, F_OK) == 0) {
+ if (access (path, W_OK) != 0) {
+ e_notice (parent, GTK_MESSAGE_ERROR,
+ _("Cannot save to `%s'\n %s"), path, g_strerror (errno));
+ return FALSE;
+ }
+
+ return em_utils_prompt_user (parent, GTK_RESPONSE_NO, NULL,
+ _("`%s' already exists.\nOverwrite it?"), path);
+ }
+
+ return TRUE;
+}
+
+static void
+emu_save_part_response(GtkFileSelection *filesel, int response, CamelMimePart *part)
+{
+ if (response == GTK_RESPONSE_OK) {
+ const char *path = gtk_file_selection_get_filename(filesel);
+
+ if (!emu_can_save((GtkWindow *)filesel, path))
+ return;
+
+ emu_update_save_path(path);
+ /* FIXME: popup error if it fails? */
+ mail_save_part(part, path, NULL, NULL);
+ }
+
+ gtk_widget_destroy((GtkWidget *)filesel);
+ camel_object_unref(part);
+}
+
+/**
+ * em_utils_save_part:
+ * @parent: parent window
+ * @prompt: prompt string
+ * @part: part to save
+ *
+ * Saves a mime part to disk (prompting the user for filename).
+ **/
+void
+em_utils_save_part(GtkWidget *parent, const char *prompt, CamelMimePart *part)
+{
+ const char *name;
+ GtkFileSelection *filesel;
+
+ name = camel_mime_part_get_filename(part);
+ if (name == NULL) {
+ if (CAMEL_IS_MIME_MESSAGE(part)) {
+ name = camel_mime_message_get_subject((CamelMimeMessage *)part);
+ if (name == NULL)
+ name = _("message");
+ } else {
+ name = _("attachment");
+ }
+ }
+
+ filesel = emu_get_save_filesel(parent, prompt, name);
+ camel_object_ref(part);
+ g_signal_connect(filesel, "response", G_CALLBACK(emu_save_part_response), part);
+ gtk_widget_show((GtkWidget *)filesel);
+}
+
+
+struct _save_messages_data {
+ CamelFolder *folder;
+ GPtrArray *uids;
+};
+
+static void
+emu_save_messages_response(GtkFileSelection *filesel, int response, struct _save_messages_data *data)
+{
+ if (response == GTK_RESPONSE_OK) {
+ const char *path = gtk_file_selection_get_filename(filesel);
+
+ if (!emu_can_save((GtkWindow *)filesel, path))
+ return;
+
+ emu_update_save_path(path);
+ mail_save_messages(data->folder, data->uids, path, NULL, NULL);
+ data->uids = NULL;
+ }
+
+ camel_object_unref(data->folder);
+ if (data->uids)
+ em_utils_uids_free(data->uids);
+ g_free(data);
+ gtk_widget_destroy((GtkWidget *)filesel);
+}
+
+/**
+ * em_utils_save_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to save
+ * @uids: uids of messages to save
+ *
+ * Saves a group of messages to disk in mbox format (prompting the
+ * user for filename).
+ **/
+void
+em_utils_save_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ struct _save_messages_data *data;
+ GtkFileSelection *filesel;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ filesel = emu_get_save_filesel(parent, _("Save Message..."), NULL);
+ camel_object_ref(folder);
+
+ data = g_malloc(sizeof(struct _save_messages_data));
+ data->folder = folder;
+ data->uids = uids;
+
+ g_signal_connect(filesel, "response", G_CALLBACK(emu_save_messages_response), data);
+ gtk_widget_show((GtkWidget *)filesel);
+}
+
+/* Flag-for-Followup... */
+
+/* tag-editor callback data */
+struct ted_t {
+ MessageTagEditor *editor;
+ CamelFolder *folder;
+ GPtrArray *uids;
+};
+
+static void
+ted_free (struct ted_t *ted)
+{
+ camel_object_unref (ted->folder);
+ em_utils_uids_free (ted->uids);
+ g_free (ted);
+}
+
+static void
+tag_editor_response (GtkWidget *dialog, int button, struct ted_t *ted)
+{
+ CamelFolder *folder;
+ CamelTag *tags, *t;
+ GPtrArray *uids;
+ int i;
+
+ if (button == GTK_RESPONSE_OK && (tags = message_tag_editor_get_tag_list (ted->editor))) {
+ folder = ted->folder;
+ uids = ted->uids;
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ for (t = tags; t; t = t->next)
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], t->name, t->value);
+ }
+
+ camel_folder_thaw (folder);
+ camel_tag_list_free (&tags);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/**
+ * em_utils_flag_for_followup:
+ * @parent: parent window
+ * @folder: folder containing messages to flag
+ * @uids: uids of messages to flag
+ *
+ * Open the Flag-for-Followup editor for the messages specified by
+ * @folder and @uids.
+ **/
+void
+em_utils_flag_for_followup (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ GtkWidget *editor;
+ struct ted_t *ted;
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ editor = (GtkWidget *) message_tag_followup_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) editor, parent);
+
+ camel_object_ref (folder);
+
+ ted = g_new (struct ted_t, 1);
+ ted->editor = MESSAGE_TAG_EDITOR (editor);
+ ted->folder = folder;
+ ted->uids = uids;
+
+ for (i = 0; i < uids->len; i++) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_get_message_info (folder, uids->pdata[i]);
+ message_tag_followup_append_message (MESSAGE_TAG_FOLLOWUP (editor),
+ camel_message_info_from (info),
+ camel_message_info_subject (info));
+ }
+
+ /* special-case... */
+ if (uids->len == 1) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_get_message_info (folder, uids->pdata[0]);
+ if (info) {
+ if (info->user_tags)
+ message_tag_editor_set_tag_list (MESSAGE_TAG_EDITOR (editor), info->user_tags);
+ camel_folder_free_message_info (folder, info);
+ }
+ }
+
+ g_signal_connect (editor, "response", G_CALLBACK (tag_editor_response), ted);
+ g_object_weak_ref ((GObject *) editor, (GWeakNotify) ted_free, ted);
+
+ gtk_widget_show (editor);
+}
+
+/**
+ * em_utils_flag_for_followup_clear:
+ * @parent: parent window
+ * @folder: folder containing messages to unflag
+ * @uids: uids of messages to unflag
+ *
+ * Clears the Flag-for-Followup flag on the messages referenced by
+ * @folder and @uids.
+ **/
+void
+em_utils_flag_for_followup_clear (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "follow-up", "");
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "due-by", "");
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "completed-on", "");
+ }
+ camel_folder_thaw (folder);
+
+ em_utils_uids_free (uids);
+}
+
+/**
+ * em_utils_flag_for_followup_completed:
+ * @parent: parent window
+ * @folder: folder containing messages to 'complete'
+ * @uids: uids of messages to 'complete'
+ *
+ * Sets the completed state (and date/time) for each message
+ * referenced by @folder and @uids that is marked for
+ * Flag-for-Followup.
+ **/
+void
+em_utils_flag_for_followup_completed (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ char *now;
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ now = header_format_date (time (NULL), 0);
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ const char *tag;
+
+ tag = camel_folder_get_message_user_tag (folder, uids->pdata[i], "follow-up");
+ if (tag == NULL || *tag == '\0')
+ continue;
+
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "completed-on", now);
+ }
+ camel_folder_thaw (folder);
+
+ g_free (now);
+
+ em_utils_uids_free (uids);
+}
+
+#include "camel/camel-stream-mem.h"
+#include "camel/camel-stream-filter.h"
+#include "camel/camel-mime-filter-from.h"
+
+/* This kind of sucks, because for various reasons most callers need to run synchronously
+ in the gui thread, however this could take a long, blocking time, to run */
+static int
+em_utils_write_messages_to_stream(CamelFolder *folder, GPtrArray *uids, CamelStream *stream)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilterFrom *from_filter;
+ int i, res = 0;
+
+ from_filter = camel_mime_filter_from_new();
+ filtered_stream = camel_stream_filter_new_with_stream(stream);
+ camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)from_filter);
+ camel_object_unref(from_filter);
+
+ for (i=0; i<uids->len; i++) {
+ CamelMimeMessage *message;
+ char *from;
+
+ message = camel_folder_get_message(folder, uids->pdata[i], NULL);
+ if (message == NULL) {
+ res = -1;
+ break;
+ }
+
+ /* we need to flush after each stream write since we are writing to the same stream */
+ from = camel_mime_message_build_mbox_from(message);
+
+ if (camel_stream_write_string(stream, from) == -1
+ || camel_stream_flush(stream) == -1
+ || camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, (CamelStream *)filtered_stream) == -1
+ || camel_stream_flush((CamelStream *)filtered_stream) == -1)
+ res = -1;
+
+ g_free(from);
+ camel_object_unref(message);
+
+ if (res == -1)
+ break;
+ }
+
+ camel_object_unref(filtered_stream);
+
+ return res;
+}
+
+/* This kind of sucks, because for various reasons most callers need to run synchronously
+ in the gui thread, however this could take a long, blocking time, to run */
+static int
+em_utils_read_messages_from_stream(CamelFolder *folder, CamelStream *stream)
+{
+ CamelException *ex = camel_exception_new();
+ CamelMimeParser *mp = camel_mime_parser_new();
+ int res = -1;
+
+ camel_mime_parser_scan_from(mp, TRUE);
+ camel_mime_parser_init_with_stream(mp, stream);
+ camel_object_unref(stream);
+
+ while (camel_mime_parser_step(mp, 0, 0) == HSCAN_FROM) {
+ CamelMimeMessage *msg;
+
+ /* NB: de-from filter, once written */
+ msg = camel_mime_message_new();
+ if (camel_mime_part_construct_from_parser((CamelMimePart *)msg, mp) == -1) {
+ camel_object_unref(msg);
+ break;
+ }
+
+ camel_folder_append_message(folder, msg, NULL, NULL, ex);
+ camel_object_unref(msg);
+
+ if (camel_exception_is_set (ex))
+ break;
+
+ camel_mime_parser_step(mp, 0, 0);
+ }
+
+ camel_object_unref(mp);
+ if (!camel_exception_is_set(ex))
+ res = 0;
+ camel_exception_free(ex);
+
+ return res;
+}
+
+/**
+ * em_utils_selection_set_mailbox:
+ * @data: selection data
+ * @folder: folder containign messages to copy into the selection
+ * @uids: uids of the messages to copy into the selection
+ *
+ * Creates a mailbox-format selection.
+ * Warning: Could be BIG!
+ * Warning: This could block the ui for an extended period.
+ **/
+void
+em_utils_selection_set_mailbox(GtkSelectionData *data, CamelFolder *folder, GPtrArray *uids)
+{
+ CamelStream *stream;
+
+ stream = camel_stream_mem_new();
+ if (em_utils_write_messages_to_stream(folder, uids, stream) == 0)
+ gtk_selection_data_set(data, data->target, 8,
+ ((CamelStreamMem *)stream)->buffer->data,
+ ((CamelStreamMem *)stream)->buffer->len);
+
+ camel_object_unref(stream);
+}
+
+/**
+ * em_utils_selection_get_mailbox:
+ * @data: selection data
+ * @folder:
+ *
+ * Receive a mailbox selection/dnd
+ * Warning: Could be BIG!
+ * Warning: This could block the ui for an extended period.
+ * FIXME: Exceptions?
+ **/
+void
+em_utils_selection_get_mailbox(GtkSelectionData *data, CamelFolder *folder)
+{
+ CamelStream *stream;
+
+ if (data->data == NULL || data->length == -1)
+ return;
+
+ /* TODO: a stream mem with read-only access to existing data? */
+ /* NB: Although copying would let us run this async ... which it should */
+ stream = camel_stream_mem_new_with_buffer(data->data, data->length);
+ em_utils_read_messages_from_stream(folder, stream);
+ camel_object_unref(stream);
+}
+
+/**
+ * em_utils_selection_set_uidlist:
+ * @data: selection data
+ * @uri:
+ * @uids:
+ *
+ * Sets a "x-evolution-message" format selection data.
+ *
+ * FIXME: be nice if this could take a folder argument rather than uri
+ **/
+void
+em_utils_selection_set_uidlist(GtkSelectionData *data, const char *uri, GPtrArray *uids)
+{
+ GByteArray *array = g_byte_array_new();
+ int i;
+
+ /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn\0" */
+ /* NB: original form missed trailing \0 */
+
+ g_byte_array_append(array, uri, strlen(uri)+1);
+
+ for (i=0; i<uids->len; i++)
+ g_byte_array_append(array, uids->pdata[i], strlen(uids->pdata[i])+1);
+
+ gtk_selection_data_set(data, data->target, 8, array->data, array->len);
+ g_byte_array_free(array, TRUE);
+}
+
+/**
+ * em_utils_selection_get_uidlist:
+ * @data: selection data
+ * @urip: Pointer to uri string, to be free'd by caller
+ * @uidsp: Pointer to an array of uid's.
+ *
+ * Convert an x-evolution-message type to a uri and a uid list.
+ *
+ * Return value: The number of uid's found. If 0, then @urip and
+ * @uidsp will be empty.
+ **/
+int
+em_utils_selection_get_uidlist(GtkSelectionData *data, char **urip, GPtrArray **uidsp)
+{
+ /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */
+ char *inptr, *inend;
+ GPtrArray *uids;
+ int res;
+
+ *urip = NULL;
+ *uidsp = NULL;
+
+ if (data == NULL || data->data == NULL || data->length == -1)
+ return 0;
+
+ uids = g_ptr_array_new();
+
+ inptr = data->data;
+ inend = data->data + data->length;
+ while (inptr < inend) {
+ char *start = inptr;
+
+ while (inptr < inend && *inptr)
+ inptr++;
+
+ if (start > (char *)data->data)
+ g_ptr_array_add(uids, g_strndup(start, inptr-start));
+
+ inptr++;
+ }
+
+ if (uids->len == 0) {
+ g_ptr_array_free(uids, TRUE);
+ res = 0;
+ } else {
+ *urip = g_strdup(data->data);
+ *uidsp = uids;
+ res = uids->len;
+ }
+
+ return res;
+}
+
+/**
+ * em_utils_selection_set_urilist:
+ * @data:
+ * @folder:
+ * @uids:
+ *
+ * Set the selection data @data to a uri which points to a file, which is
+ * a berkely mailbox format mailbox. The file is automatically cleaned
+ * up when the application quits.
+ **/
+void
+em_utils_selection_set_urilist(GtkSelectionData *data, CamelFolder *folder, GPtrArray *uids)
+{
+ const char *tmpdir;
+ CamelStream *fstream;
+ char *uri;
+ int fd;
+
+ tmpdir = e_mkdtemp("drag-n-drop-XXXXXX");
+ if (tmpdir == NULL)
+ return;
+
+ /* FIXME: this used to save a single message with the subject
+ as the filename but it was unsafe, and makes this messier,
+ the pain */
+
+ uri = alloca(strlen(tmpdir)+16);
+ sprintf(uri, "file:///%s/mbox", tmpdir);
+
+ fd = open(uri + 7, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd == -1)
+ return;
+
+ fstream = camel_stream_fs_new_with_fd(fd);
+ if (fstream) {
+ if (em_utils_write_messages_to_stream(folder, uids, fstream) == 0)
+ gtk_selection_data_set(data, data->target, 8, uri, strlen(uri));
+
+ camel_object_unref(fstream);
+ }
+}
+
+static void
+emu_save_part_done(CamelMimePart *part, char *name, int done, void *data)
+{
+ ((int *)data)[0] = done;
+}
+
+/**
+ * em_utils_temp_save_part:
+ * @parent:
+ * @part:
+ *
+ * Save a part's content to a temporary file, and return the
+ * filename.
+ *
+ * Return value: NULL if anything failed.
+ **/
+char *
+em_utils_temp_save_part(GtkWidget *parent, CamelMimePart *part)
+{
+ const char *tmpdir, *filename;
+ char *path, *mfilename = NULL;
+ int done;
+
+ tmpdir = e_mkdtemp("evolution-tmp-XXXXXX");
+ if (tmpdir == NULL) {
+ e_notice(parent, GTK_MESSAGE_ERROR,
+ _("Could not create temporary directory: %s"),
+ g_strerror (errno));
+
+ return NULL;
+ }
+
+ filename = camel_mime_part_get_filename (part);
+ if (filename == NULL) {
+ /* This is the default filename used for temporary file creation */
+ filename = _("Unknown");
+ } else {
+ mfilename = g_strdup(filename);
+ e_filename_make_safe(mfilename);
+ filename = mfilename;
+ }
+
+ path = g_build_filename(tmpdir, filename, NULL);
+ g_free(mfilename);
+
+ /* FIXME: This doesn't handle default charsets */
+ mail_msg_wait(mail_save_part(part, path, emu_save_part_done, &done));
+
+ if (!done) {
+ /* mail_save_part should popup an error box automagically */
+ g_free(path);
+ path = NULL;
+ }
+
+ return path;
+}
+
+extern CamelFolder *drafts_folder, *sent_folder, *outbox_folder;
+
+/**
+ * em_utils_folder_is_drafts:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is a Drafts folder.
+ *
+ * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_drafts(CamelFolder *folder, const char *uri)
+{
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ int is = FALSE;
+
+ if (folder == drafts_folder)
+ return TRUE;
+
+ if (uri == NULL)
+ return FALSE;
+
+ accounts = mail_config_get_accounts();
+ iter = e_list_get_iterator((EList *)accounts);
+ while (e_iterator_is_valid(iter)) {
+ account = (EAccount *)e_iterator_get(iter);
+ if (account->drafts_folder_uri &&
+ camel_store_uri_cmp(folder->parent_store, account->drafts_folder_uri, uri)) {
+ is = TRUE;
+ break;
+ }
+
+ e_iterator_next(iter);
+ }
+
+ g_object_unref(iter);
+
+ return is;
+}
+
+/**
+ * em_utils_folder_is_sent:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is a Sent folder
+ *
+ * Returns %TRUE if this is a Sent folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_sent(CamelFolder *folder, const char *uri)
+{
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ int is = FALSE;
+
+ if (folder == sent_folder)
+ return TRUE;
+
+ if (uri == NULL)
+ return FALSE;
+
+ accounts = mail_config_get_accounts();
+ iter = e_list_get_iterator((EList *)accounts);
+ while (e_iterator_is_valid(iter)) {
+ account = (EAccount *)e_iterator_get(iter);
+ if (account->sent_folder_uri &&
+ camel_store_uri_cmp(folder->parent_store, account->sent_folder_uri, uri)) {
+ is = TRUE;
+ break;
+ }
+
+ e_iterator_next(iter);
+ }
+
+ g_object_unref(iter);
+
+ return is;
+}
+
+/**
+ * em_utils_folder_is_outbox:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is an Outbox folder
+ *
+ * Returns %TRUE if this is an Outbox folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_outbox(CamelFolder *folder, const char *uri)
+{
+ /* <Highlander>There can be only one.</Highlander> */
+ return folder == outbox_folder;
+}
+
+/**
+ * em_utils_adjustment_page:
+ * @adj:
+ * @down:
+ *
+ * Move an adjustment up/down forward/back one page.
+ **/
+void
+em_utils_adjustment_page(GtkAdjustment *adj, gboolean down)
+{
+ float page_size = adj->page_size - adj->step_increment;
+
+ if (down) {
+ if (adj->value < adj->upper - adj->page_size - page_size)
+ adj->value += page_size;
+ else if (adj->upper >= adj->page_size)
+ adj->value = adj->upper - adj->page_size;
+ else
+ adj->value = adj->lower;
+ } else {
+ if (adj->value > adj->lower + page_size)
+ adj->value -= page_size;
+ else
+ adj->value = adj->lower;
+ }
+
+ gtk_adjustment_value_changed(adj);
+}
+
+/* ********************************************************************** */
+static char *emu_proxy_uri;
+
+static void
+emu_set_proxy(GConfClient *client)
+{
+ char *server;
+ int port;
+
+ if (!gconf_client_get_bool(client, "/system/http_proxy/use_http_proxy", NULL)) {
+ g_free(emu_proxy_uri);
+ emu_proxy_uri = NULL;
+
+ return;
+ }
+
+ /* TODO: Should lock ... */
+
+ server = gconf_client_get_string(client, "/system/http_proxy/host", NULL);
+ port = gconf_client_get_int(client, "/system/http_proxy/port", NULL);
+
+ if (server && server[0]) {
+ g_free(emu_proxy_uri);
+
+ if (gconf_client_get_bool(client, "/system/http_proxy/use_authentication", NULL)) {
+ char *user = gconf_client_get_string(client, "/system/http_proxy/authentication_user", NULL);
+ char *pass = gconf_client_get_string(client, "/system/http_proxy/authentication_password", NULL);
+
+ emu_proxy_uri = g_strdup_printf("http://%s:%s@%s:%d", user, pass, server, port);
+ g_free(user);
+ g_free(pass);
+ } else {
+ emu_proxy_uri = g_strdup_printf("http://%s:%d", server, port);
+ }
+ }
+
+ g_free(server);
+}
+
+static void
+emu_proxy_changed(GConfClient *client, guint32 cnxn_id, GConfEntry *entry, gpointer user_data)
+{
+ emu_set_proxy(client);
+}
+
+/**
+ * em_utils_get_proxy_uri:
+ *
+ * Get the system proxy uri.
+ *
+ * Return value: Must be freed when finished with.
+ **/
+char *
+em_utils_get_proxy_uri(void)
+{
+ static int init;
+
+ if (!init) {
+ GConfClient *client = gconf_client_get_default();
+
+ gconf_client_add_dir(client, "/system/http_proxy", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_notify_add(client, "/system/http_proxy", emu_proxy_changed, NULL, NULL, NULL);
+ emu_set_proxy(client);
+ g_object_unref(client);
+ init = TRUE;
+ }
+
+ return g_strdup(emu_proxy_uri);
+}
+
+/**
+ * em_utils_part_to_html:
+ * @part:
+ *
+ * Converts a mime part's contents into html text. If @credits is given,
+ * then it will be used as an attribution string, and the
+ * content will be cited. Otherwise no citation or attribution
+ * will be performed.
+ *
+ * Return Value: The part in displayable html format.
+ **/
+char *
+em_utils_part_to_html(CamelMimePart *part)
+{
+ EMFormatQuote *emfq;
+ CamelStreamMem *mem;
+ GByteArray *buf;
+ char *text;
+
+ buf = g_byte_array_new ();
+ mem = (CamelStreamMem *) camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (mem, buf);
+
+ emfq = em_format_quote_new(NULL, (CamelStream *)mem, 0);
+ em_format_part((EMFormat *) emfq, (CamelStream *) mem, part);
+ g_object_unref (emfq);
+
+ camel_stream_write ((CamelStream *) mem, "", 1);
+ camel_object_unref (mem);
+
+ text = buf->data;
+ g_byte_array_free (buf, FALSE);
+
+ return text;
+}
+
+/**
+ * em_utils_message_to_html:
+ * @message:
+ * @credits:
+ * @flags: EMFormatQuote flags
+ *
+ * Convert a message to html, quoting if the @credits attribution
+ * string is given.
+ *
+ * Return value: The html version.
+ **/
+char *
+em_utils_message_to_html(CamelMimeMessage *message, const char *credits, guint32 flags)
+{
+ EMFormatQuote *emfq;
+ CamelStreamMem *mem;
+ GByteArray *buf;
+ char *text;
+
+ buf = g_byte_array_new ();
+ mem = (CamelStreamMem *) camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (mem, buf);
+
+ emfq = em_format_quote_new(credits, (CamelStream *)mem, flags);
+ em_format_format((EMFormat *)emfq, (CamelMedium *)message);
+ g_object_unref (emfq);
+
+ camel_stream_write ((CamelStream *) mem, "", 1);
+ camel_object_unref (mem);
+
+ text = buf->data;
+ g_byte_array_free (buf, FALSE);
+
+ return text;
+}
+
+/* ********************************************************************** */
+
+static gboolean
+emu_confirm_expunge (GtkWidget *parent)
+{
+ gboolean res, show_again;
+ GConfClient *gconf;
+
+ gconf = mail_config_get_gconf_client ();
+
+ if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/expunge", NULL))
+ return TRUE;
+
+ /* FIXME: we need to get the parent GtkWindow from @parent... */
+
+ res = em_utils_prompt_user (NULL, GTK_RESPONSE_NO, &show_again,
+ _("This operation will permanently erase all messages marked as\n"
+ "deleted. If you continue, you will not be able to recover these messages.\n"
+ "\nReally erase these messages?"));
+
+ gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/expunge", show_again, NULL);
+
+ return res;
+}
+
+/**
+ * em_utils_expunge_folder:
+ * @parent: parent window
+ * @folder: folder to expunge
+ *
+ * Expunges @folder.
+ **/
+void
+em_utils_expunge_folder (GtkWidget *parent, CamelFolder *folder)
+{
+ if (!emu_confirm_expunge(parent))
+ return;
+
+ mail_expunge_folder(folder, NULL, NULL);
+}
+
+/**
+ * em_utils_empty_trash:
+ * @parent: parent window
+ *
+ * Empties all Trash folders.
+ **/
+void
+em_utils_empty_trash (GtkWidget *parent)
+{
+ extern CamelSession *session;
+ CamelProvider *provider;
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ CamelException ex;
+
+ if (!emu_confirm_expunge (parent))
+ return;
+
+ camel_exception_init (&ex);
+
+ /* expunge all remote stores */
+ accounts = mail_config_get_accounts ();
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ account = (EAccount *) e_iterator_get (iter);
+
+ /* make sure this is a valid source */
+ if (account->enabled && account->source->url) {
+ provider = camel_session_get_provider (session, account->source->url, &ex);
+ if (provider) {
+ /* make sure this store is a remote store */
+ if (provider->flags & CAMEL_PROVIDER_IS_STORAGE &&
+ provider->flags & CAMEL_PROVIDER_IS_REMOTE) {
+ mail_empty_trash (account, NULL, NULL);
+ }
+ }
+
+ /* clear the exception for the next round */
+ camel_exception_clear (&ex);
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ /* Now empty the local trash folder */
+ mail_empty_trash (NULL, NULL, NULL);
+}