Files
evolution/mail/mail-ops.c
Matthew Barnes 75b078e997 Kill em_utils_temp_save_part().
Rewrite the last usage of it in itip-formatter.c to use EAttachments
instead.  This also allowed me to kill mail_save_part() in mail-ops.c.

I may need to reevaluate the EAttachment API at some point for all these
fringe EAttachment uses we're accumulating.  Having to asynchronously
"load" an EAttachment whose content is already in memory kinda sucks.
2010-09-30 13:55:38 -04:00

2638 lines
65 KiB
C

/*
* mail-ops.c: callbacks for the mail toolbar/menus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Authors:
* Dan Winship <danw@ximian.com>
* Jeffrey Stedfast <fejj@ximian.com>
* Peter Williams <peterw@ximian.com>
* Michael Zucchi <notzed@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include <config.h>
#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <libedataserver/e-data-server-util.h>
#include "e-util/e-account-utils.h"
#include "em-filter-rule.h"
#include "em-utils.h"
#include "mail-mt.h"
#include "mail-ops.h"
#include "mail-session.h"
#include "mail-tools.h"
#include "e-mail-local.h"
#define w(x)
#define d(x)
/* XXX Make this a preprocessor definition. */
const gchar *x_mailer = "Evolution " VERSION SUB_VERSION " " VERSION_COMMENT;
/* used for both just filtering a folder + uid's, and for filtering a whole folder */
/* used both for fetching mail, and for filtering mail */
struct _filter_mail_msg {
MailMsg base;
CamelFolder *source_folder; /* where they come from */
GPtrArray *source_uids; /* uids to copy, or NULL == copy all */
CamelUIDCache *cache; /* UID cache if we are to cache the uids, NULL otherwise */
CamelFilterDriver *driver;
gint delete; /* delete messages after filtering them? */
CamelFolder *destination; /* default destination for any messages, NULL for none */
};
/* since fetching also filters, we subclass the data here */
struct _fetch_mail_msg {
struct _filter_mail_msg fmsg;
GCancellable *cancellable; /* we have our own cancellation
* struct, the other should be empty */
gint keep; /* keep on server? */
gchar *source_uri;
void (*done)(const gchar *source, gpointer data);
gpointer data;
};
static gchar *
em_filter_folder_element_desc (struct _filter_mail_msg *m)
{
return g_strdup (_("Filtering Selected Messages"));
}
/* filter a folder, or a subset thereof, uses source_folder/source_uids */
/* this is shared with fetch_mail */
static void
em_filter_folder_element_exec (struct _filter_mail_msg *m)
{
CamelFolder *folder;
GPtrArray *uids, *folder_uids = NULL;
folder = m->source_folder;
if (folder == NULL || camel_folder_get_message_count (folder) == 0)
return;
if (m->destination) {
camel_folder_freeze (m->destination);
camel_filter_driver_set_default_folder (m->driver, m->destination);
}
camel_folder_freeze (folder);
if (m->source_uids)
uids = m->source_uids;
else
folder_uids = uids = camel_folder_get_uids (folder);
camel_filter_driver_filter_folder (
m->driver, folder, m->cache, uids, m->delete,
m->base.cancellable, &m->base.error);
camel_filter_driver_flush (m->driver, &m->base.error);
if (folder_uids)
camel_folder_free_uids (folder, folder_uids);
/* sync our source folder */
if (!m->cache)
camel_folder_synchronize_sync (
folder, FALSE, m->base.cancellable, &m->base.error);
camel_folder_thaw (folder);
if (m->destination)
camel_folder_thaw (m->destination);
/* this may thaw/unref source folders, do it here so we dont do it in the main thread
see also fetch_mail_fetch () below */
g_object_unref (m->driver);
m->driver = NULL;
}
static void
em_filter_folder_element_done (struct _filter_mail_msg *m)
{
}
static void
em_filter_folder_element_free (struct _filter_mail_msg *m)
{
if (m->source_folder)
g_object_unref (m->source_folder);
if (m->source_uids)
em_utils_uids_free (m->source_uids);
if (m->destination)
g_object_unref (m->destination);
if (m->driver)
g_object_unref (m->driver);
mail_session_flush_filter_log ();
}
static MailMsgInfo em_filter_folder_element_info = {
sizeof (struct _filter_mail_msg),
(MailMsgDescFunc) em_filter_folder_element_desc, /* we do our own progress reporting? */
(MailMsgExecFunc) em_filter_folder_element_exec,
(MailMsgDoneFunc) em_filter_folder_element_done,
(MailMsgFreeFunc) em_filter_folder_element_free
};
void
mail_filter_folder (CamelFolder *source_folder, GPtrArray *uids,
const gchar *type, gboolean notify)
{
struct _filter_mail_msg *m;
m = mail_msg_new (&em_filter_folder_element_info);
m->source_folder = source_folder;
g_object_ref (source_folder);
m->source_uids = uids;
m->cache = NULL;
m->delete = FALSE;
m->driver = camel_session_get_filter_driver (session, type, NULL);
if (!notify) {
/* FIXME: have a #define NOTIFY_FILTER_NAME macro? */
/* the filter name has to stay in sync with mail-session::get_filter_driver */
camel_filter_driver_remove_rule_by_name (m->driver, "new-mail-notification");
}
mail_msg_unordered_push (m);
}
/* convenience functions for it */
void
mail_filter_on_demand (CamelFolder *folder, GPtrArray *uids)
{
mail_filter_folder (folder, uids, E_FILTER_SOURCE_DEMAND, FALSE);
}
void
mail_filter_junk (CamelFolder *folder, GPtrArray *uids)
{
mail_filter_folder (folder, uids, E_FILTER_SOURCE_JUNKTEST, FALSE);
}
/* ********************************************************************** */
/* Temporary workaround for various issues. Gone before 0.11 */
static gchar *
uid_cachename_hack (CamelStore *store)
{
CamelURL *url = CAMEL_SERVICE (store)->url;
gchar *encoded_url, *filename;
const gchar *data_dir;
encoded_url = g_strdup_printf ("%s%s%s@%s", url->user,
url->authmech ? ";auth=" : "",
url->authmech ? url->authmech : "",
url->host);
e_filename_make_safe (encoded_url);
data_dir = mail_session_get_data_dir ();
filename = g_build_filename (data_dir, "pop", encoded_url, "uid-cache", NULL);
g_free (encoded_url);
return filename;
}
static gchar *
fetch_mail_desc (struct _fetch_mail_msg *m)
{
return g_strdup (_("Fetching Mail"));
}
static void
fetch_mail_exec (struct _fetch_mail_msg *m)
{
struct _filter_mail_msg *fm = (struct _filter_mail_msg *)m;
gint i;
fm->destination = e_mail_local_get_folder (E_MAIL_FOLDER_LOCAL_INBOX);
if (fm->destination == NULL)
goto fail;
g_object_ref (fm->destination);
/* FIXME: this should support keep_on_server too, which would then perform a spool
access thingy, right? problem is matching raw messages to uid's etc. */
if (!strncmp (m->source_uri, "mbox:", 5)) {
gchar *path = mail_tool_do_movemail (m->source_uri, &fm->base.error);
if (path && fm->base.error == NULL) {
camel_folder_freeze (fm->destination);
camel_filter_driver_set_default_folder (
fm->driver, fm->destination);
camel_filter_driver_filter_mbox (
fm->driver, path, m->source_uri,
fm->base.cancellable, &fm->base.error);
camel_folder_thaw (fm->destination);
if (fm->base.error == NULL)
g_unlink (path);
}
g_free (path);
} else {
CamelFolder *folder;
folder = fm->source_folder =
mail_tool_get_inbox (
m->source_uri,
fm->base.cancellable,
&fm->base.error);
if (folder) {
/* this handles 'keep on server' stuff, if we have any new uid's to copy
across, we need to copy them to a new array 'cause of the way fetch_mail_free works */
CamelUIDCache *cache = NULL;
CamelStore *parent_store;
gchar *cachename;
parent_store = camel_folder_get_parent_store (folder);
cachename = uid_cachename_hack (parent_store);
cache = camel_uid_cache_new (cachename);
g_free (cachename);
if (cache) {
GPtrArray *folder_uids, *cache_uids, *uids;
folder_uids = camel_folder_get_uids (folder);
cache_uids = camel_uid_cache_get_new_uids (cache, folder_uids);
if (cache_uids) {
/* need to copy this, sigh */
fm->source_uids = uids = g_ptr_array_new ();
g_ptr_array_set_size (uids, cache_uids->len);
for (i = 0; i < cache_uids->len; i++)
uids->pdata[i] = g_strdup (cache_uids->pdata[i]);
camel_uid_cache_free_uids (cache_uids);
fm->cache = cache;
em_filter_folder_element_exec (fm);
/* need to uncancel so writes/etc. don't fail */
if (g_cancellable_is_cancelled (m->cancellable))
g_cancellable_reset (m->cancellable);
/* save the cache of uids that we've just downloaded */
camel_uid_cache_save (cache);
}
if (fm->delete && fm->base.error == NULL) {
/* not keep on server - just delete all the actual messages on the server */
for (i=0;i<folder_uids->len;i++) {
d(printf("force delete uid '%s'\n", (gchar *)folder_uids->pdata[i]));
camel_folder_delete_message (folder, folder_uids->pdata[i]);
}
}
if ((fm->delete || cache_uids) && fm->base.error == NULL) {
/* expunge messages (downloaded so far) */
/* FIXME Not passing a GCancellable or GError here. */
camel_folder_synchronize_sync (
folder, fm->delete, NULL, NULL);
}
camel_uid_cache_destroy (cache);
camel_folder_free_uids (folder, folder_uids);
} else {
em_filter_folder_element_exec (fm);
}
/* we unref the source folder here since we
may now block in finalize (we try to
disconnect cleanly) */
g_object_unref (fm->source_folder);
fm->source_folder = NULL;
}
}
fail:
/* we unref this here as it may have more work to do (syncing
folders and whatnot) before we are really done */
/* should this be cancellable too? (i.e. above unregister above) */
if (fm->driver) {
g_object_unref (fm->driver);
fm->driver = NULL;
}
}
static void
fetch_mail_done (struct _fetch_mail_msg *m)
{
if (m->done)
m->done (m->source_uri, m->data);
}
static void
fetch_mail_free (struct _fetch_mail_msg *m)
{
g_free (m->source_uri);
if (m->cancellable)
g_object_unref (m->cancellable);
em_filter_folder_element_free ((struct _filter_mail_msg *) m);
}
static MailMsgInfo fetch_mail_info = {
sizeof (struct _fetch_mail_msg),
(MailMsgDescFunc) fetch_mail_desc,
(MailMsgExecFunc) fetch_mail_exec,
(MailMsgDoneFunc) fetch_mail_done,
(MailMsgFreeFunc) fetch_mail_free
};
/* ouch, a 'do everything' interface ... */
void
mail_fetch_mail (const gchar *source,
gint keep,
const gchar *type,
GCancellable *cancellable,
CamelFilterGetFolderFunc get_folder,
gpointer get_data,
CamelFilterStatusFunc *status,
gpointer status_data,
void (*done)(const gchar *source, gpointer data),
gpointer data)
{
struct _fetch_mail_msg *m;
struct _filter_mail_msg *fm;
m = mail_msg_new (&fetch_mail_info);
fm = (struct _filter_mail_msg *)m;
m->source_uri = g_strdup (source);
fm->delete = !keep;
fm->cache = NULL;
if (cancellable)
m->cancellable = g_object_ref (cancellable);
m->done = done;
m->data = data;
fm->driver = camel_session_get_filter_driver (session, type, NULL);
camel_filter_driver_set_folder_func (fm->driver, get_folder, get_data);
if (status)
camel_filter_driver_set_status_func (fm->driver, status, status_data);
mail_msg_unordered_push (m);
}
static gchar *
escape_percent_sign (const gchar *str)
{
GString *res;
if (!str)
return NULL;
res = g_string_sized_new (strlen (str));
while (*str) {
if (*str == '%') {
g_string_append (res, "%%");
} else {
g_string_append_c (res, *str);
}
str++;
}
return g_string_free (res, FALSE);
}
/* ********************************************************************** */
/* sending stuff */
/* ** SEND MAIL *********************************************************** */
static const gchar *normal_recipients[] = {
CAMEL_RECIPIENT_TYPE_TO,
CAMEL_RECIPIENT_TYPE_CC,
CAMEL_RECIPIENT_TYPE_BCC
};
static const gchar *resent_recipients[] = {
CAMEL_RECIPIENT_TYPE_RESENT_TO,
CAMEL_RECIPIENT_TYPE_RESENT_CC,
CAMEL_RECIPIENT_TYPE_RESENT_BCC
};
struct _send_queue_msg;
static void report_status (struct _send_queue_msg *m, enum camel_filter_status_t status, gint pc, const gchar *desc, ...);
/* send 1 message to a specific transport */
static void
mail_send_message (struct _send_queue_msg *m,
CamelFolder *queue,
const gchar *uid,
const gchar *destination,
CamelFilterDriver *driver,
GCancellable *cancellable,
GError **error)
{
EAccount *account = NULL;
const CamelInternetAddress *iaddr;
CamelAddress *from, *recipients;
CamelMessageInfo *info = NULL;
CamelTransport *xport = NULL;
gchar *transport_url = NULL;
gchar *sent_folder_uri = NULL;
const gchar *resent_from, *tmp;
CamelFolder *folder = NULL;
GString *err = NULL;
struct _camel_header_raw *xev, *header;
CamelMimeMessage *message;
gint i;
GError *local_error = NULL;
message = camel_folder_get_message_sync (
queue, uid, cancellable, error);
if (!message)
return;
camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer", x_mailer);
err = g_string_new("");
xev = mail_tool_remove_xevolution_headers (message);
tmp = camel_header_raw_find(&xev, "X-Evolution-Account", NULL);
if (tmp) {
gchar *name;
name = g_strstrip (g_strdup (tmp));
if ((account = e_get_account_by_uid (name))
/* 'old' x-evolution-account stored the name, how silly */
|| (account = e_get_account_by_name (name))) {
if (account->transport && account->transport->url)
transport_url = g_strdup (account->transport->url);
sent_folder_uri = g_strdup (account->sent_folder_uri);
}
g_free (name);
}
if (!account) {
/* default back to these headers */
tmp = camel_header_raw_find(&xev, "X-Evolution-Transport", NULL);
if (tmp)
transport_url = g_strstrip (g_strdup (tmp));
tmp = camel_header_raw_find(&xev, "X-Evolution-Fcc", NULL);
if (tmp)
sent_folder_uri = g_strstrip (g_strdup (tmp));
}
if (transport_url || destination) {
gchar *escaped = escape_percent_sign (transport_url ? transport_url : destination);
/* let the dialog know the right account it is using; percentage is ignored */
report_status (m, CAMEL_FILTER_STATUS_ACTION, 0, escaped);
g_free (escaped);
}
/* Check for email sending */
from = (CamelAddress *) camel_internet_address_new ();
resent_from = camel_medium_get_header (CAMEL_MEDIUM (message), "Resent-From");
if (resent_from) {
camel_address_decode (from, resent_from);
} else {
iaddr = camel_mime_message_get_from (message);
camel_address_copy (from, CAMEL_ADDRESS (iaddr));
}
recipients = (CamelAddress *) camel_internet_address_new ();
for (i = 0; i < 3; i++) {
const gchar *type;
type = resent_from ? resent_recipients[i] : normal_recipients[i];
iaddr = camel_mime_message_get_recipients (message, type);
camel_address_cat (recipients, CAMEL_ADDRESS (iaddr));
}
if (camel_address_length (recipients) > 0) {
xport = camel_session_get_transport (
session, transport_url ? transport_url :
destination, error);
if (xport == NULL)
goto exit;
if (!camel_transport_send_to_sync (
xport, message, from, recipients, cancellable, error))
goto exit;
}
/* Now check for posting, failures are ignored */
info = camel_message_info_new (NULL);
camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
for (header = xev;header;header=header->next) {
gchar *uri;
if (strcmp(header->name, "X-Evolution-PostTo") != 0)
continue;
/* TODO: don't lose errors */
uri = g_strstrip (g_strdup (header->value));
/* FIXME Not passing a GCancellable or GError here. */
folder = mail_tool_uri_to_folder (uri, 0, NULL, NULL);
if (folder) {
/* FIXME Not passing a GCancellable or GError here. */
camel_folder_append_message_sync (
folder, message, info, NULL, NULL, NULL);
g_object_unref (folder);
folder = NULL;
}
g_free (uri);
}
/* post process */
mail_tool_restore_xevolution_headers (message, xev);
if (driver) {
camel_filter_driver_filter_message (
driver, message, info, NULL, NULL,
NULL, "", cancellable, &local_error);
if (local_error != NULL) {
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto exit;
/* sending mail, filtering failed */
g_string_append_printf (
err, _("Failed to apply outgoing filters: %s"),
local_error->message);
g_clear_error (&local_error);
}
}
if (xport == NULL
|| !( ((CamelService *)xport)->provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)) {
GError *local_error = NULL;
if (sent_folder_uri) {
folder = mail_tool_uri_to_folder (
sent_folder_uri, 0, cancellable, &local_error);
if (folder == NULL) {
g_string_append_printf (
err, _("Failed to append to %s: %s\n"
"Appending to local 'Sent' folder instead."),
sent_folder_uri, local_error ? local_error->message : _("Unknown error"));
if (local_error)
g_clear_error (&local_error);
}
}
if (!folder) {
folder = e_mail_local_get_folder (E_MAIL_FOLDER_SENT);
g_object_ref (folder);
}
if (!camel_folder_append_message_sync (
folder, message, info,
NULL, cancellable, &local_error)) {
CamelFolder *sent_folder;
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto exit;
sent_folder = e_mail_local_get_folder (E_MAIL_FOLDER_SENT);
if (folder != sent_folder) {
const gchar *description;
description = camel_folder_get_description (folder);
if (err->len)
g_string_append(err, "\n\n");
g_string_append_printf (
err, _("Failed to append to %s: %s\n"
"Appending to local 'Sent' folder instead."),
description, local_error->message);
g_object_ref (sent_folder);
g_object_unref (folder);
folder = sent_folder;
g_clear_error (&local_error);
camel_folder_append_message_sync (
folder, message, info,
NULL, cancellable, &local_error);
}
if (local_error != NULL) {
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto exit;
if (err->len)
g_string_append(err, "\n\n");
g_string_append_printf (
err, _("Failed to append to local 'Sent' folder: %s"),
local_error->message);
}
}
}
if (local_error == NULL) {
camel_folder_set_message_flags (
queue, uid, CAMEL_MESSAGE_DELETED |
CAMEL_MESSAGE_SEEN, ~0);
/* Sync it to disk, since if it crashes in between,
* we keep sending it again on next start. */
/* FIXME Not passing a GCancellable or GError here. */
camel_folder_synchronize_sync (queue, FALSE, NULL, NULL);
}
if (err->len) {
/* set the culmulative exception report */
g_set_error (
&local_error, CAMEL_ERROR,
CAMEL_ERROR_GENERIC, "%s", err->str);
}
exit:
if (local_error != NULL)
g_propagate_error (error, local_error);
/* FIXME Not passing a GCancellable or GError here. */
if (folder) {
camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
g_object_unref (folder);
}
if (info)
camel_message_info_free (info);
g_object_unref (recipients);
g_object_unref (from);
if (xport)
g_object_unref (xport);
g_free (sent_folder_uri);
g_free (transport_url);
camel_header_raw_clear (&xev);
g_string_free (err, TRUE);
g_object_unref (message);
return;
}
/* ** SEND MAIL QUEUE ***************************************************** */
struct _send_queue_msg {
MailMsg base;
CamelFolder *queue;
gchar *destination;
CamelFilterDriver *driver;
GCancellable *cancellable;
/* we use camelfilterstatusfunc, even though its not the filter doing it */
CamelFilterStatusFunc *status;
gpointer status_data;
void (*done)(const gchar *destination, gpointer data);
gpointer data;
};
static void
report_status (struct _send_queue_msg *m, enum camel_filter_status_t status, gint pc, const gchar *desc, ...)
{
va_list ap;
gchar *str;
if (m->status) {
va_start (ap, desc);
str = g_strdup_vprintf (desc, ap);
va_end (ap);
m->status (m->driver, status, pc, str, m->status_data);
g_free (str);
}
}
static void
send_queue_exec (struct _send_queue_msg *m)
{
CamelFolder *sent_folder;
GPtrArray *uids, *send_uids = NULL;
gint i, j;
GError *local_error = NULL;
d(printf("sending queue\n"));
sent_folder = e_mail_local_get_folder (E_MAIL_FOLDER_SENT);
if (!(uids = camel_folder_get_uids (m->queue)))
return;
send_uids = g_ptr_array_sized_new (uids->len);
for (i = 0, j = 0; i < uids->len; i++) {
CamelMessageInfo *info;
info = camel_folder_get_message_info (m->queue, uids->pdata[i]);
if (info) {
if ((camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED) == 0)
send_uids->pdata[j++] = uids->pdata[i];
camel_folder_free_message_info (m->queue, info);
}
}
send_uids->len = j;
if (send_uids->len == 0) {
/* nothing to send */
camel_folder_free_uids (m->queue, uids);
g_ptr_array_free (send_uids, TRUE);
return;
}
camel_operation_push_message (m->cancellable, _("Sending message"));
/* NB: This code somewhat abuses the 'exception' stuff. Apart from
* fatal problems, it is also used as a mechanism to accumualte
* warning messages and present them back to the user. */
for (i = 0, j = 0; i < send_uids->len; i++) {
gint pc = (100 * i) / send_uids->len;
report_status (
m, CAMEL_FILTER_STATUS_START, pc,
_("Sending message %d of %d"), i+1,
send_uids->len);
if (!m->cancellable)
camel_operation_progress (
m->cancellable, (i+1) * 100 / send_uids->len);
mail_send_message (
m, m->queue, send_uids->pdata[i], m->destination,
m->driver, m->base.cancellable, &local_error);
if (local_error != NULL) {
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* merge exceptions into one */
if (m->base.error != NULL) {
gchar *old_message;
old_message = g_strdup (
m->base.error->message);
g_clear_error (&m->base.error);
g_set_error (
&m->base.error, CAMEL_ERROR,
CAMEL_ERROR_GENERIC,
"%s\n\n%s", old_message,
local_error->message);
g_free (old_message);
g_clear_error (&local_error);
} else {
g_propagate_error (&m->base.error, local_error);
local_error = NULL;
}
/* keep track of the number of failures */
j++;
} else {
/* transfer the USER_CANCEL error to the
* async op exception and then break */
g_propagate_error (&m->base.error, local_error);
local_error = NULL;
break;
}
}
}
j += (send_uids->len - i);
if (j > 0)
report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Failed to send %d of %d messages"), j, send_uids->len);
else if (g_error_matches (m->base.error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Canceled."));
else
report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Complete."));
if (m->driver) {
g_object_unref (m->driver);
m->driver = NULL;
}
camel_folder_free_uids (m->queue, uids);
g_ptr_array_free (send_uids, TRUE);
/* FIXME Not passing a GCancellable or GError here. */
if (j <= 0 && m->base.error == NULL)
camel_folder_synchronize_sync (m->queue, TRUE, NULL, NULL);
/* FIXME Not passing a GCancellable or GError here. */
if (sent_folder)
camel_folder_synchronize_sync (sent_folder, FALSE, NULL, NULL);
camel_operation_pop_message (m->cancellable);
}
static void
send_queue_done (struct _send_queue_msg *m)
{
if (m->done)
m->done (m->destination, m->data);
}
static gchar *
send_queue_desc (struct _send_queue_msg *m)
{
return g_strdup (_("Sending message"));
}
static void
send_queue_free (struct _send_queue_msg *m)
{
if (m->driver)
g_object_unref (m->driver);
g_object_unref (m->queue);
g_free (m->destination);
if (m->cancellable)
g_object_unref (m->cancellable);
}
static MailMsgInfo send_queue_info = {
sizeof (struct _send_queue_msg),
(MailMsgDescFunc) send_queue_desc,
(MailMsgExecFunc) send_queue_exec,
(MailMsgDoneFunc) send_queue_done,
(MailMsgFreeFunc) send_queue_free
};
/* same interface as fetch_mail, just 'cause i'm lazy today (and we need to run it from the same spot?) */
void
mail_send_queue (CamelFolder *queue,
const gchar *destination,
const gchar *type,
GCancellable *cancellable,
CamelFilterGetFolderFunc get_folder,
gpointer get_data,
CamelFilterStatusFunc *status,
gpointer status_data,
void (*done)(const gchar *destination, gpointer data),
gpointer data)
{
struct _send_queue_msg *m;
m = mail_msg_new (&send_queue_info);
m->queue = g_object_ref (queue);
m->destination = g_strdup (destination);
if (G_IS_CANCELLABLE (cancellable)) {
m->cancellable = g_object_ref (cancellable);
g_object_unref (m->base.cancellable);
mail_msg_set_cancelable (m, FALSE);
m->base.cancellable = NULL;
}
m->status = status;
m->status_data = status_data;
m->done = done;
m->data = data;
m->driver = camel_session_get_filter_driver (session, type, NULL);
camel_filter_driver_set_folder_func (m->driver, get_folder, get_data);
mail_msg_unordered_push (m);
}
/* ** APPEND MESSAGE TO FOLDER ******************************************** */
struct _append_msg {
MailMsg base;
CamelFolder *folder;
CamelMimeMessage *message;
CamelMessageInfo *info;
gchar *appended_uid;
void (*done)(CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, gint ok, const gchar *appended_uid, gpointer data);
gpointer data;
};
static gchar *
append_mail_desc (struct _append_msg *m)
{
return g_strdup_printf (_("Saving message to folder '%s'"), camel_folder_get_full_name (m->folder));
}
static void
append_mail_exec (struct _append_msg *m)
{
camel_mime_message_set_date (
m->message, CAMEL_MESSAGE_DATE_CURRENT, 0);
camel_folder_append_message_sync (
m->folder, m->message,
m->info, &m->appended_uid,
m->base.cancellable, &m->base.error);
}
static void
append_mail_done (struct _append_msg *m)
{
if (m->done)
m->done (
m->folder, m->message,
m->info, m->base.error == NULL,
m->appended_uid, m->data);
}
static void
append_mail_free (struct _append_msg *m)
{
g_object_unref (m->message);
g_object_unref (m->folder);
g_free (m->appended_uid);
}
static MailMsgInfo append_mail_info = {
sizeof (struct _append_msg),
(MailMsgDescFunc) append_mail_desc,
(MailMsgExecFunc) append_mail_exec,
(MailMsgDoneFunc) append_mail_done,
(MailMsgFreeFunc) append_mail_free
};
void
mail_append_mail (CamelFolder *folder, CamelMimeMessage *message, CamelMessageInfo *info,
void (*done)(CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, gint ok, const gchar *appended_uid, gpointer data),
gpointer data)
{
struct _append_msg *m;
g_return_if_fail (CAMEL_IS_FOLDER (folder));
g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
if (!camel_medium_get_header (CAMEL_MEDIUM (message), "X-Mailer"))
camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer", x_mailer);
m = mail_msg_new (&append_mail_info);
m->folder = folder;
g_object_ref (folder);
m->message = message;
g_object_ref (message);
m->info = info;
m->done = done;
m->data = data;
mail_msg_unordered_push (m);
}
/* ** TRANSFER MESSAGES **************************************************** */
struct _transfer_msg {
MailMsg base;
CamelFolder *source;
GPtrArray *uids;
gboolean delete;
gchar *dest_uri;
guint32 dest_flags;
void (*done)(gboolean ok, gpointer data);
gpointer data;
};
static gchar *
transfer_messages_desc (struct _transfer_msg *m)
{
return g_strdup_printf (m->delete ? _("Moving messages to '%s'") : _("Copying messages to '%s'"),
m->dest_uri);
}
static void
transfer_messages_exec (struct _transfer_msg *m)
{
CamelFolder *dest;
dest = mail_tool_uri_to_folder (
m->dest_uri, m->dest_flags,
m->base.cancellable, &m->base.error);
if (dest == NULL)
return;
if (dest == m->source) {
g_object_unref (dest);
/* no-op */
return;
}
camel_folder_freeze (m->source);
camel_folder_freeze (dest);
camel_folder_transfer_messages_to_sync (
m->source, m->uids, dest, m->delete, NULL,
m->base.cancellable, &m->base.error);
/* make sure all deleted messages are marked as seen */
if (m->delete) {
gint i;
for (i = 0; i < m->uids->len; i++)
camel_folder_set_message_flags (
m->source, m->uids->pdata[i],
CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
}
camel_folder_thaw (m->source);
camel_folder_thaw (dest);
/* FIXME Not passing a GCancellable or GError here. */
camel_folder_synchronize_sync (dest, FALSE, NULL, NULL);
g_object_unref (dest);
}
static void
transfer_messages_done (struct _transfer_msg *m)
{
if (m->done)
m->done (m->base.error == NULL, m->data);
}
static void
transfer_messages_free (struct _transfer_msg *m)
{
g_object_unref (m->source);
g_free (m->dest_uri);
em_utils_uids_free (m->uids);
}
static MailMsgInfo transfer_messages_info = {
sizeof (struct _transfer_msg),
(MailMsgDescFunc) transfer_messages_desc,
(MailMsgExecFunc) transfer_messages_exec,
(MailMsgDoneFunc) transfer_messages_done,
(MailMsgFreeFunc) transfer_messages_free
};
void
mail_transfer_messages (CamelFolder *source, GPtrArray *uids,
gboolean delete_from_source,
const gchar *dest_uri,
guint32 dest_flags,
void (*done) (gboolean ok, gpointer data),
gpointer data)
{
struct _transfer_msg *m;
g_return_if_fail (CAMEL_IS_FOLDER (source));
g_return_if_fail (uids != NULL);
g_return_if_fail (dest_uri != NULL);
m = mail_msg_new (&transfer_messages_info);
m->source = source;
g_object_ref (source);
m->uids = uids;
m->delete = delete_from_source;
m->dest_uri = g_strdup (dest_uri);
m->dest_flags = dest_flags;
m->done = done;
m->data = data;
mail_msg_slow_ordered_push (m);
}
/* ** SCAN SUBFOLDERS ***************************************************** */
struct _get_folderinfo_msg {
MailMsg base;
CamelStore *store;
CamelFolderInfo *info;
gboolean (*done)(CamelStore *store, CamelFolderInfo *info, gpointer data);
gpointer data;
gboolean can_clear; /* whether we can clear folder info */
};
static gchar *
get_folderinfo_desc (struct _get_folderinfo_msg *m)
{
gchar *ret, *name;
name = camel_service_get_name ((CamelService *)m->store, TRUE);
ret = g_strdup_printf (_("Scanning folders in '%s'"), name);
g_free (name);
return ret;
}
static void
get_folderinfo_exec (struct _get_folderinfo_msg *m)
{
guint32 flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE|CAMEL_STORE_FOLDER_INFO_FAST | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
m->info = camel_store_get_folder_info_sync (
m->store, NULL, flags,
m->base.cancellable, &m->base.error);
}
static void
get_folderinfo_done (struct _get_folderinfo_msg *m)
{
if (!m->info && m->base.error != NULL) {
gchar *url;
url = camel_service_get_url (CAMEL_SERVICE (m->store));
w(g_warning ("Error getting folder info from store at %s: %s",
url, m->base.error->message));
g_free (url);
}
if (m->done)
m->can_clear = m->done (m->store, m->info, m->data);
else
m->can_clear = TRUE;
}
static void
get_folderinfo_free (struct _get_folderinfo_msg *m)
{
if (m->info && m->can_clear)
camel_store_free_folder_info (m->store, m->info);
g_object_unref (m->store);
}
static MailMsgInfo get_folderinfo_info = {
sizeof (struct _get_folderinfo_msg),
(MailMsgDescFunc) get_folderinfo_desc,
(MailMsgExecFunc) get_folderinfo_exec,
(MailMsgDoneFunc) get_folderinfo_done,
(MailMsgFreeFunc) get_folderinfo_free
};
gint
mail_get_folderinfo (CamelStore *store,
GCancellable *cancellable,
gboolean (*done)(CamelStore *store, CamelFolderInfo *info, gpointer data),
gpointer data)
{
struct _get_folderinfo_msg *m;
gint id;
m = mail_msg_new (&get_folderinfo_info);
if (G_IS_CANCELLABLE (cancellable)) {
g_object_unref (m->base.cancellable);
m->base.cancellable = g_object_ref (cancellable);
}
m->store = store;
g_object_ref (store);
m->done = done;
m->data = data;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** ATTACH MESSAGES ****************************************************** */
struct _build_data {
void (*done)(CamelFolder *folder, GPtrArray *uids, CamelMimePart *part, gchar *subject, gpointer data);
gpointer data;
};
static void
do_build_attachment (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, gpointer data)
{
struct _build_data *d = data;
CamelMultipart *multipart;
CamelMimePart *part;
gchar *subject;
gint i;
if (messages->len == 0) {
d->done (folder, messages, NULL, NULL, d->data);
g_free (d);
return;
}
if (messages->len == 1) {
part = mail_tool_make_message_attachment (messages->pdata[0]);
} else {
multipart = camel_multipart_new ();
camel_data_wrapper_set_mime_type(CAMEL_DATA_WRAPPER (multipart), "multipart/digest");
camel_multipart_set_boundary (multipart, NULL);
for (i=0;i<messages->len;i++) {
part = mail_tool_make_message_attachment (messages->pdata[i]);
camel_multipart_add_part (multipart, part);
g_object_unref (part);
}
part = camel_mime_part_new ();
camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (multipart));
g_object_unref (multipart);
camel_mime_part_set_description(part, _("Forwarded messages"));
}
subject = mail_tool_generate_forward_subject (messages->pdata[0]);
d->done (folder, messages, part, subject, d->data);
g_free (subject);
g_object_unref (part);
g_free (d);
}
void
mail_build_attachment (CamelFolder *folder, GPtrArray *uids,
void (*done)(CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, gchar *subject, gpointer data), gpointer data)
{
struct _build_data *d;
d = g_malloc (sizeof (*d));
d->done = done;
d->data = data;
mail_get_messages (folder, uids, do_build_attachment, d);
}
/* ** LOAD FOLDER ********************************************************* */
/* there should be some way to merge this and create folder, since both can
presumably create a folder ... */
struct _get_folder_msg {
MailMsg base;
gchar *uri;
guint32 flags;
CamelFolder *folder;
void (*done) (gchar *uri, CamelFolder *folder, gpointer data);
gpointer data;
};
static gchar *
get_folder_desc (struct _get_folder_msg *m)
{
return g_strdup_printf (_("Opening folder '%s'"), m->uri);
}
static void
get_folder_exec (struct _get_folder_msg *m)
{
m->folder = mail_tool_uri_to_folder (
m->uri, m->flags, m->base.cancellable, &m->base.error);
}
static void
get_folder_done (struct _get_folder_msg *m)
{
if (m->done)
m->done (m->uri, m->folder, m->data);
}
static void
get_folder_free (struct _get_folder_msg *m)
{
g_free (m->uri);
if (m->folder)
g_object_unref (m->folder);
}
static MailMsgInfo get_folder_info = {
sizeof (struct _get_folder_msg),
(MailMsgDescFunc) get_folder_desc,
(MailMsgExecFunc) get_folder_exec,
(MailMsgDoneFunc) get_folder_done,
(MailMsgFreeFunc) get_folder_free
};
gint
mail_get_folder (const gchar *uri, guint32 flags,
void (*done)(gchar *uri, CamelFolder *folder, gpointer data),
gpointer data, MailMsgDispatchFunc dispatch)
{
struct _get_folder_msg *m;
gint id;
m = mail_msg_new (&get_folder_info);
m->uri = g_strdup (uri);
m->flags = flags;
m->data = data;
m->done = done;
id = m->base.seq;
dispatch (m);
return id;
}
/* ** GET FOLDER'S QUOTA ********************************************************* */
struct _get_quota_msg {
MailMsg base;
CamelFolder *folder;
CamelFolderQuotaInfo *quota;
void (*done) (CamelFolder *folder, const gchar *folder_uri, CamelFolderQuotaInfo *quota, gpointer data);
gchar *folder_uri;
gpointer data;
};
static gchar *
get_quota_desc (struct _get_quota_msg *m)
{
return g_strdup_printf (_("Retrieving quota information for folder '%s'"), camel_folder_get_name (m->folder));
}
static void
get_quota_exec (struct _get_quota_msg *m)
{
m->quota = camel_folder_get_quota_info (m->folder);
}
static void
get_quota_done (struct _get_quota_msg *m)
{
if (m->done)
m->done (m->folder, m->folder_uri, m->quota, m->data);
}
static void
get_quota_free (struct _get_quota_msg *m)
{
if (m->folder)
g_object_unref (m->folder);
if (m->quota)
camel_folder_quota_info_free (m->quota);
g_free (m->folder_uri);
}
static MailMsgInfo get_quota_info = {
sizeof (struct _get_quota_msg),
(MailMsgDescFunc) get_quota_desc,
(MailMsgExecFunc) get_quota_exec,
(MailMsgDoneFunc) get_quota_done,
(MailMsgFreeFunc) get_quota_free
};
gint
mail_get_folder_quota (CamelFolder *folder,
const gchar *folder_uri,
void (*done)(CamelFolder *folder, const gchar *uri, CamelFolderQuotaInfo *quota, gpointer data),
gpointer data, MailMsgDispatchFunc dispatch)
{
struct _get_quota_msg *m;
gint id;
g_return_val_if_fail (folder != NULL, -1);
m = mail_msg_new (&get_quota_info);
m->folder = folder;
m->folder_uri = g_strdup (folder_uri);
m->data = data;
m->done = done;
g_object_ref (m->folder);
id = m->base.seq;
dispatch (m);
return id;
}
/* ** GET STORE ******************************************************* */
struct _get_store_msg {
MailMsg base;
gchar *uri;
CamelStore *store;
void (*done) (gchar *uri, CamelStore *store, gpointer data);
gpointer data;
};
static gchar *
get_store_desc (struct _get_store_msg *m)
{
return g_strdup_printf (_("Opening store '%s'"), m->uri);
}
static void
get_store_exec (struct _get_store_msg *m)
{
/*camel_session_get_store connects us, which we don't want to do on startup. */
m->store = (CamelStore *) camel_session_get_service (session, m->uri,
CAMEL_PROVIDER_STORE,
&m->base.error);
}
static void
get_store_done (struct _get_store_msg *m)
{
if (m->done)
m->done (m->uri, m->store, m->data);
}
static void
get_store_free (struct _get_store_msg *m)
{
g_free (m->uri);
if (m->store)
g_object_unref (m->store);
}
static MailMsgInfo get_store_info = {
sizeof (struct _get_store_msg),
(MailMsgDescFunc) get_store_desc,
(MailMsgExecFunc) get_store_exec,
(MailMsgDoneFunc) get_store_done,
(MailMsgFreeFunc) get_store_free
};
gint
mail_get_store (const gchar *uri,
GCancellable *cancellable,
void (*done) (gchar *uri, CamelStore *store, gpointer data),
gpointer data)
{
struct _get_store_msg *m;
gint id;
m = mail_msg_new (&get_store_info);
if (G_IS_CANCELLABLE (cancellable)) {
g_object_unref (m->base.cancellable);
m->base.cancellable = g_object_ref (cancellable);
}
m->uri = g_strdup (uri);
m->data = data;
m->done = done;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** REMOVE FOLDER ******************************************************* */
struct _remove_folder_msg {
MailMsg base;
CamelFolder *folder;
gboolean removed;
void (*done) (CamelFolder *folder, gboolean removed, GError **error, gpointer data);
gpointer data;
};
static gchar *
remove_folder_desc (struct _remove_folder_msg *m)
{
return g_strdup_printf (_("Removing folder '%s'"), camel_folder_get_full_name (m->folder));
}
static gboolean
remove_folder_rec (CamelStore *store,
CamelFolderInfo *fi,
GCancellable *cancellable,
GError **error)
{
while (fi) {
CamelFolder *folder;
if (fi->child) {
if (!remove_folder_rec (
store, fi->child, cancellable, error))
return FALSE;
}
d(printf ("deleting folder '%s'\n", fi->full_name));
folder = camel_store_get_folder_sync (
store, fi->full_name, 0, cancellable, error);
if (folder == NULL)
return FALSE;
if (!CAMEL_IS_VEE_FOLDER (folder)) {
GPtrArray *uids = camel_folder_get_uids (folder);
gint i;
/* Delete every message in this folder, then expunge it */
camel_folder_freeze (folder);
for (i = 0; i < uids->len; i++)
camel_folder_delete_message (
folder, uids->pdata[i]);
camel_folder_free_uids (folder, uids);
/* FIXME Not passing a GCancellable or GError here. */
camel_folder_synchronize_sync (folder, TRUE, NULL, NULL);
camel_folder_thaw (folder);
}
/* If the store supports subscriptions, unsubscribe
* from this folder.
* FIXME Not passing a GCancellable or GError here. */
if (camel_store_supports_subscriptions (store))
camel_store_unsubscribe_folder_sync (
store, fi->full_name, NULL, NULL);
/* Then delete the folder from the store */
if (!camel_store_delete_folder_sync (
store, fi->full_name, cancellable, error))
return FALSE;
fi = fi->next;
}
return TRUE;
}
static void
remove_folder_exec (struct _remove_folder_msg *m)
{
CamelFolderInfo *fi;
CamelStore *parent_store;
const gchar *full_name;
m->removed = FALSE;
full_name = camel_folder_get_full_name (m->folder);
parent_store = camel_folder_get_parent_store (m->folder);
fi = camel_store_get_folder_info_sync (
parent_store, full_name,
CAMEL_STORE_FOLDER_INFO_RECURSIVE |
CAMEL_STORE_FOLDER_INFO_FAST |
CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
m->base.cancellable,
&m->base.error);
if (fi == NULL)
return;
m->removed = remove_folder_rec (
parent_store, fi, m->base.cancellable, &m->base.error);
camel_store_free_folder_info (parent_store, fi);
}
static void
remove_folder_done (struct _remove_folder_msg *m)
{
if (m->done)
m->done (m->folder, m->removed, &m->base.error, m->data);
}
static void
remove_folder_free (struct _remove_folder_msg *m)
{
g_object_unref (m->folder);
}
static MailMsgInfo remove_folder_info = {
sizeof (struct _remove_folder_msg),
(MailMsgDescFunc) remove_folder_desc,
(MailMsgExecFunc) remove_folder_exec,
(MailMsgDoneFunc) remove_folder_done,
(MailMsgFreeFunc) remove_folder_free
};
void
mail_remove_folder (CamelFolder *folder, void (*done) (CamelFolder *folder, gboolean removed, GError **error, gpointer data), gpointer data)
{
struct _remove_folder_msg *m;
g_return_if_fail (folder != NULL);
m = mail_msg_new (&remove_folder_info);
m->folder = folder;
g_object_ref (folder);
m->data = data;
m->done = done;
mail_msg_unordered_push (m);
}
/* ** SYNC FOLDER ********************************************************* */
struct _sync_folder_msg {
MailMsg base;
CamelFolder *folder;
void (*done) (CamelFolder *folder, gpointer data);
gpointer data;
};
static gchar *
sync_folder_desc (struct _sync_folder_msg *m)
{
return g_strdup_printf (_("Storing folder '%s'"),
camel_folder_get_full_name (m->folder));
}
static void
sync_folder_exec (struct _sync_folder_msg *m)
{
camel_folder_synchronize_sync (
m->folder, FALSE, m->base.cancellable, &m->base.error);
}
static void
sync_folder_done (struct _sync_folder_msg *m)
{
if (m->done)
m->done (m->folder, m->data);
}
static void
sync_folder_free (struct _sync_folder_msg *m)
{
g_object_unref ((CamelObject *)m->folder);
}
static MailMsgInfo sync_folder_info = {
sizeof (struct _sync_folder_msg),
(MailMsgDescFunc) sync_folder_desc,
(MailMsgExecFunc) sync_folder_exec,
(MailMsgDoneFunc) sync_folder_done,
(MailMsgFreeFunc) sync_folder_free
};
void
mail_sync_folder (CamelFolder *folder, void (*done) (CamelFolder *folder, gpointer data), gpointer data)
{
struct _sync_folder_msg *m;
m = mail_msg_new (&sync_folder_info);
m->folder = folder;
g_object_ref (folder);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ** SYNC STORE ********************************************************* */
struct _sync_store_msg {
MailMsg base;
CamelStore *store;
gint expunge;
void (*done) (CamelStore *store, gpointer data);
gpointer data;
};
static gchar *
sync_store_desc (struct _sync_store_msg *m)
{
gchar *uri, *res;
uri = camel_url_to_string (((CamelService *)m->store)->url, CAMEL_URL_HIDE_ALL);
res = g_strdup_printf (m->expunge
?_("Expunging and storing account '%s'")
:_("Storing account '%s'"),
uri);
g_free (uri);
return res;
}
static void
sync_store_exec (struct _sync_store_msg *m)
{
camel_store_synchronize_sync (
m->store, m->expunge,
m->base.cancellable, &m->base.error);
}
static void
sync_store_done (struct _sync_store_msg *m)
{
if (m->done)
m->done (m->store, m->data);
}
static void
sync_store_free (struct _sync_store_msg *m)
{
g_object_unref (m->store);
}
static MailMsgInfo sync_store_info = {
sizeof (struct _sync_store_msg),
(MailMsgDescFunc) sync_store_desc,
(MailMsgExecFunc) sync_store_exec,
(MailMsgDoneFunc) sync_store_done,
(MailMsgFreeFunc) sync_store_free
};
void
mail_sync_store (CamelStore *store, gint expunge, void (*done) (CamelStore *store, gpointer data), gpointer data)
{
struct _sync_store_msg *m;
m = mail_msg_new (&sync_store_info);
m->store = store;
m->expunge = expunge;
g_object_ref (store);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ******************************************************************************** */
static gchar *
refresh_folder_desc (struct _sync_folder_msg *m)
{
return g_strdup_printf (_("Refreshing folder '%s'"), camel_folder_get_full_name (m->folder));
}
static void
refresh_folder_exec (struct _sync_folder_msg *m)
{
camel_folder_refresh_info_sync (
m->folder, m->base.cancellable, &m->base.error);
}
/* we just use the sync stuff where we can, since it would be the same */
static MailMsgInfo refresh_folder_info = {
sizeof (struct _sync_folder_msg),
(MailMsgDescFunc) refresh_folder_desc,
(MailMsgExecFunc) refresh_folder_exec,
(MailMsgDoneFunc) sync_folder_done,
(MailMsgFreeFunc) sync_folder_free
};
void
mail_refresh_folder (CamelFolder *folder, void (*done) (CamelFolder *folder, gpointer data), gpointer data)
{
struct _sync_folder_msg *m;
m = mail_msg_new (&refresh_folder_info);
m->folder = folder;
g_object_ref (folder);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ******************************************************************************** */
static gchar *
expunge_folder_desc (struct _sync_folder_msg *m)
{
return g_strdup_printf (_("Expunging folder '%s'"), camel_folder_get_full_name (m->folder));
}
static void
expunge_folder_exec (struct _sync_folder_msg *m)
{
camel_folder_expunge_sync (
m->folder, m->base.cancellable, &m->base.error);
}
/* we just use the sync stuff where we can, since it would be the same */
static MailMsgInfo expunge_folder_info = {
sizeof (struct _sync_folder_msg),
(MailMsgDescFunc) expunge_folder_desc,
(MailMsgExecFunc) expunge_folder_exec,
(MailMsgDoneFunc) sync_folder_done,
(MailMsgFreeFunc) sync_folder_free
};
void
mail_expunge_folder (CamelFolder *folder, void (*done) (CamelFolder *folder, gpointer data), gpointer data)
{
struct _sync_folder_msg *m;
m = mail_msg_new (&expunge_folder_info);
m->folder = folder;
g_object_ref (folder);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ******************************************************************************** */
struct _empty_trash_msg {
MailMsg base;
EAccount *account;
void (*done) (EAccount *account, gpointer data);
gpointer data;
};
static gchar *
empty_trash_desc (struct _empty_trash_msg *m)
{
return g_strdup_printf (_("Emptying trash in '%s'"),
m->account ? m->account->name : _("Local Folders"));
}
static void
empty_trash_exec (struct _empty_trash_msg *m)
{
const gchar *data_dir;
CamelFolder *trash;
gchar *uri;
if (m->account) {
trash = mail_tool_get_trash (
m->account->source->url, FALSE,
m->base.cancellable, &m->base.error);
} else {
data_dir = mail_session_get_data_dir ();
uri = g_strdup_printf ("mbox:%s/local", data_dir);
trash = mail_tool_get_trash (
uri, TRUE, m->base.cancellable, &m->base.error);
g_free (uri);
}
if (trash) {
camel_folder_expunge_sync (
trash, m->base.cancellable, &m->base.error);
g_object_unref (trash);
}
}
static void
empty_trash_done (struct _empty_trash_msg *m)
{
if (m->done)
m->done (m->account, m->data);
}
static void
empty_trash_free (struct _empty_trash_msg *m)
{
if (m->account)
g_object_unref (m->account);
}
static MailMsgInfo empty_trash_info = {
sizeof (struct _empty_trash_msg),
(MailMsgDescFunc) empty_trash_desc,
(MailMsgExecFunc) empty_trash_exec,
(MailMsgDoneFunc) empty_trash_done,
(MailMsgFreeFunc) empty_trash_free
};
void
mail_empty_trash (EAccount *account, void (*done) (EAccount *account, gpointer data), gpointer data)
{
struct _empty_trash_msg *m;
m = mail_msg_new (&empty_trash_info);
m->account = account;
if (account)
g_object_ref (account);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ** GET MESSAGE(s) ***************************************************** */
struct _get_message_msg {
MailMsg base;
CamelFolder *folder;
gchar *uid;
void (*done) (CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, gpointer data);
gpointer data;
CamelMimeMessage *message;
GCancellable *cancellable;
};
static gchar *
get_message_desc (struct _get_message_msg *m)
{
return g_strdup_printf (_("Retrieving message '%s'"), m->uid);
}
static void
get_message_exec (struct _get_message_msg *m)
{
if (g_cancellable_is_cancelled (m->base.cancellable))
m->message = NULL;
else
m->message = camel_folder_get_message_sync (
m->folder, m->uid,
m->base.cancellable, &m->base.error);
}
static void
get_message_done (struct _get_message_msg *m)
{
if (m->done)
m->done (m->folder, m->uid, m->message, m->data);
}
static void
get_message_free (struct _get_message_msg *m)
{
g_free (m->uid);
g_object_unref (m->folder);
g_object_unref (m->cancellable);
if (m->message)
g_object_unref (m->message);
}
static MailMsgInfo get_message_info = {
sizeof (struct _get_message_msg),
(MailMsgDescFunc) get_message_desc,
(MailMsgExecFunc) get_message_exec,
(MailMsgDoneFunc) get_message_done,
(MailMsgFreeFunc) get_message_free
};
gint
mail_get_message (CamelFolder *folder, const gchar *uid, void (*done) (CamelFolder *folder, const gchar *uid,
CamelMimeMessage *msg, gpointer data),
gpointer data, MailMsgDispatchFunc dispatch)
{
struct _get_message_msg *m;
gint id;
m = mail_msg_new (&get_message_info);
m->folder = folder;
g_object_ref (folder);
m->uid = g_strdup (uid);
m->data = data;
m->done = (void (*) (CamelFolder *, const gchar *, CamelMimeMessage *, gpointer )) done;
m->cancellable = camel_operation_new ();
id = m->base.seq;
dispatch (m);
return id;
}
typedef void (*get_done)(CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, gpointer data, GError **error);
static void
get_messagex_done (struct _get_message_msg *m)
{
get_done done;
if (!m->done)
return;
if (camel_operation_cancel_check (CAMEL_OPERATION (m->cancellable)))
return;
done = (get_done)m->done;
done (m->folder, m->uid, m->message, m->data, &m->base.error);
}
static MailMsgInfo get_messagex_info = {
sizeof (struct _get_message_msg),
(MailMsgDescFunc) get_message_desc,
(MailMsgExecFunc) get_message_exec,
(MailMsgDoneFunc) get_messagex_done,
(MailMsgFreeFunc) get_message_free
};
/* This is temporary, to avoid having to rewrite everything that uses
mail_get_message; it adds an exception argument to the callback */
gint
mail_get_messagex (CamelFolder *folder,
const gchar *uid,
void (*done) (CamelFolder *folder,
const gchar *uid,
CamelMimeMessage *msg,
gpointer data,
GError **error),
gpointer data,
MailMsgDispatchFunc dispatch)
{
struct _get_message_msg *m;
gint id;
m = mail_msg_new (&get_messagex_info);
m->folder = folder;
g_object_ref (folder);
m->uid = g_strdup (uid);
m->data = data;
m->done = (void (*) (CamelFolder *, const gchar *, CamelMimeMessage *, gpointer )) done;
m->cancellable = camel_operation_new ();
id = m->base.seq;
dispatch (m);
return id;
}
/* ********************************************************************** */
struct _get_messages_msg {
MailMsg base;
CamelFolder *folder;
GPtrArray *uids;
GPtrArray *messages;
void (*done) (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, gpointer data);
gpointer data;
};
static gchar *
get_messages_desc (struct _get_messages_msg *m)
{
return g_strdup_printf(ngettext("Retrieving %d message",
"Retrieving %d messages", m->uids->len),
m->uids->len);
}
static void
get_messages_exec (struct _get_messages_msg *m)
{
gint i;
CamelMimeMessage *message;
for (i=0; i<m->uids->len; i++) {
gint pc = ((i+1) * 100) / m->uids->len;
message = camel_folder_get_message_sync (
m->folder, m->uids->pdata[i],
m->base.cancellable, &m->base.error);
camel_operation_progress (
m->base.cancellable, pc);
if (message == NULL)
break;
g_ptr_array_add (m->messages, message);
}
}
static void
get_messages_done (struct _get_messages_msg *m)
{
if (m->done)
m->done (m->folder, m->uids, m->messages, m->data);
}
static void
get_messages_free (struct _get_messages_msg *m)
{
gint i;
em_utils_uids_free (m->uids);
for (i=0;i<m->messages->len;i++) {
if (m->messages->pdata[i])
g_object_unref (m->messages->pdata[i]);
}
g_ptr_array_free (m->messages, TRUE);
g_object_unref (m->folder);
}
static MailMsgInfo get_messages_info = {
sizeof (struct _get_messages_msg),
(MailMsgDescFunc) get_messages_desc,
(MailMsgExecFunc) get_messages_exec,
(MailMsgDoneFunc) get_messages_done,
(MailMsgFreeFunc) get_messages_free
};
gint
mail_get_messages (CamelFolder *folder, GPtrArray *uids,
void (*done) (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, gpointer data),
gpointer data)
{
struct _get_messages_msg *m;
gint id;
m = mail_msg_new (&get_messages_info);
m->folder = folder;
g_object_ref (folder);
m->uids = uids;
m->messages = g_ptr_array_new ();
m->data = data;
m->done = done;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** SAVE MESSAGES ******************************************************* */
struct _save_messages_msg {
MailMsg base;
CamelFolder *folder;
GPtrArray *uids;
gchar *path;
void (*done)(CamelFolder *folder, GPtrArray *uids, gchar *path, gpointer data);
gpointer data;
};
static gchar *
save_messages_desc (struct _save_messages_msg *m)
{
return g_strdup_printf(ngettext("Saving %d message",
"Saving %d messages", m->uids->len),
m->uids->len);
}
static void
save_prepare_part (CamelMimePart *mime_part)
{
CamelDataWrapper *wrapper;
gint parts, i;
wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
if (!wrapper)
return;
if (CAMEL_IS_MULTIPART (wrapper)) {
parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper));
for (i = 0; i < parts; i++) {
CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i);
save_prepare_part (part);
}
} else {
if (CAMEL_IS_MIME_MESSAGE (wrapper)) {
/* prepare the message parts' subparts */
save_prepare_part (CAMEL_MIME_PART (wrapper));
} else {
CamelContentType *type;
/* We want to save textual parts as 8bit instead of encoded */
type = camel_data_wrapper_get_mime_type_field (wrapper);
if (camel_content_type_is (type, "text", "*"))
camel_mime_part_set_encoding (mime_part, CAMEL_TRANSFER_ENCODING_8BIT);
}
}
}
static void
save_messages_exec (struct _save_messages_msg *m)
{
CamelStream *filtered_stream;
CamelMimeFilter *from_filter;
CamelStream *stream;
gint i;
gchar *from, *path;
if (strstr (m->path, "://"))
path = m->path;
else
path = g_filename_to_uri (m->path, NULL, NULL);
stream = camel_stream_vfs_new_with_uri (path, CAMEL_STREAM_VFS_CREATE);
from_filter = camel_mime_filter_from_new ();
filtered_stream = camel_stream_filter_new (stream);
camel_stream_filter_add (
CAMEL_STREAM_FILTER (filtered_stream), from_filter);
g_object_unref (from_filter);
if (path != m->path)
g_free (path);
for (i=0; i<m->uids->len; i++) {
CamelMimeMessage *message;
gint pc = ((i+1) * 100) / m->uids->len;
message = camel_folder_get_message_sync (
m->folder, m->uids->pdata[i],
m->base.cancellable, &m->base.error);
camel_operation_progress (
m->base.cancellable, pc);
if (message == NULL)
break;
save_prepare_part (CAMEL_MIME_PART (message));
/* we need to flush after each stream write since we are writing to the same fd */
from = camel_mime_message_build_mbox_from (message);
if (camel_stream_write_string (
stream, from,
m->base.cancellable, &m->base.error) == -1
|| camel_stream_flush (
stream, m->base.cancellable, &m->base.error) == -1
|| camel_data_wrapper_write_to_stream_sync (
(CamelDataWrapper *) message,
(CamelStream *)filtered_stream,
m->base.cancellable, &m->base.error) == -1
|| camel_stream_flush (
(CamelStream *)filtered_stream,
m->base.cancellable, &m->base.error) == -1
|| camel_stream_write_string (
stream, "\n",
m->base.cancellable, &m->base.error) == -1
|| camel_stream_flush (stream,
m->base.cancellable, &m->base.error) == -1) {
g_prefix_error (
&m->base.error,
_("Error saving messages to: %s:\n"),
m->path);
g_free (from);
g_object_unref ((CamelObject *)message);
break;
}
g_free (from);
g_object_unref (message);
}
g_object_unref (filtered_stream);
g_object_unref (stream);
}
static void
save_messages_done (struct _save_messages_msg *m)
{
if (m->done)
m->done (m->folder, m->uids, m->path, m->data);
}
static void
save_messages_free (struct _save_messages_msg *m)
{
em_utils_uids_free (m->uids);
g_object_unref (m->folder);
g_free (m->path);
}
static MailMsgInfo save_messages_info = {
sizeof (struct _save_messages_msg),
(MailMsgDescFunc) save_messages_desc,
(MailMsgExecFunc) save_messages_exec,
(MailMsgDoneFunc) save_messages_done,
(MailMsgFreeFunc) save_messages_free
};
gint
mail_save_messages (CamelFolder *folder, GPtrArray *uids, const gchar *path,
void (*done) (CamelFolder *folder, GPtrArray *uids, gchar *path, gpointer data), gpointer data)
{
struct _save_messages_msg *m;
gint id;
m = mail_msg_new (&save_messages_info);
m->folder = folder;
g_object_ref (folder);
m->uids = uids;
m->path = g_strdup (path);
m->data = data;
m->done = done;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** PREPARE OFFLINE ***************************************************** */
struct _prep_offline_msg {
MailMsg base;
GCancellable *cancel;
gchar *uri;
void (*done)(const gchar *uri, gpointer data);
gpointer data;
};
static void
prep_offline_exec (struct _prep_offline_msg *m)
{
CamelFolder *folder;
folder = mail_tool_uri_to_folder (
m->uri, 0, m->base.cancellable, &m->base.error);
if (folder) {
if (CAMEL_IS_DISCO_FOLDER (folder)) {
camel_disco_folder_prepare_for_offline (
CAMEL_DISCO_FOLDER (folder),
"(match-all)", m->cancel, &m->base.error);
} else if (CAMEL_IS_OFFLINE_FOLDER (folder)) {
camel_offline_folder_downsync_sync (
CAMEL_OFFLINE_FOLDER (folder),
"(match-all)", m->cancel, &m->base.error);
}
/* prepare_for_offline should do this? */
/* of course it should all be atomic, but ... */
/* FIXME Not passing a GCancellable here. */
camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
g_object_unref (folder);
}
}
static void
prep_offline_done (struct _prep_offline_msg *m)
{
if (m->done)
m->done (m->uri, m->data);
}
static void
prep_offline_free (struct _prep_offline_msg *m)
{
if (m->cancel)
g_object_unref (m->cancel);
g_free (m->uri);
}
static MailMsgInfo prep_offline_info = {
sizeof (struct _prep_offline_msg),
(MailMsgDescFunc) NULL, /* DO NOT CHANGE THIS, IT MUST BE NULL FOR CANCELLATION TO WORK */
(MailMsgExecFunc) prep_offline_exec,
(MailMsgDoneFunc) prep_offline_done,
(MailMsgFreeFunc) prep_offline_free
};
void
mail_prep_offline (const gchar *uri,
CamelOperation *cancel,
void (*done)(const gchar *, gpointer data),
gpointer data)
{
struct _prep_offline_msg *m;
m = mail_msg_new (&prep_offline_info);
if (G_IS_CANCELLABLE (cancel))
m->cancel = g_object_ref (cancel);
m->uri = g_strdup (uri);
m->data = data;
m->done = done;
mail_msg_slow_ordered_push (m);
}
/* ** GO OFFLINE ***************************************************** */
struct _set_offline_msg {
MailMsg base;
CamelStore *store;
gboolean offline;
void (*done)(CamelStore *store, gpointer data);
gpointer data;
};
static gchar *
set_offline_desc (struct _set_offline_msg *m)
{
gchar *service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
gchar *msg;
msg = g_strdup_printf (m->offline ? _("Disconnecting from '%s'") : _("Reconnecting to '%s'"),
service_name);
g_free (service_name);
return msg;
}
static void
set_offline_exec (struct _set_offline_msg *m)
{
if (CAMEL_IS_DISCO_STORE (m->store)) {
if (!m->offline) {
camel_disco_store_set_status (
CAMEL_DISCO_STORE (m->store),
CAMEL_DISCO_STORE_ONLINE,
m->base.cancellable,
&m->base.error);
return;
} else if (camel_disco_store_can_work_offline (CAMEL_DISCO_STORE (m->store))) {
camel_disco_store_set_status (
CAMEL_DISCO_STORE (m->store),
CAMEL_DISCO_STORE_OFFLINE,
m->base.cancellable,
&m->base.error);
return;
}
} else if (CAMEL_IS_OFFLINE_STORE (m->store)) {
camel_offline_store_set_online_sync (
CAMEL_OFFLINE_STORE (m->store),
!m->offline, m->base.cancellable,
&m->base.error);
return;
}
if (m->offline)
camel_service_disconnect_sync (
CAMEL_SERVICE (m->store), TRUE, &m->base.error);
}
static void
set_offline_done (struct _set_offline_msg *m)
{
if (m->done)
m->done (m->store, m->data);
}
static void
set_offline_free (struct _set_offline_msg *m)
{
g_object_unref (m->store);
}
static MailMsgInfo set_offline_info = {
sizeof (struct _set_offline_msg),
(MailMsgDescFunc) set_offline_desc,
(MailMsgExecFunc) set_offline_exec,
(MailMsgDoneFunc) set_offline_done,
(MailMsgFreeFunc) set_offline_free
};
gint
mail_store_set_offline (CamelStore *store, gboolean offline,
void (*done)(CamelStore *, gpointer data),
gpointer data)
{
struct _set_offline_msg *m;
gint id;
/* Cancel any pending connect first so the set_offline_op
* thread won't get queued behind a hung connect op.
*/
if (offline)
camel_service_cancel_connect (CAMEL_SERVICE (store));
m = mail_msg_new (&set_offline_info);
m->store = store;
g_object_ref (store);
m->offline = offline;
m->data = data;
m->done = done;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** Prepare OFFLINE ***************************************************** */
static gchar *
prepare_offline_desc (struct _set_offline_msg *m)
{
gchar *service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
gchar *msg;
msg = g_strdup_printf (_("Preparing account '%s' for offline"), service_name);
g_free (service_name);
return msg;
}
static void
prepare_offline_exec (struct _set_offline_msg *m)
{
if (CAMEL_IS_DISCO_STORE (m->store)) {
camel_disco_store_prepare_for_offline (
CAMEL_DISCO_STORE (m->store),
m->base.cancellable, &m->base.error);
} else if (CAMEL_IS_OFFLINE_STORE (m->store)) {
camel_offline_store_prepare_for_offline_sync (
CAMEL_OFFLINE_STORE (m->store),
m->base.cancellable, &m->base.error);
}
}
static void
prepare_offline_done (struct _set_offline_msg *m)
{
if (m->done)
m->done (m->store, m->data);
}
static void
prepare_offline_free (struct _set_offline_msg *m)
{
g_object_unref (m->store);
}
static MailMsgInfo prepare_offline_info = {
sizeof (struct _set_offline_msg),
(MailMsgDescFunc) prepare_offline_desc,
(MailMsgExecFunc) prepare_offline_exec,
(MailMsgDoneFunc) prepare_offline_done,
(MailMsgFreeFunc) prepare_offline_free
};
gint
mail_store_prepare_offline (CamelStore *store)
{
struct _set_offline_msg *m;
gint id;
/* Cancel any pending connect first so the set_offline_op
* thread won't get queued behind a hung connect op.
*/
m = mail_msg_new (&prepare_offline_info);
m->store = store;
g_object_ref (store);
m->data = NULL;
m->done = NULL;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
/* ** Execute Shell Command ***************************************************** */
void
mail_execute_shell_command (CamelFilterDriver *driver, gint argc, gchar **argv, gpointer data)
{
if (argc <= 0)
return;
g_spawn_async (NULL, argv, NULL, 0, NULL, data, NULL, NULL);
}
/* Async service-checking/authtype-lookup code. */
struct _check_msg {
MailMsg base;
gchar *url;
CamelProviderType type;
GList *authtypes;
void (*done)(const gchar *url, CamelProviderType type, GList *types, gpointer data);
gpointer data;
};
static gchar *
check_service_desc (struct _check_msg *m)
{
return g_strdup(_("Checking Service"));
}
static void
check_service_exec (struct _check_msg *m)
{
CamelService *service;
service = camel_session_get_service (session, m->url, m->type, &m->base.error);
if (!service)
return;
m->authtypes = camel_service_query_auth_types_sync (
service, m->base.cancellable, &m->base.error);
g_object_unref (service);
}
static void
check_service_done (struct _check_msg *m)
{
if (m->done)
m->done (m->url, m->type, m->authtypes, m->data);
}
static void
check_service_free (struct _check_msg *m)
{
g_free (m->url);
g_list_free (m->authtypes);
}
static MailMsgInfo check_service_info = {
sizeof (struct _check_msg),
(MailMsgDescFunc) check_service_desc,
(MailMsgExecFunc) check_service_exec,
(MailMsgDoneFunc) check_service_done,
(MailMsgFreeFunc) check_service_free
};
gint
mail_check_service (const gchar *url, CamelProviderType type, void (*done)(const gchar *url, CamelProviderType type, GList *authtypes, gpointer data), gpointer data)
{
struct _check_msg *m;
gint id;
m = mail_msg_new (&check_service_info);
m->url = g_strdup (url);
m->type = type;
m->done = done;
m->data = data;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}