evolution-3.6.4/libemail-engine/e-mail-session-utils.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-session-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-session-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-mail-session-utils.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  */
 18 
 19 #ifdef HAVE_CONFIG_H
 20 #include <config.h>
 21 #endif
 22 
 23 #include "e-mail-session-utils.h"
 24 
 25 #include <glib/gi18n-lib.h>
 26 #include <libedataserver/libedataserver.h>
 27 
 28 #include <libemail-engine/e-mail-folder-utils.h>
 29 #include <libemail-engine/e-mail-utils.h>
 30 #include <libemail-engine/mail-tools.h>
 31 
 32 /* X-Mailer header value */
 33 #define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
 34 
 35 /* FIXME: Temporary - remove this after we move filter/ to eds */
 36 #define E_FILTER_SOURCE_OUTGOING "outgoing"
 37 
 38 typedef struct _AsyncContext AsyncContext;
 39 
 40 struct _AsyncContext {
 41 	CamelFolder *sent_folder;
 42 
 43 	CamelMimeMessage *message;
 44 	CamelMessageInfo *info;
 45 
 46 	CamelAddress *from;
 47 	CamelAddress *recipients;
 48 
 49 	CamelFilterDriver *driver;
 50 
 51 	GCancellable *cancellable;
 52 	gint io_priority;
 53 
 54 	/* X-Evolution headers */
 55 	struct _camel_header_raw *xev;
 56 
 57 	GPtrArray *post_to_uris;
 58 
 59 	EMailLocalFolder local_id;
 60 
 61 	gchar *folder_uri;
 62 	gchar *message_uid;
 63 	gchar *transport_uid;
 64 	gchar *sent_folder_uri;
 65 };
 66 
 67 static void
 68 async_context_free (AsyncContext *context)
 69 {
 70 	if (context->sent_folder != NULL)
 71 		g_object_unref (context->sent_folder);
 72 
 73 	if (context->message != NULL)
 74 		g_object_unref (context->message);
 75 
 76 	if (context->info != NULL)
 77 		camel_message_info_free (context->info);
 78 
 79 	if (context->from != NULL)
 80 		g_object_unref (context->from);
 81 
 82 	if (context->recipients != NULL)
 83 		g_object_unref (context->recipients);
 84 
 85 	if (context->driver != NULL)
 86 		g_object_unref (context->driver);
 87 
 88 	if (context->cancellable != NULL) {
 89 		camel_operation_pop_message (context->cancellable);
 90 		g_object_unref (context->cancellable);
 91 	}
 92 
 93 	if (context->xev != NULL)
 94 		camel_header_raw_clear (&context->xev);
 95 
 96 	if (context->post_to_uris != NULL) {
 97 		g_ptr_array_foreach (
 98 			context->post_to_uris, (GFunc) g_free, NULL);
 99 		g_ptr_array_free (context->post_to_uris, TRUE);
100 	}
101 
102 	g_free (context->folder_uri);
103 	g_free (context->message_uid);
104 	g_free (context->transport_uid);
105 	g_free (context->sent_folder_uri);
106 
107 	g_slice_free (AsyncContext, context);
108 }
109 
110 GQuark
111 e_mail_error_quark (void)
112 {
113 	static GQuark quark = 0;
114 
115 	if (G_UNLIKELY (quark == 0)) {
116 		const gchar *string = "e-mail-error-quark";
117 		quark = g_quark_from_static_string (string);
118 	}
119 
120 	return quark;
121 }
122 
123 static void
124 mail_session_append_to_local_folder_thread (GSimpleAsyncResult *simple,
125                                             GObject *object,
126                                             GCancellable *cancellable)
127 {
128 	AsyncContext *context;
129 	GError *error = NULL;
130 
131 	context = g_simple_async_result_get_op_res_gpointer (simple);
132 
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);
138 
139 	if (error != NULL)
140 		g_simple_async_result_take_error (simple, error);
141 }
142 
143 gboolean
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,
150                                             GError **error)
151 {
152 	CamelFolder *folder;
153 	const gchar *folder_uri;
154 	gboolean success = FALSE;
155 
156 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
157 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
158 
159 	folder_uri = e_mail_session_get_local_folder_uri (session, local_id);
160 	g_return_val_if_fail (folder_uri != NULL, FALSE);
161 
162 	folder = e_mail_session_uri_to_folder_sync (
163 		session, folder_uri, CAMEL_STORE_FOLDER_CREATE,
164 		cancellable, error);
165 
166 	if (folder != NULL) {
167 		success = e_mail_folder_append_message_sync (
168 			folder, message, info, appended_uid,
169 			cancellable, error);
170 		g_object_unref (folder);
171 	}
172 
173 	return success;
174 }
175 
176 void
177 e_mail_session_append_to_local_folder (EMailSession *session,
178                                        EMailLocalFolder local_id,
179                                        CamelMimeMessage *message,
180                                        CamelMessageInfo *info,
181                                        gint io_priority,
182                                        GCancellable *cancellable,
183                                        GAsyncReadyCallback callback,
184                                        gpointer user_data)
185 {
186 	GSimpleAsyncResult *simple;
187 	AsyncContext *context;
188 
189 	g_return_if_fail (E_IS_MAIL_SESSION (session));
190 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
191 
192 	context = g_slice_new0 (AsyncContext);
193 	context->local_id = local_id;
194 	context->message = g_object_ref (message);
195 
196 	if (info != NULL)
197 		context->info = camel_message_info_ref (info);
198 
199 	simple = g_simple_async_result_new (
200 		G_OBJECT (session), callback, user_data,
201 		e_mail_session_append_to_local_folder);
202 
203 	g_simple_async_result_set_check_cancellable (simple, cancellable);
204 
205 	g_simple_async_result_set_op_res_gpointer (
206 		simple, context, (GDestroyNotify) async_context_free);
207 
208 	g_simple_async_result_run_in_thread (
209 		simple, mail_session_append_to_local_folder_thread,
210 		io_priority, cancellable);
211 
212 	g_object_unref (simple);
213 }
214 
215 gboolean
216 e_mail_session_append_to_local_folder_finish (EMailSession *session,
217                                               GAsyncResult *result,
218                                               gchar **appended_uid,
219                                               GError **error)
220 {
221 	GSimpleAsyncResult *simple;
222 	AsyncContext *context;
223 
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);
228 
229 	simple = G_SIMPLE_ASYNC_RESULT (result);
230 	context = g_simple_async_result_get_op_res_gpointer (simple);
231 
232 	if (appended_uid != NULL) {
233 		*appended_uid = context->message_uid;
234 		context->message_uid = NULL;
235 	}
236 
237 	/* Assume success unless a GError is set. */
238 	return !g_simple_async_result_propagate_error (simple, error);
239 }
240 
241 static void
242 mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
243                                           EMailSession *session,
244                                           GCancellable *cancellable)
245 {
246 	AsyncContext *context;
247 	GError *error = NULL;
248 
249 	context = g_simple_async_result_get_op_res_gpointer (simple);
250 
251 	e_mail_session_handle_draft_headers_sync (
252 		session, context->message, cancellable, &error);
253 
254 	if (error != NULL)
255 		g_simple_async_result_take_error (simple, error);
256 }
257 
258 gboolean
259 e_mail_session_handle_draft_headers_sync (EMailSession *session,
260                                           CamelMimeMessage *message,
261                                           GCancellable *cancellable,
262                                           GError **error)
263 {
264 	CamelFolder *folder;
265 	CamelMedium *medium;
266 	const gchar *folder_uri;
267 	const gchar *message_uid;
268 	const gchar *header_name;
269 	gboolean success;
270 
271 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
272 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
273 
274 	medium = CAMEL_MEDIUM (message);
275 
276 	header_name = "X-Evolution-Draft-Folder";
277 	folder_uri = camel_medium_get_header (medium, header_name);
278 
279 	header_name = "X-Evolution-Draft-Message";
280 	message_uid = camel_medium_get_header (medium, header_name);
281 
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)
286 		return TRUE;
287 
288 	folder = e_mail_session_uri_to_folder_sync (
289 		session, folder_uri, 0, cancellable, error);
290 
291 	if (folder == NULL)
292 		return FALSE;
293 
294 	camel_folder_set_message_flags (
295 		folder, message_uid,
296 		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
297 		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
298 
299 	success = camel_folder_synchronize_message_sync (
300 		folder, message_uid, cancellable, error);
301 
302 	g_object_unref (folder);
303 
304 	return success;
305 }
306 
307 void
308 e_mail_session_handle_draft_headers (EMailSession *session,
309                                      CamelMimeMessage *message,
310                                      gint io_priority,
311                                      GCancellable *cancellable,
312                                      GAsyncReadyCallback callback,
313                                      gpointer user_data)
314 {
315 	GSimpleAsyncResult *simple;
316 	AsyncContext *context;
317 
318 	g_return_if_fail (E_IS_MAIL_SESSION (session));
319 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
320 
321 	context = g_slice_new0 (AsyncContext);
322 	context->message = g_object_ref (message);
323 
324 	simple = g_simple_async_result_new (
325 		G_OBJECT (session), callback, user_data,
326 		e_mail_session_handle_draft_headers);
327 
328 	g_simple_async_result_set_check_cancellable (simple, cancellable);
329 
330 	g_simple_async_result_set_op_res_gpointer (
331 		simple, context, (GDestroyNotify) async_context_free);
332 
333 	g_simple_async_result_run_in_thread (
334 		simple, (GSimpleAsyncThreadFunc)
335 		mail_session_handle_draft_headers_thread,
336 		io_priority, cancellable);
337 
338 	g_object_unref (simple);
339 }
340 
341 gboolean
342 e_mail_session_handle_draft_headers_finish (EMailSession *session,
343                                             GAsyncResult *result,
344                                             GError **error)
345 {
346 	GSimpleAsyncResult *simple;
347 
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);
352 
353 	simple = G_SIMPLE_ASYNC_RESULT (result);
354 
355 	/* Assume success unless a GError is set. */
356 	return !g_simple_async_result_propagate_error (simple, error);
357 }
358 
359 static void
360 mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
361                                            EMailSession *session,
362                                            GCancellable *cancellable)
363 {
364 	AsyncContext *context;
365 	GError *error = NULL;
366 
367 	context = g_simple_async_result_get_op_res_gpointer (simple);
368 
369 	e_mail_session_handle_source_headers_sync (
370 		session, context->message, cancellable, &error);
371 
372 	if (error != NULL)
373 		g_simple_async_result_take_error (simple, error);
374 }
375 
376 gboolean
377 e_mail_session_handle_source_headers_sync (EMailSession *session,
378                                            CamelMimeMessage *message,
379                                            GCancellable *cancellable,
380                                            GError **error)
381 {
382 	CamelFolder *folder;
383 	CamelMedium *medium;
384 	CamelMessageFlags flags = 0;
385 	const gchar *folder_uri;
386 	const gchar *message_uid;
387 	const gchar *flag_string;
388 	const gchar *header_name;
389 	gboolean success;
390 	guint length, ii;
391 	gchar **tokens;
392 	gchar *string;
393 
394 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
395 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
396 
397 	medium = CAMEL_MEDIUM (message);
398 
399 	header_name = "X-Evolution-Source-Folder";
400 	folder_uri = camel_medium_get_header (medium, header_name);
401 
402 	header_name = "X-Evolution-Source-Message";
403 	message_uid = camel_medium_get_header (medium, header_name);
404 
405 	header_name = "X-Evolution-Source-Flags";
406 	flag_string = camel_medium_get_header (medium, header_name);
407 
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)
412 		return TRUE;
413 
414 	/* Convert the flag string to CamelMessageFlags. */
415 
416 	string = g_strstrip (g_strdup (flag_string));
417 	tokens = g_strsplit (string, " ", 0);
418 	g_free (string);
419 
420 	/* If tokens is NULL, a length of 0 will skip the loop. */
421 	length = (tokens != NULL) ? g_strv_length (tokens) : 0;
422 
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;
435 		else
436 			g_warning (
437 				"Unknown flag '%s' in %s",
438 				tokens[ii], header_name);
439 	}
440 
441 	g_strfreev (tokens);
442 
443 	folder = e_mail_session_uri_to_folder_sync (
444 		session, folder_uri, 0, cancellable, error);
445 
446 	if (folder == NULL)
447 		return FALSE;
448 
449 	camel_folder_set_message_flags (
450 		folder, message_uid, flags, flags);
451 
452 	success = camel_folder_synchronize_message_sync (
453 		folder, message_uid, cancellable, error);
454 
455 	g_object_unref (folder);
456 
457 	return success;
458 }
459 
460 void
461 e_mail_session_handle_source_headers (EMailSession *session,
462                                       CamelMimeMessage *message,
463                                       gint io_priority,
464                                       GCancellable *cancellable,
465                                       GAsyncReadyCallback callback,
466                                       gpointer user_data)
467 {
468 	GSimpleAsyncResult *simple;
469 	AsyncContext *context;
470 
471 	g_return_if_fail (E_IS_MAIL_SESSION (session));
472 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
473 
474 	context = g_slice_new0 (AsyncContext);
475 	context->message = g_object_ref (message);
476 
477 	simple = g_simple_async_result_new (
478 		G_OBJECT (session), callback, user_data,
479 		e_mail_session_handle_source_headers);
480 
481 	g_simple_async_result_set_check_cancellable (simple, cancellable);
482 
483 	g_simple_async_result_set_op_res_gpointer (
484 		simple, context, (GDestroyNotify) async_context_free);
485 
486 	g_simple_async_result_run_in_thread (
487 		simple, (GSimpleAsyncThreadFunc)
488 		mail_session_handle_source_headers_thread,
489 		io_priority, cancellable);
490 
491 	g_object_unref (simple);
492 }
493 
494 gboolean
495 e_mail_session_handle_source_headers_finish (EMailSession *session,
496                                              GAsyncResult *result,
497                                              GError **error)
498 {
499 	GSimpleAsyncResult *simple;
500 
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);
505 
506 	simple = G_SIMPLE_ASYNC_RESULT (result);
507 
508 	/* Assume success unless a GError is set. */
509 	return !g_simple_async_result_propagate_error (simple, error);
510 }
511 
512 static void
513 mail_session_send_to_thread (GSimpleAsyncResult *simple,
514                              EMailSession *session,
515                              GCancellable *cancellable)
516 {
517 	AsyncContext *context;
518 	CamelFolder *local_sent_folder;
519 	CamelServiceConnectionStatus status;
520 	GString *error_messages;
521 	gboolean copy_to_sent = TRUE;
522 	guint ii;
523 	GError *error = NULL;
524 
525 	context = g_simple_async_result_get_op_res_gpointer (simple);
526 
527 	/* Send the message to all recipients. */
528 	if (camel_address_length (context->recipients) > 0) {
529 		CamelProvider *provider;
530 		CamelService *service;
531 		gboolean did_connect = FALSE;
532 
533 		service = camel_session_ref_service (
534 			CAMEL_SESSION (session), context->transport_uid);
535 
536 		if (service == NULL) {
537 			g_simple_async_result_set_error (
538 				simple, CAMEL_SERVICE_ERROR,
539 				CAMEL_SERVICE_ERROR_URL_INVALID,
540 				_("No mail service found with UID '%s'"),
541 				context->transport_uid);
542 			return;
543 		}
544 
545 		if (!CAMEL_IS_TRANSPORT (service)) {
546 			g_simple_async_result_set_error (
547 				simple, CAMEL_SERVICE_ERROR,
548 				CAMEL_SERVICE_ERROR_URL_INVALID,
549 				_("UID '%s' is not a mail transport"),
550 				context->transport_uid);
551 			g_object_unref (service);
552 			return;
553 		}
554 
555 		status = camel_service_get_connection_status (service);
556 		if (status != CAMEL_SERVICE_CONNECTED) {
557 			did_connect = TRUE;
558 
559 			camel_service_connect_sync (
560 				service, cancellable, &error);
561 
562 			if (error != NULL) {
563 				g_simple_async_result_take_error (simple, error);
564 				g_object_unref (service);
565 				return;
566 			}
567 		}
568 
569 		provider = camel_service_get_provider (service);
570 
571 		if (provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
572 			copy_to_sent = FALSE;
573 
574 		camel_transport_send_to_sync (
575 			CAMEL_TRANSPORT (service),
576 			context->message, context->from,
577 			context->recipients, cancellable, &error);
578 
579 		if (did_connect) {
580 			/* if the cancellable is cancelled, then the disconnect will not run,
581 			   thus reset it to ensure the service will be properly disconnected */
582 			if (cancellable)
583 				g_cancellable_reset (cancellable);
584 
585 			camel_service_disconnect_sync (
586 				service, error == NULL,
587 				cancellable, error ? NULL : &error);
588 		}
589 
590 		g_object_unref (service);
591 
592 		if (error != NULL) {
593 			g_simple_async_result_take_error (simple, error);
594 			return;
595 		}
596 	}
597 
598 	/* Post the message to requested folders. */
599 	for (ii = 0; ii < context->post_to_uris->len; ii++) {
600 		CamelFolder *folder;
601 		const gchar *folder_uri;
602 
603 		folder_uri = g_ptr_array_index (context->post_to_uris, ii);
604 
605 		folder = e_mail_session_uri_to_folder_sync (
606 			session, folder_uri, 0, cancellable, &error);
607 
608 		if (error != NULL) {
609 			g_warn_if_fail (folder == NULL);
610 			g_simple_async_result_take_error (simple, error);
611 			return;
612 		}
613 
614 		g_return_if_fail (CAMEL_IS_FOLDER (folder));
615 
616 		camel_folder_append_message_sync (
617 			folder, context->message, context->info,
618 			NULL, cancellable, &error);
619 
620 		g_object_unref (folder);
621 
622 		if (error != NULL) {
623 			g_simple_async_result_take_error (simple, error);
624 			return;
625 		}
626 	}
627 
628 	/*** Post Processing ***/
629 
630 	/* This accumulates error messages during post-processing. */
631 	error_messages = g_string_sized_new (256);
632 
633 	mail_tool_restore_xevolution_headers (context->message, context->xev);
634 
635 	/* Run filters on the outgoing message. */
636 	if (context->driver != NULL) {
637 		camel_filter_driver_filter_message (
638 			context->driver, context->message, context->info,
639 			NULL, NULL, NULL, "", cancellable, &error);
640 
641 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
642 			goto exit;
643 
644 		if (error != NULL) {
645 			g_string_append_printf (
646 				error_messages,
647 				_("Failed to apply outgoing filters: %s"),
648 				error->message);
649 			g_clear_error (&error);
650 		}
651 
652 		if ((camel_message_info_flags (context->info) & CAMEL_MESSAGE_DELETED) != 0)
653 			copy_to_sent = FALSE;
654 	}
655 
656 	if (!copy_to_sent)
657 		goto cleanup;
658 
659 	/* Append the sent message to a Sent folder. */
660 
661 	local_sent_folder =
662 		e_mail_session_get_local_folder (
663 		session, E_MAIL_LOCAL_FOLDER_SENT);
664 
665 	/* Try to extract a CamelFolder from the Sent folder URI. */
666 	if (context->sent_folder_uri != NULL) {
667 		context->sent_folder = e_mail_session_uri_to_folder_sync (
668 			session, context->sent_folder_uri, 0,
669 			cancellable, &error);
670 		if (error != NULL) {
671 			g_warn_if_fail (context->sent_folder == NULL);
672 			if (error_messages->len > 0)
673 				g_string_append (error_messages, "\n\n");
674 			g_string_append_printf (
675 				error_messages,
676 				_("Failed to append to %s: %s\n"
677 				"Appending to local 'Sent' folder instead."),
678 				context->sent_folder_uri, error->message);
679 			g_clear_error (&error);
680 		}
681 	}
682 
683 	/* Fall back to the local Sent folder. */
684 	if (context->sent_folder == NULL)
685 		context->sent_folder = g_object_ref (local_sent_folder);
686 
687 	/* Append the message. */
688 	camel_folder_append_message_sync (
689 		context->sent_folder, context->message,
690 		context->info, NULL, cancellable, &error);
691 
692 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
693 		goto exit;
694 
695 	if (error == NULL)
696 		goto cleanup;
697 
698 	/* If appending to a remote Sent folder failed,
699 	 * try appending to the local Sent folder. */
700 	if (context->sent_folder != local_sent_folder) {
701 		const gchar *description;
702 
703 		description = camel_folder_get_description (
704 			context->sent_folder);
705 
706 		if (error_messages->len > 0)
707 			g_string_append (error_messages, "\n\n");
708 		g_string_append_printf (
709 			error_messages,
710 			_("Failed to append to %s: %s\n"
711 			"Appending to local 'Sent' folder instead."),
712 			description, error->message);
713 		g_clear_error (&error);
714 
715 		camel_folder_append_message_sync (
716 			local_sent_folder, context->message,
717 			context->info, NULL, cancellable, &error);
718 	}
719 
720 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
721 		goto exit;
722 
723 	/* We can't even append to the local Sent folder?
724 	 * In that case just leave the message in Outbox. */
725 	if (error != NULL) {
726 		if (error_messages->len > 0)
727 			g_string_append (error_messages, "\n\n");
728 		g_string_append_printf (
729 			error_messages,
730 			_("Failed to append to local 'Sent' folder: %s"),
731 			error->message);
732 		g_clear_error (&error);
733 		goto exit;
734 	}
735 
736 cleanup:
737 
738 	/* The send operation was successful; ignore cleanup errors. */
739 
740 	/* Mark the draft message for deletion, if present. */
741 	e_mail_session_handle_draft_headers_sync (
742 		session, context->message, cancellable, &error);
743 	if (error != NULL) {
744 		g_warning ("%s", error->message);
745 		g_clear_error (&error);
746 	}
747 
748 	/* Set flags on the original source message, if present.
749 	 * Source message refers to the message being forwarded
750 	 * or replied to. */
751 	e_mail_session_handle_source_headers_sync (
752 		session, context->message, cancellable, &error);
753 	if (error != NULL) {
754 		g_warning ("%s", error->message);
755 		g_clear_error (&error);
756 	}
757 
758 exit:
759 
760 	/* If we were cancelled, disregard any other errors. */
761 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
762 		g_simple_async_result_take_error (simple, error);
763 
764 	/* Stuff the accumulated error messages in a GError. */
765 	} else if (error_messages->len > 0) {
766 		g_simple_async_result_set_error (
767 			simple, E_MAIL_ERROR,
768 			E_MAIL_ERROR_POST_PROCESSING,
769 			"%s", error_messages->str);
770 	}
771 
772 	/* Synchronize the Sent folder. */
773 	if (context->sent_folder != NULL)
774 		camel_folder_synchronize_sync (
775 			context->sent_folder, FALSE, cancellable, NULL);
776 
777 	g_string_free (error_messages, TRUE);
778 }
779 
780 static guint32
781 get_message_size (CamelMimeMessage *message,
782                   GCancellable *cancellable)
783 {
784 	guint32 res = 0;
785 	CamelStream *null;
786 
787 	g_return_val_if_fail (message != NULL, 0);
788 
789 	null = camel_stream_null_new ();
790 	camel_data_wrapper_write_to_stream_sync (CAMEL_DATA_WRAPPER (message), null, cancellable, NULL);
791 	res = CAMEL_STREAM_NULL (null)->written;
792 	g_object_unref (null);
793 
794 	return res;
795 }
796 
797 void
798 e_mail_session_send_to (EMailSession *session,
799                         CamelMimeMessage *message,
800                         gint io_priority,
801                         GCancellable *cancellable,
802                         CamelFilterGetFolderFunc get_folder_func,
803                         gpointer get_folder_data,
804                         GAsyncReadyCallback callback,
805                         gpointer user_data)
806 {
807 	GSimpleAsyncResult *simple;
808 	AsyncContext *context;
809 	CamelAddress *from;
810 	CamelAddress *recipients;
811 	CamelMedium *medium;
812 	CamelMessageInfo *info;
813 	ESourceRegistry *registry;
814 	ESource *source = NULL;
815 	GPtrArray *post_to_uris;
816 	struct _camel_header_raw *xev;
817 	struct _camel_header_raw *header;
818 	const gchar *string;
819 	const gchar *resent_from;
820 	gchar *transport_uid = NULL;
821 	gchar *sent_folder_uri = NULL;
822 	GError *error = NULL;
823 
824 	g_return_if_fail (E_IS_MAIL_SESSION (session));
825 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
826 
827 	registry = e_mail_session_get_registry (session);
828 
829 	medium = CAMEL_MEDIUM (message);
830 
831 	camel_medium_set_header (medium, "X-Mailer", X_MAILER);
832 
833 	xev = mail_tool_remove_xevolution_headers (message);
834 
835 	/* Extract directives from X-Evolution headers. */
836 
837 	string = camel_header_raw_find (&xev, "X-Evolution-Identity", NULL);
838 	if (string != NULL) {
839 		gchar *uid = g_strstrip (g_strdup (string));
840 		source = e_source_registry_ref_source (registry, uid);
841 		g_free (uid);
842 	}
843 
844 	if (E_IS_SOURCE (source)) {
845 		ESourceMailSubmission *extension;
846 		const gchar *extension_name;
847 
848 		extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
849 		extension = e_source_get_extension (source, extension_name);
850 
851 		string = e_source_mail_submission_get_sent_folder (extension);
852 		sent_folder_uri = g_strdup (string);
853 
854 		string = e_source_mail_submission_get_transport_uid (extension);
855 		transport_uid = g_strdup (string);
856 
857 		g_object_unref (source);
858 	}
859 
860 	string = camel_header_raw_find (&xev, "X-Evolution-Fcc", NULL);
861 	if (sent_folder_uri == NULL && string != NULL)
862 		sent_folder_uri = g_strstrip (g_strdup (string));
863 
864 	string = camel_header_raw_find (&xev, "X-Evolution-Transport", NULL);
865 	if (transport_uid == NULL && string != NULL)
866 		transport_uid = g_strstrip (g_strdup (string));
867 
868 	post_to_uris = g_ptr_array_new ();
869 	for (header = xev; header != NULL; header = header->next) {
870 		gchar *folder_uri;
871 
872 		if (g_strcmp0 (header->name, "X-Evolution-PostTo") != 0)
873 			continue;
874 
875 		folder_uri = g_strstrip (g_strdup (header->value));
876 		g_ptr_array_add (post_to_uris, folder_uri);
877 	}
878 
879 	/* Collect sender and recipients from headers. */
880 
881 	from = (CamelAddress *) camel_internet_address_new ();
882 	recipients = (CamelAddress *) camel_internet_address_new ();
883 	resent_from = camel_medium_get_header (medium, "Resent-From");
884 
885 	if (resent_from != NULL) {
886 		const CamelInternetAddress *addr;
887 		const gchar *type;
888 
889 		camel_address_decode (from, resent_from);
890 
891 		type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
892 		addr = camel_mime_message_get_recipients (message, type);
893 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
894 
895 		type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
896 		addr = camel_mime_message_get_recipients (message, type);
897 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
898 
899 		type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
900 		addr = camel_mime_message_get_recipients (message, type);
901 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
902 
903 	} else {
904 		const CamelInternetAddress *addr;
905 		const gchar *type;
906 
907 		addr = camel_mime_message_get_from (message);
908 		camel_address_copy (from, CAMEL_ADDRESS (addr));
909 
910 		type = CAMEL_RECIPIENT_TYPE_TO;
911 		addr = camel_mime_message_get_recipients (message, type);
912 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
913 
914 		type = CAMEL_RECIPIENT_TYPE_CC;
915 		addr = camel_mime_message_get_recipients (message, type);
916 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
917 
918 		type = CAMEL_RECIPIENT_TYPE_BCC;
919 		addr = camel_mime_message_get_recipients (message, type);
920 		camel_address_cat (recipients, CAMEL_ADDRESS (addr));
921 	}
922 
923 	/* Miscellaneous preparations. */
924 
925 	info = camel_message_info_new_from_header (NULL, ((CamelMimePart *) message)->headers);
926 	((CamelMessageInfoBase *) info)->size = get_message_size (message, cancellable);
927 	camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
928 
929 	/* The rest of the processing happens in a thread. */
930 
931 	context = g_slice_new0 (AsyncContext);
932 	context->message = g_object_ref (message);
933 	context->io_priority = io_priority;
934 	context->from = from;
935 	context->recipients = recipients;
936 	context->info = info;
937 	context->xev = xev;
938 	context->post_to_uris = post_to_uris;
939 	context->transport_uid = transport_uid;
940 	context->sent_folder_uri = sent_folder_uri;
941 
942 	if (G_IS_CANCELLABLE (cancellable))
943 		context->cancellable = g_object_ref (cancellable);
944 
945 	/* Failure here emits a runtime warning but is non-fatal. */
946 	context->driver = camel_session_get_filter_driver (
947 		CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, &error);
948 	if (context->driver != NULL && get_folder_func)
949 		camel_filter_driver_set_folder_func (
950 			context->driver, get_folder_func, get_folder_data);
951 	if (error != NULL) {
952 		g_warn_if_fail (context->driver == NULL);
953 		g_warning ("%s", error->message);
954 		g_error_free (error);
955 	}
956 
957 	/* This gets popped in async_context_free(). */
958 	camel_operation_push_message (
959 		context->cancellable, _("Sending message"));
960 
961 	simple = g_simple_async_result_new (
962 		G_OBJECT (session), callback,
963 		user_data, e_mail_session_send_to);
964 
965 	g_simple_async_result_set_check_cancellable (simple, cancellable);
966 
967 	g_simple_async_result_set_op_res_gpointer (
968 		simple, context, (GDestroyNotify) async_context_free);
969 
970 	g_simple_async_result_run_in_thread (
971 		simple, (GSimpleAsyncThreadFunc)
972 		mail_session_send_to_thread,
973 		context->io_priority,
974 		context->cancellable);
975 
976 	g_object_unref (simple);
977 }
978 
979 gboolean
980 e_mail_session_send_to_finish (EMailSession *session,
981                                GAsyncResult *result,
982                                GError **error)
983 {
984 	GSimpleAsyncResult *simple;
985 
986 	g_return_val_if_fail (
987 		g_simple_async_result_is_valid (
988 		result, G_OBJECT (session),
989 		e_mail_session_send_to), FALSE);
990 
991 	simple = G_SIMPLE_ASYNC_RESULT (result);
992 
993 	/* Assume success unless a GError is set. */
994 	return !g_simple_async_result_propagate_error (simple, error);
995 }