aboutsummaryrefslogtreecommitdiffstats
path: root/libemail-engine/e-mail-session-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libemail-engine/e-mail-session-utils.c')
-rw-r--r--libemail-engine/e-mail-session-utils.c1601
1 files changed, 1601 insertions, 0 deletions
diff --git a/libemail-engine/e-mail-session-utils.c b/libemail-engine/e-mail-session-utils.c
new file mode 100644
index 0000000000..1c9dcbb544
--- /dev/null
+++ b/libemail-engine/e-mail-session-utils.c
@@ -0,0 +1,1601 @@
+/*
+ * e-mail-session-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-session-utils.h"
+
+#include <glib/gi18n-lib.h>
+#include <libedataserver/libedataserver.h>
+
+#include <libemail-engine/e-mail-folder-utils.h>
+#include <libemail-engine/e-mail-utils.h>
+#include <libemail-engine/mail-tools.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
+
+/* FIXME: Temporary - remove this after we move filter/ to eds */
+#define E_FILTER_SOURCE_OUTGOING "outgoing"
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ CamelFolder *folder;
+
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+
+ CamelAddress *from;
+ CamelAddress *recipients;
+
+ CamelFilterDriver *driver;
+
+ CamelService *transport;
+
+ GCancellable *cancellable;
+ gint io_priority;
+
+ /* X-Evolution headers */
+ struct _camel_header_raw *xev;
+
+ GPtrArray *post_to_uris;
+
+ EMailLocalFolder local_id;
+
+ gchar *folder_uri;
+ gchar *message_uid;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->folder != NULL)
+ g_object_unref (context->folder);
+
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->info != NULL)
+ camel_message_info_free (context->info);
+
+ if (context->from != NULL)
+ g_object_unref (context->from);
+
+ if (context->recipients != NULL)
+ g_object_unref (context->recipients);
+
+ if (context->driver != NULL)
+ g_object_unref (context->driver);
+
+ if (context->transport != NULL)
+ g_object_unref (context->transport);
+
+ if (context->cancellable != NULL) {
+ camel_operation_pop_message (context->cancellable);
+ g_object_unref (context->cancellable);
+ }
+
+ if (context->xev != NULL)
+ camel_header_raw_clear (&context->xev);
+
+ if (context->post_to_uris != NULL) {
+ g_ptr_array_foreach (
+ context->post_to_uris, (GFunc) g_free, NULL);
+ g_ptr_array_free (context->post_to_uris, TRUE);
+ }
+
+ g_free (context->folder_uri);
+ g_free (context->message_uid);
+
+ g_slice_free (AsyncContext, context);
+}
+
+GQuark
+e_mail_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0)) {
+ const gchar *string = "e-mail-error-quark";
+ quark = g_quark_from_static_string (string);
+ }
+
+ return quark;
+}
+
+static void
+mail_session_append_to_local_folder_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_append_to_local_folder_sync (
+ E_MAIL_SESSION (object),
+ context->local_id, context->message,
+ context->info, &context->message_uid,
+ cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+gboolean
+e_mail_session_append_to_local_folder_sync (EMailSession *session,
+ EMailLocalFolder local_id,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gchar **appended_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ const gchar *folder_uri;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ folder_uri = e_mail_session_get_local_folder_uri (session, local_id);
+ g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, CAMEL_STORE_FOLDER_CREATE,
+ cancellable, error);
+
+ if (folder != NULL) {
+ success = e_mail_folder_append_message_sync (
+ folder, message, info, appended_uid,
+ cancellable, error);
+ g_object_unref (folder);
+ }
+
+ return success;
+}
+
+void
+e_mail_session_append_to_local_folder (EMailSession *session,
+ EMailLocalFolder local_id,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->local_id = local_id;
+ context->message = g_object_ref (message);
+
+ if (info != NULL)
+ context->info = camel_message_info_ref (info);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_append_to_local_folder);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_session_append_to_local_folder_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_append_to_local_folder_finish (EMailSession *session,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_append_to_local_folder), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (appended_uid != NULL) {
+ *appended_uid = context->message_uid;
+ context->message_uid = NULL;
+ }
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+gboolean
+e_mail_session_handle_draft_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *header_name;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Draft-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Draft-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Draft
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL)
+ return TRUE;
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_draft_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_draft_headers);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_draft_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_draft_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+gboolean
+e_mail_session_handle_source_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ CamelMessageFlags flags = 0;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *flag_string;
+ const gchar *header_name;
+ gboolean success;
+ guint length, ii;
+ gchar **tokens;
+ gchar *string;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Source-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Flags";
+ flag_string = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Source
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL || flag_string == NULL)
+ return TRUE;
+
+ /* Convert the flag string to CamelMessageFlags. */
+
+ string = g_strstrip (g_strdup (flag_string));
+ tokens = g_strsplit (string, " ", 0);
+ g_free (string);
+
+ /* If tokens is NULL, a length of 0 will skip the loop. */
+ length = (tokens != NULL) ? g_strv_length (tokens) : 0;
+
+ for (ii = 0; ii < length; ii++) {
+ /* Note: We're only checking for flags known to
+ * be used in X-Evolution-Source-Flags headers.
+ * Add more as needed. */
+ if (g_strcmp0 (tokens[ii], "ANSWERED") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED;
+ else if (g_strcmp0 (tokens[ii], "ANSWERED_ALL") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ else if (g_strcmp0 (tokens[ii], "FORWARDED") == 0)
+ flags |= CAMEL_MESSAGE_FORWARDED;
+ else if (g_strcmp0 (tokens[ii], "SEEN") == 0)
+ flags |= CAMEL_MESSAGE_SEEN;
+ else
+ g_warning (
+ "Unknown flag '%s' in %s",
+ tokens[ii], header_name);
+ }
+
+ g_strfreev (tokens);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid, flags, flags);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_source_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_source_headers);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_source_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_source_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_send_to_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ CamelProvider *provider;
+ CamelFolder *folder = NULL;
+ CamelFolder *local_sent_folder;
+ CamelServiceConnectionStatus status;
+ GString *error_messages;
+ gboolean copy_to_sent = TRUE;
+ gboolean did_connect = FALSE;
+ guint ii;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (camel_address_length (context->recipients) == 0)
+ goto skip_send;
+
+ /* Send the message to all recipients. */
+
+ if (context->transport == NULL) {
+ g_simple_async_result_set_error (
+ simple, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_UNAVAILABLE,
+ _("No mail transport service available"));
+ return;
+ }
+
+ status = camel_service_get_connection_status (context->transport);
+ if (status != CAMEL_SERVICE_CONNECTED) {
+ did_connect = TRUE;
+
+ camel_service_connect_sync (
+ context->transport, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_take_error (simple, error);
+ return;
+ }
+ }
+
+ provider = camel_service_get_provider (context->transport);
+
+ if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
+ copy_to_sent = FALSE;
+
+ camel_transport_send_to_sync (
+ CAMEL_TRANSPORT (context->transport),
+ context->message, context->from,
+ context->recipients, cancellable, &error);
+
+ if (did_connect) {
+ /* Disconnect regardless of error or cancellation,
+ * but be mindful of these conditions when calling
+ * camel_service_disconnect_sync(). */
+ if (g_cancellable_is_cancelled (cancellable)) {
+ camel_service_disconnect_sync (
+ context->transport, FALSE, NULL, NULL);
+ } else if (error != NULL) {
+ camel_service_disconnect_sync (
+ context->transport, FALSE, cancellable, NULL);
+ } else {
+ camel_service_disconnect_sync (
+ context->transport, TRUE, cancellable, &error);
+ }
+
+ if (error != NULL) {
+ g_simple_async_result_take_error (simple, error);
+ return;
+ }
+ }
+
+skip_send:
+ /* Post the message to requested folders. */
+ for (ii = 0; ii < context->post_to_uris->len; ii++) {
+ CamelFolder *folder;
+ const gchar *folder_uri;
+
+ folder_uri = g_ptr_array_index (context->post_to_uris, ii);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_simple_async_result_take_error (simple, error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ camel_folder_append_message_sync (
+ folder, context->message, context->info,
+ NULL, cancellable, &error);
+
+ g_object_unref (folder);
+
+ if (error != NULL) {
+ g_simple_async_result_take_error (simple, error);
+ return;
+ }
+ }
+
+ /*** Post Processing ***/
+
+ /* This accumulates error messages during post-processing. */
+ error_messages = g_string_sized_new (256);
+
+ mail_tool_restore_xevolution_headers (context->message, context->xev);
+
+ /* Run filters on the outgoing message. */
+ if (context->driver != NULL) {
+ camel_filter_driver_filter_message (
+ context->driver, context->message, context->info,
+ NULL, NULL, NULL, "", cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error != NULL) {
+ g_string_append_printf (
+ error_messages,
+ _("Failed to apply outgoing filters: %s"),
+ error->message);
+ g_clear_error (&error);
+ }
+
+ if ((camel_message_info_flags (context->info) & CAMEL_MESSAGE_DELETED) != 0)
+ copy_to_sent = FALSE;
+ }
+
+ if (!copy_to_sent)
+ goto cleanup;
+
+ /* Append the sent message to a Sent folder. */
+
+ local_sent_folder =
+ e_mail_session_get_local_folder (
+ session, E_MAIL_LOCAL_FOLDER_SENT);
+
+ folder = e_mail_session_get_fcc_for_message_sync (
+ session, context->message, cancellable, &error);
+
+ /* Sanity check. */
+ g_return_if_fail (
+ ((folder != NULL) && (error == NULL)) ||
+ ((folder == NULL) && (error != NULL)));
+
+ /* Append the message. */
+ if (folder != NULL)
+ camel_folder_append_message_sync (
+ folder, context->message,
+ context->info, NULL, cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error == NULL)
+ goto cleanup;
+
+ if (folder != NULL && folder != local_sent_folder) {
+ const gchar *description;
+
+ description = camel_folder_get_description (folder);
+
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ description, error->message);
+ }
+
+ /* If appending to a remote Sent folder failed,
+ * try appending to the local Sent folder. */
+ if (folder != local_sent_folder) {
+
+ g_clear_error (&error);
+
+ camel_folder_append_message_sync (
+ local_sent_folder, context->message,
+ context->info, NULL, cancellable, &error);
+ }
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ /* We can't even append to the local Sent folder?
+ * In that case just leave the message in Outbox. */
+ if (error != NULL) {
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to local 'Sent' folder: %s"),
+ error->message);
+ g_clear_error (&error);
+ goto exit;
+ }
+
+cleanup:
+
+ /* The send operation was successful; ignore cleanup errors. */
+
+ /* Mark the draft message for deletion, if present. */
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Set flags on the original source message, if present.
+ * Source message refers to the message being forwarded
+ * or replied to. */
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+exit:
+
+ /* If we were cancelled, disregard any other errors. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_simple_async_result_take_error (simple, error);
+
+ /* Stuff the accumulated error messages in a GError. */
+ } else if (error_messages->len > 0) {
+ g_simple_async_result_set_error (
+ simple, E_MAIL_ERROR,
+ E_MAIL_ERROR_POST_PROCESSING,
+ "%s", error_messages->str);
+ }
+
+ /* Synchronize the Sent folder. */
+ if (folder != NULL) {
+ camel_folder_synchronize_sync (
+ folder, FALSE, cancellable, NULL);
+ g_object_unref (folder);
+ }
+
+ g_string_free (error_messages, TRUE);
+}
+
+static guint32
+get_message_size (CamelMimeMessage *message,
+ GCancellable *cancellable)
+{
+ CamelStream *null;
+ guint32 size;
+
+ null = camel_stream_null_new ();
+ camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message), null, cancellable, NULL);
+ size = CAMEL_STREAM_NULL (null)->written;
+ g_object_unref (null);
+
+ return size;
+}
+
+void
+e_mail_session_send_to (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder_func,
+ gpointer get_folder_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ CamelAddress *from;
+ CamelAddress *recipients;
+ CamelMedium *medium;
+ CamelMessageInfo *info;
+ CamelService *transport;
+ GPtrArray *post_to_uris;
+ struct _camel_header_raw *xev;
+ struct _camel_header_raw *header;
+ const gchar *resent_from;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ medium = CAMEL_MEDIUM (message);
+
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ /* Do this before removing "X-Evolution" headers. */
+ transport = e_mail_session_ref_transport_for_message (
+ session, message);
+
+ xev = mail_tool_remove_xevolution_headers (message);
+
+ /* Extract directives from X-Evolution headers. */
+
+ post_to_uris = g_ptr_array_new ();
+ for (header = xev; header != NULL; header = header->next) {
+ gchar *folder_uri;
+
+ if (g_strcmp0 (header->name, "X-Evolution-PostTo") != 0)
+ continue;
+
+ folder_uri = g_strstrip (g_strdup (header->value));
+ g_ptr_array_add (post_to_uris, folder_uri);
+ }
+
+ /* Collect sender and recipients from headers. */
+
+ from = (CamelAddress *) camel_internet_address_new ();
+ recipients = (CamelAddress *) camel_internet_address_new ();
+ resent_from = camel_medium_get_header (medium, "Resent-From");
+
+ if (resent_from != NULL) {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ camel_address_decode (from, resent_from);
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ } else {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ addr = camel_mime_message_get_from (message);
+ camel_address_copy (from, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+ }
+
+ /* Miscellaneous preparations. */
+
+ info = camel_message_info_new_from_header (
+ NULL, CAMEL_MIME_PART (message)->headers);
+ ((CamelMessageInfoBase *) info)->size =
+ get_message_size (message, cancellable);
+ camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+ /* expand, or remove empty, group addresses */
+ em_utils_expand_groups (CAMEL_INTERNET_ADDRESS (recipients));
+
+ /* The rest of the processing happens in a thread. */
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+ context->io_priority = io_priority;
+ context->from = from;
+ context->recipients = recipients;
+ context->info = info;
+ context->xev = xev;
+ context->post_to_uris = post_to_uris;
+ context->transport = transport;
+
+ if (G_IS_CANCELLABLE (cancellable))
+ context->cancellable = g_object_ref (cancellable);
+
+ /* Failure here emits a runtime warning but is non-fatal. */
+ context->driver = camel_session_get_filter_driver (
+ CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, &error);
+ if (context->driver != NULL && get_folder_func)
+ camel_filter_driver_set_folder_func (
+ context->driver, get_folder_func, get_folder_data);
+ if (error != NULL) {
+ g_warn_if_fail (context->driver == NULL);
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ /* This gets popped in async_context_free(). */
+ camel_operation_push_message (
+ context->cancellable, _("Sending message"));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_send_to);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_send_to_thread,
+ context->io_priority,
+ context->cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_send_to_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_send_to), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_try_uri_to_folder (EMailSession *session,
+ const gchar *folder_uri,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ GError *local_error = NULL;
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, &local_error);
+
+ /* Sanity check. */
+ g_return_val_if_fail (
+ ((folder != NULL) && (local_error == NULL)) ||
+ ((folder == NULL) && (local_error != NULL)), NULL);
+
+ /* Disregard specific errors. */
+
+ /* Invalid URI. */
+ if (g_error_matches (
+ local_error, CAMEL_FOLDER_ERROR,
+ CAMEL_FOLDER_ERROR_INVALID))
+ g_clear_error (&local_error);
+
+ /* Folder not found. */
+ if (g_error_matches (
+ local_error, CAMEL_STORE_ERROR,
+ CAMEL_STORE_ERROR_NO_FOLDER))
+ g_clear_error (&local_error);
+
+ if (local_error != NULL)
+ g_propagate_error (error, local_error);
+
+ return folder;
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_ref_origin_folder (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelMedium *medium;
+ const gchar *header_name;
+ const gchar *header_value;
+
+ medium = CAMEL_MEDIUM (message);
+
+ /* Check that a "X-Evolution-Source-Flags" header is present
+ * and its value does not contain the substring "FORWARDED". */
+
+ header_name = "X-Evolution-Source-Flags";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ if (strstr (header_value, "FORWARDED") != NULL)
+ return NULL;
+
+ /* Check that a "X-Evolution-Source-Message" header is present. */
+
+ header_name = "X-Evolution-Source-Message";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ /* Check that a "X-Evolution-Source-Folder" header is present.
+ * Its value specifies the origin folder as a folder URI. */
+
+ header_name = "X-Evolution-Source-Folder";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ /* This may return NULL without setting a GError. */
+ return mail_session_try_uri_to_folder (
+ session, header_value, cancellable, error);
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_ref_fcc_from_identity (EMailSession *session,
+ ESource *source,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceRegistry *registry;
+ ESourceMailSubmission *extension;
+ CamelFolder *folder = NULL;
+ const gchar *extension_name;
+ gchar *folder_uri;
+
+ registry = e_mail_session_get_registry (session);
+ extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
+
+ if (source == NULL)
+ return NULL;
+
+ if (!e_source_registry_check_enabled (registry, source))
+ return NULL;
+
+ if (!e_source_has_extension (source, extension_name))
+ return NULL;
+
+ extension = e_source_get_extension (source, extension_name);
+
+ if (e_source_mail_submission_get_replies_to_origin_folder (extension)) {
+ GError *local_error = NULL;
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_origin_folder (
+ session, message, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+ }
+
+ folder_uri = e_source_mail_submission_dup_sent_folder (extension);
+
+ if (folder_uri != NULL && folder == NULL) {
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_try_uri_to_folder (
+ session, folder_uri, cancellable, error);
+ }
+
+ g_free (folder_uri);
+
+ return folder;
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_ref_fcc_from_x_identity (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESource *source;
+ ESourceRegistry *registry;
+ CamelFolder *folder;
+ CamelMedium *medium;
+ const gchar *header_name;
+ const gchar *header_value;
+ gchar *uid;
+
+ medium = CAMEL_MEDIUM (message);
+ header_name = "X-Evolution-Identity";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ uid = g_strstrip (g_strdup (header_value));
+
+ registry = e_mail_session_get_registry (session);
+ source = e_source_registry_ref_source (registry, uid);
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_fcc_from_identity (
+ session, source, message, cancellable, error);
+
+ g_clear_object (&source);
+
+ g_free (uid);
+
+ return folder;
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_ref_fcc_from_x_fcc (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelMedium *medium;
+ const gchar *header_name;
+ const gchar *header_value;
+
+ medium = CAMEL_MEDIUM (message);
+ header_name = "X-Evolution-Fcc";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ /* This may return NULL without setting a GError. */
+ return mail_session_try_uri_to_folder (
+ session, header_value, cancellable, error);
+}
+
+/* Helper for e_mail_session_get_fcc_for_message_sync() */
+static CamelFolder *
+mail_session_ref_fcc_from_default_identity (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESource *source;
+ ESourceRegistry *registry;
+ CamelFolder *folder;
+
+ registry = e_mail_session_get_registry (session);
+ source = e_source_registry_ref_default_mail_identity (registry);
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_fcc_from_identity (
+ session, source, message, cancellable, error);
+
+ g_clear_object (&source);
+
+ return folder;
+}
+
+/**
+ * e_mail_session_get_fcc_for_message_sync:
+ * @session: an #EMailSession
+ * @message: a #CamelMimeMessage
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Obtains the preferred "carbon-copy" folder (a.k.a Fcc) for @message
+ * by first checking @message for an "X-Evolution-Identity" header, and
+ * then an "X-Evolution-Fcc" header. Failing that, the function checks
+ * the default mail identity (if available), and failing even that, the
+ * function falls back to the Sent folder from the built-in mail store.
+ *
+ * Where applicable, the function attempts to honor the
+ * #ESourceMailSubmission:replies-to-origin-folder preference.
+ *
+ * The returned #CamelFolder is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * If a non-recoverable error occurs, the function sets @error and returns
+ * %NULL.
+ *
+ * Returns: a #CamelFolder, or %NULL
+ **/
+CamelFolder *
+e_mail_session_get_fcc_for_message_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder = NULL;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+ /* Check for "X-Evolution-Identity" header. */
+ if (folder == NULL) {
+ GError *local_error = NULL;
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_fcc_from_x_identity (
+ session, message, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+ }
+
+ /* Check for "X-Evolution-Fcc" header. */
+ if (folder == NULL) {
+ GError *local_error = NULL;
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_fcc_from_x_fcc (
+ session, message, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+ }
+
+ /* Check the default mail identity. */
+ if (folder == NULL) {
+ GError *local_error = NULL;
+
+ /* This may return NULL without setting a GError. */
+ folder = mail_session_ref_fcc_from_default_identity (
+ session, message, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+ }
+
+ /* Last resort - local Sent folder. */
+ if (folder == NULL) {
+ folder = e_mail_session_get_local_folder (
+ session, E_MAIL_LOCAL_FOLDER_SENT);
+ g_object_ref (folder);
+ }
+
+ return folder;
+}
+
+/* Helper for e_mail_session_get_fcc_for_message() */
+static void
+mail_session_get_fcc_for_message_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ AsyncContext *async_context;
+ GError *local_error = NULL;
+
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ async_context->folder =
+ e_mail_session_get_fcc_for_message_sync (
+ E_MAIL_SESSION (source_object),
+ async_context->message,
+ cancellable, &local_error);
+
+ if (local_error != NULL)
+ g_simple_async_result_take_error (simple, local_error);
+}
+
+/**
+ * e_mail_session_get_fcc_for_message:
+ * @session: an #EMailSession
+ * @message: a #CamelMimeMessage
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously obtains the preferred "carbon-copy" folder (a.k.a Fcc) for
+ * @message by first checking @message for an "X-Evolution-Identity" header,
+ * and then an "X-Evolution-Fcc" header. Failing that, the function checks
+ * the default mail identity (if available), and failing even that, the
+ * function falls back to the Sent folder from the built-in mail store.
+ *
+ * Where applicable, the function attempts to honor the
+ * #ESourceMailSubmission:replies-to-origin-folder preference.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_mail_session_get_fcc_for_message_finish() to get the result of the
+ * operation.
+ **/
+void
+e_mail_session_get_fcc_for_message (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_get_fcc_for_message);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, async_context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_session_get_fcc_for_message_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_mail_session_get_fcc_for_message_finish:
+ * @session: an #EMailSession
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_mail_session_get_fcc_for_message().
+ *
+ * The returned #CamelFolder is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * If a non-recoverable error occurred, the function sets @error and
+ * returns %NULL.
+ *
+ * Returns: a #CamelFolder, or %NULL
+ **/
+CamelFolder *
+e_mail_session_get_fcc_for_message_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_get_fcc_for_message), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (async_context->folder != NULL, NULL);
+
+ return g_object_ref (async_context->folder);
+}
+
+/**
+ * e_mail_session_ref_transport:
+ * @session: an #EMailSession
+ * @transport_uid: the UID of a mail transport
+ *
+ * Returns the transport #CamelService instance for @transport_uid,
+ * verifying first that the @transport_uid is indeed a mail transport and
+ * that the corresponding #ESource is enabled. If these checks fail, the
+ * function returns %NULL.
+ *
+ * The returned #CamelService is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelService, or %NULL
+ **/
+CamelService *
+e_mail_session_ref_transport (EMailSession *session,
+ const gchar *transport_uid)
+{
+ ESourceRegistry *registry;
+ ESource *source = NULL;
+ CamelService *transport = NULL;
+ const gchar *extension_name;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (transport_uid != NULL, NULL);
+
+ registry = e_mail_session_get_registry (session);
+ extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+
+ source = e_source_registry_ref_source (registry, transport_uid);
+
+ if (source == NULL)
+ goto exit;
+
+ if (!e_source_registry_check_enabled (registry, source))
+ goto exit;
+
+ if (!e_source_has_extension (source, extension_name))
+ goto exit;
+
+ transport = camel_session_ref_service (
+ CAMEL_SESSION (session), transport_uid);
+
+ /* Sanity check. */
+ if (transport != NULL)
+ g_warn_if_fail (CAMEL_IS_TRANSPORT (transport));
+
+exit:
+ g_clear_object (&source);
+
+ return transport;
+}
+
+/* Helper for e_mail_session_ref_default_transport()
+ * and mail_session_ref_transport_from_x_identity(). */
+static CamelService *
+mail_session_ref_transport_for_identity (EMailSession *session,
+ ESource *source)
+{
+ ESourceRegistry *registry;
+ ESourceMailSubmission *extension;
+ CamelService *transport = NULL;
+ const gchar *extension_name;
+ gchar *uid;
+
+ registry = e_mail_session_get_registry (session);
+ extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
+
+ if (source == NULL)
+ return NULL;
+
+ if (!e_source_registry_check_enabled (registry, source))
+ return NULL;
+
+ if (!e_source_has_extension (source, extension_name))
+ return NULL;
+
+ extension = e_source_get_extension (source, extension_name);
+ uid = e_source_mail_submission_dup_transport_uid (extension);
+
+ if (uid != NULL) {
+ transport = e_mail_session_ref_transport (session, uid);
+ g_free (uid);
+ }
+
+ return transport;
+}
+
+/**
+ * e_mail_session_ref_default_transport:
+ * @session: an #EMailSession
+ *
+ * Returns the default transport #CamelService instance according to
+ * #ESourceRegistry's #ESourceRegistry:default-mail-identity setting,
+ * verifying first that the #ESourceMailSubmission:transport-uid named by
+ * the #ESourceRegistry:default-mail-identity is indeed a mail transport,
+ * and that the corresponding #ESource is enabled. If these checks fail,
+ * the function returns %NULL.
+ *
+ * The returned #CamelService is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelService, or %NULL
+ **/
+CamelService *
+e_mail_session_ref_default_transport (EMailSession *session)
+{
+ ESource *source;
+ ESourceRegistry *registry;
+ CamelService *transport;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ registry = e_mail_session_get_registry (session);
+ source = e_source_registry_ref_default_mail_identity (registry);
+ transport = mail_session_ref_transport_for_identity (session, source);
+ g_clear_object (&source);
+
+ return transport;
+}
+
+/* Helper for e_mail_session_ref_transport_for_message() */
+static CamelService *
+mail_session_ref_transport_from_x_identity (EMailSession *session,
+ CamelMimeMessage *message)
+{
+ ESource *source;
+ ESourceRegistry *registry;
+ CamelMedium *medium;
+ CamelService *transport;
+ const gchar *header_name;
+ const gchar *header_value;
+ gchar *uid;
+
+ medium = CAMEL_MEDIUM (message);
+ header_name = "X-Evolution-Identity";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ uid = g_strstrip (g_strdup (header_value));
+
+ registry = e_mail_session_get_registry (session);
+ source = e_source_registry_ref_source (registry, uid);
+ transport = mail_session_ref_transport_for_identity (session, source);
+ g_clear_object (&source);
+
+ g_free (uid);
+
+ return transport;
+}
+
+/* Helper for e_mail_session_ref_transport_for_message() */
+static CamelService *
+mail_session_ref_transport_from_x_transport (EMailSession *session,
+ CamelMimeMessage *message)
+{
+ CamelMedium *medium;
+ CamelService *transport;
+ const gchar *header_name;
+ const gchar *header_value;
+ gchar *uid;
+
+ medium = CAMEL_MEDIUM (message);
+ header_name = "X-Evolution-Transport";
+ header_value = camel_medium_get_header (medium, header_name);
+
+ if (header_value == NULL)
+ return NULL;
+
+ uid = g_strstrip (g_strdup (header_value));
+
+ transport = e_mail_session_ref_transport (session, uid);
+
+ g_free (uid);
+
+ return transport;
+}
+
+/**
+ * e_mail_session_ref_transport_for_message:
+ * @session: an #EMailSession
+ * @message: a #CamelMimeMessage
+ *
+ * Returns the preferred transport #CamelService instance for @message by
+ * first checking @message for an "X-Evolution-Identity" header, and then
+ * an "X-Evolution-Transport" header. Failing that, the function returns
+ * the default transport #CamelService instance (if available).
+ *
+ * The returned #CamelService is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelService, or %NULL
+ **/
+CamelService *
+e_mail_session_ref_transport_for_message (EMailSession *session,
+ CamelMimeMessage *message)
+{
+ CamelService *transport = NULL;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+
+ /* Check for "X-Evolution-Identity" header. */
+ if (transport == NULL)
+ transport = mail_session_ref_transport_from_x_identity (
+ session, message);
+
+ /* Check for "X-Evolution-Transport" header. */
+ if (transport == NULL)
+ transport = mail_session_ref_transport_from_x_transport (
+ session, message);
+
+ /* Fall back to the default mail transport. */
+ if (transport == NULL)
+ transport = e_mail_session_ref_default_transport (session);
+
+ return transport;
+}
+