1 /*
2  * e-mail-reader-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  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18  *
19  */
20 
21 /* Miscellaneous utility functions used by EMailReader actions. */
22 
23 #include "evolution-config.h"
24 
25 #include "e-mail-reader-utils.h"
26 
27 #include <glib/gi18n.h>
28 #include <libxml/tree.h>
29 #include <camel/camel.h>
30 
31 #include <shell/e-shell-utils.h>
32 
33 #include <libemail-engine/libemail-engine.h>
34 
35 #include <em-format/e-mail-parser.h>
36 #include <em-format/e-mail-part-utils.h>
37 
38 #include <composer/e-composer-actions.h>
39 
40 #include "e-mail-backend.h"
41 #include "e-mail-browser.h"
42 #include "e-mail-printer.h"
43 #include "e-mail-display.h"
44 #include "em-composer-utils.h"
45 #include "em-utils.h"
46 #include "mail-autofilter.h"
47 #include "mail-vfolder-ui.h"
48 #include "message-list.h"
49 
50 #define d(x)
51 
52 typedef struct _AsyncContext AsyncContext;
53 
54 struct _AsyncContext {
55 	EActivity *activity;
56 	CamelFolder *folder;
57 	CamelMimeMessage *message;
58 	EMailPartList *part_list;
59 	EMailReader *reader;
60 	CamelInternetAddress *address;
61 	GPtrArray *uids;
62 	gchar *folder_name;
63 	gchar *message_uid;
64 
65 	EMailReplyType reply_type;
66 	EMailReplyStyle reply_style;
67 	EMailForwardStyle forward_style;
68 	GtkPrintOperationAction print_action;
69 	const gchar *filter_source;
70 	gint filter_type;
71 	gboolean replace;
72 	gboolean keep_signature;
73 };
74 
75 static void
async_context_free(AsyncContext * async_context)76 async_context_free (AsyncContext *async_context)
77 {
78 	g_clear_object (&async_context->activity);
79 	g_clear_object (&async_context->folder);
80 	g_clear_object (&async_context->message);
81 	g_clear_object (&async_context->part_list);
82 	g_clear_object (&async_context->reader);
83 	g_clear_object (&async_context->address);
84 
85 	if (async_context->uids != NULL)
86 		g_ptr_array_unref (async_context->uids);
87 
88 	g_free (async_context->folder_name);
89 	g_free (async_context->message_uid);
90 
91 	g_slice_free (AsyncContext, async_context);
92 }
93 
94 static gboolean
mail_reader_is_special_local_folder(const gchar * name)95 mail_reader_is_special_local_folder (const gchar *name)
96 {
97 	return (strcmp (name, "Drafts") == 0 ||
98 		strcmp (name, "Inbox") == 0 ||
99 		strcmp (name, "Outbox") == 0 ||
100 		strcmp (name, "Sent") == 0 ||
101 		strcmp (name, "Templates") == 0);
102 }
103 
104 gboolean
e_mail_reader_confirm_delete(EMailReader * reader)105 e_mail_reader_confirm_delete (EMailReader *reader)
106 {
107 	CamelFolder *folder;
108 	CamelStore *parent_store;
109 	GtkWidget *check_button;
110 	GtkWidget *container;
111 	GtkWidget *dialog;
112 	GtkWindow *window;
113 	GSettings *settings;
114 	const gchar *label;
115 	gboolean prompt_delete_in_vfolder;
116 	gint response = GTK_RESPONSE_OK;
117 
118 	/* Remind users what deleting from a search folder does. */
119 
120 	g_return_val_if_fail (E_IS_MAIL_READER (reader), FALSE);
121 
122 	folder = e_mail_reader_ref_folder (reader);
123 	window = e_mail_reader_get_window (reader);
124 
125 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
126 
127 	prompt_delete_in_vfolder = g_settings_get_boolean (
128 		settings, "prompt-on-delete-in-vfolder");
129 
130 	parent_store = camel_folder_get_parent_store (folder);
131 
132 	if (!CAMEL_IS_VEE_STORE (parent_store))
133 		goto exit;
134 
135 	if (!prompt_delete_in_vfolder)
136 		goto exit;
137 
138 	dialog = e_alert_dialog_new_for_args (
139 		window, "mail:ask-delete-vfolder-msg",
140 		camel_folder_get_full_name (folder), NULL);
141 
142 	container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
143 
144 	label = _("Do not warn me again");
145 	check_button = gtk_check_button_new_with_label (label);
146 	gtk_box_pack_start (GTK_BOX (container), check_button, TRUE, TRUE, 6);
147 	gtk_widget_show (check_button);
148 
149 	response = gtk_dialog_run (GTK_DIALOG (dialog));
150 
151 	if (response != GTK_RESPONSE_DELETE_EVENT)
152 		g_settings_set_boolean (
153 			settings,
154 			"prompt-on-delete-in-vfolder",
155 			!gtk_toggle_button_get_active (
156 			GTK_TOGGLE_BUTTON (check_button)));
157 
158 	gtk_widget_destroy (dialog);
159 
160 exit:
161 	g_clear_object (&folder);
162 	g_clear_object (&settings);
163 
164 	return (response == GTK_RESPONSE_OK);
165 }
166 
167 static void
mail_reader_delete_folder_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)168 mail_reader_delete_folder_cb (GObject *source_object,
169                               GAsyncResult *result,
170                               gpointer user_data)
171 {
172 	CamelFolder *folder;
173 	EActivity *activity;
174 	EAlertSink *alert_sink;
175 	AsyncContext *async_context;
176 	GError *local_error = NULL;
177 
178 	folder = CAMEL_FOLDER (source_object);
179 	async_context = (AsyncContext *) user_data;
180 
181 	activity = async_context->activity;
182 	alert_sink = e_activity_get_alert_sink (activity);
183 
184 	e_mail_folder_remove_finish (folder, result, &local_error);
185 
186 	if (e_activity_handle_cancellation (activity, local_error)) {
187 		g_error_free (local_error);
188 
189 	} else if (local_error != NULL) {
190 		e_alert_submit (
191 			alert_sink, "mail:no-delete-folder",
192 			camel_folder_get_full_name (folder),
193 			local_error->message, NULL);
194 		g_error_free (local_error);
195 
196 	} else {
197 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
198 	}
199 
200 	async_context_free (async_context);
201 }
202 
203 void
e_mail_reader_delete_folder(EMailReader * reader,CamelFolder * folder)204 e_mail_reader_delete_folder (EMailReader *reader,
205                              CamelFolder *folder)
206 {
207 	EMailBackend *backend;
208 	EMailSession *session;
209 	EShell *shell;
210 	EAlertSink *alert_sink;
211 	CamelStore *parent_store;
212 	CamelProvider *provider;
213 	MailFolderCache *folder_cache;
214 	GtkWindow *parent = e_shell_get_active_window (NULL);
215 	GtkWidget *dialog;
216 	gboolean store_is_local;
217 	const gchar *display_name;
218 	const gchar *full_name;
219 	gchar *full_display_name;
220 	CamelFolderInfoFlags flags = 0;
221 	gboolean have_flags;
222 
223 	g_return_if_fail (E_IS_MAIL_READER (reader));
224 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
225 
226 	full_name = camel_folder_get_full_name (folder);
227 	display_name = camel_folder_get_display_name (folder);
228 	full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
229 	parent_store = camel_folder_get_parent_store (folder);
230 	provider = camel_service_get_provider (CAMEL_SERVICE (parent_store));
231 
232 	store_is_local = (provider->flags & CAMEL_PROVIDER_IS_LOCAL) != 0;
233 
234 	backend = e_mail_reader_get_backend (reader);
235 	session = e_mail_backend_get_session (backend);
236 
237 	alert_sink = e_mail_reader_get_alert_sink (reader);
238 	folder_cache = e_mail_session_get_folder_cache (session);
239 
240 	if (store_is_local &&
241 		mail_reader_is_special_local_folder (full_name)) {
242 		e_alert_submit (
243 			alert_sink, "mail:no-delete-special-folder",
244 			full_display_name ? full_display_name : display_name, NULL);
245 		g_free (full_display_name);
246 		return;
247 	}
248 
249 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
250 
251 	if (!store_is_local && !e_shell_get_online (shell)) {
252 		e_alert_submit (
253 			alert_sink, "mail:online-operation",
254 			full_display_name ? full_display_name : display_name, NULL);
255 		g_free (full_display_name);
256 		return;
257 	}
258 
259 	have_flags = mail_folder_cache_get_folder_info_flags (
260 		folder_cache, parent_store, full_name, &flags);
261 
262 	if (have_flags && (flags & CAMEL_FOLDER_SYSTEM)) {
263 		e_alert_submit (
264 			alert_sink, "mail:no-delete-special-folder",
265 			full_display_name ? full_display_name : display_name, NULL);
266 		g_free (full_display_name);
267 		return;
268 	}
269 
270 	if (have_flags && (flags & CAMEL_FOLDER_CHILDREN)) {
271 		if (CAMEL_IS_VEE_STORE (parent_store))
272 			dialog = e_alert_dialog_new_for_args (
273 				parent, "mail:ask-delete-vfolder",
274 				full_display_name ? full_display_name : display_name, NULL);
275 		else
276 			dialog = e_alert_dialog_new_for_args (
277 				parent, "mail:ask-delete-folder",
278 				full_display_name ? full_display_name : display_name, NULL);
279 	} else {
280 		if (CAMEL_IS_VEE_STORE (parent_store))
281 			dialog = e_alert_dialog_new_for_args (
282 				parent, "mail:ask-delete-vfolder-nochild",
283 				full_display_name ? full_display_name : display_name, NULL);
284 		else
285 			dialog = e_alert_dialog_new_for_args (
286 				parent, "mail:ask-delete-folder-nochild",
287 				full_display_name ? full_display_name : display_name, NULL);
288 	}
289 
290 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
291 		EActivity *activity;
292 		GCancellable *cancellable;
293 		AsyncContext *async_context;
294 
295 		activity = e_mail_reader_new_activity (reader);
296 		cancellable = e_activity_get_cancellable (activity);
297 
298 		async_context = g_slice_new0 (AsyncContext);
299 		async_context->activity = g_object_ref (activity);
300 		async_context->reader = g_object_ref (reader);
301 
302 		/* Disable the dialog until the activity finishes. */
303 		gtk_widget_set_sensitive (dialog, FALSE);
304 
305 		/* Destroy the dialog once the activity finishes. */
306 		g_object_set_data_full (
307 			G_OBJECT (activity), "delete-dialog",
308 			dialog, (GDestroyNotify) gtk_widget_destroy);
309 
310 		e_mail_folder_remove (
311 			folder,
312 			G_PRIORITY_DEFAULT,
313 			cancellable,
314 			mail_reader_delete_folder_cb,
315 			async_context);
316 
317 		g_object_unref (activity);
318 	} else {
319 		gtk_widget_destroy (dialog);
320 	}
321 
322 	g_free (full_display_name);
323 }
324 
325 static void
mail_reader_delete_folder_name_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)326 mail_reader_delete_folder_name_cb (GObject *source_object,
327                                    GAsyncResult *result,
328                                    gpointer user_data)
329 {
330 	CamelFolder *folder;
331 	EActivity *activity;
332 	EAlertSink *alert_sink;
333 	AsyncContext *async_context;
334 	GError *local_error = NULL;
335 
336 	async_context = (AsyncContext *) user_data;
337 
338 	activity = async_context->activity;
339 	alert_sink = e_activity_get_alert_sink (activity);
340 
341 	/* XXX The returned CamelFolder is a borrowed reference. */
342 	folder = camel_store_get_folder_finish (
343 		CAMEL_STORE (source_object), result, &local_error);
344 
345 	/* Sanity check. */
346 	g_return_if_fail (
347 		((folder != NULL) && (local_error == NULL)) ||
348 		((folder == NULL) && (local_error != NULL)));
349 
350 	if (e_activity_handle_cancellation (activity, local_error)) {
351 		g_error_free (local_error);
352 
353 	} else if (local_error != NULL) {
354 		e_alert_submit (
355 			alert_sink, "mail:no-delete-folder",
356 			async_context->folder_name,
357 			local_error->message, NULL);
358 		g_error_free (local_error);
359 
360 	} else {
361 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
362 		e_mail_reader_delete_folder (async_context->reader, folder);
363 	}
364 
365 	async_context_free (async_context);
366 }
367 
368 void
e_mail_reader_delete_folder_name(EMailReader * reader,CamelStore * store,const gchar * folder_name)369 e_mail_reader_delete_folder_name (EMailReader *reader,
370                                   CamelStore *store,
371                                   const gchar *folder_name)
372 {
373 	EActivity *activity;
374 	GCancellable *cancellable;
375 	AsyncContext *async_context;
376 
377 	g_return_if_fail (E_IS_MAIL_READER (reader));
378 	g_return_if_fail (CAMEL_IS_STORE (store));
379 	g_return_if_fail (folder_name != NULL);
380 
381 	activity = e_mail_reader_new_activity (reader);
382 	cancellable = e_activity_get_cancellable (activity);
383 
384 	async_context = g_slice_new0 (AsyncContext);
385 	async_context->activity = g_object_ref (activity);
386 	async_context->reader = g_object_ref (reader);
387 	async_context->folder_name = g_strdup (folder_name);
388 
389 	camel_store_get_folder (
390 		store, folder_name,
391 		0, G_PRIORITY_DEFAULT, cancellable,
392 		mail_reader_delete_folder_name_cb,
393 		async_context);
394 
395 	g_object_unref (activity);
396 }
397 
398 /* Helper for e_mail_reader_expunge_folder() */
399 static void
mail_reader_expunge_folder_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)400 mail_reader_expunge_folder_cb (GObject *source_object,
401                                GAsyncResult *result,
402                                gpointer user_data)
403 {
404 	CamelFolder *folder;
405 	EActivity *activity;
406 	EAlertSink *alert_sink;
407 	AsyncContext *async_context;
408 	GError *local_error = NULL;
409 
410 	folder = CAMEL_FOLDER (source_object);
411 	async_context = (AsyncContext *) user_data;
412 
413 	activity = async_context->activity;
414 	alert_sink = e_activity_get_alert_sink (activity);
415 
416 	e_mail_folder_expunge_finish (folder, result, &local_error);
417 
418 	if (e_activity_handle_cancellation (activity, local_error)) {
419 		g_error_free (local_error);
420 
421 	} else if (local_error != NULL) {
422 		gchar *full_display_name;
423 
424 		full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
425 
426 		e_alert_submit (
427 			alert_sink, "mail:no-expunge-folder",
428 			full_display_name ? full_display_name : camel_folder_get_display_name (folder),
429 			local_error->message, NULL);
430 
431 		g_free (full_display_name);
432 		g_error_free (local_error);
433 
434 	} else {
435 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
436 	}
437 
438 	async_context_free (async_context);
439 }
440 
441 void
e_mail_reader_expunge_folder(EMailReader * reader,CamelFolder * folder)442 e_mail_reader_expunge_folder (EMailReader *reader,
443                               CamelFolder *folder)
444 {
445 	GtkWindow *window;
446 	const gchar *display_name;
447 	gchar *full_display_name;
448 	gboolean proceed;
449 
450 	g_return_if_fail (E_IS_MAIL_READER (reader));
451 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
452 
453 	window = e_mail_reader_get_window (reader);
454 	display_name = camel_folder_get_display_name (folder);
455 	full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
456 
457 	proceed = e_util_prompt_user (
458 		window, "org.gnome.evolution.mail", "prompt-on-expunge",
459 		"mail:ask-expunge", full_display_name ? full_display_name : display_name, NULL);
460 
461 	g_free (full_display_name);
462 
463 	if (proceed) {
464 		EActivity *activity;
465 		GCancellable *cancellable;
466 		AsyncContext *async_context;
467 
468 		activity = e_mail_reader_new_activity (reader);
469 		cancellable = e_activity_get_cancellable (activity);
470 
471 		async_context = g_slice_new0 (AsyncContext);
472 		async_context->activity = g_object_ref (activity);
473 		async_context->reader = g_object_ref (reader);
474 
475 		e_mail_folder_expunge (
476 			folder,
477 			G_PRIORITY_DEFAULT, cancellable,
478 			mail_reader_expunge_folder_cb,
479 			async_context);
480 
481 		g_object_unref (activity);
482 	}
483 }
484 
485 /* Helper for e_mail_reader_expunge_folder_name() */
486 static void
mail_reader_expunge_folder_name_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)487 mail_reader_expunge_folder_name_cb (GObject *source_object,
488                                     GAsyncResult *result,
489                                     gpointer user_data)
490 {
491 	CamelFolder *folder;
492 	EActivity *activity;
493 	EAlertSink *alert_sink;
494 	AsyncContext *async_context;
495 	GError *local_error = NULL;
496 
497 	async_context = (AsyncContext *) user_data;
498 
499 	activity = async_context->activity;
500 	alert_sink = e_activity_get_alert_sink (activity);
501 
502 	/* XXX The returned CamelFolder is a borrowed reference. */
503 	folder = camel_store_get_folder_finish (
504 		CAMEL_STORE (source_object), result, &local_error);
505 
506 	if (e_activity_handle_cancellation (activity, local_error)) {
507 		g_error_free (local_error);
508 
509 	} else if (local_error != NULL) {
510 		e_alert_submit (
511 			alert_sink, "mail:no-expunge-folder",
512 			async_context->folder_name,
513 			local_error->message, NULL);
514 		g_error_free (local_error);
515 
516 	} else {
517 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
518 		e_mail_reader_expunge_folder (async_context->reader, folder);
519 	}
520 
521 	async_context_free (async_context);
522 }
523 
524 void
e_mail_reader_expunge_folder_name(EMailReader * reader,CamelStore * store,const gchar * folder_name)525 e_mail_reader_expunge_folder_name (EMailReader *reader,
526                                    CamelStore *store,
527                                    const gchar *folder_name)
528 {
529 	EActivity *activity;
530 	GCancellable *cancellable;
531 	AsyncContext *async_context;
532 
533 	g_return_if_fail (E_IS_MAIL_READER (reader));
534 	g_return_if_fail (CAMEL_IS_STORE (store));
535 	g_return_if_fail (folder_name != NULL);
536 
537 	activity = e_mail_reader_new_activity (reader);
538 	cancellable = e_activity_get_cancellable (activity);
539 
540 	async_context = g_slice_new0 (AsyncContext);
541 	async_context->activity = g_object_ref (activity);
542 	async_context->reader = g_object_ref (reader);
543 	async_context->folder_name = g_strdup (folder_name);
544 
545 	camel_store_get_folder (
546 		store, folder_name,
547 		0, G_PRIORITY_DEFAULT, cancellable,
548 		mail_reader_expunge_folder_name_cb,
549 		async_context);
550 
551 	g_object_unref (activity);
552 }
553 
554 static void
mail_reader_empty_junk_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)555 mail_reader_empty_junk_thread (EAlertSinkThreadJobData *job_data,
556 			       gpointer user_data,
557 			       GCancellable *cancellable,
558 			       GError **error)
559 {
560 	AsyncContext *async_context = user_data;
561 	CamelFolder *folder;
562 	CamelFolderSummary *summary;
563 	GPtrArray *uids;
564 	guint ii;
565 
566 	g_return_if_fail (async_context != NULL);
567 
568 	folder = async_context->folder;
569 
570 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
571 
572 	camel_folder_freeze (folder);
573 
574 	summary = camel_folder_get_folder_summary (folder);
575 	if (summary)
576 		camel_folder_summary_prepare_fetch_all (summary, NULL);
577 
578 	uids = camel_folder_get_uids (folder);
579 	if (uids) {
580 		for (ii = 0; ii < uids->len; ii++) {
581 			CamelMessageInfo *nfo;
582 
583 			nfo = camel_folder_get_message_info (folder, uids->pdata[ii]);
584 			if (nfo) {
585 				camel_message_info_set_flags (nfo, CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
586 				g_object_unref (nfo);
587 			}
588 		}
589 
590 		if (uids->len > 0)
591 			camel_folder_synchronize_sync (folder, FALSE, cancellable, error);
592 
593 		camel_folder_free_uids (folder, uids);
594 	}
595 
596 	camel_folder_thaw (folder);
597 }
598 
599 void
e_mail_reader_empty_junk_folder(EMailReader * reader,CamelFolder * folder)600 e_mail_reader_empty_junk_folder (EMailReader *reader,
601 				 CamelFolder *folder)
602 {
603 	GtkWindow *window;
604 	const gchar *display_name;
605 	gchar *full_display_name;
606 	gboolean proceed;
607 
608 	g_return_if_fail (E_IS_MAIL_READER (reader));
609 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
610 
611 	window = e_mail_reader_get_window (reader);
612 	display_name = camel_folder_get_display_name (folder);
613 	full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
614 	if (full_display_name)
615 		display_name = full_display_name;
616 
617 	proceed = e_util_prompt_user (window, "org.gnome.evolution.mail", "prompt-on-empty-junk",
618 		"mail:ask-empty-junk", display_name, NULL);
619 
620 	if (proceed) {
621 		AsyncContext *async_context;
622 		EAlertSink *alert_sink;
623 		EActivity *activity;
624 		gchar *description;
625 
626 		alert_sink = e_mail_reader_get_alert_sink (reader);
627 
628 		async_context = g_slice_new0 (AsyncContext);
629 		async_context->reader = g_object_ref (reader);
630 		async_context->folder = g_object_ref (folder);
631 
632 		description = g_strdup_printf (_("Deleting messages in Junk folder “%s”…"), display_name);
633 
634 		activity = e_alert_sink_submit_thread_job (alert_sink,
635 			description, "mail:failed-empty-junk", display_name,
636 			mail_reader_empty_junk_thread, async_context,
637 			(GDestroyNotify) async_context_free);
638 
639 		g_clear_object (&activity);
640 		g_free (description);
641 	}
642 
643 	g_free (full_display_name);
644 }
645 
646 static void
mail_reader_empty_junk_folder_name_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)647 mail_reader_empty_junk_folder_name_cb (GObject *source_object,
648 				       GAsyncResult *result,
649 				       gpointer user_data)
650 {
651 	CamelFolder *folder;
652 	EActivity *activity;
653 	EAlertSink *alert_sink;
654 	AsyncContext *async_context;
655 	GError *local_error = NULL;
656 
657 	async_context = (AsyncContext *) user_data;
658 
659 	activity = async_context->activity;
660 	alert_sink = e_activity_get_alert_sink (activity);
661 
662 	folder = camel_store_get_folder_finish (CAMEL_STORE (source_object), result, &local_error);
663 
664 	if (e_activity_handle_cancellation (activity, local_error)) {
665 		g_error_free (local_error);
666 
667 	} else if (local_error != NULL) {
668 		e_alert_submit (
669 			alert_sink, "mail:failed-empty-junk",
670 			async_context->folder_name,
671 			local_error->message, NULL);
672 		g_error_free (local_error);
673 
674 	} else {
675 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
676 		e_mail_reader_empty_junk_folder (async_context->reader, folder);
677 	}
678 
679 	async_context_free (async_context);
680 	g_clear_object (&folder);
681 }
682 
683 void
e_mail_reader_empty_junk_folder_name(EMailReader * reader,CamelStore * store,const gchar * folder_name)684 e_mail_reader_empty_junk_folder_name (EMailReader *reader,
685 				      CamelStore *store,
686 				      const gchar *folder_name)
687 {
688 	EActivity *activity;
689 	GCancellable *cancellable;
690 	AsyncContext *async_context;
691 
692 	g_return_if_fail (E_IS_MAIL_READER (reader));
693 	g_return_if_fail (CAMEL_IS_STORE (store));
694 	g_return_if_fail (folder_name != NULL);
695 
696 	activity = e_mail_reader_new_activity (reader);
697 	cancellable = e_activity_get_cancellable (activity);
698 
699 	async_context = g_slice_new0 (AsyncContext);
700 	async_context->activity = g_object_ref (activity);
701 	async_context->reader = g_object_ref (reader);
702 	async_context->folder_name = g_strdup (folder_name);
703 
704 	camel_store_get_folder (
705 		store, folder_name,
706 		0, G_PRIORITY_DEFAULT, cancellable,
707 		mail_reader_empty_junk_folder_name_cb,
708 		async_context);
709 
710 	g_object_unref (activity);
711 }
712 
713 struct _process_autoarchive_msg {
714 	MailMsg base;
715 
716 	AsyncContext *async_context;
717 };
718 
719 static gchar *
process_autoarchive_desc(struct _process_autoarchive_msg * m)720 process_autoarchive_desc (struct _process_autoarchive_msg *m)
721 {
722 	gchar *desc, *full_display_name;
723 
724 	full_display_name = e_mail_folder_to_full_display_name (m->async_context->folder, NULL);
725 
726 	desc = g_strdup_printf (
727 		_("Refreshing folder “%s”"),
728 		full_display_name ? full_display_name : camel_folder_get_display_name (m->async_context->folder));
729 
730 	g_free (full_display_name);
731 
732 	return desc;
733 }
734 
735 static void
process_autoarchive_exec(struct _process_autoarchive_msg * m,GCancellable * cancellable,GError ** error)736 process_autoarchive_exec (struct _process_autoarchive_msg *m,
737 			  GCancellable *cancellable,
738 			  GError **error)
739 {
740 	gchar *folder_uri;
741 
742 	folder_uri = e_mail_folder_uri_from_folder (m->async_context->folder);
743 
744 	em_utils_process_autoarchive_sync (
745 		e_mail_reader_get_backend (m->async_context->reader),
746 		m->async_context->folder, folder_uri, cancellable, error);
747 
748 	g_free (folder_uri);
749 }
750 
751 static void
process_autoarchive_done(struct _process_autoarchive_msg * m)752 process_autoarchive_done (struct _process_autoarchive_msg *m)
753 {
754 	EActivity *activity;
755 	EAlertSink *alert_sink;
756 
757 	activity = m->async_context->activity;
758 	alert_sink = e_activity_get_alert_sink (activity);
759 
760 	if (e_activity_handle_cancellation (activity, m->base.error)) {
761 	} else if (m->base.error != NULL) {
762 		gchar *full_display_name;
763 
764 		full_display_name = e_mail_folder_to_full_display_name (m->async_context->folder, NULL);
765 
766 		e_alert_submit (
767 			alert_sink, "mail:no-refresh-folder",
768 			full_display_name ? full_display_name : camel_folder_get_display_name (m->async_context->folder),
769 			m->base.error->message, NULL);
770 
771 		g_free (full_display_name);
772 	} else {
773 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
774 	}
775 }
776 
777 static void
process_autoarchive_free(struct _process_autoarchive_msg * m)778 process_autoarchive_free (struct _process_autoarchive_msg *m)
779 {
780 	async_context_free (m->async_context);
781 }
782 
783 static MailMsgInfo process_autoarchive_info = {
784 	sizeof (struct _process_autoarchive_msg),
785 	(MailMsgDescFunc) process_autoarchive_desc,
786 	(MailMsgExecFunc) process_autoarchive_exec,
787 	(MailMsgDoneFunc) process_autoarchive_done,
788 	(MailMsgFreeFunc) process_autoarchive_free
789 };
790 
791 /* Helper for e_mail_reader_refresh_folder() */
792 static void
mail_reader_refresh_folder_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)793 mail_reader_refresh_folder_cb (GObject *source_object,
794                                GAsyncResult *result,
795                                gpointer user_data)
796 {
797 	CamelFolder *folder;
798 	EActivity *activity;
799 	EAlertSink *alert_sink;
800 	AsyncContext *async_context;
801 	GError *local_error = NULL;
802 
803 	folder = CAMEL_FOLDER (source_object);
804 	if (!camel_folder_refresh_info_finish (folder, result, &local_error) && !local_error)
805 		local_error = g_error_new_literal (CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Unknown error"));
806 
807 	async_context = (AsyncContext *) user_data;
808 
809 	activity = async_context->activity;
810 	alert_sink = e_activity_get_alert_sink (activity);
811 
812 	if (e_activity_handle_cancellation (activity, local_error)) {
813 		g_error_free (local_error);
814 
815 	} else if (local_error != NULL) {
816 		gchar *full_display_name;
817 
818 		full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
819 
820 		e_alert_submit (
821 			alert_sink, "mail:no-refresh-folder",
822 			full_display_name ? full_display_name : camel_folder_get_display_name (folder),
823 			local_error->message, NULL);
824 
825 		g_free (full_display_name);
826 		g_error_free (local_error);
827 
828 	} else {
829 		struct _process_autoarchive_msg *m;
830 
831 		g_warn_if_fail (async_context->folder == NULL);
832 
833 		async_context->folder = g_object_ref (folder);
834 
835 		m = mail_msg_new (&process_autoarchive_info);
836 		m->async_context = async_context;
837 
838 		mail_msg_unordered_push (m);
839 
840 		async_context = NULL;
841 	}
842 
843 	if (async_context)
844 		async_context_free (async_context);
845 }
846 
847 void
e_mail_reader_refresh_folder(EMailReader * reader,CamelFolder * folder)848 e_mail_reader_refresh_folder (EMailReader *reader,
849                               CamelFolder *folder)
850 {
851 	EActivity *activity;
852 	GCancellable *cancellable;
853 	AsyncContext *async_context;
854 
855 	g_return_if_fail (E_IS_MAIL_READER (reader));
856 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
857 
858 	activity = e_mail_reader_new_activity (reader);
859 	cancellable = e_activity_get_cancellable (activity);
860 
861 	async_context = g_slice_new0 (AsyncContext);
862 	async_context->activity = g_object_ref (activity);
863 	async_context->reader = g_object_ref (reader);
864 
865 	camel_folder_refresh_info (
866 		folder,
867 		G_PRIORITY_DEFAULT, cancellable,
868 		mail_reader_refresh_folder_cb,
869 		async_context);
870 
871 	g_object_unref (activity);
872 }
873 
874 /* Helper for e_mail_reader_refresh_folder_name() */
875 static void
mail_reader_refresh_folder_name_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)876 mail_reader_refresh_folder_name_cb (GObject *source_object,
877                                     GAsyncResult *result,
878                                     gpointer user_data)
879 {
880 	CamelFolder *folder;
881 	EActivity *activity;
882 	EAlertSink *alert_sink;
883 	AsyncContext *async_context;
884 	GError *local_error = NULL;
885 
886 	async_context = (AsyncContext *) user_data;
887 
888 	activity = async_context->activity;
889 	alert_sink = e_activity_get_alert_sink (activity);
890 
891 	/* XXX The returned CamelFolder is a borrowed reference. */
892 	folder = camel_store_get_folder_finish (
893 		CAMEL_STORE (source_object), result, &local_error);
894 
895 	if (e_activity_handle_cancellation (activity, local_error)) {
896 		g_error_free (local_error);
897 
898 	} else if (local_error != NULL) {
899 		gchar *full_display_name;
900 
901 		full_display_name = g_strdup_printf ("%s : %s",
902 			camel_service_get_display_name (CAMEL_SERVICE (source_object)),
903 			async_context->folder_name);
904 
905 		e_alert_submit (
906 			alert_sink, "mail:no-refresh-folder",
907 			full_display_name,
908 			local_error->message, NULL);
909 
910 		g_free (full_display_name);
911 		g_error_free (local_error);
912 
913 	} else {
914 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
915 		e_mail_reader_refresh_folder (async_context->reader, folder);
916 	}
917 
918 	async_context_free (async_context);
919 }
920 
921 void
e_mail_reader_refresh_folder_name(EMailReader * reader,CamelStore * store,const gchar * folder_name)922 e_mail_reader_refresh_folder_name (EMailReader *reader,
923                                    CamelStore *store,
924                                    const gchar *folder_name)
925 {
926 	EActivity *activity;
927 	GCancellable *cancellable;
928 	AsyncContext *async_context;
929 
930 	g_return_if_fail (E_IS_MAIL_READER (reader));
931 	g_return_if_fail (CAMEL_IS_STORE (store));
932 	g_return_if_fail (folder_name != NULL);
933 
934 	activity = e_mail_reader_new_activity (reader);
935 	cancellable = e_activity_get_cancellable (activity);
936 
937 	async_context = g_slice_new0 (AsyncContext);
938 	async_context->activity = g_object_ref (activity);
939 	async_context->reader = g_object_ref (reader);
940 	async_context->folder_name = g_strdup (folder_name);
941 
942 	camel_store_get_folder (
943 		store, folder_name,
944 		CAMEL_STORE_FOLDER_INFO_FAST | CAMEL_STORE_FOLDER_INFO_REFRESH,
945 		G_PRIORITY_DEFAULT, cancellable,
946 		mail_reader_refresh_folder_name_cb,
947 		async_context);
948 
949 	g_object_unref (activity);
950 }
951 
952 /* Helper for e_mail_reader_unsubscribe_folder_name() */
953 static void
mail_reader_unsubscribe_folder_name_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)954 mail_reader_unsubscribe_folder_name_cb (GObject *source_object,
955                                         GAsyncResult *result,
956                                         gpointer user_data)
957 {
958 	EActivity *activity;
959 	EAlertSink *alert_sink;
960 	AsyncContext *async_context;
961 	GError *local_error = NULL;
962 
963 	async_context = (AsyncContext *) user_data;
964 
965 	activity = async_context->activity;
966 	alert_sink = e_activity_get_alert_sink (activity);
967 
968 	camel_subscribable_unsubscribe_folder_finish (
969 		CAMEL_SUBSCRIBABLE (source_object), result, &local_error);
970 
971 	if (e_activity_handle_cancellation (activity, local_error)) {
972 		g_error_free (local_error);
973 
974 	} else if (local_error != NULL) {
975 		e_alert_submit (
976 			alert_sink, "mail:folder-unsubscribe",
977 			async_context->folder_name,
978 			local_error->message, NULL);
979 		g_error_free (local_error);
980 
981 	} else {
982 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
983 	}
984 
985 	async_context_free (async_context);
986 }
987 
988 void
e_mail_reader_unsubscribe_folder_name(EMailReader * reader,CamelStore * store,const gchar * folder_name)989 e_mail_reader_unsubscribe_folder_name (EMailReader *reader,
990                                        CamelStore *store,
991                                        const gchar *folder_name)
992 {
993 	EActivity *activity;
994 	GCancellable *cancellable;
995 	AsyncContext *async_context;
996 
997 	g_return_if_fail (E_IS_MAIL_READER (reader));
998 	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (store));
999 	g_return_if_fail (folder_name != NULL);
1000 
1001 	activity = e_mail_reader_new_activity (reader);
1002 	cancellable = e_activity_get_cancellable (activity);
1003 
1004 	async_context = g_slice_new0 (AsyncContext);
1005 	async_context->activity = g_object_ref (activity);
1006 	async_context->reader = g_object_ref (reader);
1007 	async_context->folder_name = g_strdup (folder_name);
1008 
1009 	camel_subscribable_unsubscribe_folder (
1010 		CAMEL_SUBSCRIBABLE (store), folder_name,
1011 		G_PRIORITY_DEFAULT, cancellable,
1012 		mail_reader_unsubscribe_folder_name_cb,
1013 		async_context);
1014 
1015 	g_object_unref (activity);
1016 }
1017 
1018 guint
e_mail_reader_mark_selected(EMailReader * reader,guint32 mask,guint32 set)1019 e_mail_reader_mark_selected (EMailReader *reader,
1020                              guint32 mask,
1021                              guint32 set)
1022 {
1023 	CamelFolder *folder;
1024 	guint ii = 0;
1025 
1026 	g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
1027 
1028 	folder = e_mail_reader_ref_folder (reader);
1029 
1030 	if (folder != NULL) {
1031 		GPtrArray *uids;
1032 
1033 		camel_folder_freeze (folder);
1034 
1035 		uids = e_mail_reader_get_selected_uids_with_collapsed_threads (reader);
1036 
1037 		for (ii = 0; ii < uids->len; ii++)
1038 			camel_folder_set_message_flags (
1039 				folder, uids->pdata[ii], mask, set);
1040 
1041 		/* This function is called on user interaction, thus make sure the message list
1042 		   will scroll to the selected message, which can eventually change due to
1043 		   view filters on the folder. */
1044 		if (uids->len > 0) {
1045 			GtkWidget *message_list = e_mail_reader_get_message_list (reader);
1046 
1047 			if (message_list)
1048 				e_tree_show_cursor_after_reflow (E_TREE (message_list));
1049 		}
1050 
1051 		g_ptr_array_unref (uids);
1052 
1053 		camel_folder_thaw (folder);
1054 
1055 		g_object_unref (folder);
1056 	}
1057 
1058 	return ii;
1059 }
1060 
1061 static guint
summary_msgid_hash(gconstpointer key)1062 summary_msgid_hash (gconstpointer key)
1063 {
1064 	const CamelSummaryMessageID *id = (const CamelSummaryMessageID *) key;
1065 
1066 	return id->id.part.lo;
1067 }
1068 
1069 static gboolean
summary_msgid_equal(gconstpointer a,gconstpointer b)1070 summary_msgid_equal (gconstpointer a,
1071 		     gconstpointer b)
1072 {
1073 	return ((const CamelSummaryMessageID *) a)->id.id == ((const CamelSummaryMessageID *) b)->id.id;
1074 }
1075 
1076 typedef struct {
1077 	CamelFolder *folder;
1078 	GSList *uids;
1079 	EIgnoreThreadKind kind;
1080 } MarkIgnoreThreadData;
1081 
1082 static void
mark_ignore_thread_data_free(gpointer ptr)1083 mark_ignore_thread_data_free (gpointer ptr)
1084 {
1085 	MarkIgnoreThreadData *mit = ptr;
1086 
1087 	if (mit) {
1088 		g_clear_object (&mit->folder);
1089 		g_slist_free_full (mit->uids, (GDestroyNotify) camel_pstring_free);
1090 		g_slice_free (MarkIgnoreThreadData, mit);
1091 	}
1092 }
1093 
1094 static void
insert_to_checked_msgids(GHashTable * checked_msgids,const CamelSummaryMessageID msgid)1095 insert_to_checked_msgids (GHashTable *checked_msgids,
1096 			  const CamelSummaryMessageID msgid)
1097 {
1098 	CamelSummaryMessageID *msgid_copy;
1099 
1100 	if (!msgid.id.id)
1101 		return;
1102 
1103 	msgid_copy = g_new0 (CamelSummaryMessageID, 1);
1104 	msgid_copy->id.id = msgid.id.id;
1105 
1106 	g_hash_table_insert (checked_msgids, msgid_copy, GINT_TO_POINTER (1));
1107 }
1108 
1109 static gboolean
mark_ignore_thread_traverse_uids(CamelFolder * folder,const gchar * in_uid,GHashTable * checked_uids,GHashTable * checked_msgids,gboolean whole_thread,gboolean ignore_thread,GCancellable * cancellable,GError ** error)1110 mark_ignore_thread_traverse_uids (CamelFolder *folder,
1111 				  const gchar *in_uid,
1112 				  GHashTable *checked_uids,
1113 				  GHashTable *checked_msgids,
1114 				  gboolean whole_thread,
1115 				  gboolean ignore_thread,
1116 				  GCancellable *cancellable,
1117 				  GError **error)
1118 {
1119 	GSList *to_check;
1120 	GPtrArray *uids;
1121 	guint ii;
1122 	gboolean success;
1123 
1124 	success = !g_cancellable_set_error_if_cancelled (cancellable, error);
1125 	if (!success)
1126 		return success;
1127 
1128 	if (g_hash_table_contains (checked_uids, in_uid))
1129 		return success;
1130 
1131 	to_check = g_slist_prepend (NULL, (gpointer) camel_pstring_strdup (in_uid));
1132 
1133 	while (to_check != NULL && !g_cancellable_set_error_if_cancelled (cancellable, error)) {
1134 		CamelMessageInfo *mi;
1135 		CamelSummaryMessageID msgid;
1136 		const gchar *uid = to_check->data;
1137 		gchar *sexp;
1138 		GError *local_error = NULL;
1139 
1140 		to_check = g_slist_remove (to_check, uid);
1141 
1142 		if (!uid || g_hash_table_contains (checked_uids, uid)) {
1143 			camel_pstring_free (uid);
1144 			continue;
1145 		}
1146 
1147 		g_hash_table_insert (checked_uids, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
1148 
1149 		mi = camel_folder_get_message_info (folder, uid);
1150 		if (!mi || !camel_message_info_get_message_id (mi)) {
1151 			g_clear_object (&mi);
1152 			camel_pstring_free (uid);
1153 			continue;
1154 		}
1155 
1156 		camel_message_info_set_user_flag (mi, "ignore-thread", ignore_thread);
1157 
1158 		msgid.id.id = camel_message_info_get_message_id (mi);
1159 		insert_to_checked_msgids (checked_msgids, msgid);
1160 
1161 		if (whole_thread) {
1162 			GArray *references;
1163 
1164 			/* Search for parents */
1165 			references = camel_message_info_dup_references (mi);
1166 			if (references) {
1167 				GString *expr = NULL;
1168 
1169 				for (ii = 0; ii < references->len; ii++) {
1170 					CamelSummaryMessageID ref_msgid;
1171 
1172 					ref_msgid.id.id = g_array_index (references, guint64, ii);
1173 					if (!ref_msgid.id.id ||
1174 					    g_hash_table_contains (checked_msgids, &ref_msgid))
1175 						continue;
1176 
1177 					insert_to_checked_msgids (checked_msgids, ref_msgid);
1178 
1179 					if (!expr)
1180 						expr = g_string_new ("(match-all (or ");
1181 
1182 					g_string_append_printf (expr, "(= \"msgid\" \"%lu %lu\")",
1183 						(gulong) ref_msgid.id.part.hi,
1184 						(gulong) ref_msgid.id.part.lo);
1185 				}
1186 
1187 				if (expr) {
1188 					g_string_append (expr, "))");
1189 
1190 					uids = camel_folder_search_by_expression (folder, expr->str, cancellable, &local_error);
1191 					if (uids) {
1192 						for (ii = 0; ii < uids->len; ii++) {
1193 							const gchar *refruid = uids->pdata[ii];
1194 
1195 							if (refruid && !g_hash_table_contains (checked_uids, refruid))
1196 								to_check = g_slist_prepend (to_check, (gpointer) camel_pstring_strdup (refruid));
1197 						}
1198 
1199 						camel_folder_search_free (folder, uids);
1200 					}
1201 
1202 					g_string_free (expr, TRUE);
1203 
1204 					if (local_error) {
1205 						g_propagate_error (error, local_error);
1206 						g_clear_object (&mi);
1207 						camel_pstring_free (uid);
1208 						success = FALSE;
1209 						break;
1210 					}
1211 				}
1212 
1213 				g_array_unref (references);
1214 			}
1215 		}
1216 
1217 		/* Search for children */
1218 		sexp = g_strdup_printf ("(match-all (= \"references\" \"%lu %lu\"))", (gulong) msgid.id.part.hi, (gulong) msgid.id.part.lo);
1219 		uids = camel_folder_search_by_expression (folder, sexp, cancellable, &local_error);
1220 		if (uids) {
1221 			for (ii = 0; ii < uids->len; ii++) {
1222 				const gchar *refruid = uids->pdata[ii];
1223 
1224 				if (refruid && !g_hash_table_contains (checked_uids, refruid)) {
1225 					CamelMessageInfo *refrmi = camel_folder_get_message_info (folder, refruid);
1226 					guint64 msg_id = 0;
1227 
1228 					if (refrmi)
1229 						msg_id = camel_message_info_get_message_id (refrmi);
1230 
1231 					if (refrmi && msg_id && !g_hash_table_contains (checked_msgids, &msg_id)) {
1232 						GArray *references;
1233 
1234 						/* The 'references' filter search can return false positives */
1235 						references = camel_message_info_dup_references (refrmi);
1236 						if (references) {
1237 							guint jj;
1238 
1239 							for (jj = 0; jj < references->len; jj++) {
1240 								guint64 ref_msgid;
1241 
1242 								ref_msgid = g_array_index (references, guint64, jj);
1243 								if (ref_msgid == msgid.id.id) {
1244 									to_check = g_slist_prepend (to_check, (gpointer) camel_pstring_strdup (refruid));
1245 									break;
1246 								}
1247 							}
1248 
1249 							g_array_unref (references);
1250 						}
1251 					}
1252 
1253 					g_clear_object (&refrmi);
1254 				}
1255 			}
1256 
1257 			camel_folder_search_free (folder, uids);
1258 		}
1259 		g_free (sexp);
1260 
1261 		g_clear_object (&mi);
1262 		camel_pstring_free (uid);
1263 
1264 		if (local_error) {
1265 			g_propagate_error (error, local_error);
1266 			success = FALSE;
1267 			break;
1268 		}
1269 	}
1270 
1271 	g_slist_free_full (to_check, (GDestroyNotify) camel_pstring_free);
1272 
1273 	return success;
1274 }
1275 
1276 static void
mail_reader_utils_mark_ignore_thread_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1277 mail_reader_utils_mark_ignore_thread_thread (EAlertSinkThreadJobData *job_data,
1278 					     gpointer user_data,
1279 					     GCancellable *cancellable,
1280 					     GError **error)
1281 {
1282 	MarkIgnoreThreadData *mit = user_data;
1283 	GHashTable *checked_uids; /* gchar * (UID) ~> 1 */
1284 	GHashTable *checked_msgids; /* CamelSummaryMessageID * ~> 1 */
1285 	gboolean ignore_thread, whole_thread;
1286 	GSList *link;
1287 
1288 	g_return_if_fail (mit != NULL);
1289 
1290 	camel_folder_freeze (mit->folder);
1291 
1292 	whole_thread = mit->kind == E_IGNORE_THREAD_WHOLE_SET || mit->kind == E_IGNORE_THREAD_WHOLE_UNSET;
1293 	ignore_thread = mit->kind == E_IGNORE_THREAD_WHOLE_SET || mit->kind == E_IGNORE_THREAD_SUBSET_SET;
1294 
1295 	checked_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
1296 	checked_msgids = g_hash_table_new_full (summary_msgid_hash, summary_msgid_equal, g_free, NULL);
1297 
1298 	for (link = mit->uids; link; link = g_slist_next (link)) {
1299 		if (!mark_ignore_thread_traverse_uids (mit->folder, link->data, checked_uids, checked_msgids,
1300 			whole_thread, ignore_thread, cancellable, error)) {
1301 			break;
1302 		}
1303 	}
1304 
1305 	camel_folder_thaw (mit->folder);
1306 
1307 	g_hash_table_destroy (checked_msgids);
1308 	g_hash_table_destroy (checked_uids);
1309 }
1310 
1311 void
e_mail_reader_mark_selected_ignore_thread(EMailReader * reader,EIgnoreThreadKind kind)1312 e_mail_reader_mark_selected_ignore_thread (EMailReader *reader,
1313 					   EIgnoreThreadKind kind)
1314 {
1315 	CamelFolder *folder;
1316 
1317 	g_return_if_fail (E_IS_MAIL_READER (reader));
1318 
1319 	folder = e_mail_reader_ref_folder (reader);
1320 
1321 	if (folder != NULL) {
1322 		GPtrArray *uids;
1323 		guint ii;
1324 
1325 		uids = e_mail_reader_get_selected_uids_with_collapsed_threads (reader);
1326 		if (uids && uids->len > 0) {
1327 			MarkIgnoreThreadData *mit;
1328 			EAlertSink *alert_sink;
1329 			EActivity *activity;
1330 			const gchar *description = NULL, *alert_id = NULL;
1331 
1332 			switch (kind) {
1333 			case E_IGNORE_THREAD_WHOLE_SET:
1334 				description = _("Marking thread to be ignored");
1335 				alert_id = "mail:failed-mark-ignore-thread";
1336 				break;
1337 			case E_IGNORE_THREAD_WHOLE_UNSET:
1338 				description = _("Unmarking thread from being ignored");
1339 				alert_id = "mail:failed-mark-unignore-thread";
1340 				break;
1341 			case E_IGNORE_THREAD_SUBSET_SET:
1342 				description = _("Marking subthread to be ignored");
1343 				alert_id = "mail:failed-mark-ignore-subthread";
1344 				break;
1345 			case E_IGNORE_THREAD_SUBSET_UNSET:
1346 				description = _("Unmarking subthread from being ignored");
1347 				alert_id = "mail:failed-mark-unignore-subthread";
1348 				break;
1349 			}
1350 
1351 			mit = g_slice_new0 (MarkIgnoreThreadData);
1352 			mit->folder = g_object_ref (folder);
1353 			mit->kind = kind;
1354 
1355 			for (ii = 0; ii < uids->len; ii++) {
1356 				mit->uids = g_slist_prepend (mit->uids, (gpointer) camel_pstring_strdup (uids->pdata[ii]));
1357 			}
1358 
1359 			alert_sink = e_mail_reader_get_alert_sink (reader);
1360 
1361 			activity = e_alert_sink_submit_thread_job (alert_sink, description, alert_id,
1362 				camel_folder_get_full_name (folder), mail_reader_utils_mark_ignore_thread_thread,
1363 				mit, mark_ignore_thread_data_free);
1364 
1365 
1366 			if (activity)
1367 				e_shell_backend_add_activity (E_SHELL_BACKEND (e_mail_reader_get_backend (reader)), activity);
1368 
1369 			g_clear_object (&activity);
1370 		}
1371 
1372 		g_ptr_array_unref (uids);
1373 		g_object_unref (folder);
1374 	}
1375 }
1376 
1377 static void
copy_tree_state(EMailReader * src_reader,EMailReader * des_reader)1378 copy_tree_state (EMailReader *src_reader,
1379                  EMailReader *des_reader)
1380 {
1381 	GtkWidget *src_mlist, *des_mlist;
1382 	ETableState *state;
1383 
1384 	g_return_if_fail (src_reader != NULL);
1385 	g_return_if_fail (des_reader != NULL);
1386 
1387 	src_mlist = e_mail_reader_get_message_list (src_reader);
1388 	if (!src_mlist)
1389 		return;
1390 
1391 	des_mlist = e_mail_reader_get_message_list (des_reader);
1392 	if (!des_mlist)
1393 		return;
1394 
1395 	state = e_tree_get_state_object (E_TREE (src_mlist));
1396 	e_tree_set_state_object (E_TREE (des_mlist), state);
1397 	g_object_unref (state);
1398 
1399 	message_list_set_search (MESSAGE_LIST (des_mlist), MESSAGE_LIST (src_mlist)->search);
1400 }
1401 
1402 guint
e_mail_reader_open_selected(EMailReader * reader)1403 e_mail_reader_open_selected (EMailReader *reader)
1404 {
1405 	EShell *shell;
1406 	EMailBackend *backend;
1407 	ESourceRegistry *registry;
1408 	CamelFolder *folder;
1409 	GtkWindow *window;
1410 	GPtrArray *views;
1411 	GPtrArray *uids;
1412 	guint ii = 0;
1413 	gboolean prefer_existing;
1414 
1415 	g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
1416 
1417 	backend = e_mail_reader_get_backend (reader);
1418 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1419 	registry = e_shell_get_registry (shell);
1420 
1421 	folder = e_mail_reader_ref_folder (reader);
1422 	uids = e_mail_reader_get_selected_uids (reader);
1423 	window = e_mail_reader_get_window (reader);
1424 
1425 	if (!em_utils_ask_open_many (window, uids->len))
1426 		goto exit;
1427 
1428 	if (em_utils_folder_is_drafts (registry, folder) ||
1429 		em_utils_folder_is_outbox (registry, folder) ||
1430 		em_utils_folder_is_templates (registry, folder)) {
1431 
1432 		e_mail_reader_edit_messages (reader, folder, uids, TRUE, TRUE);
1433 
1434 		ii = uids->len;
1435 
1436 		goto exit;
1437 	}
1438 
1439 	prefer_existing = !E_IS_MAIL_BROWSER (window);
1440 
1441 	views = g_ptr_array_new ();
1442 
1443 	/* For vfolders we need to edit the original, not the vfolder copy. */
1444 	for (ii = 0; ii < uids->len; ii++) {
1445 		const gchar *uid = uids->pdata[ii];
1446 		CamelFolder *real_folder;
1447 		CamelMessageInfo *info;
1448 		gchar *real_uid;
1449 
1450 		if (!CAMEL_IS_VEE_FOLDER (folder)) {
1451 			g_ptr_array_add (views, g_strdup (uid));
1452 			continue;
1453 		}
1454 
1455 		info = camel_folder_get_message_info (folder, uid);
1456 		if (info == NULL)
1457 			continue;
1458 
1459 		real_folder = camel_vee_folder_get_location (
1460 			CAMEL_VEE_FOLDER (folder),
1461 			(CamelVeeMessageInfo *) info, &real_uid);
1462 
1463 		if (em_utils_folder_is_drafts (registry, real_folder) ||
1464 			em_utils_folder_is_outbox (registry, real_folder)) {
1465 			GPtrArray *edits;
1466 
1467 			edits = g_ptr_array_new ();
1468 			g_ptr_array_add (edits, real_uid);
1469 			e_mail_reader_edit_messages (
1470 				reader, real_folder, edits, TRUE, TRUE);
1471 			g_ptr_array_unref (edits);
1472 		} else {
1473 			g_free (real_uid);
1474 			g_ptr_array_add (views, g_strdup (uid));
1475 		}
1476 
1477 		g_clear_object (&info);
1478 	}
1479 
1480 	for (ii = 0; ii < views->len; ii++) {
1481 		const gchar *uid = views->pdata[ii];
1482 		GtkWidget *browser;
1483 		MessageList *ml;
1484 
1485 		if (prefer_existing) {
1486 			EMailBrowser *mail_browser;
1487 
1488 			mail_browser = em_utils_find_message_window (E_MAIL_FORMATTER_MODE_NORMAL, folder, uid);
1489 
1490 			if (mail_browser) {
1491 				gtk_window_present (GTK_WINDOW (mail_browser));
1492 				continue;
1493 			}
1494 		}
1495 
1496 		browser = e_mail_browser_new (backend, E_MAIL_FORMATTER_MODE_NORMAL);
1497 
1498 		ml = MESSAGE_LIST (e_mail_reader_get_message_list (
1499 			E_MAIL_READER (browser)));
1500 		message_list_freeze (ml);
1501 
1502 		e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
1503 		e_mail_reader_set_message (E_MAIL_READER (browser), uid);
1504 
1505 		copy_tree_state (reader, E_MAIL_READER (browser));
1506 		e_mail_reader_set_group_by_threads (
1507 			E_MAIL_READER (browser),
1508 			e_mail_reader_get_group_by_threads (reader));
1509 
1510 		message_list_thaw (ml);
1511 		gtk_widget_show (browser);
1512 	}
1513 
1514 	g_ptr_array_foreach (views, (GFunc) g_free, NULL);
1515 	g_ptr_array_free (views, TRUE);
1516 
1517 exit:
1518 	g_clear_object (&folder);
1519 	g_ptr_array_unref (uids);
1520 
1521 	return ii;
1522 }
1523 
1524 static void
mail_reader_print_message_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1525 mail_reader_print_message_cb (GObject *source_object,
1526                               GAsyncResult *result,
1527                               gpointer user_data)
1528 {
1529 	EActivity *activity;
1530 	EAlertSink *alert_sink;
1531 	AsyncContext *async_context;
1532 	GError *local_error = NULL;
1533 
1534 	async_context = (AsyncContext *) user_data;
1535 
1536 	activity = async_context->activity;
1537 	alert_sink = e_activity_get_alert_sink (activity);
1538 
1539 	e_mail_printer_print_finish (
1540 		E_MAIL_PRINTER (source_object), result, &local_error);
1541 
1542 	if (e_activity_handle_cancellation (activity, local_error)) {
1543 		g_error_free (local_error);
1544 
1545 	} else if (local_error != NULL) {
1546 		e_alert_submit (
1547 			alert_sink, "mail:printing-failed",
1548 			local_error->message, NULL);
1549 		g_error_free (local_error);
1550 
1551 	} else {
1552 		/* Set activity as completed, and keep it displayed for a few
1553 		 * seconds so that user can actually see the printing was
1554 		 * successfully finished. */
1555 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1556 	}
1557 
1558 	async_context_free (async_context);
1559 }
1560 
1561 static void
mail_reader_print_parse_message_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1562 mail_reader_print_parse_message_cb (GObject *source_object,
1563                                     GAsyncResult *result,
1564                                     gpointer user_data)
1565 {
1566 	EMailReader *reader;
1567 	EMailDisplay *mail_display;
1568 	EMailFormatter *formatter;
1569 	EActivity *activity;
1570 	GCancellable *cancellable;
1571 	EMailPrinter *printer;
1572 	EMailPartList *part_list;
1573 	EMailRemoteContent *remote_content;
1574 	AsyncContext *async_context;
1575 	GError *local_error = NULL;
1576 	gchar *export_basename;
1577 
1578 	reader = E_MAIL_READER (source_object);
1579 	async_context = (AsyncContext *) user_data;
1580 
1581 	activity = async_context->activity;
1582 	cancellable = e_activity_get_cancellable (activity);
1583 
1584 	part_list = e_mail_reader_parse_message_finish (reader, result, &local_error);
1585 
1586 	if (local_error) {
1587 		g_warn_if_fail (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED));
1588 
1589 		/* coverity[check_return] */
1590 		e_activity_handle_cancellation (activity, local_error);
1591 		g_clear_error (&local_error);
1592 		async_context_free (async_context);
1593 
1594 		return;
1595 	}
1596 
1597 	mail_display = e_mail_reader_get_mail_display (reader);
1598 	formatter = e_mail_display_get_formatter (mail_display);
1599 	remote_content = e_mail_display_ref_remote_content (mail_display);
1600 
1601 	printer = e_mail_printer_new (part_list, remote_content);
1602 	export_basename = em_utils_build_export_basename (
1603 		CAMEL_FOLDER (async_context->folder),
1604 		e_mail_part_list_get_message_uid (part_list),
1605 		NULL);
1606 	e_util_make_safe_filename (export_basename);
1607 	e_mail_printer_set_export_filename (printer, export_basename);
1608 	g_free (export_basename);
1609 
1610 	if (e_mail_display_get_mode (mail_display) == E_MAIL_FORMATTER_MODE_SOURCE)
1611 		e_mail_printer_set_mode (printer, E_MAIL_FORMATTER_MODE_SOURCE);
1612 
1613 	g_clear_object (&remote_content);
1614 	g_clear_object (&part_list);
1615 
1616 	e_activity_set_text (activity, _("Printing"));
1617 
1618 	e_mail_printer_print (
1619 		printer,
1620 		async_context->print_action,
1621 		formatter,
1622 		cancellable,
1623 		mail_reader_print_message_cb,
1624 		async_context);
1625 
1626 	g_object_unref (printer);
1627 }
1628 
1629 static void
mail_reader_print_get_message_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1630 mail_reader_print_get_message_cb (GObject *source_object,
1631                                   GAsyncResult *result,
1632                                   gpointer user_data)
1633 {
1634 	EActivity *activity;
1635 	EAlertSink *alert_sink;
1636 	CamelMimeMessage *message;
1637 	GCancellable *cancellable;
1638 	AsyncContext *async_context;
1639 	GError *local_error = NULL;
1640 
1641 	async_context = (AsyncContext *) user_data;
1642 
1643 	activity = async_context->activity;
1644 	alert_sink = e_activity_get_alert_sink (activity);
1645 	cancellable = e_activity_get_cancellable (activity);
1646 
1647 	message = camel_folder_get_message_finish (
1648 		CAMEL_FOLDER (source_object), result, &local_error);
1649 
1650 	/* Sanity check. */
1651 	g_return_if_fail (
1652 		((message != NULL) && (local_error == NULL)) ||
1653 		((message == NULL) && (local_error != NULL)));
1654 
1655 	if (e_activity_handle_cancellation (activity, local_error)) {
1656 		async_context_free (async_context);
1657 		g_error_free (local_error);
1658 		return;
1659 
1660 	} else if (local_error != NULL) {
1661 		e_alert_submit (
1662 			alert_sink, "mail:no-retrieve-message",
1663 			local_error->message, NULL);
1664 		async_context_free (async_context);
1665 		g_error_free (local_error);
1666 		return;
1667 	}
1668 
1669 	e_activity_set_text (activity, "");
1670 
1671 	e_mail_reader_parse_message (
1672 		async_context->reader,
1673 		async_context->folder,
1674 		async_context->message_uid,
1675 		message,
1676 		cancellable,
1677 		mail_reader_print_parse_message_cb,
1678 		async_context);
1679 
1680 	g_object_unref (message);
1681 }
1682 
1683 void
e_mail_reader_print(EMailReader * reader,GtkPrintOperationAction action)1684 e_mail_reader_print (EMailReader *reader,
1685                      GtkPrintOperationAction action)
1686 {
1687 	EActivity *activity;
1688 	GCancellable *cancellable;
1689 	MessageList *message_list;
1690 	AsyncContext *async_context;
1691 
1692 	g_return_if_fail (E_IS_MAIL_READER (reader));
1693 
1694 	message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
1695 
1696 	activity = e_mail_reader_new_activity (reader);
1697 	cancellable = e_activity_get_cancellable (activity);
1698 
1699 	async_context = g_slice_new0 (AsyncContext);
1700 	async_context->activity = g_object_ref (activity);
1701 	async_context->folder = e_mail_reader_ref_folder (reader);
1702 	async_context->reader = g_object_ref (reader);
1703 	async_context->message_uid = g_strdup (message_list->cursor_uid);
1704 	async_context->print_action = action;
1705 
1706 	camel_folder_get_message (
1707 		async_context->folder,
1708 		async_context->message_uid,
1709 		G_PRIORITY_DEFAULT, cancellable,
1710 		mail_reader_print_get_message_cb,
1711 		async_context);
1712 
1713 	g_object_unref (activity);
1714 }
1715 
1716 static void
mail_reader_remove_attachments_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1717 mail_reader_remove_attachments_cb (GObject *source_object,
1718                                    GAsyncResult *result,
1719                                    gpointer user_data)
1720 {
1721 	EActivity *activity;
1722 	EAlertSink *alert_sink;
1723 	AsyncContext *async_context;
1724 	GError *local_error = NULL;
1725 
1726 	async_context = (AsyncContext *) user_data;
1727 
1728 	activity = async_context->activity;
1729 	alert_sink = e_activity_get_alert_sink (activity);
1730 
1731 	e_mail_folder_remove_attachments_finish (
1732 		CAMEL_FOLDER (source_object), result, &local_error);
1733 
1734 	if (e_activity_handle_cancellation (activity, local_error)) {
1735 		g_error_free (local_error);
1736 
1737 	} else if (local_error != NULL) {
1738 		e_alert_submit (
1739 			alert_sink,
1740 			"mail:remove-attachments",
1741 			local_error->message, NULL);
1742 		g_error_free (local_error);
1743 	}
1744 
1745 	async_context_free (async_context);
1746 }
1747 
1748 void
e_mail_reader_remove_attachments(EMailReader * reader)1749 e_mail_reader_remove_attachments (EMailReader *reader)
1750 {
1751 	EActivity *activity;
1752 	AsyncContext *async_context;
1753 	GCancellable *cancellable;
1754 	CamelFolder *folder;
1755 	GPtrArray *uids;
1756 
1757 	g_return_if_fail (E_IS_MAIL_READER (reader));
1758 
1759 	uids = e_mail_reader_get_selected_uids (reader);
1760 	g_return_if_fail (uids != NULL);
1761 
1762 	/* Remove attachments asynchronously. */
1763 
1764 	activity = e_mail_reader_new_activity (reader);
1765 	cancellable = e_activity_get_cancellable (activity);
1766 
1767 	async_context = g_slice_new0 (AsyncContext);
1768 	async_context->activity = g_object_ref (activity);
1769 	async_context->reader = g_object_ref (reader);
1770 
1771 	folder = e_mail_reader_ref_folder (reader);
1772 
1773 	e_mail_folder_remove_attachments (
1774 		folder, uids,
1775 		G_PRIORITY_DEFAULT,
1776 		cancellable,
1777 		mail_reader_remove_attachments_cb,
1778 		async_context);
1779 
1780 	g_object_unref (folder);
1781 
1782 	g_object_unref (activity);
1783 
1784 	g_ptr_array_unref (uids);
1785 }
1786 
1787 static void
mail_reader_remove_duplicates_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1788 mail_reader_remove_duplicates_cb (GObject *source_object,
1789                                   GAsyncResult *result,
1790                                   gpointer user_data)
1791 {
1792 	EActivity *activity;
1793 	EAlertSink *alert_sink;
1794 	CamelFolder *folder;
1795 	GHashTable *duplicates;
1796 	GtkWindow *parent_window;
1797 	guint n_duplicates;
1798 	gchar *full_display_name;
1799 	AsyncContext *async_context;
1800 	GError *local_error = NULL;
1801 
1802 	folder = CAMEL_FOLDER (source_object);
1803 	async_context = (AsyncContext *) user_data;
1804 
1805 	activity = async_context->activity;
1806 	alert_sink = e_activity_get_alert_sink (activity);
1807 
1808 	parent_window = e_mail_reader_get_window (async_context->reader);
1809 
1810 	duplicates = e_mail_folder_find_duplicate_messages_finish (
1811 		folder, result, &local_error);
1812 
1813 	/* Sanity check. */
1814 	g_return_if_fail (
1815 		((duplicates != NULL) && (local_error == NULL)) ||
1816 		((duplicates == NULL) && (local_error != NULL)));
1817 
1818 	if (e_activity_handle_cancellation (activity, local_error)) {
1819 		async_context_free (async_context);
1820 		g_error_free (local_error);
1821 		return;
1822 
1823 	} else if (local_error != NULL) {
1824 		e_alert_submit (
1825 			alert_sink,
1826 			"mail:find-duplicate-messages",
1827 			local_error->message, NULL);
1828 		async_context_free (async_context);
1829 		g_error_free (local_error);
1830 		return;
1831 	}
1832 
1833 	/* Finalize the activity here so we don't leave a message in
1834 	 * the task bar while prompting the user for confirmation. */
1835 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1836 	g_clear_object (&async_context->activity);
1837 
1838 	n_duplicates = g_hash_table_size (duplicates);
1839 	full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
1840 
1841 	if (n_duplicates == 0) {
1842 		e_util_prompt_user (
1843 			parent_window, "org.gnome.evolution.mail", NULL,
1844 			"mail:info-no-remove-duplicates",
1845 			full_display_name ? full_display_name : camel_folder_get_display_name (folder), NULL);
1846 	} else {
1847 		gchar *confirmation;
1848 		gboolean proceed;
1849 
1850 		confirmation = g_strdup_printf (ngettext (
1851 			/* Translators: %s is replaced with a folder
1852 			 * name %u with count of duplicate messages. */
1853 			"Folder “%s” contains %u duplicate message. "
1854 			"Are you sure you want to delete it?",
1855 			"Folder “%s” contains %u duplicate messages. "
1856 			"Are you sure you want to delete them?",
1857 			n_duplicates),
1858 			full_display_name ? full_display_name : camel_folder_get_display_name (folder),
1859 			n_duplicates);
1860 
1861 		proceed = e_util_prompt_user (
1862 			parent_window, "org.gnome.evolution.mail", NULL,
1863 			"mail:ask-remove-duplicates",
1864 			confirmation, NULL);
1865 
1866 		if (proceed) {
1867 			GHashTableIter iter;
1868 			gpointer key;
1869 
1870 			camel_folder_freeze (folder);
1871 
1872 			g_hash_table_iter_init (&iter, duplicates);
1873 
1874 			/* Mark duplicate messages for deletion. */
1875 			while (g_hash_table_iter_next (&iter, &key, NULL))
1876 				camel_folder_delete_message (folder, key);
1877 
1878 			camel_folder_thaw (folder);
1879 		}
1880 
1881 		g_free (confirmation);
1882 	}
1883 
1884 	g_hash_table_destroy (duplicates);
1885 	g_free (full_display_name);
1886 
1887 	async_context_free (async_context);
1888 }
1889 
1890 void
e_mail_reader_remove_duplicates(EMailReader * reader)1891 e_mail_reader_remove_duplicates (EMailReader *reader)
1892 {
1893 	EActivity *activity;
1894 	GCancellable *cancellable;
1895 	AsyncContext *async_context;
1896 	CamelFolder *folder;
1897 	GPtrArray *uids;
1898 
1899 	g_return_if_fail (E_IS_MAIL_READER (reader));
1900 
1901 	uids = e_mail_reader_get_selected_uids_with_collapsed_threads (reader);
1902 	g_return_if_fail (uids != NULL);
1903 
1904 	/* Find duplicate messages asynchronously. */
1905 
1906 	activity = e_mail_reader_new_activity (reader);
1907 	cancellable = e_activity_get_cancellable (activity);
1908 
1909 	async_context = g_slice_new0 (AsyncContext);
1910 	async_context->activity = g_object_ref (activity);
1911 	async_context->reader = g_object_ref (reader);
1912 
1913 	folder = e_mail_reader_ref_folder (reader);
1914 
1915 	e_mail_folder_find_duplicate_messages (
1916 		folder, uids,
1917 		G_PRIORITY_DEFAULT,
1918 		cancellable,
1919 		mail_reader_remove_duplicates_cb,
1920 		async_context);
1921 
1922 	g_object_unref (folder);
1923 
1924 	g_object_unref (activity);
1925 
1926 	g_ptr_array_unref (uids);
1927 }
1928 
1929 typedef struct _CreateComposerData {
1930 	EMailReader *reader;
1931 	CamelFolder *folder;
1932 	CamelMimeMessage *message;
1933 	const gchar *message_uid; /* Allocated on the string pool, use camel_pstring_strdup/free */
1934 	gboolean keep_signature;
1935 
1936 	EMailPartList *part_list;
1937 	EMailReplyType reply_type;
1938 	EMailReplyStyle reply_style;
1939 	CamelInternetAddress *address;
1940 	EMailPartValidityFlags validity_pgp_sum;
1941 	EMailPartValidityFlags validity_smime_sum;
1942 	gboolean is_selection;
1943 
1944 	EMailForwardStyle forward_style;
1945 
1946 	CamelMimePart *attached_part;
1947 	gchar *attached_subject;
1948 	GPtrArray *attached_uids;
1949 } CreateComposerData;
1950 
1951 static CreateComposerData *
create_composer_data_new(void)1952 create_composer_data_new (void)
1953 {
1954 	return g_slice_new0 (CreateComposerData);
1955 }
1956 
1957 static void
create_composer_data_free(CreateComposerData * ccd)1958 create_composer_data_free (CreateComposerData *ccd)
1959 {
1960 	if (ccd) {
1961 		if (ccd->attached_uids)
1962 			g_ptr_array_unref (ccd->attached_uids);
1963 
1964 		g_clear_object (&ccd->reader);
1965 		g_clear_object (&ccd->folder);
1966 		g_clear_object (&ccd->message);
1967 		g_clear_object (&ccd->part_list);
1968 		g_clear_object (&ccd->address);
1969 		g_clear_object (&ccd->attached_part);
1970 		camel_pstring_free (ccd->message_uid);
1971 		g_free (ccd->attached_subject);
1972 		g_slice_free (CreateComposerData, ccd);
1973 	}
1974 }
1975 
1976 static void
mail_reader_edit_messages_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1977 mail_reader_edit_messages_composer_created_cb (GObject *source_object,
1978 					       GAsyncResult *result,
1979 					       gpointer user_data)
1980 {
1981 	CreateComposerData *ccd = user_data;
1982 	EMsgComposer *composer;
1983 	GError *error = NULL;
1984 
1985 	g_return_if_fail (ccd != NULL);
1986 
1987 	composer = e_msg_composer_new_finish (result, &error);
1988 	if (error) {
1989 		g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
1990 		g_clear_error (&error);
1991 	} else {
1992 		camel_medium_remove_header (CAMEL_MEDIUM (ccd->message), "User-Agent");
1993 		camel_medium_remove_header (CAMEL_MEDIUM (ccd->message), "X-Mailer");
1994 		camel_medium_remove_header (CAMEL_MEDIUM (ccd->message), "X-Newsreader");
1995 		camel_medium_remove_header (CAMEL_MEDIUM (ccd->message), "X-MimeOLE");
1996 
1997 		em_utils_edit_message (
1998 			composer, ccd->folder, ccd->message, ccd->message_uid,
1999 			ccd->keep_signature);
2000 
2001 		e_mail_reader_composer_created (
2002 			ccd->reader, composer, ccd->message);
2003 	}
2004 
2005 	create_composer_data_free (ccd);
2006 }
2007 
2008 static void
mail_reader_edit_messages_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2009 mail_reader_edit_messages_cb (GObject *source_object,
2010                               GAsyncResult *result,
2011                               gpointer user_data)
2012 {
2013 	CamelFolder *folder;
2014 	EShell *shell;
2015 	EMailBackend *backend;
2016 	EActivity *activity;
2017 	EAlertSink *alert_sink;
2018 	GHashTable *hash_table;
2019 	GHashTableIter iter;
2020 	gpointer key, value;
2021 	AsyncContext *async_context;
2022 	GError *local_error = NULL;
2023 
2024 	folder = CAMEL_FOLDER (source_object);
2025 	async_context = (AsyncContext *) user_data;
2026 
2027 	activity = async_context->activity;
2028 	alert_sink = e_activity_get_alert_sink (activity);
2029 
2030 	hash_table = e_mail_folder_get_multiple_messages_finish (
2031 		folder, result, &local_error);
2032 
2033 	/* Sanity check. */
2034 	g_return_if_fail (
2035 		((hash_table != NULL) && (local_error == NULL)) ||
2036 		((hash_table == NULL) && (local_error != NULL)));
2037 
2038 	if (e_activity_handle_cancellation (activity, local_error)) {
2039 		g_error_free (local_error);
2040 		goto exit;
2041 
2042 	} else if (local_error != NULL) {
2043 		e_alert_submit (
2044 			alert_sink,
2045 			"mail:get-multiple-messages",
2046 			local_error->message, NULL);
2047 		g_error_free (local_error);
2048 		goto exit;
2049 	}
2050 
2051 	backend = e_mail_reader_get_backend (async_context->reader);
2052 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
2053 
2054 	/* Open each message in its own composer window. */
2055 
2056 	g_hash_table_iter_init (&iter, hash_table);
2057 
2058 	while (g_hash_table_iter_next (&iter, &key, &value)) {
2059 		CreateComposerData *ccd;
2060 
2061 		ccd = create_composer_data_new ();
2062 		ccd->reader = g_object_ref (async_context->reader);
2063 		ccd->folder = g_object_ref (folder);
2064 		ccd->message = g_object_ref (CAMEL_MIME_MESSAGE (value));
2065 		ccd->keep_signature = async_context->keep_signature;
2066 
2067 		if (async_context->replace)
2068 			ccd->message_uid = camel_pstring_strdup ((const gchar *) key);
2069 
2070 		e_msg_composer_new (shell, mail_reader_edit_messages_composer_created_cb, ccd);
2071 	}
2072 
2073 	g_hash_table_unref (hash_table);
2074 
2075 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
2076 
2077 exit:
2078 	async_context_free (async_context);
2079 }
2080 
2081 void
e_mail_reader_edit_messages(EMailReader * reader,CamelFolder * folder,GPtrArray * uids,gboolean replace,gboolean keep_signature)2082 e_mail_reader_edit_messages (EMailReader *reader,
2083                              CamelFolder *folder,
2084                              GPtrArray *uids,
2085                              gboolean replace,
2086                              gboolean keep_signature)
2087 {
2088 	EActivity *activity;
2089 	GCancellable *cancellable;
2090 	AsyncContext *async_context;
2091 
2092 	g_return_if_fail (E_IS_MAIL_READER (reader));
2093 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
2094 	g_return_if_fail (uids != NULL);
2095 
2096 	activity = e_mail_reader_new_activity (reader);
2097 	cancellable = e_activity_get_cancellable (activity);
2098 
2099 	async_context = g_slice_new0 (AsyncContext);
2100 	async_context->activity = g_object_ref (activity);
2101 	async_context->reader = g_object_ref (reader);
2102 	async_context->replace = replace;
2103 	async_context->keep_signature = keep_signature;
2104 
2105 	e_mail_folder_get_multiple_messages (
2106 		folder, uids,
2107 		G_PRIORITY_DEFAULT,
2108 		cancellable,
2109 		mail_reader_edit_messages_cb,
2110 		async_context);
2111 
2112 	g_object_unref (activity);
2113 }
2114 
2115 static void
mail_reader_forward_attached_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2116 mail_reader_forward_attached_composer_created_cb (GObject *source_object,
2117 						  GAsyncResult *result,
2118 						  gpointer user_data)
2119 {
2120 	CreateComposerData *ccd = user_data;
2121 	EMsgComposer *composer;
2122 	GError *error = NULL;
2123 
2124 	composer = e_msg_composer_new_finish (result, &error);
2125 	if (error) {
2126 		g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
2127 		g_clear_error (&error);
2128 	} else {
2129 		CamelDataWrapper *content;
2130 
2131 		em_utils_forward_attachment (composer, ccd->attached_part, ccd->attached_subject, ccd->folder, ccd->attached_uids);
2132 
2133 		content = camel_medium_get_content (CAMEL_MEDIUM (ccd->attached_part));
2134 		if (CAMEL_IS_MIME_MESSAGE (content)) {
2135 			e_mail_reader_composer_created (ccd->reader, composer, CAMEL_MIME_MESSAGE (content));
2136 		} else {
2137 			/* XXX What to do for the multipart/digest case?
2138 			 *     Extract the first message from the digest, or
2139 			 *     change the argument type to CamelMimePart and
2140 			 *     just pass the whole digest through?
2141 			 *
2142 			 *     This signal is primarily serving EMailBrowser,
2143 			 *     which can only forward one message at a time.
2144 			 *     So for the moment it doesn't matter, but still
2145 			 *     something to consider. */
2146 			e_mail_reader_composer_created (ccd->reader, composer, NULL);
2147 		}
2148 	}
2149 
2150 	create_composer_data_free (ccd);
2151 }
2152 
2153 static void
mail_reader_forward_attachment_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2154 mail_reader_forward_attachment_cb (GObject *source_object,
2155                                    GAsyncResult *result,
2156                                    gpointer user_data)
2157 {
2158 	CamelFolder *folder;
2159 	EMailBackend *backend;
2160 	EActivity *activity;
2161 	EAlertSink *alert_sink;
2162 	EShell *shell;
2163 	CamelMimePart *part;
2164 	CreateComposerData *ccd;
2165 	gchar *subject = NULL;
2166 	AsyncContext *async_context;
2167 	GError *local_error = NULL;
2168 
2169 	folder = CAMEL_FOLDER (source_object);
2170 	async_context = (AsyncContext *) user_data;
2171 
2172 	activity = async_context->activity;
2173 	alert_sink = e_activity_get_alert_sink (activity);
2174 
2175 	part = e_mail_folder_build_attachment_finish (
2176 		folder, result, &subject, &local_error);
2177 
2178 	/* Sanity check. */
2179 	g_return_if_fail (
2180 		((part != NULL) && (local_error == NULL)) ||
2181 		((part == NULL) && (local_error != NULL)));
2182 
2183 	if (e_activity_handle_cancellation (activity, local_error)) {
2184 		g_warn_if_fail (subject == NULL);
2185 		g_error_free (local_error);
2186 		goto exit;
2187 
2188 	} else if (local_error != NULL) {
2189 		g_warn_if_fail (subject == NULL);
2190 		e_alert_submit (
2191 			alert_sink,
2192 			"mail:get-multiple-messages",
2193 			local_error->message, NULL);
2194 		g_error_free (local_error);
2195 		goto exit;
2196 	}
2197 
2198 	ccd = create_composer_data_new ();
2199 	ccd->reader = g_object_ref (async_context->reader);
2200 	ccd->folder = g_object_ref (folder);
2201 	ccd->attached_part = part;
2202 	ccd->attached_subject = subject;
2203 	ccd->attached_uids = async_context->uids ? g_ptr_array_ref (async_context->uids) : NULL;
2204 
2205 	backend = e_mail_reader_get_backend (async_context->reader);
2206 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
2207 
2208 	e_msg_composer_new (shell, mail_reader_forward_attached_composer_created_cb, ccd);
2209 
2210 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
2211 
2212 exit:
2213 	async_context_free (async_context);
2214 }
2215 
2216 static void
mail_reader_forward_message_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2217 mail_reader_forward_message_composer_created_cb (GObject *source_object,
2218 						 GAsyncResult *result,
2219 						 gpointer user_data)
2220 {
2221 	CreateComposerData *ccd = user_data;
2222 	EMsgComposer *composer;
2223 	GError *error = NULL;
2224 
2225 	g_return_if_fail (ccd != NULL);
2226 
2227 	composer = e_msg_composer_new_finish (result, &error);
2228 	if (error) {
2229 		g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
2230 		g_clear_error (&error);
2231 	} else {
2232 		em_utils_forward_message (
2233 			composer, ccd->message,
2234 			ccd->forward_style,
2235 			ccd->folder, ccd->message_uid);
2236 
2237 		e_mail_reader_composer_created (
2238 			ccd->reader, composer, ccd->message);
2239 	}
2240 
2241 	create_composer_data_free (ccd);
2242 }
2243 
2244 static void
mail_reader_forward_messages_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2245 mail_reader_forward_messages_cb (GObject *source_object,
2246                                  GAsyncResult *result,
2247                                  gpointer user_data)
2248 {
2249 	CamelFolder *folder;
2250 	EMailBackend *backend;
2251 	EActivity *activity;
2252 	EAlertSink *alert_sink;
2253 	EShell *shell;
2254 	GHashTable *hash_table;
2255 	GHashTableIter iter;
2256 	gpointer key, value;
2257 	AsyncContext *async_context;
2258 	GError *local_error = NULL;
2259 
2260 	folder = CAMEL_FOLDER (source_object);
2261 	async_context = (AsyncContext *) user_data;
2262 
2263 	activity = async_context->activity;
2264 	alert_sink = e_activity_get_alert_sink (activity);
2265 
2266 	backend = e_mail_reader_get_backend (async_context->reader);
2267 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
2268 
2269 	hash_table = e_mail_folder_get_multiple_messages_finish (
2270 		folder, result, &local_error);
2271 
2272 	/* Sanity check. */
2273 	g_return_if_fail (
2274 		((hash_table != NULL) && (local_error == NULL)) ||
2275 		((hash_table == NULL) && (local_error != NULL)));
2276 
2277 	if (e_activity_handle_cancellation (activity, local_error)) {
2278 		g_error_free (local_error);
2279 		goto exit;
2280 
2281 	} else if (local_error != NULL) {
2282 		e_alert_submit (
2283 			alert_sink,
2284 			"mail:get-multiple-messages",
2285 			local_error->message, NULL);
2286 		g_error_free (local_error);
2287 		goto exit;
2288 	}
2289 
2290 	/* Create a new composer window for each message. */
2291 
2292 	g_hash_table_iter_init (&iter, hash_table);
2293 
2294 	while (g_hash_table_iter_next (&iter, &key, &value)) {
2295 		CreateComposerData *ccd;
2296 		CamelMimeMessage *message;
2297 		const gchar *message_uid;
2298 
2299 		message_uid = (const gchar *) key;
2300 		message = CAMEL_MIME_MESSAGE (value);
2301 
2302 		ccd = create_composer_data_new ();
2303 		ccd->reader = g_object_ref (async_context->reader);
2304 		ccd->folder = g_object_ref (folder);
2305 		ccd->message = g_object_ref (message);
2306 		ccd->message_uid = camel_pstring_strdup (message_uid);
2307 		ccd->forward_style = async_context->forward_style;
2308 
2309 		e_msg_composer_new (shell, mail_reader_forward_message_composer_created_cb, ccd);
2310 	}
2311 
2312 	g_hash_table_unref (hash_table);
2313 
2314 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
2315 
2316 exit:
2317 	async_context_free (async_context);
2318 }
2319 
2320 void
e_mail_reader_forward_messages(EMailReader * reader,CamelFolder * folder,GPtrArray * uids,EMailForwardStyle style)2321 e_mail_reader_forward_messages (EMailReader *reader,
2322                                 CamelFolder *folder,
2323                                 GPtrArray *uids,
2324                                 EMailForwardStyle style)
2325 {
2326 	EActivity *activity;
2327 	GCancellable *cancellable;
2328 	AsyncContext *async_context;
2329 
2330 	g_return_if_fail (E_IS_MAIL_READER (reader));
2331 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
2332 	g_return_if_fail (uids != NULL);
2333 
2334 	activity = e_mail_reader_new_activity (reader);
2335 	cancellable = e_activity_get_cancellable (activity);
2336 
2337 	async_context = g_slice_new0 (AsyncContext);
2338 	async_context->activity = g_object_ref (activity);
2339 	async_context->reader = g_object_ref (reader);
2340 	async_context->uids = g_ptr_array_ref (uids);
2341 	async_context->forward_style = style;
2342 
2343 	switch (style) {
2344 		case E_MAIL_FORWARD_STYLE_ATTACHED:
2345 			e_mail_folder_build_attachment (
2346 				folder, uids,
2347 				G_PRIORITY_DEFAULT,
2348 				cancellable,
2349 				mail_reader_forward_attachment_cb,
2350 				async_context);
2351 			break;
2352 
2353 		case E_MAIL_FORWARD_STYLE_INLINE:
2354 		case E_MAIL_FORWARD_STYLE_QUOTED:
2355 			e_mail_folder_get_multiple_messages (
2356 				folder, uids,
2357 				G_PRIORITY_DEFAULT,
2358 				cancellable,
2359 				mail_reader_forward_messages_cb,
2360 				async_context);
2361 			break;
2362 
2363 		default:
2364 			g_warn_if_reached ();
2365 	}
2366 
2367 	g_object_unref (activity);
2368 }
2369 
2370 /* Helper for e_mail_reader_reply_to_message()
2371  * XXX This function belongs in e-html-utils.c */
2372 static gboolean
html_contains_nonwhitespace(const gchar * html,gint len)2373 html_contains_nonwhitespace (const gchar *html,
2374                              gint len)
2375 {
2376 	const gchar *cp;
2377 	gunichar uc = 0;
2378 
2379 	if (html == NULL || len <= 0)
2380 		return FALSE;
2381 
2382 	cp = html;
2383 
2384 	while (cp != NULL && cp - html < len) {
2385 		uc = g_utf8_get_char (cp);
2386 		if (uc == 0)
2387 			break;
2388 
2389 		if (uc == '<') {
2390 			/* skip until next '>' */
2391 			uc = g_utf8_get_char (cp);
2392 			while (uc != 0 && uc != '>' && cp - html < len) {
2393 				cp = g_utf8_next_char (cp);
2394 				uc = g_utf8_get_char (cp);
2395 			}
2396 			if (uc == 0)
2397 				break;
2398 		} else if (uc == '&') {
2399 			/* sequence '&nbsp;' is a space */
2400 			if (g_ascii_strncasecmp (cp, "&nbsp;", 6) == 0)
2401 				cp = cp + 5;
2402 			else
2403 				break;
2404 		} else if (!g_unichar_isspace (uc))
2405 			break;
2406 
2407 		cp = g_utf8_next_char (cp);
2408 	}
2409 
2410 	return cp - html < len - 1 && uc != 0;
2411 }
2412 
2413 static gboolean
plaintext_contains_nonwhitespace(const gchar * text,gint len)2414 plaintext_contains_nonwhitespace (const gchar *text,
2415 				  gint len)
2416 {
2417 	const gchar *cp;
2418 	gunichar uc = 0;
2419 
2420 	if (!text || len <= 0)
2421 		return FALSE;
2422 
2423 	cp = text;
2424 
2425 	while (cp != NULL && cp - text < len) {
2426 		uc = g_utf8_get_char (cp);
2427 		if (uc == 0)
2428 			break;
2429 
2430 		if (!g_unichar_isspace (uc))
2431 			break;
2432 
2433 		cp = g_utf8_next_char (cp);
2434 	}
2435 
2436 	return cp - text < len - 1 && uc != 0;
2437 }
2438 
2439 static void
maybe_mangle_plaintext_signature_delimiter(gchar ** inout_text)2440 maybe_mangle_plaintext_signature_delimiter (gchar **inout_text)
2441 {
2442 	GString *text;
2443 
2444 	g_return_if_fail (inout_text != NULL);
2445 
2446 	if (!*inout_text || (!strstr (*inout_text, "\n-- \n") && g_ascii_strncasecmp (*inout_text, "-- \n", 4)  != 0))
2447 		return;
2448 
2449 	text = e_str_replace_string (*inout_text, "\n-- \n", "\n--\n");
2450 	if (!text)
2451 		return;
2452 
2453 	if (text->len >= 4 && g_ascii_strncasecmp (text->str, "-- \n", 4)  == 0) {
2454 		/* Remove the space at the third byte */
2455 		g_string_erase (text, 2, 1);
2456 	}
2457 
2458 	g_free (*inout_text);
2459 	*inout_text = g_string_free (text, FALSE);
2460 }
2461 
2462 static void
mail_reader_reply_to_message_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2463 mail_reader_reply_to_message_composer_created_cb (GObject *source_object,
2464 						  GAsyncResult *result,
2465 						  gpointer user_data)
2466 {
2467 	CreateComposerData *ccd = user_data;
2468 	EMsgComposer *composer;
2469 	GError *error = NULL;
2470 
2471 	g_return_if_fail (ccd != NULL);
2472 
2473 	composer = e_msg_composer_new_finish (result, &error);
2474 	if (error) {
2475 		g_warning ("%s: failed to create msg composer: %s", G_STRFUNC, error->message);
2476 		g_clear_error (&error);
2477 	} else {
2478 		em_utils_reply_to_message (
2479 			composer, ccd->message, ccd->folder, ccd->message_uid,
2480 			ccd->reply_type, ccd->reply_style, ccd->is_selection ? NULL : ccd->part_list, ccd->address, E_MAIL_REPLY_FLAG_NONE);
2481 
2482 		em_composer_utils_update_security (composer, ccd->validity_pgp_sum, ccd->validity_smime_sum);
2483 
2484 		e_mail_reader_composer_created (ccd->reader, composer, ccd->message);
2485 	}
2486 
2487 	create_composer_data_free (ccd);
2488 }
2489 
2490 static CamelMimeMessage *
e_mail_reader_utils_selection_as_message(CamelMimeMessage * src_message,const gchar * selection,gboolean selection_is_html)2491 e_mail_reader_utils_selection_as_message (CamelMimeMessage *src_message,
2492 					  const gchar *selection,
2493 					  gboolean selection_is_html)
2494 {
2495 	const CamelNameValueArray *headers;
2496 	CamelMimeMessage *new_message;
2497 	guint ii, len;
2498 	gint length;
2499 
2500 	if (!selection || !*selection)
2501 		return NULL;
2502 
2503 	length = strlen (selection);
2504 	if ((selection_is_html && !html_contains_nonwhitespace (selection, length)) ||
2505 	    (!selection_is_html && !plaintext_contains_nonwhitespace (selection, length))) {
2506 		return NULL;
2507 	}
2508 
2509 	new_message = camel_mime_message_new ();
2510 
2511 	if (src_message) {
2512 		/* Filter out "content-*" headers. */
2513 		headers = camel_medium_get_headers (CAMEL_MEDIUM (src_message));
2514 		len = camel_name_value_array_get_length (headers);
2515 		for (ii = 0; ii < len; ii++) {
2516 			const gchar *header_name = NULL, *header_value = NULL;
2517 
2518 			if (camel_name_value_array_get (headers, ii, &header_name, &header_value) &&
2519 			    header_name &&
2520 			    g_ascii_strncasecmp (header_name, "content-", 8) != 0) {
2521 				camel_medium_add_header (CAMEL_MEDIUM (new_message), header_name, header_value);
2522 			}
2523 		}
2524 	}
2525 
2526 	camel_medium_add_header (
2527 		CAMEL_MEDIUM (new_message),
2528 		"X-Evolution-Content-Source", "selection");
2529 
2530 	camel_mime_part_set_encoding (
2531 		CAMEL_MIME_PART (new_message),
2532 		CAMEL_TRANSFER_ENCODING_8BIT);
2533 
2534 	camel_mime_part_set_content (
2535 		CAMEL_MIME_PART (new_message),
2536 		selection,
2537 		length,
2538 		selection_is_html ? "text/html; charset=utf-8" : "text/plain; charset=utf-8");
2539 
2540 	return new_message;
2541 }
2542 
2543 typedef struct _SelectionOrMessageData {
2544 	GTask *task; /* not referenced */
2545 	EActivity *activity;
2546 	CamelFolder *folder;
2547 	CamelMimeMessage *preloaded_message;
2548 	CamelMimeMessage *message;
2549 	EMailPartList *part_list;
2550 	EMailPartValidityFlags orig_validity_pgp_sum;
2551 	EMailPartValidityFlags orig_validity_smime_sum;
2552 	const gchar *message_uid; /* Allocated on the string pool, use camel_pstring_strdup/free */
2553 	gboolean is_selection;
2554 	gboolean selection_is_html;
2555 } SelectionOrMessageData;
2556 
2557 static void
selection_or_message_data_free(gpointer ptr)2558 selection_or_message_data_free (gpointer ptr)
2559 {
2560 	SelectionOrMessageData *smd = ptr;
2561 
2562 	if (smd) {
2563 		g_clear_object (&smd->activity);
2564 		g_clear_object (&smd->folder);
2565 		g_clear_object (&smd->preloaded_message);
2566 		g_clear_object (&smd->message);
2567 		g_clear_object (&smd->part_list);
2568 		camel_pstring_free (smd->message_uid);
2569 		g_slice_free (SelectionOrMessageData, smd);
2570 	}
2571 }
2572 
2573 static void
selection_or_message_message_parsed_cb(GObject * object,GAsyncResult * result,gpointer user_data)2574 selection_or_message_message_parsed_cb (GObject *object,
2575 					GAsyncResult *result,
2576 					gpointer user_data)
2577 {
2578 	SelectionOrMessageData *smd = user_data;
2579 	GError *local_error = NULL;
2580 
2581 	smd->part_list = e_mail_reader_parse_message_finish (E_MAIL_READER (object), result, &local_error);
2582 
2583 	if (local_error) {
2584 		g_warn_if_fail (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED));
2585 		g_task_return_error (smd->task, local_error);
2586 	} else {
2587 		if (!smd->orig_validity_pgp_sum && !smd->orig_validity_smime_sum)
2588 			e_mail_part_list_sum_validity (smd->part_list, &smd->orig_validity_pgp_sum, &smd->orig_validity_smime_sum);
2589 
2590 		g_task_return_boolean (smd->task, TRUE);
2591 	}
2592 
2593 	g_clear_object (&smd->task);
2594 }
2595 
2596 static void
selection_or_message_got_message_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2597 selection_or_message_got_message_cb (GObject *source_object,
2598 				     GAsyncResult *result,
2599 				     gpointer user_data)
2600 {
2601 	SelectionOrMessageData *smd = user_data;
2602 	EActivity *activity;
2603 	EAlertSink *alert_sink;
2604 	GCancellable *cancellable;
2605 	GError *local_error = NULL;
2606 
2607 	activity = smd->activity;
2608 	alert_sink = e_activity_get_alert_sink (activity);
2609 	cancellable = e_activity_get_cancellable (activity);
2610 
2611 	g_warn_if_fail (smd->message == NULL);
2612 	smd->message = camel_folder_get_message_finish (CAMEL_FOLDER (source_object), result, &local_error);
2613 
2614 	/* Sanity check. */
2615 	g_return_if_fail (
2616 		((smd->message != NULL) && (local_error == NULL)) ||
2617 		((smd->message == NULL) && (local_error != NULL)));
2618 
2619 	if (e_activity_handle_cancellation (activity, local_error)) {
2620 		g_task_return_error (smd->task, local_error);
2621 		g_clear_object (&smd->task);
2622 		return;
2623 
2624 	} else if (local_error != NULL) {
2625 		e_alert_submit (
2626 			alert_sink, "mail:no-retrieve-message",
2627 			local_error->message, NULL);
2628 		g_task_return_error (smd->task, local_error);
2629 		g_clear_object (&smd->task);
2630 		return;
2631 	}
2632 
2633 	e_mail_reader_parse_message (
2634 		E_MAIL_READER (g_task_get_source_object (smd->task)),
2635 		smd->folder,
2636 		smd->message_uid,
2637 		smd->message,
2638 		cancellable,
2639 		selection_or_message_message_parsed_cb,
2640 		smd);
2641 }
2642 
2643 static void
selection_or_message_get_message(EMailReader * reader,SelectionOrMessageData * smd)2644 selection_or_message_get_message (EMailReader *reader,
2645 				  SelectionOrMessageData *smd)
2646 {
2647 	CamelObjectBag *registry;
2648 	GCancellable *cancellable;
2649 	gchar *mail_uri;
2650 
2651 	g_return_if_fail (E_IS_MAIL_READER (reader));
2652 	g_return_if_fail (smd != NULL);
2653 
2654 	smd->is_selection = FALSE;
2655 
2656 	registry = e_mail_part_list_get_registry ();
2657 	mail_uri = e_mail_part_build_uri (smd->folder, smd->message_uid, NULL, NULL);
2658 	smd->part_list = camel_object_bag_get (registry, mail_uri);
2659 	g_free (mail_uri);
2660 
2661 	if (smd->part_list) {
2662 		g_warn_if_fail (smd->message == NULL);
2663 		if (smd->preloaded_message)
2664 			smd->message = smd->preloaded_message;
2665 		else
2666 			smd->message = e_mail_part_list_get_message (smd->part_list);
2667 
2668 		if (smd->message)
2669 			g_object_ref (smd->message);
2670 		else
2671 			g_clear_object (&smd->part_list);
2672 
2673 		if (smd->message) {
2674 			e_mail_part_list_sum_validity (smd->part_list, &smd->orig_validity_pgp_sum, &smd->orig_validity_smime_sum);
2675 
2676 			g_task_return_boolean (smd->task, TRUE);
2677 			g_clear_object (&smd->task);
2678 			return;
2679 		}
2680 	}
2681 
2682 	cancellable = g_task_get_cancellable (smd->task);
2683 
2684 	smd->activity = e_mail_reader_new_activity (reader);
2685 	e_activity_set_cancellable (smd->activity, cancellable);
2686 
2687 	if (smd->preloaded_message) {
2688 		g_warn_if_fail (smd->message == NULL);
2689 		smd->message = g_object_ref (smd->preloaded_message);
2690 
2691 		e_mail_reader_parse_message (
2692 			reader,
2693 			smd->folder,
2694 			smd->message_uid,
2695 			smd->message,
2696 			cancellable,
2697 			selection_or_message_message_parsed_cb,
2698 			smd);
2699 	} else {
2700 		camel_folder_get_message (
2701 			smd->folder,
2702 			smd->message_uid,
2703 			G_PRIORITY_DEFAULT,
2704 			cancellable,
2705 			selection_or_message_got_message_cb,
2706 			smd);
2707 	}
2708 }
2709 
2710 static void
selection_or_message_got_selection_jsc_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2711 selection_or_message_got_selection_jsc_cb (GObject *source_object,
2712 					   GAsyncResult *result,
2713 					   gpointer user_data)
2714 {
2715 	SelectionOrMessageData *smd = user_data;
2716 	CamelMimeMessage *new_message;
2717 	gchar *selection;
2718 	GSList *texts = NULL;
2719 	GError *error = NULL;
2720 
2721 	g_return_if_fail (smd != NULL);
2722 	g_return_if_fail (E_IS_WEB_VIEW (source_object));
2723 
2724 	if (!e_web_view_jsc_get_selection_finish (WEBKIT_WEB_VIEW (source_object), result, &texts, &error)) {
2725 		texts = NULL;
2726 		g_warning ("%s: Failed to get view selection: %s", G_STRFUNC, error ? error->message : "Unknown error");
2727 	}
2728 
2729 	g_clear_error (&error);
2730 
2731 	selection = texts ? texts->data : NULL;
2732 
2733 	if (selection && !smd->selection_is_html) {
2734 		maybe_mangle_plaintext_signature_delimiter (&selection);
2735 		texts->data = selection;
2736 	}
2737 
2738 	new_message = e_mail_reader_utils_selection_as_message (smd->message, selection, smd->selection_is_html);
2739 
2740 	smd->is_selection = new_message != NULL;
2741 
2742 	if (new_message) {
2743 		g_clear_object (&smd->message);
2744 		smd->message = new_message;
2745 	}
2746 
2747 	g_task_return_boolean (smd->task, TRUE);
2748 	g_clear_object (&smd->task);
2749 
2750 	g_slist_free_full (texts, g_free);
2751 }
2752 
2753 void
e_mail_reader_utils_get_selection_or_message(EMailReader * reader,CamelMimeMessage * preloaded_message,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2754 e_mail_reader_utils_get_selection_or_message (EMailReader *reader,
2755 					      CamelMimeMessage *preloaded_message,
2756 					      GCancellable *cancellable,
2757 					      GAsyncReadyCallback callback,
2758 					      gpointer user_data)
2759 {
2760 	SelectionOrMessageData *smd;
2761 	EMailDisplay *display;
2762 	EWebView *web_view;
2763 	GtkWidget *message_list;
2764 	const gchar *uid;
2765 
2766 	message_list = e_mail_reader_get_message_list (reader);
2767 
2768 	uid = MESSAGE_LIST (message_list)->cursor_uid;
2769 	g_return_if_fail (uid != NULL);
2770 
2771 	smd = g_slice_new0 (SelectionOrMessageData);
2772 	smd->task = g_task_new (reader, cancellable, callback, user_data);
2773 	g_task_set_source_tag (smd->task, e_mail_reader_utils_get_selection_or_message);
2774 	g_task_set_task_data (smd->task, smd, selection_or_message_data_free);
2775 
2776 	display = e_mail_reader_get_mail_display (reader);
2777 	web_view = E_WEB_VIEW (display);
2778 
2779 	smd->message_uid = camel_pstring_strdup (uid);
2780 	smd->folder = e_mail_reader_ref_folder (reader);
2781 
2782 	if (preloaded_message)
2783 		smd->preloaded_message = g_object_ref (preloaded_message);
2784 
2785 	if (gtk_widget_get_visible (GTK_WIDGET (web_view)) &&
2786 	    e_web_view_has_selection (web_view)) {
2787 		EMailPartList *part_list;
2788 		CamelMimeMessage *message = NULL;
2789 
2790 		part_list = e_mail_display_get_part_list (display);
2791 		if (part_list)
2792 			message = e_mail_part_list_get_message (part_list);
2793 
2794 		if (message) {
2795 			CamelContentType *ct;
2796 
2797 			e_mail_part_list_sum_validity (part_list, &smd->orig_validity_pgp_sum, &smd->orig_validity_smime_sum);
2798 
2799 			smd->message = g_object_ref (message);
2800 			smd->part_list = g_object_ref (part_list);
2801 
2802 			ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (message));
2803 
2804 			if (camel_content_type_is (ct, "text", "plain")) {
2805 				smd->selection_is_html = FALSE;
2806 				e_web_view_jsc_get_selection (WEBKIT_WEB_VIEW (web_view), E_TEXT_FORMAT_PLAIN, NULL,
2807 					selection_or_message_got_selection_jsc_cb, smd);
2808 			} else {
2809 				smd->selection_is_html = TRUE;
2810 				e_web_view_jsc_get_selection (WEBKIT_WEB_VIEW (web_view), E_TEXT_FORMAT_HTML, NULL,
2811 					selection_or_message_got_selection_jsc_cb, smd);
2812 			}
2813 
2814 			return;
2815 		}
2816 	}
2817 
2818 	selection_or_message_get_message (reader, smd);
2819 }
2820 
2821 CamelMimeMessage *
e_mail_reader_utils_get_selection_or_message_finish(EMailReader * reader,GAsyncResult * result,gboolean * out_is_selection,CamelFolder ** out_folder,const gchar ** out_message_uid,EMailPartList ** out_part_list,EMailPartValidityFlags * out_orig_validity_pgp_sum,EMailPartValidityFlags * out_orig_validity_smime_sum,GError ** error)2822 e_mail_reader_utils_get_selection_or_message_finish (EMailReader *reader,
2823 						     GAsyncResult *result,
2824 						     gboolean *out_is_selection,
2825 						     CamelFolder **out_folder,
2826 						     const gchar **out_message_uid, /* free with camel_pstring_free() */
2827 						     EMailPartList **out_part_list,
2828 						     EMailPartValidityFlags *out_orig_validity_pgp_sum,
2829 						     EMailPartValidityFlags *out_orig_validity_smime_sum,
2830 						     GError **error)
2831 {
2832 	SelectionOrMessageData *smd;
2833 	CamelMimeMessage *message;
2834 	GTask *task;
2835 
2836 	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
2837 	g_return_val_if_fail (g_task_is_valid (result, reader), NULL);
2838 	g_return_val_if_fail (g_async_result_is_tagged (result, e_mail_reader_utils_get_selection_or_message), NULL);
2839 
2840 	task = G_TASK (result);
2841 
2842 	smd = g_task_get_task_data (task);
2843 	g_return_val_if_fail (smd != NULL, NULL);
2844 
2845 	if (g_task_propagate_boolean (task, error)) {
2846 		message = g_steal_pointer (&smd->message);
2847 
2848 		if (out_is_selection)
2849 			*out_is_selection = smd->is_selection;
2850 
2851 		if (out_folder)
2852 			*out_folder = g_steal_pointer (&smd->folder);
2853 
2854 		if (out_message_uid)
2855 			*out_message_uid = g_steal_pointer (&smd->message_uid);
2856 
2857 		if (out_part_list)
2858 			*out_part_list = g_steal_pointer (&smd->part_list);
2859 
2860 		if (out_orig_validity_pgp_sum)
2861 			*out_orig_validity_pgp_sum = smd->orig_validity_pgp_sum;
2862 
2863 		if (out_orig_validity_smime_sum)
2864 			*out_orig_validity_smime_sum = smd->orig_validity_smime_sum;
2865 	} else {
2866 		message = NULL;
2867 	}
2868 
2869 	return message;
2870 }
2871 
2872 static void
reply_to_message_got_message_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2873 reply_to_message_got_message_cb (GObject *source_object,
2874 				 GAsyncResult *result,
2875 				 gpointer user_data)
2876 {
2877 	EMailReplyType reply_type = GPOINTER_TO_INT (user_data);
2878 	EMailReader *reader = E_MAIL_READER (source_object);
2879 	EShell *shell;
2880 	CreateComposerData *ccd;
2881 	GError *error = NULL;
2882 
2883 	ccd = create_composer_data_new ();
2884 	ccd->reader = g_object_ref (reader);
2885 	ccd->reply_type = reply_type;
2886 	ccd->reply_style = e_mail_reader_get_reply_style (reader);
2887 
2888 	ccd->message = e_mail_reader_utils_get_selection_or_message_finish (reader, result,
2889 			&ccd->is_selection, &ccd->folder, &ccd->message_uid, &ccd->part_list,
2890 			&ccd->validity_pgp_sum, &ccd->validity_smime_sum, &error);
2891 
2892 	if (!ccd->message) {
2893 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
2894 			g_warning ("%s: Failed to get message: %s", G_STRFUNC, error ? error->message : "Unknown error");
2895 		g_clear_error (&error);
2896 		create_composer_data_free (ccd);
2897 		return;
2898 	}
2899 
2900 	if (reply_type == E_MAIL_REPLY_TO_RECIPIENT) {
2901 		EMailDisplay *display;
2902 		const gchar *uri;
2903 
2904 		display = e_mail_reader_get_mail_display (reader);
2905 		uri = e_web_view_get_selected_uri (E_WEB_VIEW (display));
2906 
2907 		if (uri) {
2908 			CamelURL *curl;
2909 
2910 			curl = camel_url_new (uri, NULL);
2911 
2912 			if (curl && curl->path && *curl->path) {
2913 				ccd->address = camel_internet_address_new ();
2914 				if (camel_address_decode (CAMEL_ADDRESS (ccd->address), curl->path) < 0) {
2915 					g_clear_object (&ccd->address);
2916 				}
2917 			}
2918 
2919 			if (curl)
2920 				camel_url_free (curl);
2921 		}
2922 	}
2923 
2924 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (e_mail_reader_get_backend (reader)));
2925 
2926 	e_msg_composer_new (shell, mail_reader_reply_to_message_composer_created_cb, ccd);
2927 }
2928 
2929 void
e_mail_reader_reply_to_message(EMailReader * reader,CamelMimeMessage * src_message,EMailReplyType reply_type)2930 e_mail_reader_reply_to_message (EMailReader *reader,
2931                                 CamelMimeMessage *src_message,
2932                                 EMailReplyType reply_type)
2933 {
2934 	g_return_if_fail (E_IS_MAIL_READER (reader));
2935 
2936 	e_mail_reader_utils_get_selection_or_message (reader, src_message, NULL,
2937 		reply_to_message_got_message_cb, GINT_TO_POINTER (reply_type));
2938 }
2939 
2940 static void
mail_reader_save_messages_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2941 mail_reader_save_messages_cb (GObject *source_object,
2942                               GAsyncResult *result,
2943                               gpointer user_data)
2944 {
2945 	EActivity *activity;
2946 	EAlertSink *alert_sink;
2947 	AsyncContext *async_context;
2948 	GError *local_error = NULL;
2949 
2950 	async_context = (AsyncContext *) user_data;
2951 
2952 	activity = async_context->activity;
2953 	alert_sink = e_activity_get_alert_sink (activity);
2954 
2955 	e_mail_folder_save_messages_finish (
2956 		CAMEL_FOLDER (source_object), result, &local_error);
2957 
2958 	if (e_activity_handle_cancellation (activity, local_error)) {
2959 		g_error_free (local_error);
2960 
2961 	} else if (local_error != NULL) {
2962 		e_alert_submit (
2963 			alert_sink,
2964 			"mail:save-messages",
2965 			local_error->message, NULL);
2966 		g_error_free (local_error);
2967 	}
2968 
2969 	async_context_free (async_context);
2970 }
2971 
2972 void
e_mail_reader_save_messages(EMailReader * reader)2973 e_mail_reader_save_messages (EMailReader *reader)
2974 {
2975 	EShell *shell;
2976 	EActivity *activity;
2977 	EMailBackend *backend;
2978 	GCancellable *cancellable;
2979 	AsyncContext *async_context;
2980 	EShellBackend *shell_backend;
2981 	CamelMessageInfo *info;
2982 	CamelFolder *folder;
2983 	GFile *destination;
2984 	GPtrArray *uids;
2985 	const gchar *message_uid;
2986 	const gchar *title;
2987 	gchar *suggestion = NULL;
2988 
2989 	folder = e_mail_reader_ref_folder (reader);
2990 	backend = e_mail_reader_get_backend (reader);
2991 
2992 	uids = e_mail_reader_get_selected_uids (reader);
2993 	g_return_if_fail (uids != NULL && uids->len > 0);
2994 
2995 	if (uids->len > 1) {
2996 		GtkWidget *message_list;
2997 
2998 		message_list = e_mail_reader_get_message_list (reader);
2999 		message_list_sort_uids (MESSAGE_LIST (message_list), uids);
3000 	}
3001 
3002 	message_uid = g_ptr_array_index (uids, 0);
3003 
3004 	title = ngettext ("Save Message", "Save Messages", uids->len);
3005 
3006 	/* Suggest as a filename the subject of the first message. */
3007 	info = camel_folder_get_message_info (folder, message_uid);
3008 	if (info != NULL) {
3009 		const gchar *subject;
3010 
3011 		subject = camel_message_info_get_subject (info);
3012 		if (subject != NULL)
3013 			suggestion = g_strconcat (subject, ".mbox", NULL);
3014 		g_clear_object (&info);
3015 	}
3016 
3017 	if (suggestion == NULL) {
3018 		const gchar *basename;
3019 
3020 		/* Translators: This is part of a suggested file name
3021 		 * used when saving a message or multiple messages to
3022 		 * mbox format, when the first message doesn't have a
3023 		 * subject.  The extension ".mbox" is appended to the
3024 		 * string; for example "Message.mbox". */
3025 		basename = ngettext ("Message", "Messages", uids->len);
3026 		suggestion = g_strconcat (basename, ".mbox", NULL);
3027 	}
3028 
3029 	shell_backend = E_SHELL_BACKEND (backend);
3030 	shell = e_shell_backend_get_shell (shell_backend);
3031 
3032 	destination = e_shell_run_save_dialog (
3033 		shell, title, suggestion,
3034 		"*.mbox:application/mbox,message/rfc822", NULL, NULL);
3035 
3036 	if (destination == NULL)
3037 		goto exit;
3038 
3039 	/* Save messages asynchronously. */
3040 
3041 	activity = e_mail_reader_new_activity (reader);
3042 	cancellable = e_activity_get_cancellable (activity);
3043 
3044 	async_context = g_slice_new0 (AsyncContext);
3045 	async_context->activity = g_object_ref (activity);
3046 	async_context->reader = g_object_ref (reader);
3047 
3048 	e_mail_folder_save_messages (
3049 		folder, uids,
3050 		destination,
3051 		G_PRIORITY_DEFAULT,
3052 		cancellable,
3053 		mail_reader_save_messages_cb,
3054 		async_context);
3055 
3056 	g_object_unref (activity);
3057 
3058 	g_object_unref (destination);
3059 
3060 exit:
3061 	g_clear_object (&folder);
3062 	g_ptr_array_unref (uids);
3063 }
3064 
3065 void
e_mail_reader_select_next_message(EMailReader * reader,gboolean or_else_previous)3066 e_mail_reader_select_next_message (EMailReader *reader,
3067                                    gboolean or_else_previous)
3068 {
3069 	GtkWidget *message_list;
3070 	gboolean hide_deleted;
3071 	gboolean success;
3072 
3073 	g_return_if_fail (E_IS_MAIL_READER (reader));
3074 
3075 	hide_deleted = e_mail_reader_get_hide_deleted (reader);
3076 	message_list = e_mail_reader_get_message_list (reader);
3077 
3078 	success = message_list_select (
3079 		MESSAGE_LIST (message_list),
3080 		MESSAGE_LIST_SELECT_NEXT, 0, 0);
3081 
3082 	if (!success && (hide_deleted || or_else_previous))
3083 		message_list_select (
3084 			MESSAGE_LIST (message_list),
3085 			MESSAGE_LIST_SELECT_PREVIOUS, 0, 0);
3086 }
3087 
3088 void
e_mail_reader_select_previous_message(EMailReader * reader,gboolean or_else_next)3089 e_mail_reader_select_previous_message (EMailReader *reader,
3090 				       gboolean or_else_next)
3091 {
3092 	GtkWidget *message_list;
3093 	gboolean hide_deleted;
3094 	gboolean success;
3095 
3096 	g_return_if_fail (E_IS_MAIL_READER (reader));
3097 
3098 	hide_deleted = e_mail_reader_get_hide_deleted (reader);
3099 	message_list = e_mail_reader_get_message_list (reader);
3100 
3101 	success = message_list_select (
3102 		MESSAGE_LIST (message_list),
3103 		MESSAGE_LIST_SELECT_PREVIOUS, 0, 0);
3104 
3105 	if (!success && (hide_deleted || or_else_next))
3106 		message_list_select (
3107 			MESSAGE_LIST (message_list),
3108 			MESSAGE_LIST_SELECT_NEXT, 0, 0);
3109 }
3110 
3111 /* Helper for e_mail_reader_create_filter_from_selected() */
3112 static void
mail_reader_create_filter_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3113 mail_reader_create_filter_cb (GObject *source_object,
3114                               GAsyncResult *result,
3115                               gpointer user_data)
3116 {
3117 	EActivity *activity;
3118 	EMailBackend *backend;
3119 	EMailSession *session;
3120 	EAlertSink *alert_sink;
3121 	CamelMimeMessage *message;
3122 	AsyncContext *async_context;
3123 	GError *local_error = NULL;
3124 
3125 	async_context = (AsyncContext *) user_data;
3126 
3127 	activity = async_context->activity;
3128 	alert_sink = e_activity_get_alert_sink (activity);
3129 
3130 	message = camel_folder_get_message_finish (
3131 		CAMEL_FOLDER (source_object), result, &local_error);
3132 
3133 	/* Sanity check. */
3134 	g_return_if_fail (
3135 		((message != NULL) && (local_error == NULL)) ||
3136 		((message == NULL) && (local_error != NULL)));
3137 
3138 	if (e_activity_handle_cancellation (activity, local_error)) {
3139 		async_context_free (async_context);
3140 		g_error_free (local_error);
3141 		return;
3142 
3143 	} else if (local_error != NULL) {
3144 		e_alert_submit (
3145 			alert_sink, "mail:no-retrieve-message",
3146 			local_error->message, NULL);
3147 		async_context_free (async_context);
3148 		g_error_free (local_error);
3149 		return;
3150 	}
3151 
3152 	/* Finalize the activity here so we don't leave a message
3153 	 * in the task bar while displaying the filter editor. */
3154 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
3155 	g_clear_object (&async_context->activity);
3156 
3157 	backend = e_mail_reader_get_backend (async_context->reader);
3158 	session = e_mail_backend_get_session (backend);
3159 
3160 	/* Switch to Incoming filter in case the message contains a Received header */
3161 	if (g_str_equal (async_context->filter_source, E_FILTER_SOURCE_OUTGOING) &&
3162 	    camel_medium_get_header (CAMEL_MEDIUM (message), "received"))
3163 		async_context->filter_source = E_FILTER_SOURCE_INCOMING;
3164 
3165 	filter_gui_add_from_message (
3166 		session, message,
3167 		async_context->filter_source,
3168 		async_context->filter_type);
3169 
3170 	g_object_unref (message);
3171 
3172 	async_context_free (async_context);
3173 }
3174 
3175 void
e_mail_reader_create_filter_from_selected(EMailReader * reader,gint filter_type)3176 e_mail_reader_create_filter_from_selected (EMailReader *reader,
3177                                            gint filter_type)
3178 {
3179 	EShell *shell;
3180 	EActivity *activity;
3181 	EMailBackend *backend;
3182 	AsyncContext *async_context;
3183 	GCancellable *cancellable;
3184 	ESourceRegistry *registry;
3185 	CamelFolder *folder;
3186 	GPtrArray *uids;
3187 	const gchar *filter_source;
3188 	const gchar *message_uid;
3189 
3190 	g_return_if_fail (E_IS_MAIL_READER (reader));
3191 
3192 	backend = e_mail_reader_get_backend (reader);
3193 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
3194 	registry = e_shell_get_registry (shell);
3195 
3196 	folder = e_mail_reader_ref_folder (reader);
3197 	g_return_if_fail (folder != NULL);
3198 
3199 	if (em_utils_folder_is_sent (registry, folder) ||
3200 	    em_utils_folder_is_outbox (registry, folder))
3201 		filter_source = E_FILTER_SOURCE_OUTGOING;
3202 	else
3203 		filter_source = E_FILTER_SOURCE_INCOMING;
3204 
3205 	uids = e_mail_reader_get_selected_uids (reader);
3206 	g_return_if_fail (uids != NULL && uids->len == 1);
3207 	message_uid = g_ptr_array_index (uids, 0);
3208 
3209 	activity = e_mail_reader_new_activity (reader);
3210 	cancellable = e_activity_get_cancellable (activity);
3211 
3212 	async_context = g_slice_new0 (AsyncContext);
3213 	async_context->activity = g_object_ref (activity);
3214 	async_context->reader = g_object_ref (reader);
3215 	async_context->filter_source = filter_source;
3216 	async_context->filter_type = filter_type;
3217 
3218 	camel_folder_get_message (
3219 		folder, message_uid,
3220 		G_PRIORITY_DEFAULT,
3221 		cancellable,
3222 		mail_reader_create_filter_cb,
3223 		async_context);
3224 
3225 	g_object_unref (activity);
3226 
3227 	g_ptr_array_unref (uids);
3228 
3229 	g_object_unref (folder);
3230 }
3231 
3232 /* Helper for e_mail_reader_create_vfolder_from_selected() */
3233 static void
mail_reader_create_vfolder_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3234 mail_reader_create_vfolder_cb (GObject *source_object,
3235                                GAsyncResult *result,
3236                                gpointer user_data)
3237 {
3238 	EActivity *activity;
3239 	EMailBackend *backend;
3240 	EMailSession *session;
3241 	EAlertSink *alert_sink;
3242 	CamelMimeMessage *message;
3243 	CamelFolder *use_folder;
3244 	AsyncContext *async_context;
3245 	GError *local_error = NULL;
3246 
3247 	async_context = (AsyncContext *) user_data;
3248 
3249 	activity = async_context->activity;
3250 	alert_sink = e_activity_get_alert_sink (activity);
3251 
3252 	message = camel_folder_get_message_finish (
3253 		CAMEL_FOLDER (source_object), result, &local_error);
3254 
3255 	/* Sanity check. */
3256 	g_return_if_fail (
3257 		((message != NULL) && (local_error == NULL)) ||
3258 		((message == NULL) && (local_error != NULL)));
3259 
3260 	if (e_activity_handle_cancellation (activity, local_error)) {
3261 		async_context_free (async_context);
3262 		g_error_free (local_error);
3263 		return;
3264 
3265 	} else if (local_error != NULL) {
3266 		e_alert_submit (
3267 			alert_sink, "mail:no-retrieve-message",
3268 			local_error->message, NULL);
3269 		async_context_free (async_context);
3270 		g_error_free (local_error);
3271 		return;
3272 	}
3273 
3274 	/* Finalize the activity here so we don't leave a message
3275 	 * in the task bar while displaying the vfolder editor. */
3276 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
3277 	g_clear_object (&async_context->activity);
3278 
3279 	backend = e_mail_reader_get_backend (async_context->reader);
3280 	session = e_mail_backend_get_session (backend);
3281 
3282 	use_folder = async_context->folder;
3283 	if (CAMEL_IS_VEE_FOLDER (use_folder)) {
3284 		CamelStore *parent_store;
3285 		CamelVeeFolder *vfolder;
3286 
3287 		parent_store = camel_folder_get_parent_store (use_folder);
3288 		vfolder = CAMEL_VEE_FOLDER (use_folder);
3289 
3290 		if (CAMEL_IS_VEE_STORE (parent_store) &&
3291 		    vfolder == camel_vee_store_get_unmatched_folder (CAMEL_VEE_STORE (parent_store))) {
3292 			/* use source folder instead of the Unmatched folder */
3293 			use_folder = camel_vee_folder_get_vee_uid_folder (
3294 				vfolder, async_context->message_uid);
3295 		}
3296 	}
3297 
3298 	vfolder_gui_add_from_message (
3299 		session, message,
3300 		async_context->filter_type,
3301 		use_folder);
3302 
3303 	g_object_unref (message);
3304 
3305 	async_context_free (async_context);
3306 }
3307 
3308 void
e_mail_reader_create_vfolder_from_selected(EMailReader * reader,gint vfolder_type)3309 e_mail_reader_create_vfolder_from_selected (EMailReader *reader,
3310                                             gint vfolder_type)
3311 {
3312 	EActivity *activity;
3313 	GCancellable *cancellable;
3314 	AsyncContext *async_context;
3315 	GPtrArray *uids;
3316 	const gchar *message_uid;
3317 
3318 	g_return_if_fail (E_IS_MAIL_READER (reader));
3319 
3320 	uids = e_mail_reader_get_selected_uids (reader);
3321 	g_return_if_fail (uids != NULL && uids->len == 1);
3322 	message_uid = g_ptr_array_index (uids, 0);
3323 
3324 	activity = e_mail_reader_new_activity (reader);
3325 	cancellable = e_activity_get_cancellable (activity);
3326 
3327 	async_context = g_slice_new0 (AsyncContext);
3328 	async_context->activity = g_object_ref (activity);
3329 	async_context->folder = e_mail_reader_ref_folder (reader);
3330 	async_context->reader = g_object_ref (reader);
3331 	async_context->message_uid = g_strdup (message_uid);
3332 	async_context->filter_type = vfolder_type;
3333 
3334 	camel_folder_get_message (
3335 		async_context->folder,
3336 		async_context->message_uid,
3337 		G_PRIORITY_DEFAULT,
3338 		cancellable,
3339 		mail_reader_create_vfolder_cb,
3340 		async_context);
3341 
3342 	g_object_unref (activity);
3343 
3344 	g_ptr_array_unref (uids);
3345 }
3346 
3347 static void
mail_reader_parse_message_run(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)3348 mail_reader_parse_message_run (GSimpleAsyncResult *simple,
3349                                GObject *object,
3350                                GCancellable *cancellable)
3351 {
3352 	EMailReader *reader = E_MAIL_READER (object);
3353 	CamelObjectBag *registry;
3354 	EMailPartList *part_list;
3355 	EMailDisplay *mail_display;
3356 	AsyncContext *async_context;
3357 	gchar *mail_uri;
3358 	gboolean is_source;
3359 	GError *local_error = NULL;
3360 
3361 	mail_display = e_mail_reader_get_mail_display (reader);
3362 	is_source = e_mail_display_get_mode (mail_display) == E_MAIL_FORMATTER_MODE_SOURCE;
3363 
3364 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3365 
3366 	registry = e_mail_part_list_get_registry ();
3367 
3368 	mail_uri = e_mail_part_build_uri (
3369 		async_context->folder,
3370 		async_context->message_uid, NULL, NULL);
3371 
3372 	part_list = camel_object_bag_reserve (registry, mail_uri);
3373 
3374 	if (!part_list && is_source) {
3375 		EMailPart *mail_part;
3376 
3377 		part_list = e_mail_part_list_new (async_context->message, async_context->message_uid, async_context->folder);
3378 		mail_part = e_mail_part_new (CAMEL_MIME_PART (async_context->message), ".message");
3379 		e_mail_part_list_add_part (part_list, mail_part);
3380 		g_object_unref (mail_part);
3381 
3382 		/* Do not store it, it'll be taken from EMailDisplay */
3383 		camel_object_bag_abort (registry, mail_uri);
3384 	}
3385 
3386 	if (part_list == NULL) {
3387 		EMailBackend *mail_backend;
3388 		EMailSession *mail_session;
3389 		EMailParser *parser;
3390 
3391 		mail_backend = e_mail_reader_get_backend (reader);
3392 		mail_session = e_mail_backend_get_session (mail_backend);
3393 
3394 		parser = e_mail_parser_new (CAMEL_SESSION (mail_session));
3395 
3396 		part_list = e_mail_parser_parse_sync (
3397 			parser,
3398 			async_context->folder,
3399 			async_context->message_uid,
3400 			async_context->message,
3401 			cancellable);
3402 
3403 		g_object_unref (parser);
3404 
3405 		if (part_list == NULL)
3406 			camel_object_bag_abort (registry, mail_uri);
3407 		else
3408 			camel_object_bag_add (registry, mail_uri, part_list);
3409 	}
3410 
3411 	g_free (mail_uri);
3412 
3413 	async_context->part_list = part_list;
3414 
3415 	if (g_cancellable_set_error_if_cancelled (cancellable, &local_error))
3416 		g_simple_async_result_take_error (simple, local_error);
3417 }
3418 
3419 void
e_mail_reader_parse_message(EMailReader * reader,CamelFolder * folder,const gchar * message_uid,CamelMimeMessage * message,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3420 e_mail_reader_parse_message (EMailReader *reader,
3421                              CamelFolder *folder,
3422                              const gchar *message_uid,
3423                              CamelMimeMessage *message,
3424                              GCancellable *cancellable,
3425                              GAsyncReadyCallback callback,
3426                              gpointer user_data)
3427 {
3428 	GSimpleAsyncResult *simple;
3429 	AsyncContext *async_context;
3430 	EActivity *activity;
3431 
3432 	g_return_if_fail (E_IS_MAIL_READER (reader));
3433 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
3434 	g_return_if_fail (message_uid != NULL);
3435 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
3436 
3437 	activity = e_mail_reader_new_activity (reader);
3438 	e_activity_set_cancellable (activity, cancellable);
3439 	e_activity_set_text (activity, _("Parsing message"));
3440 
3441 	async_context = g_slice_new0 (AsyncContext);
3442 	async_context->activity = g_object_ref (activity);
3443 	async_context->folder = g_object_ref (folder);
3444 	async_context->message_uid = g_strdup (message_uid);
3445 	async_context->message = g_object_ref (message);
3446 
3447 	simple = g_simple_async_result_new (
3448 		G_OBJECT (reader), callback, user_data,
3449 		e_mail_reader_parse_message);
3450 
3451 	g_simple_async_result_set_check_cancellable (simple, cancellable);
3452 
3453 	g_simple_async_result_set_op_res_gpointer (
3454 		simple, async_context, (GDestroyNotify) async_context_free);
3455 
3456 	g_simple_async_result_run_in_thread (
3457 		simple, mail_reader_parse_message_run,
3458 		G_PRIORITY_DEFAULT, cancellable);
3459 
3460 	g_object_unref (simple);
3461 	g_object_unref (activity);
3462 }
3463 
3464 EMailPartList *
e_mail_reader_parse_message_finish(EMailReader * reader,GAsyncResult * result,GError ** error)3465 e_mail_reader_parse_message_finish (EMailReader *reader,
3466                                     GAsyncResult *result,
3467 				    GError **error)
3468 {
3469 	GSimpleAsyncResult *simple;
3470 	AsyncContext *async_context;
3471 
3472 	g_return_val_if_fail (
3473 		g_simple_async_result_is_valid (
3474 		result, G_OBJECT (reader),
3475 		e_mail_reader_parse_message), NULL);
3476 
3477 	simple = G_SIMPLE_ASYNC_RESULT (result);
3478 
3479 	if (g_simple_async_result_propagate_error (simple, error))
3480 		return NULL;
3481 
3482 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3483 
3484 	if (async_context->part_list != NULL)
3485 		g_object_ref (async_context->part_list);
3486 
3487 	return async_context->part_list;
3488 }
3489 
3490 gboolean
e_mail_reader_utils_get_mark_seen_setting(EMailReader * reader,gint * out_timeout_interval)3491 e_mail_reader_utils_get_mark_seen_setting (EMailReader *reader,
3492 					   gint *out_timeout_interval)
3493 {
3494 	CamelFolder *folder;
3495 	GSettings *settings;
3496 	gboolean mark_seen = FALSE;
3497 
3498 	g_return_val_if_fail (E_IS_MAIL_READER (reader), FALSE);
3499 
3500 	folder = e_mail_reader_ref_folder (reader);
3501 
3502 	if (CAMEL_IS_VEE_FOLDER (folder)) {
3503 		GtkWidget *message_list_widget;
3504 
3505 		message_list_widget = e_mail_reader_get_message_list (reader);
3506 
3507 		if (IS_MESSAGE_LIST (message_list_widget)) {
3508 			MessageList *message_list;
3509 
3510 			message_list = MESSAGE_LIST (message_list_widget);
3511 
3512 			if (message_list->cursor_uid) {
3513 				CamelMessageInfo *nfo;
3514 
3515 				nfo = camel_folder_get_message_info (folder, message_list->cursor_uid);
3516 
3517 				if (nfo && CAMEL_IS_VEE_MESSAGE_INFO (nfo)) {
3518 					CamelFolder *real_folder;
3519 
3520 					real_folder = camel_vee_folder_get_location (CAMEL_VEE_FOLDER (folder),
3521 						CAMEL_VEE_MESSAGE_INFO (nfo), NULL);
3522 
3523 					if (real_folder) {
3524 						g_object_ref (real_folder);
3525 						g_object_unref (folder);
3526 						folder = real_folder;
3527 					}
3528 				}
3529 
3530 				g_clear_object (&nfo);
3531 			}
3532 		}
3533 	}
3534 
3535 	if (folder) {
3536 		CamelStore *store;
3537 		CamelThreeState cts_value;
3538 
3539 		cts_value = camel_folder_get_mark_seen (folder);
3540 		if (cts_value == CAMEL_THREE_STATE_OFF || cts_value == CAMEL_THREE_STATE_ON) {
3541 			if (out_timeout_interval)
3542 				*out_timeout_interval = camel_folder_get_mark_seen_timeout (folder);
3543 
3544 			g_clear_object (&folder);
3545 
3546 			return cts_value == CAMEL_THREE_STATE_ON;
3547 		}
3548 
3549 		store = camel_folder_get_parent_store (folder);
3550 		if (store) {
3551 			ESourceRegistry *registry;
3552 			ESource *source;
3553 			EThreeState ets_value = E_THREE_STATE_INCONSISTENT;
3554 
3555 			registry = e_mail_session_get_registry (e_mail_backend_get_session (e_mail_reader_get_backend (reader)));
3556 			source = e_source_registry_ref_source (registry, camel_service_get_uid (CAMEL_SERVICE (store)));
3557 
3558 			if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
3559 				ESourceMailAccount *account_ext;
3560 
3561 				account_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
3562 				ets_value = e_source_mail_account_get_mark_seen (account_ext);
3563 
3564 				if (out_timeout_interval && ets_value != E_THREE_STATE_INCONSISTENT)
3565 					*out_timeout_interval = e_source_mail_account_get_mark_seen_timeout (account_ext);
3566 			}
3567 
3568 			g_clear_object (&source);
3569 
3570 			if (ets_value == E_THREE_STATE_OFF || ets_value == E_THREE_STATE_ON) {
3571 				g_clear_object (&folder);
3572 
3573 				return ets_value == E_THREE_STATE_ON;
3574 			}
3575 		}
3576 
3577 		g_clear_object (&folder);
3578 	}
3579 
3580 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
3581 
3582 	mark_seen = g_settings_get_boolean (settings, "mark-seen");
3583 
3584 	if (out_timeout_interval)
3585 		*out_timeout_interval = g_settings_get_int (settings, "mark-seen-timeout");
3586 
3587 	g_object_unref (settings);
3588 
3589 	return mark_seen;
3590 }
3591