Address some issues reported by Undefined Behavior Sanitizer
[evolution.git] / src / libemail-engine / e-mail-session-utils.c
blobcdcea02339a94b498c5f78d83ada3d43a0a532c0
1 /*
2 * e-mail-session-utils.c
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include "e-mail-session-utils.h"
22 #include <glib/gi18n-lib.h>
23 #include <libedataserver/libedataserver.h>
25 #include <libemail-engine/e-mail-folder-utils.h>
26 #include <libemail-engine/e-mail-utils.h>
27 #include <libemail-engine/mail-tools.h>
29 /* X-Mailer header value */
30 #define X_MAILER ("Evolution " VERSION VERSION_SUBSTRING " " VERSION_COMMENT)
32 /* FIXME: Temporary - remove this after we move filter/ to eds */
33 #define E_FILTER_SOURCE_OUTGOING "outgoing"
35 typedef struct _AsyncContext AsyncContext;
37 struct _AsyncContext {
38 CamelFolder *folder;
40 CamelMimeMessage *message;
41 CamelMessageInfo *info;
43 CamelAddress *from;
44 CamelAddress *recipients;
46 CamelFilterDriver *driver;
48 CamelService *transport;
50 GCancellable *cancellable;
51 gint io_priority;
53 /* X-Evolution headers */
54 CamelNameValueArray *xev_headers;
56 GPtrArray *post_to_uris;
58 EMailLocalFolder local_id;
60 gchar *folder_uri;
61 gchar *message_uid;
63 gboolean use_sent_folder;
66 static void
67 async_context_free (AsyncContext *context)
69 g_clear_object (&context->folder);
70 g_clear_object (&context->message);
71 g_clear_object (&context->info);
72 g_clear_object (&context->from);
73 g_clear_object (&context->recipients);
74 g_clear_object (&context->driver);
75 g_clear_object (&context->transport);
77 if (context->cancellable != NULL) {
78 camel_operation_pop_message (context->cancellable);
79 g_object_unref (context->cancellable);
82 camel_name_value_array_free (context->xev_headers);
84 if (context->post_to_uris != NULL) {
85 g_ptr_array_foreach (
86 context->post_to_uris, (GFunc) g_free, NULL);
87 g_ptr_array_free (context->post_to_uris, TRUE);
90 g_free (context->folder_uri);
91 g_free (context->message_uid);
93 g_slice_free (AsyncContext, context);
96 GQuark
97 e_mail_error_quark (void)
99 static GQuark quark = 0;
101 if (G_UNLIKELY (quark == 0)) {
102 const gchar *string = "e-mail-error-quark";
103 quark = g_quark_from_static_string (string);
106 return quark;
109 static void
110 mail_session_append_to_local_folder_thread (GSimpleAsyncResult *simple,
111 GObject *object,
112 GCancellable *cancellable)
114 AsyncContext *context;
115 GError *error = NULL;
117 context = g_simple_async_result_get_op_res_gpointer (simple);
119 e_mail_session_append_to_local_folder_sync (
120 E_MAIL_SESSION (object),
121 context->local_id, context->message,
122 context->info, &context->message_uid,
123 cancellable, &error);
125 if (error != NULL)
126 g_simple_async_result_take_error (simple, error);
129 gboolean
130 e_mail_session_append_to_local_folder_sync (EMailSession *session,
131 EMailLocalFolder local_id,
132 CamelMimeMessage *message,
133 CamelMessageInfo *info,
134 gchar **appended_uid,
135 GCancellable *cancellable,
136 GError **error)
138 CamelFolder *folder;
139 const gchar *folder_uri;
140 gboolean success = FALSE;
142 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
143 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
145 folder_uri = e_mail_session_get_local_folder_uri (session, local_id);
146 g_return_val_if_fail (folder_uri != NULL, FALSE);
148 folder = e_mail_session_uri_to_folder_sync (
149 session, folder_uri, CAMEL_STORE_FOLDER_CREATE,
150 cancellable, error);
152 if (folder != NULL) {
153 success = e_mail_folder_append_message_sync (
154 folder, message, info, appended_uid,
155 cancellable, error);
156 g_object_unref (folder);
159 return success;
162 void
163 e_mail_session_append_to_local_folder (EMailSession *session,
164 EMailLocalFolder local_id,
165 CamelMimeMessage *message,
166 CamelMessageInfo *info,
167 gint io_priority,
168 GCancellable *cancellable,
169 GAsyncReadyCallback callback,
170 gpointer user_data)
172 GSimpleAsyncResult *simple;
173 AsyncContext *context;
175 g_return_if_fail (E_IS_MAIL_SESSION (session));
176 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
178 context = g_slice_new0 (AsyncContext);
179 context->local_id = local_id;
180 context->message = g_object_ref (message);
182 if (info != NULL)
183 context->info = g_object_ref (info);
185 simple = g_simple_async_result_new (
186 G_OBJECT (session), callback, user_data,
187 e_mail_session_append_to_local_folder);
189 g_simple_async_result_set_check_cancellable (simple, cancellable);
191 g_simple_async_result_set_op_res_gpointer (
192 simple, context, (GDestroyNotify) async_context_free);
194 g_simple_async_result_run_in_thread (
195 simple, mail_session_append_to_local_folder_thread,
196 io_priority, cancellable);
198 g_object_unref (simple);
201 gboolean
202 e_mail_session_append_to_local_folder_finish (EMailSession *session,
203 GAsyncResult *result,
204 gchar **appended_uid,
205 GError **error)
207 GSimpleAsyncResult *simple;
208 AsyncContext *context;
210 g_return_val_if_fail (
211 g_simple_async_result_is_valid (
212 result, G_OBJECT (session),
213 e_mail_session_append_to_local_folder), FALSE);
215 simple = G_SIMPLE_ASYNC_RESULT (result);
216 context = g_simple_async_result_get_op_res_gpointer (simple);
218 if (appended_uid != NULL) {
219 *appended_uid = context->message_uid;
220 context->message_uid = NULL;
223 /* Assume success unless a GError is set. */
224 return !g_simple_async_result_propagate_error (simple, error);
227 static void
228 mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
229 EMailSession *session,
230 GCancellable *cancellable)
232 AsyncContext *context;
233 GError *error = NULL;
235 context = g_simple_async_result_get_op_res_gpointer (simple);
237 e_mail_session_handle_draft_headers_sync (
238 session, context->message, cancellable, &error);
240 if (error != NULL)
241 g_simple_async_result_take_error (simple, error);
244 gboolean
245 e_mail_session_handle_draft_headers_sync (EMailSession *session,
246 CamelMimeMessage *message,
247 GCancellable *cancellable,
248 GError **error)
250 CamelFolder *folder;
251 CamelMedium *medium;
252 const gchar *folder_uri;
253 const gchar *message_uid;
254 const gchar *header_name;
255 gboolean success;
257 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
258 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
260 medium = CAMEL_MEDIUM (message);
262 header_name = "X-Evolution-Draft-Folder";
263 folder_uri = camel_medium_get_header (medium, header_name);
265 header_name = "X-Evolution-Draft-Message";
266 message_uid = camel_medium_get_header (medium, header_name);
268 /* Don't report errors about missing X-Evolution-Draft
269 * headers. These headers are optional, so their absence
270 * is handled by doing nothing. */
271 if (folder_uri == NULL || message_uid == NULL)
272 return TRUE;
274 folder = e_mail_session_uri_to_folder_sync (
275 session, folder_uri, 0, cancellable, error);
277 if (folder == NULL)
278 return FALSE;
280 camel_folder_set_message_flags (
281 folder, message_uid,
282 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
283 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
285 success = camel_folder_synchronize_message_sync (
286 folder, message_uid, cancellable, error);
288 g_object_unref (folder);
290 return success;
293 void
294 e_mail_session_handle_draft_headers (EMailSession *session,
295 CamelMimeMessage *message,
296 gint io_priority,
297 GCancellable *cancellable,
298 GAsyncReadyCallback callback,
299 gpointer user_data)
301 GSimpleAsyncResult *simple;
302 AsyncContext *context;
304 g_return_if_fail (E_IS_MAIL_SESSION (session));
305 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
307 context = g_slice_new0 (AsyncContext);
308 context->message = g_object_ref (message);
310 simple = g_simple_async_result_new (
311 G_OBJECT (session), callback, user_data,
312 e_mail_session_handle_draft_headers);
314 g_simple_async_result_set_check_cancellable (simple, cancellable);
316 g_simple_async_result_set_op_res_gpointer (
317 simple, context, (GDestroyNotify) async_context_free);
319 g_simple_async_result_run_in_thread (
320 simple, (GSimpleAsyncThreadFunc)
321 mail_session_handle_draft_headers_thread,
322 io_priority, cancellable);
324 g_object_unref (simple);
327 gboolean
328 e_mail_session_handle_draft_headers_finish (EMailSession *session,
329 GAsyncResult *result,
330 GError **error)
332 GSimpleAsyncResult *simple;
334 g_return_val_if_fail (
335 g_simple_async_result_is_valid (
336 result, G_OBJECT (session),
337 e_mail_session_handle_draft_headers), FALSE);
339 simple = G_SIMPLE_ASYNC_RESULT (result);
341 /* Assume success unless a GError is set. */
342 return !g_simple_async_result_propagate_error (simple, error);
345 static void
346 mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
347 EMailSession *session,
348 GCancellable *cancellable)
350 AsyncContext *context;
351 GError *error = NULL;
353 context = g_simple_async_result_get_op_res_gpointer (simple);
355 e_mail_session_handle_source_headers_sync (
356 session, context->message, cancellable, &error);
358 if (error != NULL)
359 g_simple_async_result_take_error (simple, error);
362 gboolean
363 e_mail_session_handle_source_headers_sync (EMailSession *session,
364 CamelMimeMessage *message,
365 GCancellable *cancellable,
366 GError **error)
368 CamelFolder *folder;
369 CamelMedium *medium;
370 CamelMessageFlags flags = 0;
371 const gchar *folder_uri;
372 const gchar *message_uid;
373 const gchar *flag_string;
374 const gchar *header_name;
375 gboolean success;
376 guint length, ii;
377 gchar **tokens;
378 gchar *string;
380 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
381 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
383 medium = CAMEL_MEDIUM (message);
385 header_name = "X-Evolution-Source-Folder";
386 folder_uri = camel_medium_get_header (medium, header_name);
388 header_name = "X-Evolution-Source-Message";
389 message_uid = camel_medium_get_header (medium, header_name);
391 header_name = "X-Evolution-Source-Flags";
392 flag_string = camel_medium_get_header (medium, header_name);
394 /* Don't report errors about missing X-Evolution-Source
395 * headers. These headers are optional, so their absence
396 * is handled by doing nothing. */
397 if (folder_uri == NULL || message_uid == NULL || flag_string == NULL)
398 return TRUE;
400 /* Convert the flag string to CamelMessageFlags. */
402 string = g_strstrip (g_strdup (flag_string));
403 tokens = g_strsplit (string, " ", 0);
404 g_free (string);
406 /* If tokens is NULL, a length of 0 will skip the loop. */
407 length = (tokens != NULL) ? g_strv_length (tokens) : 0;
409 for (ii = 0; ii < length; ii++) {
410 /* Note: We're only checking for flags known to
411 * be used in X-Evolution-Source-Flags headers.
412 * Add more as needed. */
413 if (g_strcmp0 (tokens[ii], "ANSWERED") == 0)
414 flags |= CAMEL_MESSAGE_ANSWERED;
415 else if (g_strcmp0 (tokens[ii], "ANSWERED_ALL") == 0)
416 flags |= CAMEL_MESSAGE_ANSWERED_ALL;
417 else if (g_strcmp0 (tokens[ii], "FORWARDED") == 0)
418 flags |= CAMEL_MESSAGE_FORWARDED;
419 else if (g_strcmp0 (tokens[ii], "SEEN") == 0)
420 flags |= CAMEL_MESSAGE_SEEN;
421 else
422 g_warning (
423 "Unknown flag '%s' in %s",
424 tokens[ii], header_name);
427 g_strfreev (tokens);
429 folder = e_mail_session_uri_to_folder_sync (
430 session, folder_uri, 0, cancellable, error);
432 if (folder == NULL)
433 return FALSE;
435 camel_folder_set_message_flags (
436 folder, message_uid, flags, flags);
438 success = camel_folder_synchronize_message_sync (
439 folder, message_uid, cancellable, error);
441 g_object_unref (folder);
443 return success;
446 void
447 e_mail_session_handle_source_headers (EMailSession *session,
448 CamelMimeMessage *message,
449 gint io_priority,
450 GCancellable *cancellable,
451 GAsyncReadyCallback callback,
452 gpointer user_data)
454 GSimpleAsyncResult *simple;
455 AsyncContext *context;
457 g_return_if_fail (E_IS_MAIL_SESSION (session));
458 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
460 context = g_slice_new0 (AsyncContext);
461 context->message = g_object_ref (message);
463 simple = g_simple_async_result_new (
464 G_OBJECT (session), callback, user_data,
465 e_mail_session_handle_source_headers);
467 g_simple_async_result_set_check_cancellable (simple, cancellable);
469 g_simple_async_result_set_op_res_gpointer (
470 simple, context, (GDestroyNotify) async_context_free);
472 g_simple_async_result_run_in_thread (
473 simple, (GSimpleAsyncThreadFunc)
474 mail_session_handle_source_headers_thread,
475 io_priority, cancellable);
477 g_object_unref (simple);
480 gboolean
481 e_mail_session_handle_source_headers_finish (EMailSession *session,
482 GAsyncResult *result,
483 GError **error)
485 GSimpleAsyncResult *simple;
487 g_return_val_if_fail (
488 g_simple_async_result_is_valid (
489 result, G_OBJECT (session),
490 e_mail_session_handle_draft_headers), FALSE);
492 simple = G_SIMPLE_ASYNC_RESULT (result);
494 /* Assume success unless a GError is set. */
495 return !g_simple_async_result_propagate_error (simple, error);
498 static void
499 mail_session_send_to_thread (GSimpleAsyncResult *simple,
500 EMailSession *session,
501 GCancellable *cancellable)
503 AsyncContext *context;
504 CamelProvider *provider;
505 CamelFolder *folder = NULL;
506 CamelFolder *local_sent_folder;
507 CamelServiceConnectionStatus status;
508 GString *error_messages;
509 gboolean copy_to_sent = TRUE;
510 gboolean sent_message_saved = FALSE;
511 gboolean did_connect = FALSE;
512 guint ii;
513 GError *error = NULL;
515 context = g_simple_async_result_get_op_res_gpointer (simple);
517 if (camel_address_length (context->recipients) == 0)
518 goto skip_send;
520 /* Send the message to all recipients. */
522 if (context->transport == NULL) {
523 g_simple_async_result_set_error (
524 simple, CAMEL_SERVICE_ERROR,
525 CAMEL_SERVICE_ERROR_UNAVAILABLE,
526 _("No mail transport service available"));
527 return;
530 if (!e_mail_session_mark_service_used_sync (session, context->transport, cancellable)) {
531 g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
532 g_simple_async_result_take_error (simple, error);
533 return;
536 status = camel_service_get_connection_status (context->transport);
537 if (status != CAMEL_SERVICE_CONNECTED) {
538 EMailSession *session;
539 ESourceRegistry *registry;
540 ESource *source;
542 /* Make sure user will be asked for a password, in case he/she cancelled it */
543 session = E_MAIL_SESSION (camel_service_ref_session (context->transport));
544 registry = e_mail_session_get_registry (session);
545 source = e_source_registry_ref_source (registry, camel_service_get_uid (context->transport));
546 g_object_unref (session);
548 if (source) {
549 e_mail_session_emit_allow_auth_prompt (session, source);
550 g_object_unref (source);
553 did_connect = TRUE;
555 camel_service_connect_sync (context->transport, cancellable, &error);
557 if (error != NULL) {
558 g_simple_async_result_take_error (simple, error);
559 e_mail_session_unmark_service_used (session, context->transport);
560 return;
564 provider = camel_service_get_provider (context->transport);
566 if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
567 copy_to_sent = FALSE;
569 camel_transport_send_to_sync (
570 CAMEL_TRANSPORT (context->transport),
571 context->message, context->from,
572 context->recipients, &sent_message_saved, cancellable, &error);
574 if (did_connect) {
575 /* Disconnect regardless of error or cancellation,
576 * but be mindful of these conditions when calling
577 * camel_service_disconnect_sync(). */
578 if (g_cancellable_is_cancelled (cancellable)) {
579 camel_service_disconnect_sync (
580 context->transport, FALSE, NULL, NULL);
581 } else if (error != NULL) {
582 camel_service_disconnect_sync (
583 context->transport, FALSE, cancellable, NULL);
584 } else {
585 camel_service_disconnect_sync (
586 context->transport, TRUE, cancellable, &error);
590 e_mail_session_unmark_service_used (session, context->transport);
592 if (error != NULL) {
593 g_simple_async_result_take_error (simple, error);
594 return;
597 skip_send:
598 /* Post the message to requested folders. */
599 for (ii = 0; ii < context->post_to_uris->len; ii++) {
600 CamelFolder *folder;
601 const gchar *folder_uri;
603 folder_uri = g_ptr_array_index (context->post_to_uris, ii);
605 folder = e_mail_session_uri_to_folder_sync (
606 session, folder_uri, 0, cancellable, &error);
608 if (error != NULL) {
609 g_warn_if_fail (folder == NULL);
610 g_simple_async_result_take_error (simple, error);
611 return;
614 g_return_if_fail (CAMEL_IS_FOLDER (folder));
616 camel_operation_push_message (cancellable, _("Posting message to “%s”"), camel_folder_get_full_name (folder));
618 camel_folder_append_message_sync (
619 folder, context->message, context->info,
620 NULL, cancellable, &error);
622 camel_operation_pop_message (cancellable);
624 g_object_unref (folder);
626 if (error != NULL) {
627 g_simple_async_result_take_error (simple, error);
628 return;
632 /*** Post Processing ***/
634 /* This accumulates error messages during post-processing. */
635 error_messages = g_string_sized_new (256);
637 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
639 /* Run filters on the outgoing message. */
640 if (context->driver != NULL) {
641 CamelMessageFlags message_flags;
643 camel_filter_driver_filter_message (
644 context->driver, context->message, context->info,
645 NULL, NULL, NULL, "", cancellable, &error);
647 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
648 goto exit;
650 if (error != NULL) {
651 g_string_append_printf (
652 error_messages,
653 _("Failed to apply outgoing filters: %s"),
654 error->message);
655 g_clear_error (&error);
658 message_flags = camel_message_info_get_flags (context->info);
660 if (message_flags & CAMEL_MESSAGE_DELETED)
661 copy_to_sent = FALSE;
664 if (!copy_to_sent || sent_message_saved)
665 goto cleanup;
667 /* Append the sent message to a Sent folder. */
669 local_sent_folder =
670 e_mail_session_get_local_folder (
671 session, E_MAIL_LOCAL_FOLDER_SENT);
673 folder = e_mail_session_get_fcc_for_message_sync (
674 session, context->message, &copy_to_sent, cancellable, &error);
676 if (!copy_to_sent)
677 goto cleanup;
679 /* Sanity check. */
680 g_return_if_fail (
681 ((folder != NULL) && (error == NULL)) ||
682 ((folder == NULL) && (error != NULL)));
684 /* Append the message. */
685 if (folder != NULL) {
686 camel_operation_push_message (cancellable, _("Storing sent message to “%s”"), camel_folder_get_full_name (folder));
688 camel_folder_append_message_sync (
689 folder, context->message,
690 context->info, NULL, cancellable, &error);
692 camel_operation_pop_message (cancellable);
695 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
696 goto exit;
698 if (error == NULL)
699 goto cleanup;
701 if (folder != NULL && folder != local_sent_folder) {
702 const gchar *description;
704 description = camel_folder_get_description (folder);
706 if (error_messages->len > 0)
707 g_string_append (error_messages, "\n\n");
708 g_string_append_printf (
709 error_messages,
710 _("Failed to append to %s: %s\n"
711 "Appending to local “Sent” folder instead."),
712 description, error->message);
715 /* If appending to a remote Sent folder failed,
716 * try appending to the local Sent folder. */
717 if (folder != local_sent_folder) {
719 g_clear_error (&error);
721 camel_operation_push_message (cancellable, _("Storing sent message to “%s”"), camel_folder_get_full_name (local_sent_folder));
723 camel_folder_append_message_sync (
724 local_sent_folder, context->message,
725 context->info, NULL, cancellable, &error);
727 camel_operation_pop_message (cancellable);
730 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
731 goto exit;
733 /* We can't even append to the local Sent folder?
734 * In that case just leave the message in Outbox. */
735 if (error != NULL) {
736 if (error_messages->len > 0)
737 g_string_append (error_messages, "\n\n");
738 g_string_append_printf (
739 error_messages,
740 _("Failed to append to local “Sent” folder: %s"),
741 error->message);
742 g_clear_error (&error);
743 goto exit;
746 cleanup:
748 /* The send operation was successful; ignore cleanup errors. */
750 /* Mark the draft message for deletion, if present. */
751 e_mail_session_handle_draft_headers_sync (
752 session, context->message, cancellable, &error);
753 if (error != NULL) {
754 g_warning ("%s", error->message);
755 g_clear_error (&error);
758 /* Set flags on the original source message, if present.
759 * Source message refers to the message being forwarded
760 * or replied to. */
761 e_mail_session_handle_source_headers_sync (
762 session, context->message, cancellable, &error);
763 if (error != NULL) {
764 g_warning ("%s", error->message);
765 g_clear_error (&error);
768 exit:
770 /* If we were cancelled, disregard any other errors. */
771 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
772 g_simple_async_result_take_error (simple, error);
774 /* Stuff the accumulated error messages in a GError. */
775 } else if (error_messages->len > 0) {
776 g_simple_async_result_set_error (
777 simple, E_MAIL_ERROR,
778 E_MAIL_ERROR_POST_PROCESSING,
779 "%s", error_messages->str);
782 /* Synchronize the Sent folder. */
783 if (folder != NULL) {
784 camel_folder_synchronize_sync (
785 folder, FALSE, cancellable, NULL);
786 g_object_unref (folder);
789 g_string_free (error_messages, TRUE);
792 void
793 e_mail_session_send_to (EMailSession *session,
794 CamelMimeMessage *message,
795 gint io_priority,
796 GCancellable *cancellable,
797 CamelFilterGetFolderFunc get_folder_func,
798 gpointer get_folder_data,
799 GAsyncReadyCallback callback,
800 gpointer user_data)
802 GSimpleAsyncResult *simple;
803 AsyncContext *context;
804 CamelAddress *from;
805 CamelAddress *recipients;
806 CamelMedium *medium;
807 CamelMessageInfo *info;
808 CamelService *transport;
809 GPtrArray *post_to_uris;
810 CamelNameValueArray *xev_headers;
811 const gchar *resent_from;
812 guint ii, len;
813 GError *error = NULL;
815 g_return_if_fail (E_IS_MAIL_SESSION (session));
816 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
818 medium = CAMEL_MEDIUM (message);
820 camel_medium_set_header (medium, "X-Mailer", X_MAILER);
822 /* Do this before removing "X-Evolution" headers. */
823 transport = e_mail_session_ref_transport_for_message (
824 session, message);
826 xev_headers = mail_tool_remove_xevolution_headers (message);
827 len = camel_name_value_array_get_length (xev_headers);
829 /* Extract directives from X-Evolution headers. */
831 post_to_uris = g_ptr_array_new ();
832 for (ii = 0; ii < len; ii++) {
833 const gchar *header_name = NULL, *header_value = NULL;
834 gchar *folder_uri;
836 if (!camel_name_value_array_get (xev_headers, ii, &header_name, &header_value) ||
837 !header_name ||
838 g_ascii_strcasecmp (header_name, "X-Evolution-PostTo") != 0)
839 continue;
841 folder_uri = g_strstrip (g_strdup (header_value));
842 g_ptr_array_add (post_to_uris, folder_uri);
845 /* Collect sender and recipients from headers. */
847 from = (CamelAddress *) camel_internet_address_new ();
848 recipients = (CamelAddress *) camel_internet_address_new ();
849 resent_from = camel_medium_get_header (medium, "Resent-From");
851 if (resent_from != NULL) {
852 const CamelInternetAddress *addr;
853 const gchar *type;
855 camel_address_decode (from, resent_from);
857 type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
858 addr = camel_mime_message_get_recipients (message, type);
859 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
861 type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
862 addr = camel_mime_message_get_recipients (message, type);
863 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
865 type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
866 addr = camel_mime_message_get_recipients (message, type);
867 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
869 } else {
870 const CamelInternetAddress *addr;
871 const gchar *type;
873 addr = camel_mime_message_get_from (message);
874 camel_address_copy (from, CAMEL_ADDRESS (addr));
876 type = CAMEL_RECIPIENT_TYPE_TO;
877 addr = camel_mime_message_get_recipients (message, type);
878 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
880 type = CAMEL_RECIPIENT_TYPE_CC;
881 addr = camel_mime_message_get_recipients (message, type);
882 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
884 type = CAMEL_RECIPIENT_TYPE_BCC;
885 addr = camel_mime_message_get_recipients (message, type);
886 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
889 /* Miscellaneous preparations. */
891 info = camel_message_info_new_from_headers (NULL, camel_medium_get_headers (CAMEL_MEDIUM (message)));
893 camel_message_info_set_size (info, camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (message), cancellable, NULL));
894 camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN |
895 (camel_mime_message_has_attachment (message) ? CAMEL_MESSAGE_ATTACHMENTS : 0), ~0);
897 /* expand, or remove empty, group addresses */
898 em_utils_expand_groups (CAMEL_INTERNET_ADDRESS (recipients));
900 /* The rest of the processing happens in a thread. */
902 context = g_slice_new0 (AsyncContext);
903 context->message = g_object_ref (message);
904 context->io_priority = io_priority;
905 context->from = from;
906 context->recipients = recipients;
907 context->info = info;
908 context->xev_headers = xev_headers;
909 context->post_to_uris = post_to_uris;
910 context->transport = transport;
912 if (G_IS_CANCELLABLE (cancellable))
913 context->cancellable = g_object_ref (cancellable);
915 /* Failure here emits a runtime warning but is non-fatal. */
916 context->driver = camel_session_get_filter_driver (CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, NULL, &error);
917 if (context->driver != NULL && get_folder_func)
918 camel_filter_driver_set_folder_func (
919 context->driver, get_folder_func, get_folder_data);
920 if (error != NULL) {
921 g_warn_if_fail (context->driver == NULL);
922 g_warning ("%s", error->message);
923 g_error_free (error);
926 /* This gets popped in async_context_free(). */
927 camel_operation_push_message (
928 context->cancellable, _("Sending message"));
930 simple = g_simple_async_result_new (
931 G_OBJECT (session), callback,
932 user_data, e_mail_session_send_to);
934 g_simple_async_result_set_check_cancellable (simple, cancellable);
936 g_simple_async_result_set_op_res_gpointer (
937 simple, context, (GDestroyNotify) async_context_free);
939 g_simple_async_result_run_in_thread (
940 simple, (GSimpleAsyncThreadFunc)
941 mail_session_send_to_thread,
942 context->io_priority,
943 context->cancellable);
945 g_object_unref (simple);
948 gboolean
949 e_mail_session_send_to_finish (EMailSession *session,
950 GAsyncResult *result,
951 GError **error)
953 GSimpleAsyncResult *simple;
955 g_return_val_if_fail (
956 g_simple_async_result_is_valid (
957 result, G_OBJECT (session),
958 e_mail_session_send_to), FALSE);
960 simple = G_SIMPLE_ASYNC_RESULT (result);
962 /* Assume success unless a GError is set. */
963 return !g_simple_async_result_propagate_error (simple, error);
966 /* Helper for e_mail_session_get_fcc_for_message_sync() */
967 static CamelFolder *
968 mail_session_try_uri_to_folder (EMailSession *session,
969 const gchar *folder_uri,
970 GCancellable *cancellable,
971 GError **error)
973 CamelFolder *folder;
974 GError *local_error = NULL;
976 folder = e_mail_session_uri_to_folder_sync (
977 session, folder_uri, 0, cancellable, &local_error);
979 /* Sanity check. */
980 g_return_val_if_fail (
981 ((folder != NULL) && (local_error == NULL)) ||
982 ((folder == NULL) && (local_error != NULL)), NULL);
984 /* Disregard specific errors. */
986 /* Invalid URI. */
987 if (g_error_matches (
988 local_error, CAMEL_FOLDER_ERROR,
989 CAMEL_FOLDER_ERROR_INVALID))
990 g_clear_error (&local_error);
992 /* Folder not found. */
993 if (g_error_matches (
994 local_error, CAMEL_STORE_ERROR,
995 CAMEL_STORE_ERROR_NO_FOLDER))
996 g_clear_error (&local_error);
998 if (local_error != NULL)
999 g_propagate_error (error, local_error);
1001 return folder;
1004 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1005 static CamelFolder *
1006 mail_session_ref_origin_folder (EMailSession *session,
1007 CamelMimeMessage *message,
1008 GCancellable *cancellable,
1009 GError **error)
1011 CamelMedium *medium;
1012 const gchar *header_name;
1013 const gchar *header_value;
1015 medium = CAMEL_MEDIUM (message);
1017 /* Check that a "X-Evolution-Source-Flags" header is present
1018 * and its value does not contain the substring "FORWARDED". */
1020 header_name = "X-Evolution-Source-Flags";
1021 header_value = camel_medium_get_header (medium, header_name);
1023 if (header_value == NULL)
1024 return NULL;
1026 if (strstr (header_value, "FORWARDED") != NULL)
1027 return NULL;
1029 /* Check that a "X-Evolution-Source-Message" header is present. */
1031 header_name = "X-Evolution-Source-Message";
1032 header_value = camel_medium_get_header (medium, header_name);
1034 if (header_value == NULL)
1035 return NULL;
1037 /* Check that a "X-Evolution-Source-Folder" header is present.
1038 * Its value specifies the origin folder as a folder URI. */
1040 header_name = "X-Evolution-Source-Folder";
1041 header_value = camel_medium_get_header (medium, header_name);
1043 if (header_value == NULL)
1044 return NULL;
1046 /* This may return NULL without setting a GError. */
1047 return mail_session_try_uri_to_folder (
1048 session, header_value, cancellable, error);
1051 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1052 static CamelFolder *
1053 mail_session_ref_fcc_from_identity (EMailSession *session,
1054 ESource *source,
1055 CamelMimeMessage *message,
1056 gboolean *out_use_sent_folder,
1057 GCancellable *cancellable,
1058 GError **error)
1060 ESourceRegistry *registry;
1061 ESourceMailSubmission *extension;
1062 CamelFolder *folder = NULL;
1063 const gchar *extension_name;
1064 gchar *folder_uri;
1065 gboolean use_sent_folder;
1067 registry = e_mail_session_get_registry (session);
1068 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
1070 if (source == NULL)
1071 return NULL;
1073 if (!e_source_registry_check_enabled (registry, source))
1074 return NULL;
1076 if (!e_source_has_extension (source, extension_name))
1077 return NULL;
1079 extension = e_source_get_extension (source, extension_name);
1080 use_sent_folder = e_source_mail_submission_get_use_sent_folder (extension);
1082 if (out_use_sent_folder)
1083 *out_use_sent_folder = use_sent_folder;
1085 if (!use_sent_folder)
1086 return NULL;
1088 if (e_source_mail_submission_get_replies_to_origin_folder (extension)) {
1089 GError *local_error = NULL;
1091 /* This may return NULL without setting a GError. */
1092 folder = mail_session_ref_origin_folder (
1093 session, message, cancellable, &local_error);
1095 if (local_error != NULL) {
1096 g_warn_if_fail (folder == NULL);
1097 g_propagate_error (error, local_error);
1098 return NULL;
1102 folder_uri = e_source_mail_submission_dup_sent_folder (extension);
1104 if (folder_uri != NULL && folder == NULL) {
1105 /* This may return NULL without setting a GError. */
1106 folder = mail_session_try_uri_to_folder (
1107 session, folder_uri, cancellable, error);
1110 g_free (folder_uri);
1112 return folder;
1115 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1116 static CamelFolder *
1117 mail_session_ref_fcc_from_x_identity (EMailSession *session,
1118 CamelMimeMessage *message,
1119 gboolean *out_use_sent_folder,
1120 GCancellable *cancellable,
1121 GError **error)
1123 ESource *source;
1124 ESourceRegistry *registry;
1125 CamelFolder *folder;
1126 CamelMedium *medium;
1127 const gchar *header_name;
1128 const gchar *header_value;
1129 gchar *uid;
1131 medium = CAMEL_MEDIUM (message);
1132 header_name = "X-Evolution-Identity";
1133 header_value = camel_medium_get_header (medium, header_name);
1135 if (header_value == NULL)
1136 return NULL;
1138 uid = g_strstrip (g_strdup (header_value));
1140 registry = e_mail_session_get_registry (session);
1141 source = e_source_registry_ref_source (registry, uid);
1143 /* This may return NULL without setting a GError. */
1144 folder = mail_session_ref_fcc_from_identity (
1145 session, source, message, out_use_sent_folder, cancellable, error);
1147 g_clear_object (&source);
1149 g_free (uid);
1151 return folder;
1154 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1155 static CamelFolder *
1156 mail_session_ref_fcc_from_x_fcc (EMailSession *session,
1157 CamelMimeMessage *message,
1158 GCancellable *cancellable,
1159 GError **error)
1161 CamelMedium *medium;
1162 const gchar *header_name;
1163 const gchar *header_value;
1165 medium = CAMEL_MEDIUM (message);
1166 header_name = "X-Evolution-Fcc";
1167 header_value = camel_medium_get_header (medium, header_name);
1169 if (header_value == NULL)
1170 return NULL;
1172 /* This may return NULL without setting a GError. */
1173 return mail_session_try_uri_to_folder (
1174 session, header_value, cancellable, error);
1177 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1178 static CamelFolder *
1179 mail_session_ref_fcc_from_default_identity (EMailSession *session,
1180 CamelMimeMessage *message,
1181 gboolean *out_use_sent_folder,
1182 GCancellable *cancellable,
1183 GError **error)
1185 ESource *source;
1186 ESourceRegistry *registry;
1187 CamelFolder *folder;
1189 registry = e_mail_session_get_registry (session);
1190 source = e_source_registry_ref_default_mail_identity (registry);
1192 /* This may return NULL without setting a GError. */
1193 folder = mail_session_ref_fcc_from_identity (
1194 session, source, message, out_use_sent_folder, cancellable, error);
1196 g_clear_object (&source);
1198 return folder;
1202 * e_mail_session_get_fcc_for_message_sync:
1203 * @session: an #EMailSession
1204 * @message: a #CamelMimeMessage
1205 * @out_use_sent_folder: (out) (nullable): optional return location to store
1206 * corresponding use-sent-folder for the mail account, or %NULL
1207 * @cancellable: optional #GCancellable object, or %NULL
1208 * @error: return location for a #GError, or %NULL
1210 * Obtains the preferred "carbon-copy" folder (a.k.a Fcc) for @message
1211 * by first checking @message for an "X-Evolution-Identity" header, and
1212 * then an "X-Evolution-Fcc" header. Failing that, the function checks
1213 * the default mail identity (if available), and failing even that, the
1214 * function falls back to the Sent folder from the built-in mail store.
1216 * Where applicable, the function attempts to honor the
1217 * #ESourceMailSubmission:replies-to-origin-folder preference.
1219 * The returned #CamelFolder is referenced for thread-safety and must be
1220 * unreferenced with g_object_unref() when finished with it.
1222 * If a non-recoverable error occurs, the function sets @error and returns
1223 * %NULL. It returns %NULL without setting @error when the mail account
1224 * has set to not use sent folder, in which case it indicates that
1225 * in @out_use_sent_folder too.
1227 * Returns: a #CamelFolder, or %NULL
1229 CamelFolder *
1230 e_mail_session_get_fcc_for_message_sync (EMailSession *session,
1231 CamelMimeMessage *message,
1232 gboolean *out_use_sent_folder,
1233 GCancellable *cancellable,
1234 GError **error)
1236 CamelFolder *folder = NULL;
1237 gboolean use_sent_folder = TRUE;
1239 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1240 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1242 if (out_use_sent_folder)
1243 *out_use_sent_folder = TRUE;
1245 /* Check for "X-Evolution-Identity" header. */
1246 if (folder == NULL) {
1247 GError *local_error = NULL;
1249 /* This may return NULL without setting a GError. */
1250 folder = mail_session_ref_fcc_from_x_identity (
1251 session, message, &use_sent_folder, cancellable, &local_error);
1253 if (local_error != NULL) {
1254 g_warn_if_fail (folder == NULL);
1255 g_propagate_error (error, local_error);
1256 return NULL;
1259 if (!use_sent_folder) {
1260 if (out_use_sent_folder)
1261 *out_use_sent_folder = use_sent_folder;
1262 return NULL;
1266 /* Check for "X-Evolution-Fcc" header. */
1267 if (folder == NULL) {
1268 GError *local_error = NULL;
1270 /* This may return NULL without setting a GError. */
1271 folder = mail_session_ref_fcc_from_x_fcc (
1272 session, message, cancellable, &local_error);
1274 if (local_error != NULL) {
1275 g_warn_if_fail (folder == NULL);
1276 g_propagate_error (error, local_error);
1277 return NULL;
1281 /* Check the default mail identity. */
1282 if (folder == NULL) {
1283 GError *local_error = NULL;
1285 /* This may return NULL without setting a GError. */
1286 folder = mail_session_ref_fcc_from_default_identity (
1287 session, message, &use_sent_folder, cancellable, &local_error);
1289 if (local_error != NULL) {
1290 g_warn_if_fail (folder == NULL);
1291 g_propagate_error (error, local_error);
1292 return NULL;
1295 if (!use_sent_folder) {
1296 if (out_use_sent_folder)
1297 *out_use_sent_folder = use_sent_folder;
1298 return NULL;
1302 /* Last resort - local Sent folder. */
1303 if (folder == NULL) {
1304 folder = e_mail_session_get_local_folder (
1305 session, E_MAIL_LOCAL_FOLDER_SENT);
1306 g_object_ref (folder);
1309 return folder;
1312 /* Helper for e_mail_session_get_fcc_for_message() */
1313 static void
1314 mail_session_get_fcc_for_message_thread (GSimpleAsyncResult *simple,
1315 GObject *source_object,
1316 GCancellable *cancellable)
1318 AsyncContext *async_context;
1319 GError *local_error = NULL;
1321 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1323 async_context->folder =
1324 e_mail_session_get_fcc_for_message_sync (
1325 E_MAIL_SESSION (source_object),
1326 async_context->message,
1327 &async_context->use_sent_folder,
1328 cancellable, &local_error);
1330 if (local_error != NULL)
1331 g_simple_async_result_take_error (simple, local_error);
1335 * e_mail_session_get_fcc_for_message:
1336 * @session: an #EMailSession
1337 * @message: a #CamelMimeMessage
1338 * @io_priority: the I/O priority of the request
1339 * @cancellable: optional #GCancellable object, or %NULL
1340 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1341 * @user_data: data to pass to the callback function
1343 * Asynchronously obtains the preferred "carbon-copy" folder (a.k.a Fcc) for
1344 * @message by first checking @message for an "X-Evolution-Identity" header,
1345 * and then an "X-Evolution-Fcc" header. Failing that, the function checks
1346 * the default mail identity (if available), and failing even that, the
1347 * function falls back to the Sent folder from the built-in mail store.
1349 * Where applicable, the function attempts to honor the
1350 * #ESourceMailSubmission:replies-to-origin-folder preference.
1352 * When the operation is finished, @callback will be called. You can then
1353 * call e_mail_session_get_fcc_for_message_finish() to get the result of the
1354 * operation.
1356 void
1357 e_mail_session_get_fcc_for_message (EMailSession *session,
1358 CamelMimeMessage *message,
1359 gint io_priority,
1360 GCancellable *cancellable,
1361 GAsyncReadyCallback callback,
1362 gpointer user_data)
1364 GSimpleAsyncResult *simple;
1365 AsyncContext *async_context;
1367 g_return_if_fail (E_IS_MAIL_SESSION (session));
1368 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1370 async_context = g_slice_new0 (AsyncContext);
1371 async_context->message = g_object_ref (message);
1373 simple = g_simple_async_result_new (
1374 G_OBJECT (session), callback, user_data,
1375 e_mail_session_get_fcc_for_message);
1377 g_simple_async_result_set_check_cancellable (simple, cancellable);
1379 g_simple_async_result_set_op_res_gpointer (
1380 simple, async_context, (GDestroyNotify) async_context_free);
1382 g_simple_async_result_run_in_thread (
1383 simple, mail_session_get_fcc_for_message_thread,
1384 io_priority, cancellable);
1386 g_object_unref (simple);
1390 * e_mail_session_get_fcc_for_message_finish:
1391 * @session: an #EMailSession
1392 * @result: a #GAsyncResult
1393 * @out_use_sent_folder: (out) (nullable): optional return location to store
1394 * corresponding use-sent-folder for the mail account, or %NULL
1395 * @error: return location for a #GError, or %NULL
1397 * Finishes the operation started with e_mail_session_get_fcc_for_message().
1399 * The returned #CamelFolder is referenced for thread-safety and must be
1400 * unreferenced with g_object_unref() when finished with it.
1402 * If a non-recoverable error occurred, the function sets @error and
1403 * returns %NULL. It returns %NULL without setting @error when the mail account
1404 * has set to not use sent folder, in which case it indicates that
1405 * in @out_use_sent_folder too.
1407 * Returns: a #CamelFolder, or %NULL
1409 CamelFolder *
1410 e_mail_session_get_fcc_for_message_finish (EMailSession *session,
1411 GAsyncResult *result,
1412 gboolean *out_use_sent_folder,
1413 GError **error)
1415 GSimpleAsyncResult *simple;
1416 AsyncContext *async_context;
1418 g_return_val_if_fail (
1419 g_simple_async_result_is_valid (
1420 result, G_OBJECT (session),
1421 e_mail_session_get_fcc_for_message), NULL);
1423 simple = G_SIMPLE_ASYNC_RESULT (result);
1424 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1426 if (g_simple_async_result_propagate_error (simple, error))
1427 return NULL;
1429 if (out_use_sent_folder)
1430 *out_use_sent_folder = async_context->use_sent_folder;
1432 if (!async_context->use_sent_folder) {
1433 g_return_val_if_fail (async_context->folder == NULL, NULL);
1434 return NULL;
1437 g_return_val_if_fail (async_context->folder != NULL, NULL);
1439 return g_object_ref (async_context->folder);
1443 * e_mail_session_ref_transport:
1444 * @session: an #EMailSession
1445 * @transport_uid: the UID of a mail transport
1447 * Returns the transport #CamelService instance for @transport_uid,
1448 * verifying first that the @transport_uid is indeed a mail transport and
1449 * that the corresponding #ESource is enabled. If these checks fail, the
1450 * function returns %NULL.
1452 * The returned #CamelService is referenced for thread-safety and must be
1453 * unreferenced with g_object_unref() when finished with it.
1455 * Returns: a #CamelService, or %NULL
1457 CamelService *
1458 e_mail_session_ref_transport (EMailSession *session,
1459 const gchar *transport_uid)
1461 ESourceRegistry *registry;
1462 ESource *source = NULL;
1463 CamelService *transport = NULL;
1464 const gchar *extension_name;
1466 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1467 g_return_val_if_fail (transport_uid != NULL, NULL);
1469 registry = e_mail_session_get_registry (session);
1470 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
1472 source = e_source_registry_ref_source (registry, transport_uid);
1474 if (source == NULL)
1475 goto exit;
1477 if (!e_source_registry_check_enabled (registry, source))
1478 goto exit;
1480 if (!e_source_has_extension (source, extension_name))
1481 goto exit;
1483 transport = camel_session_ref_service (
1484 CAMEL_SESSION (session), transport_uid);
1486 /* Sanity check. */
1487 if (transport != NULL)
1488 g_warn_if_fail (CAMEL_IS_TRANSPORT (transport));
1490 exit:
1491 g_clear_object (&source);
1493 return transport;
1496 /* Helper for e_mail_session_ref_default_transport()
1497 * and mail_session_ref_transport_from_x_identity(). */
1498 static CamelService *
1499 mail_session_ref_transport_for_identity (EMailSession *session,
1500 ESource *source)
1502 ESourceRegistry *registry;
1503 ESourceMailSubmission *extension;
1504 CamelService *transport = NULL;
1505 const gchar *extension_name;
1506 gchar *uid;
1508 registry = e_mail_session_get_registry (session);
1509 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
1511 if (source == NULL)
1512 return NULL;
1514 if (!e_source_registry_check_enabled (registry, source))
1515 return NULL;
1517 if (!e_source_has_extension (source, extension_name))
1518 return NULL;
1520 extension = e_source_get_extension (source, extension_name);
1521 uid = e_source_mail_submission_dup_transport_uid (extension);
1523 if (uid != NULL) {
1524 transport = e_mail_session_ref_transport (session, uid);
1525 g_free (uid);
1528 return transport;
1532 * e_mail_session_ref_default_transport:
1533 * @session: an #EMailSession
1535 * Returns the default transport #CamelService instance according to
1536 * #ESourceRegistry's #ESourceRegistry:default-mail-identity setting,
1537 * verifying first that the #ESourceMailSubmission:transport-uid named by
1538 * the #ESourceRegistry:default-mail-identity is indeed a mail transport,
1539 * and that the corresponding #ESource is enabled. If these checks fail,
1540 * the function returns %NULL.
1542 * The returned #CamelService is referenced for thread-safety and must be
1543 * unreferenced with g_object_unref() when finished with it.
1545 * Returns: a #CamelService, or %NULL
1547 CamelService *
1548 e_mail_session_ref_default_transport (EMailSession *session)
1550 ESource *source;
1551 ESourceRegistry *registry;
1552 CamelService *transport;
1554 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1556 registry = e_mail_session_get_registry (session);
1557 source = e_source_registry_ref_default_mail_identity (registry);
1558 transport = mail_session_ref_transport_for_identity (session, source);
1559 g_clear_object (&source);
1561 return transport;
1564 /* Helper for e_mail_session_ref_transport_for_message() */
1565 static CamelService *
1566 mail_session_ref_transport_from_x_identity (EMailSession *session,
1567 CamelMimeMessage *message)
1569 ESource *source;
1570 ESourceRegistry *registry;
1571 CamelMedium *medium;
1572 CamelService *transport;
1573 const gchar *header_name;
1574 const gchar *header_value;
1575 gchar *uid;
1577 medium = CAMEL_MEDIUM (message);
1578 header_name = "X-Evolution-Identity";
1579 header_value = camel_medium_get_header (medium, header_name);
1581 if (header_value == NULL)
1582 return NULL;
1584 uid = g_strstrip (g_strdup (header_value));
1586 registry = e_mail_session_get_registry (session);
1587 source = e_source_registry_ref_source (registry, uid);
1588 transport = mail_session_ref_transport_for_identity (session, source);
1589 g_clear_object (&source);
1591 g_free (uid);
1593 return transport;
1596 /* Helper for e_mail_session_ref_transport_for_message() */
1597 static CamelService *
1598 mail_session_ref_transport_from_x_transport (EMailSession *session,
1599 CamelMimeMessage *message)
1601 CamelMedium *medium;
1602 CamelService *transport;
1603 const gchar *header_name;
1604 const gchar *header_value;
1605 gchar *uid;
1607 medium = CAMEL_MEDIUM (message);
1608 header_name = "X-Evolution-Transport";
1609 header_value = camel_medium_get_header (medium, header_name);
1611 if (header_value == NULL)
1612 return NULL;
1614 uid = g_strstrip (g_strdup (header_value));
1616 transport = e_mail_session_ref_transport (session, uid);
1618 g_free (uid);
1620 return transport;
1624 * e_mail_session_ref_transport_for_message:
1625 * @session: an #EMailSession
1626 * @message: a #CamelMimeMessage
1628 * Returns the preferred transport #CamelService instance for @message by
1629 * first checking @message for an "X-Evolution-Identity" header, and then
1630 * an "X-Evolution-Transport" header. Failing that, the function returns
1631 * the default transport #CamelService instance (if available).
1633 * The returned #CamelService is referenced for thread-safety and must be
1634 * unreferenced with g_object_unref() when finished with it.
1636 * Returns: a #CamelService, or %NULL
1638 CamelService *
1639 e_mail_session_ref_transport_for_message (EMailSession *session,
1640 CamelMimeMessage *message)
1642 CamelService *transport = NULL;
1644 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1645 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1647 /* Check for "X-Evolution-Identity" header. */
1648 if (transport == NULL)
1649 transport = mail_session_ref_transport_from_x_identity (
1650 session, message);
1652 /* Check for "X-Evolution-Transport" header. */
1653 if (transport == NULL)
1654 transport = mail_session_ref_transport_from_x_transport (
1655 session, message);
1657 /* Fall back to the default mail transport. */
1658 if (transport == NULL)
1659 transport = e_mail_session_ref_default_transport (session);
1661 return transport;