From dea7daf4c33ce4bdd020c19a3440538371b415a7 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 24 May 2013 14:09:54 -0400 Subject: EMailSession: Add helper functions for sending messages. New functions: e_mail_session_get_fcc_for_message_sync() e_mail_session_get_fcc_for_message() e_mail_session_get_fcc_for_message_finish() e_mail_session_ref_transport() e_mail_session_ref_default_transport() e_mail_session_ref_transport_for_message() --- libemail-engine/e-mail-session-utils.c | 677 ++++++++++++++++++++++++++++++++- libemail-engine/e-mail-session-utils.h | 23 ++ 2 files changed, 687 insertions(+), 13 deletions(-) diff --git a/libemail-engine/e-mail-session-utils.c b/libemail-engine/e-mail-session-utils.c index da54b7640d..45033b01da 100644 --- a/libemail-engine/e-mail-session-utils.c +++ b/libemail-engine/e-mail-session-utils.c @@ -38,7 +38,7 @@ typedef struct _AsyncContext AsyncContext; struct _AsyncContext { - CamelFolder *sent_folder; + CamelFolder *folder; CamelMimeMessage *message; CamelMessageInfo *info; @@ -67,8 +67,8 @@ struct _AsyncContext { static void async_context_free (AsyncContext *context) { - if (context->sent_folder != NULL) - g_object_unref (context->sent_folder); + if (context->folder != NULL) + g_object_unref (context->folder); if (context->message != NULL) g_object_unref (context->message); @@ -670,11 +670,11 @@ mail_session_send_to_thread (GSimpleAsyncResult *simple, /* Try to extract a CamelFolder from the Sent folder URI. */ if (context->sent_folder_uri != NULL) { - context->sent_folder = e_mail_session_uri_to_folder_sync ( + context->folder = e_mail_session_uri_to_folder_sync ( session, context->sent_folder_uri, 0, cancellable, &error); if (error != NULL) { - g_warn_if_fail (context->sent_folder == NULL); + g_warn_if_fail (context->folder == NULL); if (error_messages->len > 0) g_string_append (error_messages, "\n\n"); g_string_append_printf ( @@ -687,12 +687,12 @@ mail_session_send_to_thread (GSimpleAsyncResult *simple, } /* Fall back to the local Sent folder. */ - if (context->sent_folder == NULL) - context->sent_folder = g_object_ref (local_sent_folder); + if (context->folder == NULL) + context->folder = g_object_ref (local_sent_folder); /* Append the message. */ camel_folder_append_message_sync ( - context->sent_folder, context->message, + context->folder, context->message, context->info, NULL, cancellable, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) @@ -703,11 +703,10 @@ mail_session_send_to_thread (GSimpleAsyncResult *simple, /* If appending to a remote Sent folder failed, * try appending to the local Sent folder. */ - if (context->sent_folder != local_sent_folder) { + if (context->folder != local_sent_folder) { const gchar *description; - description = camel_folder_get_description ( - context->sent_folder); + description = camel_folder_get_description (context->folder); if (error_messages->len > 0) g_string_append (error_messages, "\n\n"); @@ -776,9 +775,9 @@ exit: } /* Synchronize the Sent folder. */ - if (context->sent_folder != NULL) + if (context->folder != NULL) camel_folder_synchronize_sync ( - context->sent_folder, FALSE, cancellable, NULL); + context->folder, FALSE, cancellable, NULL); g_string_free (error_messages, TRUE); } @@ -1014,3 +1013,655 @@ e_mail_session_send_to_finish (EMailSession *session, 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; +} + diff --git a/libemail-engine/e-mail-session-utils.h b/libemail-engine/e-mail-session-utils.h index 0c7cff3f97..6349834d8d 100644 --- a/libemail-engine/e-mail-session-utils.h +++ b/libemail-engine/e-mail-session-utils.h @@ -97,6 +97,29 @@ void e_mail_session_send_to (EMailSession *session, gboolean e_mail_session_send_to_finish (EMailSession *session, GAsyncResult *result, GError **error); +CamelFolder * e_mail_session_get_fcc_for_message_sync + (EMailSession *session, + CamelMimeMessage *message, + GCancellable *cancellable, + GError **error); +void e_mail_session_get_fcc_for_message + (EMailSession *session, + CamelMimeMessage *message, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +CamelFolder * e_mail_session_get_fcc_for_message_finish + (EMailSession *session, + GAsyncResult *result, + GError **error); +CamelService * e_mail_session_ref_transport (EMailSession *session, + const gchar *transport_uid); +CamelService * e_mail_session_ref_default_transport + (EMailSession *session); +CamelService * e_mail_session_ref_transport_for_message + (EMailSession *session, + CamelMimeMessage *message); G_END_DECLS -- cgit v1.2.3