1 /*
2 * e-mail-session-utils.c
3 *
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.
7 *
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.
12 *
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/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include "e-mail-session-utils.h"
21
22 #include <glib/gi18n-lib.h>
23 #include <libedataserver/libedataserver.h>
24
25 #include <libemail-engine/e-mail-folder-utils.h>
26 #include <libemail-engine/e-mail-utils.h>
27 #include <libemail-engine/mail-tools.h>
28
29 /* User-Agent header value */
30 #define USER_AGENT ("Evolution " VERSION VERSION_SUBSTRING " " VERSION_COMMENT)
31
32 /* FIXME: Temporary - remove this after we move filter/ to eds */
33 #define E_FILTER_SOURCE_OUTGOING "outgoing"
34
35 typedef struct _AsyncContext AsyncContext;
36
37 struct _AsyncContext {
38 CamelFolder *folder;
39
40 CamelMimeMessage *message;
41 CamelMessageInfo *info;
42
43 CamelAddress *from;
44 CamelAddress *recipients;
45
46 CamelFilterDriver *driver;
47
48 CamelService *transport;
49
50 GCancellable *cancellable;
51 gint io_priority;
52
53 /* X-Evolution headers */
54 CamelNameValueArray *xev_headers;
55
56 GPtrArray *post_to_uris;
57
58 EMailLocalFolder local_id;
59
60 gchar *folder_uri;
61 gchar *message_uid;
62
63 gboolean use_sent_folder;
64 };
65
66 static void
async_context_free(AsyncContext * context)67 async_context_free (AsyncContext *context)
68 {
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);
76
77 if (context->cancellable != NULL) {
78 camel_operation_pop_message (context->cancellable);
79 g_object_unref (context->cancellable);
80 }
81
82 camel_name_value_array_free (context->xev_headers);
83
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);
88 }
89
90 g_free (context->folder_uri);
91 g_free (context->message_uid);
92
93 g_slice_free (AsyncContext, context);
94 }
95
96 GQuark
e_mail_error_quark(void)97 e_mail_error_quark (void)
98 {
99 static GQuark quark = 0;
100
101 if (G_UNLIKELY (quark == 0)) {
102 const gchar *string = "e-mail-error-quark";
103 quark = g_quark_from_static_string (string);
104 }
105
106 return quark;
107 }
108
109 static void
mail_session_append_to_local_folder_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)110 mail_session_append_to_local_folder_thread (GSimpleAsyncResult *simple,
111 GObject *object,
112 GCancellable *cancellable)
113 {
114 AsyncContext *context;
115 GError *error = NULL;
116
117 context = g_simple_async_result_get_op_res_gpointer (simple);
118
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);
124
125 if (error != NULL)
126 g_simple_async_result_take_error (simple, error);
127 }
128
129 gboolean
e_mail_session_append_to_local_folder_sync(EMailSession * session,EMailLocalFolder local_id,CamelMimeMessage * message,CamelMessageInfo * info,gchar ** appended_uid,GCancellable * cancellable,GError ** error)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)
137 {
138 CamelFolder *folder;
139 const gchar *folder_uri;
140 gboolean success = FALSE;
141
142 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
143 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
144
145 folder_uri = e_mail_session_get_local_folder_uri (session, local_id);
146 g_return_val_if_fail (folder_uri != NULL, FALSE);
147
148 folder = e_mail_session_uri_to_folder_sync (
149 session, folder_uri, CAMEL_STORE_FOLDER_CREATE,
150 cancellable, error);
151
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);
157 }
158
159 return success;
160 }
161
162 void
e_mail_session_append_to_local_folder(EMailSession * session,EMailLocalFolder local_id,CamelMimeMessage * message,CamelMessageInfo * info,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)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)
171 {
172 GSimpleAsyncResult *simple;
173 AsyncContext *context;
174
175 g_return_if_fail (E_IS_MAIL_SESSION (session));
176 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
177
178 context = g_slice_new0 (AsyncContext);
179 context->local_id = local_id;
180 context->message = g_object_ref (message);
181
182 if (info != NULL)
183 context->info = g_object_ref (info);
184
185 simple = g_simple_async_result_new (
186 G_OBJECT (session), callback, user_data,
187 e_mail_session_append_to_local_folder);
188
189 g_simple_async_result_set_check_cancellable (simple, cancellable);
190
191 g_simple_async_result_set_op_res_gpointer (
192 simple, context, (GDestroyNotify) async_context_free);
193
194 g_simple_async_result_run_in_thread (
195 simple, mail_session_append_to_local_folder_thread,
196 io_priority, cancellable);
197
198 g_object_unref (simple);
199 }
200
201 gboolean
e_mail_session_append_to_local_folder_finish(EMailSession * session,GAsyncResult * result,gchar ** appended_uid,GError ** error)202 e_mail_session_append_to_local_folder_finish (EMailSession *session,
203 GAsyncResult *result,
204 gchar **appended_uid,
205 GError **error)
206 {
207 GSimpleAsyncResult *simple;
208 AsyncContext *context;
209
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);
214
215 simple = G_SIMPLE_ASYNC_RESULT (result);
216 context = g_simple_async_result_get_op_res_gpointer (simple);
217
218 if (appended_uid != NULL) {
219 *appended_uid = context->message_uid;
220 context->message_uid = NULL;
221 }
222
223 /* Assume success unless a GError is set. */
224 return !g_simple_async_result_propagate_error (simple, error);
225 }
226
227 static void
mail_session_handle_draft_headers_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)228 mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
229 EMailSession *session,
230 GCancellable *cancellable)
231 {
232 AsyncContext *context;
233 GError *error = NULL;
234
235 context = g_simple_async_result_get_op_res_gpointer (simple);
236
237 e_mail_session_handle_draft_headers_sync (
238 session, context->message, cancellable, &error);
239
240 if (error != NULL)
241 g_simple_async_result_take_error (simple, error);
242 }
243
244 gboolean
e_mail_session_handle_draft_headers_sync(EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable,GError ** error)245 e_mail_session_handle_draft_headers_sync (EMailSession *session,
246 CamelMimeMessage *message,
247 GCancellable *cancellable,
248 GError **error)
249 {
250 CamelFolder *folder;
251 CamelMedium *medium;
252 const gchar *folder_uri;
253 const gchar *message_uid;
254 const gchar *header_name;
255 gboolean success;
256
257 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
258 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
259
260 medium = CAMEL_MEDIUM (message);
261
262 header_name = "X-Evolution-Draft-Folder";
263 folder_uri = camel_medium_get_header (medium, header_name);
264
265 header_name = "X-Evolution-Draft-Message";
266 message_uid = camel_medium_get_header (medium, header_name);
267
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;
273
274 folder = e_mail_session_uri_to_folder_sync (
275 session, folder_uri, 0, cancellable, error);
276
277 if (folder == NULL)
278 return FALSE;
279
280 camel_folder_set_message_flags (
281 folder, message_uid,
282 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
283 CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
284
285 success = camel_folder_synchronize_message_sync (
286 folder, message_uid, cancellable, error);
287
288 g_object_unref (folder);
289
290 return success;
291 }
292
293 void
e_mail_session_handle_draft_headers(EMailSession * session,CamelMimeMessage * message,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)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)
300 {
301 GSimpleAsyncResult *simple;
302 AsyncContext *context;
303
304 g_return_if_fail (E_IS_MAIL_SESSION (session));
305 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
306
307 context = g_slice_new0 (AsyncContext);
308 context->message = g_object_ref (message);
309
310 simple = g_simple_async_result_new (
311 G_OBJECT (session), callback, user_data,
312 e_mail_session_handle_draft_headers);
313
314 g_simple_async_result_set_check_cancellable (simple, cancellable);
315
316 g_simple_async_result_set_op_res_gpointer (
317 simple, context, (GDestroyNotify) async_context_free);
318
319 g_simple_async_result_run_in_thread (
320 simple, (GSimpleAsyncThreadFunc)
321 mail_session_handle_draft_headers_thread,
322 io_priority, cancellable);
323
324 g_object_unref (simple);
325 }
326
327 gboolean
e_mail_session_handle_draft_headers_finish(EMailSession * session,GAsyncResult * result,GError ** error)328 e_mail_session_handle_draft_headers_finish (EMailSession *session,
329 GAsyncResult *result,
330 GError **error)
331 {
332 GSimpleAsyncResult *simple;
333
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);
338
339 simple = G_SIMPLE_ASYNC_RESULT (result);
340
341 /* Assume success unless a GError is set. */
342 return !g_simple_async_result_propagate_error (simple, error);
343 }
344
345 static void
mail_session_handle_source_headers_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)346 mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
347 EMailSession *session,
348 GCancellable *cancellable)
349 {
350 AsyncContext *context;
351 GError *error = NULL;
352
353 context = g_simple_async_result_get_op_res_gpointer (simple);
354
355 e_mail_session_handle_source_headers_sync (
356 session, context->message, cancellable, &error);
357
358 if (error != NULL)
359 g_simple_async_result_take_error (simple, error);
360 }
361
362 gboolean
e_mail_session_handle_source_headers_sync(EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable,GError ** error)363 e_mail_session_handle_source_headers_sync (EMailSession *session,
364 CamelMimeMessage *message,
365 GCancellable *cancellable,
366 GError **error)
367 {
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;
379
380 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
381 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
382
383 medium = CAMEL_MEDIUM (message);
384
385 header_name = "X-Evolution-Source-Folder";
386 folder_uri = camel_medium_get_header (medium, header_name);
387
388 header_name = "X-Evolution-Source-Message";
389 message_uid = camel_medium_get_header (medium, header_name);
390
391 header_name = "X-Evolution-Source-Flags";
392 flag_string = camel_medium_get_header (medium, header_name);
393
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;
399
400 /* Convert the flag string to CamelMessageFlags. */
401
402 string = g_strstrip (g_strdup (flag_string));
403 tokens = g_strsplit (string, " ", 0);
404 g_free (string);
405
406 /* If tokens is NULL, a length of 0 will skip the loop. */
407 length = (tokens != NULL) ? g_strv_length (tokens) : 0;
408
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);
425 }
426
427 g_strfreev (tokens);
428
429 folder = e_mail_session_uri_to_folder_sync (
430 session, folder_uri, 0, cancellable, error);
431
432 if (folder == NULL)
433 return FALSE;
434
435 camel_folder_set_message_flags (
436 folder, message_uid, flags, flags);
437
438 success = camel_folder_synchronize_message_sync (
439 folder, message_uid, cancellable, error);
440
441 g_object_unref (folder);
442
443 return success;
444 }
445
446 void
e_mail_session_handle_source_headers(EMailSession * session,CamelMimeMessage * message,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)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)
453 {
454 GSimpleAsyncResult *simple;
455 AsyncContext *context;
456
457 g_return_if_fail (E_IS_MAIL_SESSION (session));
458 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
459
460 context = g_slice_new0 (AsyncContext);
461 context->message = g_object_ref (message);
462
463 simple = g_simple_async_result_new (
464 G_OBJECT (session), callback, user_data,
465 e_mail_session_handle_source_headers);
466
467 g_simple_async_result_set_check_cancellable (simple, cancellable);
468
469 g_simple_async_result_set_op_res_gpointer (
470 simple, context, (GDestroyNotify) async_context_free);
471
472 g_simple_async_result_run_in_thread (
473 simple, (GSimpleAsyncThreadFunc)
474 mail_session_handle_source_headers_thread,
475 io_priority, cancellable);
476
477 g_object_unref (simple);
478 }
479
480 gboolean
e_mail_session_handle_source_headers_finish(EMailSession * session,GAsyncResult * result,GError ** error)481 e_mail_session_handle_source_headers_finish (EMailSession *session,
482 GAsyncResult *result,
483 GError **error)
484 {
485 GSimpleAsyncResult *simple;
486
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);
491
492 simple = G_SIMPLE_ASYNC_RESULT (result);
493
494 /* Assume success unless a GError is set. */
495 return !g_simple_async_result_propagate_error (simple, error);
496 }
497
498 static void
mail_session_send_to_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)499 mail_session_send_to_thread (GSimpleAsyncResult *simple,
500 EMailSession *session,
501 GCancellable *cancellable)
502 {
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;
514
515 context = g_simple_async_result_get_op_res_gpointer (simple);
516
517 if (camel_address_length (context->recipients) == 0)
518 goto skip_send;
519
520 /* Send the message to all recipients. */
521
522 if (context->transport == NULL) {
523 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
524 g_simple_async_result_set_error (
525 simple, CAMEL_SERVICE_ERROR,
526 CAMEL_SERVICE_ERROR_UNAVAILABLE,
527 _("No mail transport service available"));
528 return;
529 }
530
531 if (!e_mail_session_mark_service_used_sync (session, context->transport, cancellable)) {
532 g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
533 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
534 g_simple_async_result_take_error (simple, error);
535 return;
536 }
537
538 status = camel_service_get_connection_status (context->transport);
539 if (status != CAMEL_SERVICE_CONNECTED) {
540 ESourceRegistry *registry;
541 ESource *source;
542
543 /* Make sure user will be asked for a password, in case he/she cancelled it */
544 registry = e_mail_session_get_registry (session);
545 source = e_source_registry_ref_source (registry, camel_service_get_uid (context->transport));
546
547 if (source) {
548 e_mail_session_emit_allow_auth_prompt (session, source);
549 g_object_unref (source);
550 }
551
552 did_connect = TRUE;
553
554 camel_service_connect_sync (context->transport, cancellable, &error);
555
556 if (error != NULL) {
557 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
558 g_simple_async_result_take_error (simple, error);
559 e_mail_session_unmark_service_used (session, context->transport);
560 return;
561 }
562 }
563
564 provider = camel_service_get_provider (context->transport);
565
566 if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
567 copy_to_sent = FALSE;
568
569 camel_transport_send_to_sync (
570 CAMEL_TRANSPORT (context->transport),
571 context->message, context->from,
572 context->recipients, &sent_message_saved, cancellable, &error);
573
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);
587 }
588 }
589
590 e_mail_session_unmark_service_used (session, context->transport);
591
592 if (error != NULL) {
593 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
594 g_simple_async_result_take_error (simple, error);
595 return;
596 }
597
598 skip_send:
599 /* Post the message to requested folders. */
600 for (ii = 0; ii < context->post_to_uris->len; ii++) {
601 CamelFolder *folder;
602 const gchar *folder_uri;
603
604 folder_uri = g_ptr_array_index (context->post_to_uris, ii);
605
606 folder = e_mail_session_uri_to_folder_sync (
607 session, folder_uri, 0, cancellable, &error);
608
609 if (error != NULL) {
610 g_warn_if_fail (folder == NULL);
611 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
612 g_simple_async_result_take_error (simple, error);
613 return;
614 }
615
616 g_return_if_fail (CAMEL_IS_FOLDER (folder));
617
618 camel_operation_push_message (cancellable, _("Posting message to “%s”"), camel_folder_get_full_name (folder));
619
620 camel_folder_append_message_sync (
621 folder, context->message, context->info,
622 NULL, cancellable, &error);
623
624 camel_operation_pop_message (cancellable);
625
626 g_object_unref (folder);
627
628 if (error != NULL) {
629 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
630 g_simple_async_result_take_error (simple, error);
631 return;
632 }
633 }
634
635 /*** Post Processing ***/
636
637 /* This accumulates error messages during post-processing. */
638 error_messages = g_string_sized_new (256);
639
640 mail_tool_restore_xevolution_headers (context->message, context->xev_headers);
641
642 /* Run filters on the outgoing message. */
643 if (context->driver != NULL) {
644 CamelMessageFlags message_flags;
645
646 camel_filter_driver_filter_message (
647 context->driver, context->message, context->info,
648 NULL, NULL, NULL, "", cancellable, &error);
649
650 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
651 goto exit;
652
653 if (error != NULL) {
654 g_string_append_printf (
655 error_messages,
656 _("Failed to apply outgoing filters: %s"),
657 error->message);
658 g_clear_error (&error);
659 }
660
661 message_flags = camel_message_info_get_flags (context->info);
662
663 if (message_flags & CAMEL_MESSAGE_DELETED)
664 copy_to_sent = FALSE;
665 }
666
667 if (!copy_to_sent || sent_message_saved)
668 goto cleanup;
669
670 /* Append the sent message to a Sent folder. */
671
672 local_sent_folder =
673 e_mail_session_get_local_folder (
674 session, E_MAIL_LOCAL_FOLDER_SENT);
675
676 folder = e_mail_session_get_fcc_for_message_sync (
677 session, context->message, ©_to_sent, cancellable, &error);
678
679 if (!copy_to_sent)
680 goto cleanup;
681
682 /* Sanity check. */
683 g_return_if_fail (
684 ((folder != NULL) && (error == NULL)) ||
685 ((folder == NULL) && (error != NULL)));
686
687 /* Append the message. */
688 if (folder != NULL) {
689 camel_operation_push_message (cancellable, _("Storing sent message to “%s”"), camel_folder_get_full_name (folder));
690
691 camel_folder_append_message_sync (
692 folder, context->message,
693 context->info, NULL, cancellable, &error);
694
695 camel_operation_pop_message (cancellable);
696 }
697
698 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
699 goto exit;
700
701 if (error == NULL)
702 goto cleanup;
703
704 if (folder != NULL && folder != local_sent_folder) {
705 const gchar *description;
706
707 description = camel_folder_get_description (folder);
708
709 if (error_messages->len > 0)
710 g_string_append (error_messages, "\n\n");
711 g_string_append_printf (
712 error_messages,
713 _("Failed to append to %s: %s\n"
714 "Appending to local “Sent” folder instead."),
715 description, error->message);
716 }
717
718 /* If appending to a remote Sent folder failed,
719 * try appending to the local Sent folder. */
720 if (folder != local_sent_folder) {
721
722 g_clear_error (&error);
723
724 camel_operation_push_message (cancellable, _("Storing sent message to “%s”"), camel_folder_get_full_name (local_sent_folder));
725
726 camel_folder_append_message_sync (
727 local_sent_folder, context->message,
728 context->info, NULL, cancellable, &error);
729
730 camel_operation_pop_message (cancellable);
731 }
732
733 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
734 goto exit;
735
736 /* We can't even append to the local Sent folder?
737 * In that case just leave the message in Outbox. */
738 if (error != NULL) {
739 if (error_messages->len > 0)
740 g_string_append (error_messages, "\n\n");
741 g_string_append_printf (
742 error_messages,
743 _("Failed to append to local “Sent” folder: %s"),
744 error->message);
745 g_clear_error (&error);
746 goto exit;
747 }
748
749 cleanup:
750
751 /* The send operation was successful; ignore cleanup errors. */
752
753 /* Mark the draft message for deletion, if present. */
754 e_mail_session_handle_draft_headers_sync (
755 session, context->message, cancellable, &error);
756 if (error != NULL) {
757 g_warning ("%s", error->message);
758 g_clear_error (&error);
759 }
760
761 /* Set flags on the original source message, if present.
762 * Source message refers to the message being forwarded
763 * or replied to. */
764 e_mail_session_handle_source_headers_sync (
765 session, context->message, cancellable, &error);
766 if (error &&
767 !g_error_matches (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_UID) &&
768 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
769 g_warning (
770 "%s: Failed to handle source headers: %s",
771 G_STRFUNC, error->message);
772 }
773 g_clear_error (&error);
774
775 exit:
776
777 /* If we were cancelled, disregard any other errors. */
778 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
779 g_simple_async_result_take_error (simple, error);
780
781 /* Stuff the accumulated error messages in a GError. */
782 } else if (error_messages->len > 0) {
783 g_simple_async_result_set_error (
784 simple, E_MAIL_ERROR,
785 E_MAIL_ERROR_POST_PROCESSING,
786 "%s", error_messages->str);
787 }
788
789 /* Synchronize the Sent folder. */
790 if (folder != NULL) {
791 camel_folder_synchronize_sync (
792 folder, FALSE, cancellable, NULL);
793 g_object_unref (folder);
794 }
795
796 g_string_free (error_messages, TRUE);
797 }
798
799 void
e_mail_session_send_to(EMailSession * session,CamelMimeMessage * message,gint io_priority,GCancellable * cancellable,CamelFilterGetFolderFunc get_folder_func,gpointer get_folder_data,GAsyncReadyCallback callback,gpointer user_data)800 e_mail_session_send_to (EMailSession *session,
801 CamelMimeMessage *message,
802 gint io_priority,
803 GCancellable *cancellable,
804 CamelFilterGetFolderFunc get_folder_func,
805 gpointer get_folder_data,
806 GAsyncReadyCallback callback,
807 gpointer user_data)
808 {
809 GSimpleAsyncResult *simple;
810 AsyncContext *context;
811 CamelAddress *from;
812 CamelAddress *recipients;
813 CamelMedium *medium;
814 CamelMessageInfo *info;
815 CamelService *transport;
816 GPtrArray *post_to_uris;
817 CamelNameValueArray *xev_headers;
818 const gchar *resent_from;
819 guint ii, len;
820 GError *error = NULL;
821
822 g_return_if_fail (E_IS_MAIL_SESSION (session));
823 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
824
825 medium = CAMEL_MEDIUM (message);
826
827 if (!camel_medium_get_header (medium, "X-Evolution-Is-Redirect"))
828 camel_medium_set_header (medium, "User-Agent", USER_AGENT);
829
830 /* Do this before removing "X-Evolution" headers. */
831 transport = e_mail_session_ref_transport_for_message (
832 session, message);
833
834 xev_headers = mail_tool_remove_xevolution_headers (message);
835 len = camel_name_value_array_get_length (xev_headers);
836
837 /* Extract directives from X-Evolution headers. */
838
839 post_to_uris = g_ptr_array_new ();
840 for (ii = 0; ii < len; ii++) {
841 const gchar *header_name = NULL, *header_value = NULL;
842 gchar *folder_uri;
843
844 if (!camel_name_value_array_get (xev_headers, ii, &header_name, &header_value) ||
845 !header_name ||
846 g_ascii_strcasecmp (header_name, "X-Evolution-PostTo") != 0)
847 continue;
848
849 folder_uri = g_strstrip (g_strdup (header_value));
850 g_ptr_array_add (post_to_uris, folder_uri);
851 }
852
853 /* Collect sender and recipients from headers. */
854
855 from = (CamelAddress *) camel_internet_address_new ();
856 recipients = (CamelAddress *) camel_internet_address_new ();
857 resent_from = camel_medium_get_header (medium, "Resent-From");
858
859 if (resent_from != NULL) {
860 const CamelInternetAddress *addr;
861 const gchar *type;
862
863 camel_address_decode (from, resent_from);
864
865 type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
866 addr = camel_mime_message_get_recipients (message, type);
867 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
868
869 type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
870 addr = camel_mime_message_get_recipients (message, type);
871 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
872
873 type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
874 addr = camel_mime_message_get_recipients (message, type);
875 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
876
877 } else {
878 const CamelInternetAddress *addr;
879 const gchar *type;
880
881 addr = camel_mime_message_get_from (message);
882 camel_address_copy (from, CAMEL_ADDRESS (addr));
883
884 type = CAMEL_RECIPIENT_TYPE_TO;
885 addr = camel_mime_message_get_recipients (message, type);
886 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
887
888 type = CAMEL_RECIPIENT_TYPE_CC;
889 addr = camel_mime_message_get_recipients (message, type);
890 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
891
892 type = CAMEL_RECIPIENT_TYPE_BCC;
893 addr = camel_mime_message_get_recipients (message, type);
894 camel_address_cat (recipients, CAMEL_ADDRESS (addr));
895 }
896
897 /* Miscellaneous preparations. */
898
899 info = camel_message_info_new_from_headers (NULL, camel_medium_get_headers (CAMEL_MEDIUM (message)));
900
901 camel_message_info_set_size (info, camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (message), cancellable, NULL));
902 camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN |
903 (camel_mime_message_has_attachment (message) ? CAMEL_MESSAGE_ATTACHMENTS : 0), ~0);
904
905 /* expand, or remove empty, group addresses */
906 em_utils_expand_groups (CAMEL_INTERNET_ADDRESS (recipients));
907
908 /* The rest of the processing happens in a thread. */
909
910 context = g_slice_new0 (AsyncContext);
911 context->message = g_object_ref (message);
912 context->io_priority = io_priority;
913 context->from = from;
914 context->recipients = recipients;
915 context->info = info;
916 context->xev_headers = xev_headers;
917 context->post_to_uris = post_to_uris;
918 context->transport = transport;
919
920 if (G_IS_CANCELLABLE (cancellable))
921 context->cancellable = g_object_ref (cancellable);
922
923 /* Failure here emits a runtime warning but is non-fatal. */
924 context->driver = camel_session_get_filter_driver (CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, NULL, &error);
925 if (context->driver != NULL && get_folder_func)
926 camel_filter_driver_set_folder_func (
927 context->driver, get_folder_func, get_folder_data);
928 if (error != NULL) {
929 g_warn_if_fail (context->driver == NULL);
930 g_warning ("%s", error->message);
931 g_error_free (error);
932 }
933
934 /* This gets popped in async_context_free(). */
935 camel_operation_push_message (
936 context->cancellable, _("Sending message"));
937
938 simple = g_simple_async_result_new (
939 G_OBJECT (session), callback,
940 user_data, e_mail_session_send_to);
941
942 g_simple_async_result_set_check_cancellable (simple, cancellable);
943
944 g_simple_async_result_set_op_res_gpointer (
945 simple, context, (GDestroyNotify) async_context_free);
946
947 g_simple_async_result_run_in_thread (
948 simple, (GSimpleAsyncThreadFunc)
949 mail_session_send_to_thread,
950 context->io_priority,
951 context->cancellable);
952
953 g_object_unref (simple);
954 }
955
956 gboolean
e_mail_session_send_to_finish(EMailSession * session,GAsyncResult * result,GError ** error)957 e_mail_session_send_to_finish (EMailSession *session,
958 GAsyncResult *result,
959 GError **error)
960 {
961 GSimpleAsyncResult *simple;
962
963 g_return_val_if_fail (
964 g_simple_async_result_is_valid (
965 result, G_OBJECT (session),
966 e_mail_session_send_to), FALSE);
967
968 simple = G_SIMPLE_ASYNC_RESULT (result);
969
970 /* Assume success unless a GError is set. */
971 return !g_simple_async_result_propagate_error (simple, error);
972 }
973
974 /* Helper for e_mail_session_get_fcc_for_message_sync() */
975 static CamelFolder *
mail_session_try_uri_to_folder(EMailSession * session,const gchar * folder_uri,GCancellable * cancellable,GError ** error)976 mail_session_try_uri_to_folder (EMailSession *session,
977 const gchar *folder_uri,
978 GCancellable *cancellable,
979 GError **error)
980 {
981 CamelFolder *folder;
982 GError *local_error = NULL;
983
984 folder = e_mail_session_uri_to_folder_sync (
985 session, folder_uri, 0, cancellable, &local_error);
986
987 /* Sanity check. */
988 g_return_val_if_fail (
989 ((folder != NULL) && (local_error == NULL)) ||
990 ((folder == NULL) && (local_error != NULL)), NULL);
991
992 /* Disregard specific errors. */
993
994 /* Invalid URI. */
995 if (g_error_matches (
996 local_error, CAMEL_FOLDER_ERROR,
997 CAMEL_FOLDER_ERROR_INVALID))
998 g_clear_error (&local_error);
999
1000 /* Folder not found. */
1001 if (g_error_matches (
1002 local_error, CAMEL_STORE_ERROR,
1003 CAMEL_STORE_ERROR_NO_FOLDER))
1004 g_clear_error (&local_error);
1005
1006 if (local_error != NULL)
1007 g_propagate_error (error, local_error);
1008
1009 return folder;
1010 }
1011
1012 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1013 static CamelFolder *
mail_session_ref_origin_folder(EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable,GError ** error)1014 mail_session_ref_origin_folder (EMailSession *session,
1015 CamelMimeMessage *message,
1016 GCancellable *cancellable,
1017 GError **error)
1018 {
1019 CamelMedium *medium;
1020 const gchar *header_name;
1021 const gchar *header_value;
1022
1023 medium = CAMEL_MEDIUM (message);
1024
1025 /* Check that a "X-Evolution-Source-Flags" header is present
1026 * and its value does not contain the substring "FORWARDED". */
1027
1028 header_name = "X-Evolution-Source-Flags";
1029 header_value = camel_medium_get_header (medium, header_name);
1030
1031 if (header_value == NULL)
1032 return NULL;
1033
1034 if (strstr (header_value, "FORWARDED") != NULL)
1035 return NULL;
1036
1037 /* Check that a "X-Evolution-Source-Message" header is present. */
1038
1039 header_name = "X-Evolution-Source-Message";
1040 header_value = camel_medium_get_header (medium, header_name);
1041
1042 if (header_value == NULL)
1043 return NULL;
1044
1045 /* Check that a "X-Evolution-Source-Folder" header is present.
1046 * Its value specifies the origin folder as a folder URI. */
1047
1048 header_name = "X-Evolution-Source-Folder";
1049 header_value = camel_medium_get_header (medium, header_name);
1050
1051 if (header_value == NULL)
1052 return NULL;
1053
1054 /* This may return NULL without setting a GError. */
1055 return mail_session_try_uri_to_folder (
1056 session, header_value, cancellable, error);
1057 }
1058
1059 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1060 static CamelFolder *
mail_session_ref_fcc_from_identity(EMailSession * session,ESource * source,CamelMimeMessage * message,gboolean * out_use_sent_folder,GCancellable * cancellable,GError ** error)1061 mail_session_ref_fcc_from_identity (EMailSession *session,
1062 ESource *source,
1063 CamelMimeMessage *message,
1064 gboolean *out_use_sent_folder,
1065 GCancellable *cancellable,
1066 GError **error)
1067 {
1068 ESourceRegistry *registry;
1069 ESourceMailSubmission *extension;
1070 CamelFolder *folder = NULL;
1071 const gchar *extension_name;
1072 gchar *folder_uri;
1073 gboolean use_sent_folder;
1074
1075 registry = e_mail_session_get_registry (session);
1076 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
1077
1078 if (source == NULL)
1079 return NULL;
1080
1081 if (!e_source_registry_check_enabled (registry, source))
1082 return NULL;
1083
1084 if (!e_source_has_extension (source, extension_name))
1085 return NULL;
1086
1087 extension = e_source_get_extension (source, extension_name);
1088 use_sent_folder = e_source_mail_submission_get_use_sent_folder (extension);
1089
1090 if (out_use_sent_folder)
1091 *out_use_sent_folder = use_sent_folder;
1092
1093 if (!use_sent_folder)
1094 return NULL;
1095
1096 if (e_source_mail_submission_get_replies_to_origin_folder (extension)) {
1097 GError *local_error = NULL;
1098
1099 /* This may return NULL without setting a GError. */
1100 folder = mail_session_ref_origin_folder (
1101 session, message, cancellable, &local_error);
1102
1103 if (local_error != NULL) {
1104 g_warn_if_fail (folder == NULL);
1105 g_propagate_error (error, local_error);
1106 return NULL;
1107 }
1108 }
1109
1110 folder_uri = e_source_mail_submission_dup_sent_folder (extension);
1111
1112 if (folder_uri != NULL && folder == NULL) {
1113 /* This may return NULL without setting a GError. */
1114 folder = mail_session_try_uri_to_folder (
1115 session, folder_uri, cancellable, error);
1116 }
1117
1118 g_free (folder_uri);
1119
1120 return folder;
1121 }
1122
1123 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1124 static CamelFolder *
mail_session_ref_fcc_from_x_identity(EMailSession * session,CamelMimeMessage * message,gboolean * out_use_sent_folder,GCancellable * cancellable,GError ** error)1125 mail_session_ref_fcc_from_x_identity (EMailSession *session,
1126 CamelMimeMessage *message,
1127 gboolean *out_use_sent_folder,
1128 GCancellable *cancellable,
1129 GError **error)
1130 {
1131 ESource *source;
1132 ESourceRegistry *registry;
1133 CamelFolder *folder;
1134 CamelMedium *medium;
1135 const gchar *header_name;
1136 const gchar *header_value;
1137 gchar *uid;
1138
1139 medium = CAMEL_MEDIUM (message);
1140 header_name = "X-Evolution-Identity";
1141 header_value = camel_medium_get_header (medium, header_name);
1142
1143 if (header_value == NULL)
1144 return NULL;
1145
1146 uid = g_strstrip (g_strdup (header_value));
1147
1148 registry = e_mail_session_get_registry (session);
1149 source = e_source_registry_ref_source (registry, uid);
1150
1151 /* This may return NULL without setting a GError. */
1152 folder = mail_session_ref_fcc_from_identity (
1153 session, source, message, out_use_sent_folder, cancellable, error);
1154
1155 g_clear_object (&source);
1156
1157 g_free (uid);
1158
1159 return folder;
1160 }
1161
1162 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1163 static CamelFolder *
mail_session_ref_fcc_from_x_fcc(EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable,GError ** error)1164 mail_session_ref_fcc_from_x_fcc (EMailSession *session,
1165 CamelMimeMessage *message,
1166 GCancellable *cancellable,
1167 GError **error)
1168 {
1169 CamelMedium *medium;
1170 const gchar *header_name;
1171 const gchar *header_value;
1172
1173 medium = CAMEL_MEDIUM (message);
1174 header_name = "X-Evolution-Fcc";
1175 header_value = camel_medium_get_header (medium, header_name);
1176
1177 if (header_value == NULL)
1178 return NULL;
1179
1180 /* This may return NULL without setting a GError. */
1181 return mail_session_try_uri_to_folder (
1182 session, header_value, cancellable, error);
1183 }
1184
1185 /* Helper for e_mail_session_get_fcc_for_message_sync() */
1186 static CamelFolder *
mail_session_ref_fcc_from_default_identity(EMailSession * session,CamelMimeMessage * message,gboolean * out_use_sent_folder,GCancellable * cancellable,GError ** error)1187 mail_session_ref_fcc_from_default_identity (EMailSession *session,
1188 CamelMimeMessage *message,
1189 gboolean *out_use_sent_folder,
1190 GCancellable *cancellable,
1191 GError **error)
1192 {
1193 ESource *source;
1194 ESourceRegistry *registry;
1195 CamelFolder *folder;
1196
1197 registry = e_mail_session_get_registry (session);
1198 source = e_source_registry_ref_default_mail_identity (registry);
1199
1200 /* This may return NULL without setting a GError. */
1201 folder = mail_session_ref_fcc_from_identity (
1202 session, source, message, out_use_sent_folder, cancellable, error);
1203
1204 g_clear_object (&source);
1205
1206 return folder;
1207 }
1208
1209 /**
1210 * e_mail_session_get_fcc_for_message_sync:
1211 * @session: an #EMailSession
1212 * @message: a #CamelMimeMessage
1213 * @out_use_sent_folder: (out) (nullable): optional return location to store
1214 * corresponding use-sent-folder for the mail account, or %NULL
1215 * @cancellable: optional #GCancellable object, or %NULL
1216 * @error: return location for a #GError, or %NULL
1217 *
1218 * Obtains the preferred "carbon-copy" folder (a.k.a Fcc) for @message
1219 * by first checking @message for an "X-Evolution-Identity" header, and
1220 * then an "X-Evolution-Fcc" header. Failing that, the function checks
1221 * the default mail identity (if available), and failing even that, the
1222 * function falls back to the Sent folder from the built-in mail store.
1223 *
1224 * Where applicable, the function attempts to honor the
1225 * #ESourceMailSubmission:replies-to-origin-folder preference.
1226 *
1227 * The returned #CamelFolder is referenced for thread-safety and must be
1228 * unreferenced with g_object_unref() when finished with it.
1229 *
1230 * If a non-recoverable error occurs, the function sets @error and returns
1231 * %NULL. It returns %NULL without setting @error when the mail account
1232 * has set to not use sent folder, in which case it indicates that
1233 * in @out_use_sent_folder too.
1234 *
1235 * Returns: a #CamelFolder, or %NULL
1236 **/
1237 CamelFolder *
e_mail_session_get_fcc_for_message_sync(EMailSession * session,CamelMimeMessage * message,gboolean * out_use_sent_folder,GCancellable * cancellable,GError ** error)1238 e_mail_session_get_fcc_for_message_sync (EMailSession *session,
1239 CamelMimeMessage *message,
1240 gboolean *out_use_sent_folder,
1241 GCancellable *cancellable,
1242 GError **error)
1243 {
1244 CamelFolder *folder = NULL;
1245 gboolean use_sent_folder = TRUE;
1246
1247 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1248 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1249
1250 if (out_use_sent_folder)
1251 *out_use_sent_folder = TRUE;
1252
1253 /* Check for "X-Evolution-Identity" header. */
1254 if (folder == NULL) {
1255 GError *local_error = NULL;
1256
1257 /* This may return NULL without setting a GError. */
1258 folder = mail_session_ref_fcc_from_x_identity (
1259 session, message, &use_sent_folder, cancellable, &local_error);
1260
1261 if (local_error != NULL) {
1262 g_warn_if_fail (folder == NULL);
1263 g_propagate_error (error, local_error);
1264 return NULL;
1265 }
1266
1267 if (!use_sent_folder) {
1268 if (out_use_sent_folder)
1269 *out_use_sent_folder = use_sent_folder;
1270 return NULL;
1271 }
1272 }
1273
1274 /* Check for "X-Evolution-Fcc" header. */
1275 if (folder == NULL) {
1276 GError *local_error = NULL;
1277
1278 /* This may return NULL without setting a GError. */
1279 folder = mail_session_ref_fcc_from_x_fcc (
1280 session, message, cancellable, &local_error);
1281
1282 if (local_error != NULL) {
1283 g_warn_if_fail (folder == NULL);
1284 g_propagate_error (error, local_error);
1285 return NULL;
1286 }
1287 }
1288
1289 /* Check the default mail identity. */
1290 if (folder == NULL) {
1291 GError *local_error = NULL;
1292
1293 /* This may return NULL without setting a GError. */
1294 folder = mail_session_ref_fcc_from_default_identity (
1295 session, message, &use_sent_folder, cancellable, &local_error);
1296
1297 if (local_error != NULL) {
1298 g_warn_if_fail (folder == NULL);
1299 g_propagate_error (error, local_error);
1300 return NULL;
1301 }
1302
1303 if (!use_sent_folder) {
1304 if (out_use_sent_folder)
1305 *out_use_sent_folder = use_sent_folder;
1306 return NULL;
1307 }
1308 }
1309
1310 /* Last resort - local Sent folder. */
1311 if (folder == NULL) {
1312 folder = e_mail_session_get_local_folder (
1313 session, E_MAIL_LOCAL_FOLDER_SENT);
1314 g_object_ref (folder);
1315 }
1316
1317 return folder;
1318 }
1319
1320 /* Helper for e_mail_session_get_fcc_for_message() */
1321 static void
mail_session_get_fcc_for_message_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1322 mail_session_get_fcc_for_message_thread (GSimpleAsyncResult *simple,
1323 GObject *source_object,
1324 GCancellable *cancellable)
1325 {
1326 AsyncContext *async_context;
1327 GError *local_error = NULL;
1328
1329 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1330
1331 async_context->folder =
1332 e_mail_session_get_fcc_for_message_sync (
1333 E_MAIL_SESSION (source_object),
1334 async_context->message,
1335 &async_context->use_sent_folder,
1336 cancellable, &local_error);
1337
1338 if (local_error != NULL)
1339 g_simple_async_result_take_error (simple, local_error);
1340 }
1341
1342 /**
1343 * e_mail_session_get_fcc_for_message:
1344 * @session: an #EMailSession
1345 * @message: a #CamelMimeMessage
1346 * @io_priority: the I/O priority of the request
1347 * @cancellable: optional #GCancellable object, or %NULL
1348 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1349 * @user_data: data to pass to the callback function
1350 *
1351 * Asynchronously obtains the preferred "carbon-copy" folder (a.k.a Fcc) for
1352 * @message by first checking @message for an "X-Evolution-Identity" header,
1353 * and then an "X-Evolution-Fcc" header. Failing that, the function checks
1354 * the default mail identity (if available), and failing even that, the
1355 * function falls back to the Sent folder from the built-in mail store.
1356 *
1357 * Where applicable, the function attempts to honor the
1358 * #ESourceMailSubmission:replies-to-origin-folder preference.
1359 *
1360 * When the operation is finished, @callback will be called. You can then
1361 * call e_mail_session_get_fcc_for_message_finish() to get the result of the
1362 * operation.
1363 **/
1364 void
e_mail_session_get_fcc_for_message(EMailSession * session,CamelMimeMessage * message,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1365 e_mail_session_get_fcc_for_message (EMailSession *session,
1366 CamelMimeMessage *message,
1367 gint io_priority,
1368 GCancellable *cancellable,
1369 GAsyncReadyCallback callback,
1370 gpointer user_data)
1371 {
1372 GSimpleAsyncResult *simple;
1373 AsyncContext *async_context;
1374
1375 g_return_if_fail (E_IS_MAIL_SESSION (session));
1376 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1377
1378 async_context = g_slice_new0 (AsyncContext);
1379 async_context->message = g_object_ref (message);
1380
1381 simple = g_simple_async_result_new (
1382 G_OBJECT (session), callback, user_data,
1383 e_mail_session_get_fcc_for_message);
1384
1385 g_simple_async_result_set_check_cancellable (simple, cancellable);
1386
1387 g_simple_async_result_set_op_res_gpointer (
1388 simple, async_context, (GDestroyNotify) async_context_free);
1389
1390 g_simple_async_result_run_in_thread (
1391 simple, mail_session_get_fcc_for_message_thread,
1392 io_priority, cancellable);
1393
1394 g_object_unref (simple);
1395 }
1396
1397 /**
1398 * e_mail_session_get_fcc_for_message_finish:
1399 * @session: an #EMailSession
1400 * @result: a #GAsyncResult
1401 * @out_use_sent_folder: (out) (nullable): optional return location to store
1402 * corresponding use-sent-folder for the mail account, or %NULL
1403 * @error: return location for a #GError, or %NULL
1404 *
1405 * Finishes the operation started with e_mail_session_get_fcc_for_message().
1406 *
1407 * The returned #CamelFolder is referenced for thread-safety and must be
1408 * unreferenced with g_object_unref() when finished with it.
1409 *
1410 * If a non-recoverable error occurred, the function sets @error and
1411 * returns %NULL. It returns %NULL without setting @error when the mail account
1412 * has set to not use sent folder, in which case it indicates that
1413 * in @out_use_sent_folder too.
1414 *
1415 * Returns: a #CamelFolder, or %NULL
1416 **/
1417 CamelFolder *
e_mail_session_get_fcc_for_message_finish(EMailSession * session,GAsyncResult * result,gboolean * out_use_sent_folder,GError ** error)1418 e_mail_session_get_fcc_for_message_finish (EMailSession *session,
1419 GAsyncResult *result,
1420 gboolean *out_use_sent_folder,
1421 GError **error)
1422 {
1423 GSimpleAsyncResult *simple;
1424 AsyncContext *async_context;
1425
1426 g_return_val_if_fail (
1427 g_simple_async_result_is_valid (
1428 result, G_OBJECT (session),
1429 e_mail_session_get_fcc_for_message), NULL);
1430
1431 simple = G_SIMPLE_ASYNC_RESULT (result);
1432 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1433
1434 if (g_simple_async_result_propagate_error (simple, error))
1435 return NULL;
1436
1437 if (out_use_sent_folder)
1438 *out_use_sent_folder = async_context->use_sent_folder;
1439
1440 if (!async_context->use_sent_folder) {
1441 g_return_val_if_fail (async_context->folder == NULL, NULL);
1442 return NULL;
1443 }
1444
1445 g_return_val_if_fail (async_context->folder != NULL, NULL);
1446
1447 return g_object_ref (async_context->folder);
1448 }
1449
1450 /**
1451 * e_mail_session_ref_transport:
1452 * @session: an #EMailSession
1453 * @transport_uid: the UID of a mail transport
1454 *
1455 * Returns the transport #CamelService instance for @transport_uid,
1456 * verifying first that the @transport_uid is indeed a mail transport and
1457 * that the corresponding #ESource is enabled. If these checks fail, the
1458 * function returns %NULL.
1459 *
1460 * The returned #CamelService is referenced for thread-safety and must be
1461 * unreferenced with g_object_unref() when finished with it.
1462 *
1463 * Returns: a #CamelService, or %NULL
1464 **/
1465 CamelService *
e_mail_session_ref_transport(EMailSession * session,const gchar * transport_uid)1466 e_mail_session_ref_transport (EMailSession *session,
1467 const gchar *transport_uid)
1468 {
1469 ESourceRegistry *registry;
1470 ESource *source = NULL;
1471 CamelService *transport = NULL;
1472 const gchar *extension_name;
1473
1474 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1475 g_return_val_if_fail (transport_uid != NULL, NULL);
1476
1477 registry = e_mail_session_get_registry (session);
1478 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
1479
1480 source = e_source_registry_ref_source (registry, transport_uid);
1481
1482 if (source == NULL)
1483 goto exit;
1484
1485 if (!e_source_registry_check_enabled (registry, source))
1486 goto exit;
1487
1488 if (!e_source_has_extension (source, extension_name))
1489 goto exit;
1490
1491 transport = camel_session_ref_service (
1492 CAMEL_SESSION (session), transport_uid);
1493
1494 /* Sanity check. */
1495 if (transport != NULL)
1496 g_warn_if_fail (CAMEL_IS_TRANSPORT (transport));
1497
1498 exit:
1499 g_clear_object (&source);
1500
1501 return transport;
1502 }
1503
1504 /* Helper for e_mail_session_ref_default_transport()
1505 * and mail_session_ref_transport_from_x_identity(). */
1506 static CamelService *
mail_session_ref_transport_for_identity(EMailSession * session,ESource * source)1507 mail_session_ref_transport_for_identity (EMailSession *session,
1508 ESource *source)
1509 {
1510 ESourceRegistry *registry;
1511 ESourceMailSubmission *extension;
1512 CamelService *transport = NULL;
1513 const gchar *extension_name;
1514 gchar *uid;
1515
1516 registry = e_mail_session_get_registry (session);
1517 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
1518
1519 if (source == NULL)
1520 return NULL;
1521
1522 if (!e_source_registry_check_enabled (registry, source))
1523 return NULL;
1524
1525 if (!e_source_has_extension (source, extension_name))
1526 return NULL;
1527
1528 extension = e_source_get_extension (source, extension_name);
1529 uid = e_source_mail_submission_dup_transport_uid (extension);
1530
1531 if (uid != NULL) {
1532 transport = e_mail_session_ref_transport (session, uid);
1533 g_free (uid);
1534 }
1535
1536 return transport;
1537 }
1538
1539 /**
1540 * e_mail_session_ref_default_transport:
1541 * @session: an #EMailSession
1542 *
1543 * Returns the default transport #CamelService instance according to
1544 * #ESourceRegistry's #ESourceRegistry:default-mail-identity setting,
1545 * verifying first that the #ESourceMailSubmission:transport-uid named by
1546 * the #ESourceRegistry:default-mail-identity is indeed a mail transport,
1547 * and that the corresponding #ESource is enabled. If these checks fail,
1548 * the function returns %NULL.
1549 *
1550 * The returned #CamelService is referenced for thread-safety and must be
1551 * unreferenced with g_object_unref() when finished with it.
1552 *
1553 * Returns: a #CamelService, or %NULL
1554 **/
1555 CamelService *
e_mail_session_ref_default_transport(EMailSession * session)1556 e_mail_session_ref_default_transport (EMailSession *session)
1557 {
1558 ESource *source;
1559 ESourceRegistry *registry;
1560 CamelService *transport;
1561
1562 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1563
1564 registry = e_mail_session_get_registry (session);
1565 source = e_source_registry_ref_default_mail_identity (registry);
1566 transport = mail_session_ref_transport_for_identity (session, source);
1567 g_clear_object (&source);
1568
1569 return transport;
1570 }
1571
1572 /* Helper for e_mail_session_ref_transport_for_message() */
1573 static CamelService *
mail_session_ref_transport_from_x_identity(EMailSession * session,CamelMimeMessage * message)1574 mail_session_ref_transport_from_x_identity (EMailSession *session,
1575 CamelMimeMessage *message)
1576 {
1577 ESource *source;
1578 ESourceRegistry *registry;
1579 CamelMedium *medium;
1580 CamelService *transport;
1581 const gchar *header_name;
1582 const gchar *header_value;
1583 gchar *uid;
1584
1585 medium = CAMEL_MEDIUM (message);
1586 header_name = "X-Evolution-Identity";
1587 header_value = camel_medium_get_header (medium, header_name);
1588
1589 if (header_value == NULL)
1590 return NULL;
1591
1592 uid = g_strstrip (g_strdup (header_value));
1593
1594 registry = e_mail_session_get_registry (session);
1595 source = e_source_registry_ref_source (registry, uid);
1596 transport = mail_session_ref_transport_for_identity (session, source);
1597 g_clear_object (&source);
1598
1599 g_free (uid);
1600
1601 return transport;
1602 }
1603
1604 /* Helper for e_mail_session_ref_transport_for_message() */
1605 static CamelService *
mail_session_ref_transport_from_x_transport(EMailSession * session,CamelMimeMessage * message)1606 mail_session_ref_transport_from_x_transport (EMailSession *session,
1607 CamelMimeMessage *message)
1608 {
1609 CamelMedium *medium;
1610 CamelService *transport;
1611 const gchar *header_name;
1612 const gchar *header_value;
1613 gchar *uid;
1614
1615 medium = CAMEL_MEDIUM (message);
1616 header_name = "X-Evolution-Transport";
1617 header_value = camel_medium_get_header (medium, header_name);
1618
1619 if (header_value == NULL)
1620 return NULL;
1621
1622 uid = g_strstrip (g_strdup (header_value));
1623
1624 transport = e_mail_session_ref_transport (session, uid);
1625
1626 g_free (uid);
1627
1628 return transport;
1629 }
1630
1631 /**
1632 * e_mail_session_ref_transport_for_message:
1633 * @session: an #EMailSession
1634 * @message: a #CamelMimeMessage
1635 *
1636 * Returns the preferred transport #CamelService instance for @message by
1637 * first checking @message for an "X-Evolution-Identity" header, and then
1638 * an "X-Evolution-Transport" header. Failing that, the function returns
1639 * the default transport #CamelService instance (if available).
1640 *
1641 * The returned #CamelService is referenced for thread-safety and must be
1642 * unreferenced with g_object_unref() when finished with it.
1643 *
1644 * Returns: a #CamelService, or %NULL
1645 **/
1646 CamelService *
e_mail_session_ref_transport_for_message(EMailSession * session,CamelMimeMessage * message)1647 e_mail_session_ref_transport_for_message (EMailSession *session,
1648 CamelMimeMessage *message)
1649 {
1650 CamelService *transport = NULL;
1651
1652 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1653 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1654
1655 /* Check for "X-Evolution-Identity" header. */
1656 if (transport == NULL)
1657 transport = mail_session_ref_transport_from_x_identity (
1658 session, message);
1659
1660 /* Check for "X-Evolution-Transport" header. */
1661 if (transport == NULL)
1662 transport = mail_session_ref_transport_from_x_transport (
1663 session, message);
1664
1665 /* Fall back to the default mail transport. */
1666 if (transport == NULL)
1667 transport = e_mail_session_ref_default_transport (session);
1668
1669 return transport;
1670 }
1671
1672