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
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/>.
22 #include "e-mail-session-utils.h"
24 #include <glib/gi18n-lib.h>
25 #include <libedataserver/libedataserver.h>
27 #include <libemail-engine/e-mail-folder-utils.h>
28 #include <libemail-engine/e-mail-utils.h>
29 #include <libemail-engine/mail-tools.h>
31 /* X-Mailer header value */
32 #define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
34 /* FIXME: Temporary - remove this after we move filter/ to eds */
35 #define E_FILTER_SOURCE_OUTGOING "outgoing"
37 typedef struct _AsyncContext AsyncContext
;
39 struct _AsyncContext
{
42 CamelMimeMessage
*message
;
43 CamelMessageInfo
*info
;
46 CamelAddress
*recipients
;
48 CamelFilterDriver
*driver
;
50 CamelService
*transport
;
52 GCancellable
*cancellable
;
55 /* X-Evolution headers */
56 struct _camel_header_raw
*xev
;
58 GPtrArray
*post_to_uris
;
60 EMailLocalFolder local_id
;
67 async_context_free (AsyncContext
*context
)
69 if (context
->folder
!= NULL
)
70 g_object_unref (context
->folder
);
72 if (context
->message
!= NULL
)
73 g_object_unref (context
->message
);
75 if (context
->info
!= NULL
)
76 camel_message_info_unref (context
->info
);
78 if (context
->from
!= NULL
)
79 g_object_unref (context
->from
);
81 if (context
->recipients
!= NULL
)
82 g_object_unref (context
->recipients
);
84 if (context
->driver
!= NULL
)
85 g_object_unref (context
->driver
);
87 if (context
->transport
!= NULL
)
88 g_object_unref (context
->transport
);
90 if (context
->cancellable
!= NULL
) {
91 camel_operation_pop_message (context
->cancellable
);
92 g_object_unref (context
->cancellable
);
95 if (context
->xev
!= NULL
)
96 camel_header_raw_clear (&context
->xev
);
98 if (context
->post_to_uris
!= NULL
) {
100 context
->post_to_uris
, (GFunc
) g_free
, NULL
);
101 g_ptr_array_free (context
->post_to_uris
, TRUE
);
104 g_free (context
->folder_uri
);
105 g_free (context
->message_uid
);
107 g_slice_free (AsyncContext
, context
);
111 e_mail_error_quark (void)
113 static GQuark quark
= 0;
115 if (G_UNLIKELY (quark
== 0)) {
116 const gchar
*string
= "e-mail-error-quark";
117 quark
= g_quark_from_static_string (string
);
124 mail_session_append_to_local_folder_thread (GSimpleAsyncResult
*simple
,
126 GCancellable
*cancellable
)
128 AsyncContext
*context
;
129 GError
*error
= NULL
;
131 context
= g_simple_async_result_get_op_res_gpointer (simple
);
133 e_mail_session_append_to_local_folder_sync (
134 E_MAIL_SESSION (object
),
135 context
->local_id
, context
->message
,
136 context
->info
, &context
->message_uid
,
137 cancellable
, &error
);
140 g_simple_async_result_take_error (simple
, error
);
144 e_mail_session_append_to_local_folder_sync (EMailSession
*session
,
145 EMailLocalFolder local_id
,
146 CamelMimeMessage
*message
,
147 CamelMessageInfo
*info
,
148 gchar
**appended_uid
,
149 GCancellable
*cancellable
,
153 const gchar
*folder_uri
;
154 gboolean success
= FALSE
;
156 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), FALSE
);
157 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), FALSE
);
159 folder_uri
= e_mail_session_get_local_folder_uri (session
, local_id
);
160 g_return_val_if_fail (folder_uri
!= NULL
, FALSE
);
162 folder
= e_mail_session_uri_to_folder_sync (
163 session
, folder_uri
, CAMEL_STORE_FOLDER_CREATE
,
166 if (folder
!= NULL
) {
167 success
= e_mail_folder_append_message_sync (
168 folder
, message
, info
, appended_uid
,
170 g_object_unref (folder
);
177 e_mail_session_append_to_local_folder (EMailSession
*session
,
178 EMailLocalFolder local_id
,
179 CamelMimeMessage
*message
,
180 CamelMessageInfo
*info
,
182 GCancellable
*cancellable
,
183 GAsyncReadyCallback callback
,
186 GSimpleAsyncResult
*simple
;
187 AsyncContext
*context
;
189 g_return_if_fail (E_IS_MAIL_SESSION (session
));
190 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
192 context
= g_slice_new0 (AsyncContext
);
193 context
->local_id
= local_id
;
194 context
->message
= g_object_ref (message
);
197 context
->info
= camel_message_info_ref (info
);
199 simple
= g_simple_async_result_new (
200 G_OBJECT (session
), callback
, user_data
,
201 e_mail_session_append_to_local_folder
);
203 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
205 g_simple_async_result_set_op_res_gpointer (
206 simple
, context
, (GDestroyNotify
) async_context_free
);
208 g_simple_async_result_run_in_thread (
209 simple
, mail_session_append_to_local_folder_thread
,
210 io_priority
, cancellable
);
212 g_object_unref (simple
);
216 e_mail_session_append_to_local_folder_finish (EMailSession
*session
,
217 GAsyncResult
*result
,
218 gchar
**appended_uid
,
221 GSimpleAsyncResult
*simple
;
222 AsyncContext
*context
;
224 g_return_val_if_fail (
225 g_simple_async_result_is_valid (
226 result
, G_OBJECT (session
),
227 e_mail_session_append_to_local_folder
), FALSE
);
229 simple
= G_SIMPLE_ASYNC_RESULT (result
);
230 context
= g_simple_async_result_get_op_res_gpointer (simple
);
232 if (appended_uid
!= NULL
) {
233 *appended_uid
= context
->message_uid
;
234 context
->message_uid
= NULL
;
237 /* Assume success unless a GError is set. */
238 return !g_simple_async_result_propagate_error (simple
, error
);
242 mail_session_handle_draft_headers_thread (GSimpleAsyncResult
*simple
,
243 EMailSession
*session
,
244 GCancellable
*cancellable
)
246 AsyncContext
*context
;
247 GError
*error
= NULL
;
249 context
= g_simple_async_result_get_op_res_gpointer (simple
);
251 e_mail_session_handle_draft_headers_sync (
252 session
, context
->message
, cancellable
, &error
);
255 g_simple_async_result_take_error (simple
, error
);
259 e_mail_session_handle_draft_headers_sync (EMailSession
*session
,
260 CamelMimeMessage
*message
,
261 GCancellable
*cancellable
,
266 const gchar
*folder_uri
;
267 const gchar
*message_uid
;
268 const gchar
*header_name
;
271 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), FALSE
);
272 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), FALSE
);
274 medium
= CAMEL_MEDIUM (message
);
276 header_name
= "X-Evolution-Draft-Folder";
277 folder_uri
= camel_medium_get_header (medium
, header_name
);
279 header_name
= "X-Evolution-Draft-Message";
280 message_uid
= camel_medium_get_header (medium
, header_name
);
282 /* Don't report errors about missing X-Evolution-Draft
283 * headers. These headers are optional, so their absence
284 * is handled by doing nothing. */
285 if (folder_uri
== NULL
|| message_uid
== NULL
)
288 folder
= e_mail_session_uri_to_folder_sync (
289 session
, folder_uri
, 0, cancellable
, error
);
294 camel_folder_set_message_flags (
296 CAMEL_MESSAGE_DELETED
| CAMEL_MESSAGE_SEEN
,
297 CAMEL_MESSAGE_DELETED
| CAMEL_MESSAGE_SEEN
);
299 success
= camel_folder_synchronize_message_sync (
300 folder
, message_uid
, cancellable
, error
);
302 g_object_unref (folder
);
308 e_mail_session_handle_draft_headers (EMailSession
*session
,
309 CamelMimeMessage
*message
,
311 GCancellable
*cancellable
,
312 GAsyncReadyCallback callback
,
315 GSimpleAsyncResult
*simple
;
316 AsyncContext
*context
;
318 g_return_if_fail (E_IS_MAIL_SESSION (session
));
319 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
321 context
= g_slice_new0 (AsyncContext
);
322 context
->message
= g_object_ref (message
);
324 simple
= g_simple_async_result_new (
325 G_OBJECT (session
), callback
, user_data
,
326 e_mail_session_handle_draft_headers
);
328 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
330 g_simple_async_result_set_op_res_gpointer (
331 simple
, context
, (GDestroyNotify
) async_context_free
);
333 g_simple_async_result_run_in_thread (
334 simple
, (GSimpleAsyncThreadFunc
)
335 mail_session_handle_draft_headers_thread
,
336 io_priority
, cancellable
);
338 g_object_unref (simple
);
342 e_mail_session_handle_draft_headers_finish (EMailSession
*session
,
343 GAsyncResult
*result
,
346 GSimpleAsyncResult
*simple
;
348 g_return_val_if_fail (
349 g_simple_async_result_is_valid (
350 result
, G_OBJECT (session
),
351 e_mail_session_handle_draft_headers
), FALSE
);
353 simple
= G_SIMPLE_ASYNC_RESULT (result
);
355 /* Assume success unless a GError is set. */
356 return !g_simple_async_result_propagate_error (simple
, error
);
360 mail_session_handle_source_headers_thread (GSimpleAsyncResult
*simple
,
361 EMailSession
*session
,
362 GCancellable
*cancellable
)
364 AsyncContext
*context
;
365 GError
*error
= NULL
;
367 context
= g_simple_async_result_get_op_res_gpointer (simple
);
369 e_mail_session_handle_source_headers_sync (
370 session
, context
->message
, cancellable
, &error
);
373 g_simple_async_result_take_error (simple
, error
);
377 e_mail_session_handle_source_headers_sync (EMailSession
*session
,
378 CamelMimeMessage
*message
,
379 GCancellable
*cancellable
,
384 CamelMessageFlags flags
= 0;
385 const gchar
*folder_uri
;
386 const gchar
*message_uid
;
387 const gchar
*flag_string
;
388 const gchar
*header_name
;
394 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), FALSE
);
395 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), FALSE
);
397 medium
= CAMEL_MEDIUM (message
);
399 header_name
= "X-Evolution-Source-Folder";
400 folder_uri
= camel_medium_get_header (medium
, header_name
);
402 header_name
= "X-Evolution-Source-Message";
403 message_uid
= camel_medium_get_header (medium
, header_name
);
405 header_name
= "X-Evolution-Source-Flags";
406 flag_string
= camel_medium_get_header (medium
, header_name
);
408 /* Don't report errors about missing X-Evolution-Source
409 * headers. These headers are optional, so their absence
410 * is handled by doing nothing. */
411 if (folder_uri
== NULL
|| message_uid
== NULL
|| flag_string
== NULL
)
414 /* Convert the flag string to CamelMessageFlags. */
416 string
= g_strstrip (g_strdup (flag_string
));
417 tokens
= g_strsplit (string
, " ", 0);
420 /* If tokens is NULL, a length of 0 will skip the loop. */
421 length
= (tokens
!= NULL
) ? g_strv_length (tokens
) : 0;
423 for (ii
= 0; ii
< length
; ii
++) {
424 /* Note: We're only checking for flags known to
425 * be used in X-Evolution-Source-Flags headers.
426 * Add more as needed. */
427 if (g_strcmp0 (tokens
[ii
], "ANSWERED") == 0)
428 flags
|= CAMEL_MESSAGE_ANSWERED
;
429 else if (g_strcmp0 (tokens
[ii
], "ANSWERED_ALL") == 0)
430 flags
|= CAMEL_MESSAGE_ANSWERED_ALL
;
431 else if (g_strcmp0 (tokens
[ii
], "FORWARDED") == 0)
432 flags
|= CAMEL_MESSAGE_FORWARDED
;
433 else if (g_strcmp0 (tokens
[ii
], "SEEN") == 0)
434 flags
|= CAMEL_MESSAGE_SEEN
;
437 "Unknown flag '%s' in %s",
438 tokens
[ii
], header_name
);
443 folder
= e_mail_session_uri_to_folder_sync (
444 session
, folder_uri
, 0, cancellable
, error
);
449 camel_folder_set_message_flags (
450 folder
, message_uid
, flags
, flags
);
452 success
= camel_folder_synchronize_message_sync (
453 folder
, message_uid
, cancellable
, error
);
455 g_object_unref (folder
);
461 e_mail_session_handle_source_headers (EMailSession
*session
,
462 CamelMimeMessage
*message
,
464 GCancellable
*cancellable
,
465 GAsyncReadyCallback callback
,
468 GSimpleAsyncResult
*simple
;
469 AsyncContext
*context
;
471 g_return_if_fail (E_IS_MAIL_SESSION (session
));
472 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
474 context
= g_slice_new0 (AsyncContext
);
475 context
->message
= g_object_ref (message
);
477 simple
= g_simple_async_result_new (
478 G_OBJECT (session
), callback
, user_data
,
479 e_mail_session_handle_source_headers
);
481 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
483 g_simple_async_result_set_op_res_gpointer (
484 simple
, context
, (GDestroyNotify
) async_context_free
);
486 g_simple_async_result_run_in_thread (
487 simple
, (GSimpleAsyncThreadFunc
)
488 mail_session_handle_source_headers_thread
,
489 io_priority
, cancellable
);
491 g_object_unref (simple
);
495 e_mail_session_handle_source_headers_finish (EMailSession
*session
,
496 GAsyncResult
*result
,
499 GSimpleAsyncResult
*simple
;
501 g_return_val_if_fail (
502 g_simple_async_result_is_valid (
503 result
, G_OBJECT (session
),
504 e_mail_session_handle_draft_headers
), FALSE
);
506 simple
= G_SIMPLE_ASYNC_RESULT (result
);
508 /* Assume success unless a GError is set. */
509 return !g_simple_async_result_propagate_error (simple
, error
);
513 mail_session_send_to_thread (GSimpleAsyncResult
*simple
,
514 EMailSession
*session
,
515 GCancellable
*cancellable
)
517 AsyncContext
*context
;
518 CamelProvider
*provider
;
519 CamelFolder
*folder
= NULL
;
520 CamelFolder
*local_sent_folder
;
521 CamelServiceConnectionStatus status
;
522 GString
*error_messages
;
523 gboolean copy_to_sent
= TRUE
;
524 gboolean did_connect
= FALSE
;
526 GError
*error
= NULL
;
528 context
= g_simple_async_result_get_op_res_gpointer (simple
);
530 if (camel_address_length (context
->recipients
) == 0)
533 /* Send the message to all recipients. */
535 if (context
->transport
== NULL
) {
536 g_simple_async_result_set_error (
537 simple
, CAMEL_SERVICE_ERROR
,
538 CAMEL_SERVICE_ERROR_UNAVAILABLE
,
539 _("No mail transport service available"));
543 status
= camel_service_get_connection_status (context
->transport
);
544 if (status
!= CAMEL_SERVICE_CONNECTED
) {
547 camel_service_connect_sync (
548 context
->transport
, cancellable
, &error
);
551 g_simple_async_result_take_error (simple
, error
);
556 provider
= camel_service_get_provider (context
->transport
);
558 if (provider
->flags
& CAMEL_PROVIDER_DISABLE_SENT_FOLDER
)
559 copy_to_sent
= FALSE
;
561 camel_transport_send_to_sync (
562 CAMEL_TRANSPORT (context
->transport
),
563 context
->message
, context
->from
,
564 context
->recipients
, cancellable
, &error
);
567 /* Disconnect regardless of error or cancellation,
568 * but be mindful of these conditions when calling
569 * camel_service_disconnect_sync(). */
570 if (g_cancellable_is_cancelled (cancellable
)) {
571 camel_service_disconnect_sync (
572 context
->transport
, FALSE
, NULL
, NULL
);
573 } else if (error
!= NULL
) {
574 camel_service_disconnect_sync (
575 context
->transport
, FALSE
, cancellable
, NULL
);
577 camel_service_disconnect_sync (
578 context
->transport
, TRUE
, cancellable
, &error
);
583 g_simple_async_result_take_error (simple
, error
);
588 /* Post the message to requested folders. */
589 for (ii
= 0; ii
< context
->post_to_uris
->len
; ii
++) {
591 const gchar
*folder_uri
;
593 folder_uri
= g_ptr_array_index (context
->post_to_uris
, ii
);
595 folder
= e_mail_session_uri_to_folder_sync (
596 session
, folder_uri
, 0, cancellable
, &error
);
599 g_warn_if_fail (folder
== NULL
);
600 g_simple_async_result_take_error (simple
, error
);
604 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
606 camel_folder_append_message_sync (
607 folder
, context
->message
, context
->info
,
608 NULL
, cancellable
, &error
);
610 g_object_unref (folder
);
613 g_simple_async_result_take_error (simple
, error
);
618 /*** Post Processing ***/
620 /* This accumulates error messages during post-processing. */
621 error_messages
= g_string_sized_new (256);
623 mail_tool_restore_xevolution_headers (context
->message
, context
->xev
);
625 /* Run filters on the outgoing message. */
626 if (context
->driver
!= NULL
) {
627 CamelMessageFlags message_flags
;
629 camel_filter_driver_filter_message (
630 context
->driver
, context
->message
, context
->info
,
631 NULL
, NULL
, NULL
, "", cancellable
, &error
);
633 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
))
637 g_string_append_printf (
639 _("Failed to apply outgoing filters: %s"),
641 g_clear_error (&error
);
644 message_flags
= camel_message_info_flags (context
->info
);
646 if (message_flags
& CAMEL_MESSAGE_DELETED
)
647 copy_to_sent
= FALSE
;
653 /* Append the sent message to a Sent folder. */
656 e_mail_session_get_local_folder (
657 session
, E_MAIL_LOCAL_FOLDER_SENT
);
659 folder
= e_mail_session_get_fcc_for_message_sync (
660 session
, context
->message
, cancellable
, &error
);
664 ((folder
!= NULL
) && (error
== NULL
)) ||
665 ((folder
== NULL
) && (error
!= NULL
)));
667 /* Append the message. */
669 camel_folder_append_message_sync (
670 folder
, context
->message
,
671 context
->info
, NULL
, cancellable
, &error
);
673 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
))
679 if (folder
!= NULL
&& folder
!= local_sent_folder
) {
680 const gchar
*description
;
682 description
= camel_folder_get_description (folder
);
684 if (error_messages
->len
> 0)
685 g_string_append (error_messages
, "\n\n");
686 g_string_append_printf (
688 _("Failed to append to %s: %s\n"
689 "Appending to local 'Sent' folder instead."),
690 description
, error
->message
);
693 /* If appending to a remote Sent folder failed,
694 * try appending to the local Sent folder. */
695 if (folder
!= local_sent_folder
) {
697 g_clear_error (&error
);
699 camel_folder_append_message_sync (
700 local_sent_folder
, context
->message
,
701 context
->info
, NULL
, cancellable
, &error
);
704 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
))
707 /* We can't even append to the local Sent folder?
708 * In that case just leave the message in Outbox. */
710 if (error_messages
->len
> 0)
711 g_string_append (error_messages
, "\n\n");
712 g_string_append_printf (
714 _("Failed to append to local 'Sent' folder: %s"),
716 g_clear_error (&error
);
722 /* The send operation was successful; ignore cleanup errors. */
724 /* Mark the draft message for deletion, if present. */
725 e_mail_session_handle_draft_headers_sync (
726 session
, context
->message
, cancellable
, &error
);
728 g_warning ("%s", error
->message
);
729 g_clear_error (&error
);
732 /* Set flags on the original source message, if present.
733 * Source message refers to the message being forwarded
735 e_mail_session_handle_source_headers_sync (
736 session
, context
->message
, cancellable
, &error
);
738 g_warning ("%s", error
->message
);
739 g_clear_error (&error
);
744 /* If we were cancelled, disregard any other errors. */
745 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
746 g_simple_async_result_take_error (simple
, error
);
748 /* Stuff the accumulated error messages in a GError. */
749 } else if (error_messages
->len
> 0) {
750 g_simple_async_result_set_error (
751 simple
, E_MAIL_ERROR
,
752 E_MAIL_ERROR_POST_PROCESSING
,
753 "%s", error_messages
->str
);
756 /* Synchronize the Sent folder. */
757 if (folder
!= NULL
) {
758 camel_folder_synchronize_sync (
759 folder
, FALSE
, cancellable
, NULL
);
760 g_object_unref (folder
);
763 g_string_free (error_messages
, TRUE
);
767 get_message_size (CamelMimeMessage
*message
,
768 GCancellable
*cancellable
)
773 null
= camel_stream_null_new ();
774 camel_data_wrapper_write_to_stream_sync (
775 CAMEL_DATA_WRAPPER (message
), null
, cancellable
, NULL
);
776 size
= CAMEL_STREAM_NULL (null
)->written
;
777 g_object_unref (null
);
783 e_mail_session_send_to (EMailSession
*session
,
784 CamelMimeMessage
*message
,
786 GCancellable
*cancellable
,
787 CamelFilterGetFolderFunc get_folder_func
,
788 gpointer get_folder_data
,
789 GAsyncReadyCallback callback
,
792 GSimpleAsyncResult
*simple
;
793 AsyncContext
*context
;
795 CamelAddress
*recipients
;
797 CamelMessageInfo
*info
;
798 CamelService
*transport
;
799 GPtrArray
*post_to_uris
;
800 struct _camel_header_raw
*xev
;
801 struct _camel_header_raw
*header
;
802 const gchar
*resent_from
;
803 GError
*error
= NULL
;
805 g_return_if_fail (E_IS_MAIL_SESSION (session
));
806 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
808 medium
= CAMEL_MEDIUM (message
);
810 camel_medium_set_header (medium
, "X-Mailer", X_MAILER
);
812 /* Do this before removing "X-Evolution" headers. */
813 transport
= e_mail_session_ref_transport_for_message (
816 xev
= mail_tool_remove_xevolution_headers (message
);
818 /* Extract directives from X-Evolution headers. */
820 post_to_uris
= g_ptr_array_new ();
821 for (header
= xev
; header
!= NULL
; header
= header
->next
) {
824 if (g_strcmp0 (header
->name
, "X-Evolution-PostTo") != 0)
827 folder_uri
= g_strstrip (g_strdup (header
->value
));
828 g_ptr_array_add (post_to_uris
, folder_uri
);
831 /* Collect sender and recipients from headers. */
833 from
= (CamelAddress
*) camel_internet_address_new ();
834 recipients
= (CamelAddress
*) camel_internet_address_new ();
835 resent_from
= camel_medium_get_header (medium
, "Resent-From");
837 if (resent_from
!= NULL
) {
838 const CamelInternetAddress
*addr
;
841 camel_address_decode (from
, resent_from
);
843 type
= CAMEL_RECIPIENT_TYPE_RESENT_TO
;
844 addr
= camel_mime_message_get_recipients (message
, type
);
845 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
847 type
= CAMEL_RECIPIENT_TYPE_RESENT_CC
;
848 addr
= camel_mime_message_get_recipients (message
, type
);
849 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
851 type
= CAMEL_RECIPIENT_TYPE_RESENT_BCC
;
852 addr
= camel_mime_message_get_recipients (message
, type
);
853 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
856 const CamelInternetAddress
*addr
;
859 addr
= camel_mime_message_get_from (message
);
860 camel_address_copy (from
, CAMEL_ADDRESS (addr
));
862 type
= CAMEL_RECIPIENT_TYPE_TO
;
863 addr
= camel_mime_message_get_recipients (message
, type
);
864 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
866 type
= CAMEL_RECIPIENT_TYPE_CC
;
867 addr
= camel_mime_message_get_recipients (message
, type
);
868 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
870 type
= CAMEL_RECIPIENT_TYPE_BCC
;
871 addr
= camel_mime_message_get_recipients (message
, type
);
872 camel_address_cat (recipients
, CAMEL_ADDRESS (addr
));
875 /* Miscellaneous preparations. */
877 info
= camel_message_info_new_from_header (
878 NULL
, CAMEL_MIME_PART (message
)->headers
);
879 ((CamelMessageInfoBase
*) info
)->size
=
880 get_message_size (message
, cancellable
);
881 camel_message_info_set_flags (info
, CAMEL_MESSAGE_SEEN
, ~0);
883 /* expand, or remove empty, group addresses */
884 em_utils_expand_groups (CAMEL_INTERNET_ADDRESS (recipients
));
886 /* The rest of the processing happens in a thread. */
888 context
= g_slice_new0 (AsyncContext
);
889 context
->message
= g_object_ref (message
);
890 context
->io_priority
= io_priority
;
891 context
->from
= from
;
892 context
->recipients
= recipients
;
893 context
->info
= info
;
895 context
->post_to_uris
= post_to_uris
;
896 context
->transport
= transport
;
898 if (G_IS_CANCELLABLE (cancellable
))
899 context
->cancellable
= g_object_ref (cancellable
);
901 /* Failure here emits a runtime warning but is non-fatal. */
902 context
->driver
= camel_session_get_filter_driver (
903 CAMEL_SESSION (session
), E_FILTER_SOURCE_OUTGOING
, &error
);
904 if (context
->driver
!= NULL
&& get_folder_func
)
905 camel_filter_driver_set_folder_func (
906 context
->driver
, get_folder_func
, get_folder_data
);
908 g_warn_if_fail (context
->driver
== NULL
);
909 g_warning ("%s", error
->message
);
910 g_error_free (error
);
913 /* This gets popped in async_context_free(). */
914 camel_operation_push_message (
915 context
->cancellable
, _("Sending message"));
917 simple
= g_simple_async_result_new (
918 G_OBJECT (session
), callback
,
919 user_data
, e_mail_session_send_to
);
921 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
923 g_simple_async_result_set_op_res_gpointer (
924 simple
, context
, (GDestroyNotify
) async_context_free
);
926 g_simple_async_result_run_in_thread (
927 simple
, (GSimpleAsyncThreadFunc
)
928 mail_session_send_to_thread
,
929 context
->io_priority
,
930 context
->cancellable
);
932 g_object_unref (simple
);
936 e_mail_session_send_to_finish (EMailSession
*session
,
937 GAsyncResult
*result
,
940 GSimpleAsyncResult
*simple
;
942 g_return_val_if_fail (
943 g_simple_async_result_is_valid (
944 result
, G_OBJECT (session
),
945 e_mail_session_send_to
), FALSE
);
947 simple
= G_SIMPLE_ASYNC_RESULT (result
);
949 /* Assume success unless a GError is set. */
950 return !g_simple_async_result_propagate_error (simple
, error
);
953 /* Helper for e_mail_session_get_fcc_for_message_sync() */
955 mail_session_try_uri_to_folder (EMailSession
*session
,
956 const gchar
*folder_uri
,
957 GCancellable
*cancellable
,
961 GError
*local_error
= NULL
;
963 folder
= e_mail_session_uri_to_folder_sync (
964 session
, folder_uri
, 0, cancellable
, &local_error
);
967 g_return_val_if_fail (
968 ((folder
!= NULL
) && (local_error
== NULL
)) ||
969 ((folder
== NULL
) && (local_error
!= NULL
)), NULL
);
971 /* Disregard specific errors. */
974 if (g_error_matches (
975 local_error
, CAMEL_FOLDER_ERROR
,
976 CAMEL_FOLDER_ERROR_INVALID
))
977 g_clear_error (&local_error
);
979 /* Folder not found. */
980 if (g_error_matches (
981 local_error
, CAMEL_STORE_ERROR
,
982 CAMEL_STORE_ERROR_NO_FOLDER
))
983 g_clear_error (&local_error
);
985 if (local_error
!= NULL
)
986 g_propagate_error (error
, local_error
);
991 /* Helper for e_mail_session_get_fcc_for_message_sync() */
993 mail_session_ref_origin_folder (EMailSession
*session
,
994 CamelMimeMessage
*message
,
995 GCancellable
*cancellable
,
999 const gchar
*header_name
;
1000 const gchar
*header_value
;
1002 medium
= CAMEL_MEDIUM (message
);
1004 /* Check that a "X-Evolution-Source-Flags" header is present
1005 * and its value does not contain the substring "FORWARDED". */
1007 header_name
= "X-Evolution-Source-Flags";
1008 header_value
= camel_medium_get_header (medium
, header_name
);
1010 if (header_value
== NULL
)
1013 if (strstr (header_value
, "FORWARDED") != NULL
)
1016 /* Check that a "X-Evolution-Source-Message" header is present. */
1018 header_name
= "X-Evolution-Source-Message";
1019 header_value
= camel_medium_get_header (medium
, header_name
);
1021 if (header_value
== NULL
)
1024 /* Check that a "X-Evolution-Source-Folder" header is present.
1025 * Its value specifies the origin folder as a folder URI. */
1027 header_name
= "X-Evolution-Source-Folder";
1028 header_value
= camel_medium_get_header (medium
, header_name
);
1030 if (header_value
== NULL
)
1033 /* This may return NULL without setting a GError. */
1034 return mail_session_try_uri_to_folder (
1035 session
, header_value
, cancellable
, error
);
1038 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1039 static CamelFolder
*
1040 mail_session_ref_fcc_from_identity (EMailSession
*session
,
1042 CamelMimeMessage
*message
,
1043 GCancellable
*cancellable
,
1046 ESourceRegistry
*registry
;
1047 ESourceMailSubmission
*extension
;
1048 CamelFolder
*folder
= NULL
;
1049 const gchar
*extension_name
;
1052 registry
= e_mail_session_get_registry (session
);
1053 extension_name
= E_SOURCE_EXTENSION_MAIL_SUBMISSION
;
1058 if (!e_source_registry_check_enabled (registry
, source
))
1061 if (!e_source_has_extension (source
, extension_name
))
1064 extension
= e_source_get_extension (source
, extension_name
);
1066 if (e_source_mail_submission_get_replies_to_origin_folder (extension
)) {
1067 GError
*local_error
= NULL
;
1069 /* This may return NULL without setting a GError. */
1070 folder
= mail_session_ref_origin_folder (
1071 session
, message
, cancellable
, &local_error
);
1073 if (local_error
!= NULL
) {
1074 g_warn_if_fail (folder
== NULL
);
1075 g_propagate_error (error
, local_error
);
1080 folder_uri
= e_source_mail_submission_dup_sent_folder (extension
);
1082 if (folder_uri
!= NULL
&& folder
== NULL
) {
1083 /* This may return NULL without setting a GError. */
1084 folder
= mail_session_try_uri_to_folder (
1085 session
, folder_uri
, cancellable
, error
);
1088 g_free (folder_uri
);
1093 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1094 static CamelFolder
*
1095 mail_session_ref_fcc_from_x_identity (EMailSession
*session
,
1096 CamelMimeMessage
*message
,
1097 GCancellable
*cancellable
,
1101 ESourceRegistry
*registry
;
1102 CamelFolder
*folder
;
1103 CamelMedium
*medium
;
1104 const gchar
*header_name
;
1105 const gchar
*header_value
;
1108 medium
= CAMEL_MEDIUM (message
);
1109 header_name
= "X-Evolution-Identity";
1110 header_value
= camel_medium_get_header (medium
, header_name
);
1112 if (header_value
== NULL
)
1115 uid
= g_strstrip (g_strdup (header_value
));
1117 registry
= e_mail_session_get_registry (session
);
1118 source
= e_source_registry_ref_source (registry
, uid
);
1120 /* This may return NULL without setting a GError. */
1121 folder
= mail_session_ref_fcc_from_identity (
1122 session
, source
, message
, cancellable
, error
);
1124 g_clear_object (&source
);
1131 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1132 static CamelFolder
*
1133 mail_session_ref_fcc_from_x_fcc (EMailSession
*session
,
1134 CamelMimeMessage
*message
,
1135 GCancellable
*cancellable
,
1138 CamelMedium
*medium
;
1139 const gchar
*header_name
;
1140 const gchar
*header_value
;
1142 medium
= CAMEL_MEDIUM (message
);
1143 header_name
= "X-Evolution-Fcc";
1144 header_value
= camel_medium_get_header (medium
, header_name
);
1146 if (header_value
== NULL
)
1149 /* This may return NULL without setting a GError. */
1150 return mail_session_try_uri_to_folder (
1151 session
, header_value
, cancellable
, error
);
1154 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1155 static CamelFolder
*
1156 mail_session_ref_fcc_from_default_identity (EMailSession
*session
,
1157 CamelMimeMessage
*message
,
1158 GCancellable
*cancellable
,
1162 ESourceRegistry
*registry
;
1163 CamelFolder
*folder
;
1165 registry
= e_mail_session_get_registry (session
);
1166 source
= e_source_registry_ref_default_mail_identity (registry
);
1168 /* This may return NULL without setting a GError. */
1169 folder
= mail_session_ref_fcc_from_identity (
1170 session
, source
, message
, cancellable
, error
);
1172 g_clear_object (&source
);
1178 * e_mail_session_get_fcc_for_message_sync:
1179 * @session: an #EMailSession
1180 * @message: a #CamelMimeMessage
1181 * @cancellable: optional #GCancellable object, or %NULL
1182 * @error: return location for a #GError, or %NULL
1184 * Obtains the preferred "carbon-copy" folder (a.k.a Fcc) for @message
1185 * by first checking @message for an "X-Evolution-Identity" header, and
1186 * then an "X-Evolution-Fcc" header. Failing that, the function checks
1187 * the default mail identity (if available), and failing even that, the
1188 * function falls back to the Sent folder from the built-in mail store.
1190 * Where applicable, the function attempts to honor the
1191 * #ESourceMailSubmission:replies-to-origin-folder preference.
1193 * The returned #CamelFolder is referenced for thread-safety and must be
1194 * unreferenced with g_object_unref() when finished with it.
1196 * If a non-recoverable error occurs, the function sets @error and returns
1199 * Returns: a #CamelFolder, or %NULL
1202 e_mail_session_get_fcc_for_message_sync (EMailSession
*session
,
1203 CamelMimeMessage
*message
,
1204 GCancellable
*cancellable
,
1207 CamelFolder
*folder
= NULL
;
1209 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), NULL
);
1210 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
1212 /* Check for "X-Evolution-Identity" header. */
1213 if (folder
== NULL
) {
1214 GError
*local_error
= NULL
;
1216 /* This may return NULL without setting a GError. */
1217 folder
= mail_session_ref_fcc_from_x_identity (
1218 session
, message
, cancellable
, &local_error
);
1220 if (local_error
!= NULL
) {
1221 g_warn_if_fail (folder
== NULL
);
1222 g_propagate_error (error
, local_error
);
1227 /* Check for "X-Evolution-Fcc" header. */
1228 if (folder
== NULL
) {
1229 GError
*local_error
= NULL
;
1231 /* This may return NULL without setting a GError. */
1232 folder
= mail_session_ref_fcc_from_x_fcc (
1233 session
, message
, cancellable
, &local_error
);
1235 if (local_error
!= NULL
) {
1236 g_warn_if_fail (folder
== NULL
);
1237 g_propagate_error (error
, local_error
);
1242 /* Check the default mail identity. */
1243 if (folder
== NULL
) {
1244 GError
*local_error
= NULL
;
1246 /* This may return NULL without setting a GError. */
1247 folder
= mail_session_ref_fcc_from_default_identity (
1248 session
, message
, cancellable
, &local_error
);
1250 if (local_error
!= NULL
) {
1251 g_warn_if_fail (folder
== NULL
);
1252 g_propagate_error (error
, local_error
);
1257 /* Last resort - local Sent folder. */
1258 if (folder
== NULL
) {
1259 folder
= e_mail_session_get_local_folder (
1260 session
, E_MAIL_LOCAL_FOLDER_SENT
);
1261 g_object_ref (folder
);
1267 /* Helper for e_mail_session_get_fcc_for_message() */
1269 mail_session_get_fcc_for_message_thread (GSimpleAsyncResult
*simple
,
1270 GObject
*source_object
,
1271 GCancellable
*cancellable
)
1273 AsyncContext
*async_context
;
1274 GError
*local_error
= NULL
;
1276 async_context
= g_simple_async_result_get_op_res_gpointer (simple
);
1278 async_context
->folder
=
1279 e_mail_session_get_fcc_for_message_sync (
1280 E_MAIL_SESSION (source_object
),
1281 async_context
->message
,
1282 cancellable
, &local_error
);
1284 if (local_error
!= NULL
)
1285 g_simple_async_result_take_error (simple
, local_error
);
1289 * e_mail_session_get_fcc_for_message:
1290 * @session: an #EMailSession
1291 * @message: a #CamelMimeMessage
1292 * @io_priority: the I/O priority of the request
1293 * @cancellable: optional #GCancellable object, or %NULL
1294 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1295 * @user_data: data to pass to the callback function
1297 * Asynchronously obtains the preferred "carbon-copy" folder (a.k.a Fcc) for
1298 * @message by first checking @message for an "X-Evolution-Identity" header,
1299 * and then an "X-Evolution-Fcc" header. Failing that, the function checks
1300 * the default mail identity (if available), and failing even that, the
1301 * function falls back to the Sent folder from the built-in mail store.
1303 * Where applicable, the function attempts to honor the
1304 * #ESourceMailSubmission:replies-to-origin-folder preference.
1306 * When the operation is finished, @callback will be called. You can then
1307 * call e_mail_session_get_fcc_for_message_finish() to get the result of the
1311 e_mail_session_get_fcc_for_message (EMailSession
*session
,
1312 CamelMimeMessage
*message
,
1314 GCancellable
*cancellable
,
1315 GAsyncReadyCallback callback
,
1318 GSimpleAsyncResult
*simple
;
1319 AsyncContext
*async_context
;
1321 g_return_if_fail (E_IS_MAIL_SESSION (session
));
1322 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
1324 async_context
= g_slice_new0 (AsyncContext
);
1325 async_context
->message
= g_object_ref (message
);
1327 simple
= g_simple_async_result_new (
1328 G_OBJECT (session
), callback
, user_data
,
1329 e_mail_session_get_fcc_for_message
);
1331 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
1333 g_simple_async_result_set_op_res_gpointer (
1334 simple
, async_context
, (GDestroyNotify
) async_context_free
);
1336 g_simple_async_result_run_in_thread (
1337 simple
, mail_session_get_fcc_for_message_thread
,
1338 io_priority
, cancellable
);
1340 g_object_unref (simple
);
1344 * e_mail_session_get_fcc_for_message_finish:
1345 * @session: an #EMailSession
1346 * @result: a #GAsyncResult
1347 * @error: return location for a #GError, or %NULL
1349 * Finishes the operation started with e_mail_session_get_fcc_for_message().
1351 * The returned #CamelFolder is referenced for thread-safety and must be
1352 * unreferenced with g_object_unref() when finished with it.
1354 * If a non-recoverable error occurred, the function sets @error and
1357 * Returns: a #CamelFolder, or %NULL
1360 e_mail_session_get_fcc_for_message_finish (EMailSession
*session
,
1361 GAsyncResult
*result
,
1364 GSimpleAsyncResult
*simple
;
1365 AsyncContext
*async_context
;
1367 g_return_val_if_fail (
1368 g_simple_async_result_is_valid (
1369 result
, G_OBJECT (session
),
1370 e_mail_session_get_fcc_for_message
), NULL
);
1372 simple
= G_SIMPLE_ASYNC_RESULT (result
);
1373 async_context
= g_simple_async_result_get_op_res_gpointer (simple
);
1375 if (g_simple_async_result_propagate_error (simple
, error
))
1378 g_return_val_if_fail (async_context
->folder
!= NULL
, NULL
);
1380 return g_object_ref (async_context
->folder
);
1384 * e_mail_session_ref_transport:
1385 * @session: an #EMailSession
1386 * @transport_uid: the UID of a mail transport
1388 * Returns the transport #CamelService instance for @transport_uid,
1389 * verifying first that the @transport_uid is indeed a mail transport and
1390 * that the corresponding #ESource is enabled. If these checks fail, the
1391 * function returns %NULL.
1393 * The returned #CamelService is referenced for thread-safety and must be
1394 * unreferenced with g_object_unref() when finished with it.
1396 * Returns: a #CamelService, or %NULL
1399 e_mail_session_ref_transport (EMailSession
*session
,
1400 const gchar
*transport_uid
)
1402 ESourceRegistry
*registry
;
1403 ESource
*source
= NULL
;
1404 CamelService
*transport
= NULL
;
1405 const gchar
*extension_name
;
1407 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), NULL
);
1408 g_return_val_if_fail (transport_uid
!= NULL
, NULL
);
1410 registry
= e_mail_session_get_registry (session
);
1411 extension_name
= E_SOURCE_EXTENSION_MAIL_TRANSPORT
;
1413 source
= e_source_registry_ref_source (registry
, transport_uid
);
1418 if (!e_source_registry_check_enabled (registry
, source
))
1421 if (!e_source_has_extension (source
, extension_name
))
1424 transport
= camel_session_ref_service (
1425 CAMEL_SESSION (session
), transport_uid
);
1428 if (transport
!= NULL
)
1429 g_warn_if_fail (CAMEL_IS_TRANSPORT (transport
));
1432 g_clear_object (&source
);
1437 /* Helper for e_mail_session_ref_default_transport()
1438 * and mail_session_ref_transport_from_x_identity(). */
1439 static CamelService
*
1440 mail_session_ref_transport_for_identity (EMailSession
*session
,
1443 ESourceRegistry
*registry
;
1444 ESourceMailSubmission
*extension
;
1445 CamelService
*transport
= NULL
;
1446 const gchar
*extension_name
;
1449 registry
= e_mail_session_get_registry (session
);
1450 extension_name
= E_SOURCE_EXTENSION_MAIL_SUBMISSION
;
1455 if (!e_source_registry_check_enabled (registry
, source
))
1458 if (!e_source_has_extension (source
, extension_name
))
1461 extension
= e_source_get_extension (source
, extension_name
);
1462 uid
= e_source_mail_submission_dup_transport_uid (extension
);
1465 transport
= e_mail_session_ref_transport (session
, uid
);
1473 * e_mail_session_ref_default_transport:
1474 * @session: an #EMailSession
1476 * Returns the default transport #CamelService instance according to
1477 * #ESourceRegistry's #ESourceRegistry:default-mail-identity setting,
1478 * verifying first that the #ESourceMailSubmission:transport-uid named by
1479 * the #ESourceRegistry:default-mail-identity is indeed a mail transport,
1480 * and that the corresponding #ESource is enabled. If these checks fail,
1481 * the function returns %NULL.
1483 * The returned #CamelService is referenced for thread-safety and must be
1484 * unreferenced with g_object_unref() when finished with it.
1486 * Returns: a #CamelService, or %NULL
1489 e_mail_session_ref_default_transport (EMailSession
*session
)
1492 ESourceRegistry
*registry
;
1493 CamelService
*transport
;
1495 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), NULL
);
1497 registry
= e_mail_session_get_registry (session
);
1498 source
= e_source_registry_ref_default_mail_identity (registry
);
1499 transport
= mail_session_ref_transport_for_identity (session
, source
);
1500 g_clear_object (&source
);
1505 /* Helper for e_mail_session_ref_transport_for_message() */
1506 static CamelService
*
1507 mail_session_ref_transport_from_x_identity (EMailSession
*session
,
1508 CamelMimeMessage
*message
)
1511 ESourceRegistry
*registry
;
1512 CamelMedium
*medium
;
1513 CamelService
*transport
;
1514 const gchar
*header_name
;
1515 const gchar
*header_value
;
1518 medium
= CAMEL_MEDIUM (message
);
1519 header_name
= "X-Evolution-Identity";
1520 header_value
= camel_medium_get_header (medium
, header_name
);
1522 if (header_value
== NULL
)
1525 uid
= g_strstrip (g_strdup (header_value
));
1527 registry
= e_mail_session_get_registry (session
);
1528 source
= e_source_registry_ref_source (registry
, uid
);
1529 transport
= mail_session_ref_transport_for_identity (session
, source
);
1530 g_clear_object (&source
);
1537 /* Helper for e_mail_session_ref_transport_for_message() */
1538 static CamelService
*
1539 mail_session_ref_transport_from_x_transport (EMailSession
*session
,
1540 CamelMimeMessage
*message
)
1542 CamelMedium
*medium
;
1543 CamelService
*transport
;
1544 const gchar
*header_name
;
1545 const gchar
*header_value
;
1548 medium
= CAMEL_MEDIUM (message
);
1549 header_name
= "X-Evolution-Transport";
1550 header_value
= camel_medium_get_header (medium
, header_name
);
1552 if (header_value
== NULL
)
1555 uid
= g_strstrip (g_strdup (header_value
));
1557 transport
= e_mail_session_ref_transport (session
, uid
);
1565 * e_mail_session_ref_transport_for_message:
1566 * @session: an #EMailSession
1567 * @message: a #CamelMimeMessage
1569 * Returns the preferred transport #CamelService instance for @message by
1570 * first checking @message for an "X-Evolution-Identity" header, and then
1571 * an "X-Evolution-Transport" header. Failing that, the function returns
1572 * the default transport #CamelService instance (if available).
1574 * The returned #CamelService is referenced for thread-safety and must be
1575 * unreferenced with g_object_unref() when finished with it.
1577 * Returns: a #CamelService, or %NULL
1580 e_mail_session_ref_transport_for_message (EMailSession
*session
,
1581 CamelMimeMessage
*message
)
1583 CamelService
*transport
= NULL
;
1585 g_return_val_if_fail (E_IS_MAIL_SESSION (session
), NULL
);
1586 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), NULL
);
1588 /* Check for "X-Evolution-Identity" header. */
1589 if (transport
== NULL
)
1590 transport
= mail_session_ref_transport_from_x_identity (
1593 /* Check for "X-Evolution-Transport" header. */
1594 if (transport
== NULL
)
1595 transport
= mail_session_ref_transport_from_x_transport (
1598 /* Fall back to the default mail transport. */
1599 if (transport
== NULL
)
1600 transport
= e_mail_session_ref_default_transport (session
);