1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
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  * Authors:
18  *		Jeffrey Stedfast <fejj@ximian.com>
19  *
20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21  *
22  */
23 
24 #include "evolution-config.h"
25 
26 #include <locale.h>
27 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 
31 #include <e-util/e-util.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 #include <em-format/e-mail-formatter-quote.h>
38 
39 #include <shell/e-shell.h>
40 
41 #include <composer/e-msg-composer.h>
42 #include <composer/e-composer-actions.h>
43 #include <composer/e-composer-post-header.h>
44 
45 #include "e-mail-printer.h"
46 #include "e-mail-ui-session.h"
47 #include "e-mail-templates.h"
48 #include "e-mail-templates-store.h"
49 #include "em-utils.h"
50 #include "em-composer-utils.h"
51 #include "em-folder-selector.h"
52 #include "em-folder-tree.h"
53 #include "em-event.h"
54 #include "mail-send-recv.h"
55 
56 #ifdef G_OS_WIN32
57 #ifdef gmtime_r
58 #undef gmtime_r
59 #endif
60 #ifdef localtime_r
61 #undef localtime_r
62 #endif
63 
64 /* The gmtime() and localtime() in Microsoft's C library are MT-safe */
65 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
66 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
67 #endif
68 
69 typedef struct _AsyncContext AsyncContext;
70 typedef struct _ForwardData ForwardData;
71 
72 struct _AsyncContext {
73 	CamelMimeMessage *message;
74 	EMailSession *session;
75 	EMsgComposer *composer;
76 	ESource *transport_source;
77 	EActivity *activity;
78 	gchar *folder_uri;
79 	gchar *message_uid;
80 	gulong num_loading_handler_id;
81 	gulong cancelled_handler_id;
82 };
83 
84 struct _ForwardData {
85 	EShell *shell;
86 	CamelFolder *folder;
87 	GPtrArray *uids;
88 	EMailForwardStyle style;
89 };
90 
91 static void
async_context_free(AsyncContext * async_context)92 async_context_free (AsyncContext *async_context)
93 {
94 	if (async_context->cancelled_handler_id) {
95 		GCancellable *cancellable;
96 
97 		cancellable = e_activity_get_cancellable (async_context->activity);
98 		/* Cannot use g_cancellable_disconnect(), because when this is called
99 		   from inside the cancelled handler, then the GCancellable deadlocks. */
100 		g_signal_handler_disconnect (cancellable, async_context->cancelled_handler_id);
101 		async_context->cancelled_handler_id = 0;
102 	}
103 
104 	if (async_context->num_loading_handler_id) {
105 		EAttachmentView *view;
106 		EAttachmentStore *store;
107 
108 		view = e_msg_composer_get_attachment_view (async_context->composer);
109 		store = e_attachment_view_get_store (view);
110 
111 		e_signal_disconnect_notify_handler (store, &async_context->num_loading_handler_id);
112 	}
113 
114 	g_clear_object (&async_context->message);
115 	g_clear_object (&async_context->session);
116 	g_clear_object (&async_context->composer);
117 	g_clear_object (&async_context->transport_source);
118 	g_clear_object (&async_context->activity);
119 
120 	g_free (async_context->folder_uri);
121 	g_free (async_context->message_uid);
122 
123 	g_slice_free (AsyncContext, async_context);
124 }
125 
126 static void
forward_data_free(ForwardData * data)127 forward_data_free (ForwardData *data)
128 {
129 	if (data->shell != NULL)
130 		g_object_unref (data->shell);
131 
132 	if (data->folder != NULL)
133 		g_object_unref (data->folder);
134 
135 	if (data->uids != NULL)
136 		g_ptr_array_unref (data->uids);
137 
138 	g_slice_free (ForwardData, data);
139 }
140 
141 static gboolean
ask_confirm_for_unwanted_html_mail(EMsgComposer * composer,EDestination ** recipients)142 ask_confirm_for_unwanted_html_mail (EMsgComposer *composer,
143                                     EDestination **recipients)
144 {
145 	gboolean res;
146 	GString *str;
147 	gint i;
148 
149 	str = g_string_new ("");
150 	for (i = 0; recipients[i] != NULL; ++i) {
151 		if (!e_destination_get_html_mail_pref (recipients[i])) {
152 			const gchar *name;
153 
154 			name = e_destination_get_textrep (recipients[i], FALSE);
155 
156 			g_string_append_printf (str, "     %s\n", name);
157 		}
158 	}
159 
160 	if (str->len)
161 		res = e_util_prompt_user (
162 			GTK_WINDOW (composer),
163 			"org.gnome.evolution.mail", "prompt-on-unwanted-html",
164 			"mail:ask-send-html", str->str, NULL);
165 	else
166 		res = TRUE;
167 
168 	g_string_free (str, TRUE);
169 
170 	return res;
171 }
172 
173 static gboolean
ask_confirm_for_empty_subject(EMsgComposer * composer)174 ask_confirm_for_empty_subject (EMsgComposer *composer)
175 {
176 	return e_util_prompt_user (
177 		GTK_WINDOW (composer),
178 		"org.gnome.evolution.mail",
179 		"prompt-on-empty-subject",
180 		"mail:ask-send-no-subject", NULL);
181 }
182 
183 static gboolean
ask_confirm_for_only_bcc(EMsgComposer * composer,gboolean hidden_list_case)184 ask_confirm_for_only_bcc (EMsgComposer *composer,
185                           gboolean hidden_list_case)
186 {
187 	/* If the user is mailing a hidden contact list, it is possible for
188 	 * them to create a message with only Bcc recipients without really
189 	 * realizing it.  To try to avoid being totally confusing, I've changed
190 	 * this dialog to provide slightly different text in that case, to
191 	 * better explain what the hell is going on. */
192 
193 	return e_util_prompt_user (
194 		GTK_WINDOW (composer),
195 		"org.gnome.evolution.mail",
196 		"prompt-on-only-bcc",
197 		hidden_list_case ?
198 		"mail:ask-send-only-bcc-contact" :
199 		"mail:ask-send-only-bcc", NULL);
200 }
201 
202 static gboolean
is_group_definition(const gchar * str)203 is_group_definition (const gchar *str)
204 {
205 	const gchar *colon;
206 
207 	if (!str || !*str)
208 		return FALSE;
209 
210 	colon = strchr (str, ':');
211 	return colon > str && strchr (str, ';') > colon;
212 }
213 
214 static gboolean
composer_presend_check_recipients(EMsgComposer * composer,EMailSession * session)215 composer_presend_check_recipients (EMsgComposer *composer,
216                                    EMailSession *session)
217 {
218 	EDestination **recipients;
219 	EDestination **recipients_bcc;
220 	CamelInternetAddress *cia;
221 	EComposerHeaderTable *table;
222 	EComposerHeader *post_to_header;
223 	GString *invalid_addrs = NULL;
224 	GSettings *settings;
225 	gboolean check_passed = FALSE;
226 	gint hidden = 0;
227 	gint shown = 0;
228 	gint num = 0;
229 	gint num_to_cc = 0;
230 	gint num_bcc = 0;
231 	gint num_post = 0;
232 	gint ii;
233 
234 	/* We should do all of the validity checks based on the composer,
235 	 * and not on the created message, as extra interaction may occur
236 	 * when we get the message (e.g. passphrase to sign a message). */
237 
238 	table = e_msg_composer_get_header_table (composer);
239 
240 	recipients = e_composer_header_table_get_destinations_to (table);
241 	if (recipients) {
242 		for (ii = 0; recipients[ii] != NULL; ii++) {
243 			const gchar *addr;
244 
245 			addr = e_destination_get_address (recipients[ii]);
246 			if (addr == NULL || *addr == '\0')
247 				continue;
248 
249 			num_to_cc++;
250 		}
251 
252 		e_destination_freev (recipients);
253 	}
254 
255 	recipients = e_composer_header_table_get_destinations_cc (table);
256 	if (recipients) {
257 		for (ii = 0; recipients[ii] != NULL; ii++) {
258 			const gchar *addr;
259 
260 			addr = e_destination_get_address (recipients[ii]);
261 			if (addr == NULL || *addr == '\0')
262 				continue;
263 
264 			num_to_cc++;
265 		}
266 
267 		e_destination_freev (recipients);
268 	}
269 
270 	recipients = e_composer_header_table_get_destinations (table);
271 
272 	cia = camel_internet_address_new ();
273 
274 	/* See which ones are visible, present, etc. */
275 	for (ii = 0; recipients != NULL && recipients[ii] != NULL; ii++) {
276 		const gchar *addr;
277 		gint len, j;
278 
279 		addr = e_destination_get_address (recipients[ii]);
280 		if (addr == NULL || *addr == '\0')
281 			continue;
282 
283 		camel_address_decode (CAMEL_ADDRESS (cia), addr);
284 		len = camel_address_length (CAMEL_ADDRESS (cia));
285 
286 		if (len > 0) {
287 			if (!e_destination_is_evolution_list (recipients[ii])) {
288 				for (j = 0; j < len; j++) {
289 					const gchar *name = NULL, *eml = NULL;
290 
291 					if (!camel_internet_address_get (cia, j, &name, &eml) ||
292 					    !eml ||
293 					    strchr (eml, '@') <= eml) {
294 						if (!invalid_addrs)
295 							invalid_addrs = g_string_new ("");
296 						else
297 							g_string_append (invalid_addrs, ", ");
298 
299 						if (name)
300 							g_string_append (invalid_addrs, name);
301 						if (eml) {
302 							g_string_append (invalid_addrs, name ? " <" : "");
303 							g_string_append (invalid_addrs, eml);
304 							g_string_append (invalid_addrs, name ? ">" : "");
305 						}
306 					}
307 				}
308 			}
309 
310 			camel_address_remove (CAMEL_ADDRESS (cia), -1);
311 			num++;
312 			if (e_destination_is_evolution_list (recipients[ii])
313 			    && !e_destination_list_show_addresses (recipients[ii])) {
314 				hidden++;
315 			} else {
316 				shown++;
317 			}
318 		} else if (is_group_definition (addr)) {
319 			/* like an address, it will not claim on only-bcc */
320 			shown++;
321 			num++;
322 		} else if (!invalid_addrs) {
323 			invalid_addrs = g_string_new (addr);
324 		} else {
325 			g_string_append (invalid_addrs, ", ");
326 			g_string_append (invalid_addrs, addr);
327 		}
328 	}
329 
330 	recipients_bcc = e_composer_header_table_get_destinations_bcc (table);
331 	if (recipients_bcc) {
332 		for (ii = 0; recipients_bcc[ii] != NULL; ii++) {
333 			const gchar *addr;
334 
335 			addr = e_destination_get_address (recipients_bcc[ii]);
336 			if (addr == NULL || *addr == '\0')
337 				continue;
338 
339 			camel_address_decode (CAMEL_ADDRESS (cia), addr);
340 			if (camel_address_length (CAMEL_ADDRESS (cia)) > 0) {
341 				camel_address_remove (CAMEL_ADDRESS (cia), -1);
342 				num_bcc++;
343 			}
344 		}
345 
346 		e_destination_freev (recipients_bcc);
347 	}
348 
349 	g_object_unref (cia);
350 
351 	post_to_header = e_composer_header_table_get_header (
352 		table, E_COMPOSER_HEADER_POST_TO);
353 	if (e_composer_header_get_visible (post_to_header)) {
354 		GList *postlist;
355 
356 		postlist = e_composer_header_table_get_post_to (table);
357 		num_post = g_list_length (postlist);
358 		g_list_foreach (postlist, (GFunc) g_free, NULL);
359 		g_list_free (postlist);
360 	}
361 
362 	/* I'm sensing a lack of love, er, I mean recipients. */
363 	if (num == 0 && num_post == 0) {
364 		EHTMLEditor *editor;
365 
366 		editor = e_msg_composer_get_editor (composer);
367 		e_alert_submit (E_ALERT_SINK (editor), "mail:send-no-recipients", NULL);
368 
369 		goto finished;
370 	}
371 
372 	if (invalid_addrs) {
373 		if (!e_util_prompt_user (
374 			GTK_WINDOW (composer),
375 			"org.gnome.evolution.mail",
376 			"prompt-on-invalid-recip",
377 			strstr (invalid_addrs->str, ", ") ?
378 				"mail:ask-send-invalid-recip-multi" :
379 				"mail:ask-send-invalid-recip-one",
380 			invalid_addrs->str, NULL)) {
381 			g_string_free (invalid_addrs, TRUE);
382 			goto finished;
383 		}
384 
385 		g_string_free (invalid_addrs, TRUE);
386 	}
387 
388 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
389 	if (num_to_cc > 1 && num_to_cc >= g_settings_get_int (settings, "composer-many-to-cc-recips-num")) {
390 		gchar *head;
391 		gchar *msg;
392 
393 		g_clear_object (&settings);
394 
395 		head = g_strdup_printf (ngettext (
396 			/* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
397 			"Are you sure you want to send a message with %d To and CC recipients?",
398 			"Are you sure you want to send a message with %d To and CC recipients?",
399 			num_to_cc), num_to_cc);
400 
401 		msg = g_strdup_printf (ngettext (
402 			/* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
403 			"You are trying to send a message to %d recipients in To and CC fields."
404 			" This would result in all recipients seeing the email addresses of each"
405 			" other. In some cases this behaviour is undesired, especially if they"
406 			" do not know each other or if privacy is a concern. Consider adding"
407 			" recipients to the BCC field instead.",
408 			"You are trying to send a message to %d recipients in To and CC fields."
409 			" This would result in all recipients seeing the email addresses of each"
410 			" other. In some cases this behaviour is undesired, especially if they"
411 			" do not know each other or if privacy is a concern. Consider adding"
412 			" recipients to the BCC field instead.",
413 			num_to_cc), num_to_cc);
414 
415 		if (!e_util_prompt_user (
416 			GTK_WINDOW (composer),
417 			"org.gnome.evolution.mail",
418 			"prompt-on-many-to-cc-recips",
419 			"mail:ask-many-to-cc-recips",
420 			head, msg, NULL)) {
421 			GtkAction *action;
422 
423 			g_free (head);
424 			g_free (msg);
425 
426 			action = E_COMPOSER_ACTION_VIEW_BCC (composer);
427 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
428 
429 			goto finished;
430 		}
431 
432 		g_free (head);
433 		g_free (msg);
434 	}
435 	g_clear_object (&settings);
436 
437 	if (num > 0 && (num == num_bcc || shown == 0)) {
438 		/* this means that the only recipients are Bcc's */
439 		if (!ask_confirm_for_only_bcc (composer, shown == 0))
440 			goto finished;
441 	}
442 
443 	check_passed = TRUE;
444 
445 finished:
446 	if (recipients != NULL)
447 		e_destination_freev (recipients);
448 
449 	return check_passed;
450 }
451 
452 static gboolean
composer_presend_check_identity(EMsgComposer * composer,EMailSession * session)453 composer_presend_check_identity (EMsgComposer *composer,
454                                  EMailSession *session)
455 {
456 	EComposerHeaderTable *table;
457 	ESource *source = NULL;
458 	gchar *uid;
459 	gboolean success = TRUE;
460 
461 	table = e_msg_composer_get_header_table (composer);
462 
463 	uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
464 	if (uid)
465 		source = e_composer_header_table_ref_source (table, uid);
466 	g_free (uid);
467 
468 	if (source) {
469 		EClientCache *client_cache;
470 		ESourceRegistry *registry;
471 
472 		client_cache = e_composer_header_table_ref_client_cache (table);
473 		registry = e_client_cache_ref_registry (client_cache);
474 
475 		success = e_source_registry_check_enabled (registry, source);
476 		if (!success) {
477 			e_alert_submit (
478 				E_ALERT_SINK (e_msg_composer_get_editor (composer)),
479 				"mail:send-no-account-enabled", NULL);
480 		}
481 
482 		g_object_unref (client_cache);
483 		g_object_unref (registry);
484 	} else {
485 		success = FALSE;
486 		e_alert_submit (
487 			E_ALERT_SINK (e_msg_composer_get_editor (composer)),
488 			"mail:send-no-account", NULL);
489 	}
490 
491 
492 	g_clear_object (&source);
493 
494 	return success;
495 }
496 
497 static gboolean
composer_presend_check_plugins(EMsgComposer * composer,EMailSession * session)498 composer_presend_check_plugins (EMsgComposer *composer,
499                                 EMailSession *session)
500 {
501 	EMEvent *eme;
502 	EMEventTargetComposer *target;
503 	gpointer data;
504 
505 	/** @Event: composer.presendchecks
506 	 * @Title: Composer PreSend Checks
507 	 * @Target: EMEventTargetMessage
508 	 *
509 	 * composer.presendchecks is emitted during pre-checks for the
510 	 * message just before sending.  Since the e-plugin framework
511 	 * doesn't provide a way to return a value from the plugin,
512 	 * use 'presend_check_status' to set whether the check passed.
513 	 */
514 	eme = em_event_peek ();
515 	target = em_event_target_new_composer (eme, composer, 0);
516 
517 	e_event_emit (
518 		(EEvent *) eme, "composer.presendchecks",
519 		(EEventTarget *) target);
520 
521 	/* A non-NULL value for this key means the check failed. */
522 	data = g_object_get_data (G_OBJECT (composer), "presend_check_status");
523 
524 	/* Clear the value in case we have to run these checks again. */
525 	g_object_set_data (G_OBJECT (composer), "presend_check_status", NULL);
526 
527 	return (data == NULL);
528 }
529 
530 static gboolean
composer_presend_check_subject(EMsgComposer * composer,EMailSession * session)531 composer_presend_check_subject (EMsgComposer *composer,
532                                 EMailSession *session)
533 {
534 	EComposerHeaderTable *table;
535 	const gchar *subject;
536 	gboolean check_passed = TRUE;
537 
538 	table = e_msg_composer_get_header_table (composer);
539 	subject = e_composer_header_table_get_subject (table);
540 
541 	if (subject == NULL || subject[0] == '\0') {
542 		if (!ask_confirm_for_empty_subject (composer))
543 			check_passed = FALSE;
544 	}
545 
546 	return check_passed;
547 }
548 
549 static gboolean
composer_presend_check_unwanted_html(EMsgComposer * composer,EMailSession * session)550 composer_presend_check_unwanted_html (EMsgComposer *composer,
551                                       EMailSession *session)
552 {
553 	EDestination **recipients;
554 	EHTMLEditor *editor;
555 	EContentEditor *cnt_editor;
556 	EComposerHeaderTable *table;
557 	GSettings *settings;
558 	gboolean check_passed = TRUE;
559 	gboolean html_mode;
560 	gboolean send_html;
561 	gboolean confirm_html;
562 	gint ii;
563 
564 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
565 
566 	editor = e_msg_composer_get_editor (composer);
567 	cnt_editor = e_html_editor_get_content_editor (editor);
568 	html_mode = e_content_editor_get_html_mode (cnt_editor);
569 
570 	table = e_msg_composer_get_header_table (composer);
571 	recipients = e_composer_header_table_get_destinations (table);
572 
573 	send_html = g_settings_get_boolean (settings, "composer-send-html");
574 	confirm_html = g_settings_get_boolean (settings, "prompt-on-unwanted-html");
575 
576 	/* Only show this warning if our default is to send html.  If it
577 	 * isn't, we've manually switched into html mode in the composer
578 	 * and (presumably) had a good reason for doing this. */
579 	if (html_mode && send_html && confirm_html && recipients != NULL) {
580 		gboolean html_problem = FALSE;
581 
582 		for (ii = 0; recipients[ii] != NULL; ii++) {
583 			if (!e_destination_get_html_mail_pref (recipients[ii])) {
584 				html_problem = TRUE;
585 				break;
586 			}
587 		}
588 
589 		if (html_problem) {
590 			if (!ask_confirm_for_unwanted_html_mail (
591 				composer, recipients))
592 				check_passed = FALSE;
593 		}
594 	}
595 
596 	if (recipients != NULL)
597 		e_destination_freev (recipients);
598 
599 	g_object_unref (settings);
600 
601 	return check_passed;
602 }
603 
604 static void
composer_send_completed(GObject * source_object,GAsyncResult * result,gpointer user_data)605 composer_send_completed (GObject *source_object,
606                          GAsyncResult *result,
607                          gpointer user_data)
608 {
609 	EActivity *activity;
610 	gboolean service_unavailable;
611 	gboolean set_changed = FALSE;
612 	AsyncContext *async_context;
613 	GError *local_error = NULL;
614 
615 	async_context = (AsyncContext *) user_data;
616 
617 	if (async_context->transport_source) {
618 		EShell *shell;
619 
620 		shell = e_msg_composer_get_shell (async_context->composer);
621 
622 		e_shell_set_auth_prompt_parent (shell, async_context->transport_source, NULL);
623 	}
624 
625 	activity = async_context->activity;
626 
627 	e_mail_session_send_to_finish (
628 		E_MAIL_SESSION (source_object), result, &local_error);
629 
630 	if (e_activity_handle_cancellation (activity, local_error)) {
631 		set_changed = TRUE;
632 		goto exit;
633 	}
634 
635 	/* Check for error codes which may indicate we're offline
636 	 * or name resolution failed or connection attempt failed. */
637 	service_unavailable =
638 		g_error_matches (
639 			local_error, CAMEL_SERVICE_ERROR,
640 			CAMEL_SERVICE_ERROR_UNAVAILABLE) ||
641 		/* name resolution failed */
642 		g_error_matches (
643 			local_error, G_RESOLVER_ERROR,
644 			G_RESOLVER_ERROR_NOT_FOUND) ||
645 		g_error_matches (
646 			local_error, G_RESOLVER_ERROR,
647 			G_RESOLVER_ERROR_TEMPORARY_FAILURE) ||
648 		/* something internal to Camel failed */
649 		g_error_matches (
650 			local_error, CAMEL_SERVICE_ERROR,
651 			CAMEL_SERVICE_ERROR_URL_INVALID);
652 	if (service_unavailable) {
653 		/* Inform the user. */
654 		e_alert_run_dialog_for_args (
655 			GTK_WINDOW (async_context->composer),
656 			"mail-composer:saving-to-outbox", NULL);
657 		if (async_context->message)
658 			g_signal_emit_by_name (
659 				async_context->composer, "save-to-outbox",
660 				async_context->message, activity);
661 		else
662 			e_msg_composer_save_to_outbox (async_context->composer);
663 		goto exit;
664 	}
665 
666 	/* Post-processing errors are shown in the shell window. */
667 	if (g_error_matches (
668 		local_error, E_MAIL_ERROR,
669 		E_MAIL_ERROR_POST_PROCESSING)) {
670 		EAlert *alert;
671 		EShell *shell;
672 
673 		shell = e_msg_composer_get_shell (async_context->composer);
674 
675 		alert = e_alert_new (
676 			"mail-composer:send-post-processing-error",
677 			local_error->message, NULL);
678 		e_shell_submit_alert (shell, alert);
679 		g_object_unref (alert);
680 
681 	/* All other errors are shown in the composer window. */
682 	} else if (local_error != NULL) {
683 		gint response;
684 
685 		/* Clear the activity bar before
686 		 * presenting the error dialog. */
687 		g_clear_object (&async_context->activity);
688 		async_context->activity = e_html_editor_new_activity (e_msg_composer_get_editor (async_context->composer));
689 		activity = async_context->activity;
690 
691 		response = e_alert_run_dialog_for_args (
692 			GTK_WINDOW (async_context->composer),
693 			"mail-composer:send-error",
694 			local_error->message, NULL);
695 		if (response == GTK_RESPONSE_OK)  /* Try Again */
696 			e_msg_composer_send (async_context->composer);
697 		if (response == GTK_RESPONSE_ACCEPT) { /* Save to Outbox */
698 			if (async_context->message)
699 				g_signal_emit_by_name (
700 					async_context->composer, "save-to-outbox",
701 					async_context->message, activity);
702 			else
703 				e_msg_composer_save_to_outbox (async_context->composer);
704 		}
705 		set_changed = TRUE;
706 		goto exit;
707 	}
708 
709 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
710 
711 	/* Wait for the EActivity's completion message to
712 	 * time out and then destroy the composer window. */
713 	g_object_weak_ref (
714 		G_OBJECT (activity), (GWeakNotify)
715 		gtk_widget_destroy, async_context->composer);
716 
717 exit:
718 	g_clear_error (&local_error);
719 
720 	if (set_changed) {
721 		EHTMLEditor *editor;
722 		EContentEditor *cnt_editor;
723 
724 		editor = e_msg_composer_get_editor (async_context->composer);
725 		cnt_editor = e_html_editor_get_content_editor (editor);
726 
727 		e_content_editor_set_changed (cnt_editor, TRUE);
728 
729 		gtk_window_present (GTK_WINDOW (async_context->composer));
730 	}
731 
732 	async_context_free (async_context);
733 }
734 
735 static void
em_utils_composer_real_send(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)736 em_utils_composer_real_send (EMsgComposer *composer,
737 			     CamelMimeMessage *message,
738 			     EActivity *activity,
739 			     EMailSession *session)
740 {
741 	AsyncContext *async_context;
742 	CamelService *transport;
743 	GCancellable *cancellable;
744 	GSettings *settings;
745 
746 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
747 	if (g_settings_get_boolean (settings, "composer-use-outbox")) {
748 		/* Using e_msg_composer_save_to_outbox() means building
749 		   the message again; better to use the built message here. */
750 		g_signal_emit_by_name (
751 			composer, "save-to-outbox",
752 			message, activity);
753 
754 		g_object_unref (settings);
755 		return;
756 	}
757 
758 	g_object_unref (settings);
759 
760 	if (!camel_session_get_online (CAMEL_SESSION (session))) {
761 		e_alert_run_dialog_for_args (
762 			GTK_WINDOW (composer),
763 			"mail-composer:saving-to-outbox", NULL);
764 
765 		/* Using e_msg_composer_save_to_outbox() means building
766 		   the message again; better to use the built message here. */
767 		g_signal_emit_by_name (
768 			composer, "save-to-outbox",
769 			message, activity);
770 
771 		return;
772 	}
773 
774 	async_context = g_slice_new0 (AsyncContext);
775 	async_context->message = g_object_ref (message);
776 	async_context->composer = g_object_ref (composer);
777 	async_context->activity = g_object_ref (activity);
778 
779 	transport = e_mail_session_ref_transport_for_message (session, message);
780 
781 	if (transport) {
782 		EShell *shell;
783 
784 		shell = e_msg_composer_get_shell (composer);
785 
786 		async_context->transport_source = e_source_registry_ref_source (e_shell_get_registry (shell), camel_service_get_uid (transport));
787 
788 		if (async_context->transport_source)
789 			e_shell_set_auth_prompt_parent (shell, async_context->transport_source, GTK_WINDOW (composer));
790 
791 		g_object_unref (transport);
792 	}
793 
794 	cancellable = e_activity_get_cancellable (activity);
795 
796 	e_mail_session_send_to (
797 		session, message,
798 		G_PRIORITY_DEFAULT,
799 		cancellable, NULL, NULL,
800 		composer_send_completed,
801 		async_context);
802 }
803 
804 static void
composer_num_loading_notify_cb(EAttachmentStore * store,GParamSpec * param,AsyncContext * async_context)805 composer_num_loading_notify_cb (EAttachmentStore *store,
806 				GParamSpec *param,
807 				AsyncContext *async_context)
808 {
809 	if (e_attachment_store_get_num_loading (store) > 0)
810 		return;
811 
812 	em_utils_composer_real_send (
813 		async_context->composer,
814 		async_context->message,
815 		async_context->activity,
816 		async_context->session);
817 
818 	async_context_free (async_context);
819 }
820 
821 static void
composer_wait_for_attachment_load_cancelled_cb(GCancellable * cancellable,AsyncContext * async_context)822 composer_wait_for_attachment_load_cancelled_cb (GCancellable *cancellable,
823 						AsyncContext *async_context)
824 {
825 	async_context_free (async_context);
826 }
827 
828 static void
em_utils_composer_send_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)829 em_utils_composer_send_cb (EMsgComposer *composer,
830                            CamelMimeMessage *message,
831                            EActivity *activity,
832                            EMailSession *session)
833 {
834 	AsyncContext *async_context;
835 	GCancellable *cancellable;
836 	EAttachmentView *view;
837 	EAttachmentStore *store;
838 
839 	view = e_msg_composer_get_attachment_view (composer);
840 	store = e_attachment_view_get_store (view);
841 
842 	if (e_attachment_store_get_num_loading (store) <= 0) {
843 		em_utils_composer_real_send (composer, message, activity, session);
844 		return;
845 	}
846 
847 	async_context = g_slice_new0 (AsyncContext);
848 	async_context->session = g_object_ref (session);
849 	async_context->message = g_object_ref (message);
850 	async_context->composer = g_object_ref (composer);
851 	async_context->activity = g_object_ref (activity);
852 
853 	cancellable = e_activity_get_cancellable (activity);
854 	/* This message is never removed from the camel operation, otherwise the GtkInfoBar
855 	   hides itself and the user sees no feedback. */
856 	camel_operation_push_message (cancellable, "%s", _("Waiting for attachments to load…"));
857 
858 	async_context->num_loading_handler_id = e_signal_connect_notify (store, "notify::num-loading",
859 		G_CALLBACK (composer_num_loading_notify_cb), async_context);
860 	/* Cannot use g_cancellable_connect() here, see async_context_free() */
861 	async_context->cancelled_handler_id = g_signal_connect (cancellable, "cancelled",
862 		G_CALLBACK (composer_wait_for_attachment_load_cancelled_cb), async_context);
863 }
864 
865 static void
composer_set_no_change(EMsgComposer * composer)866 composer_set_no_change (EMsgComposer *composer)
867 {
868 	EHTMLEditor *editor;
869 	EContentEditor *cnt_editor;
870 
871 	g_return_if_fail (composer != NULL);
872 
873 	editor = e_msg_composer_get_editor (composer);
874 	cnt_editor = e_html_editor_get_content_editor (editor);
875 
876 	e_content_editor_set_changed (cnt_editor, FALSE);
877 }
878 
879 /* delete original messages from Outbox folder */
880 static void
manage_x_evolution_replace_outbox(EMsgComposer * composer,EMailSession * session,CamelMimeMessage * message,GCancellable * cancellable)881 manage_x_evolution_replace_outbox (EMsgComposer *composer,
882                                    EMailSession *session,
883                                    CamelMimeMessage *message,
884                                    GCancellable *cancellable)
885 {
886 	const gchar *message_uid;
887 	const gchar *header;
888 	CamelFolder *outbox;
889 
890 	g_return_if_fail (composer != NULL);
891 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
892 
893 	header = "X-Evolution-Replace-Outbox-UID";
894 	message_uid = camel_medium_get_header (CAMEL_MEDIUM (message), header);
895 	e_msg_composer_remove_header (composer, header);
896 
897 	if (!message_uid)
898 		return;
899 
900 	outbox = e_mail_session_get_local_folder (
901 		session, E_MAIL_LOCAL_FOLDER_OUTBOX);
902 	g_return_if_fail (outbox != NULL);
903 
904 	camel_folder_set_message_flags (
905 		outbox, message_uid,
906 		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
907 		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
908 
909 	/* ignore errors here */
910 	camel_folder_synchronize_message_sync (
911 		outbox, message_uid, cancellable, NULL);
912 }
913 
914 static void
composer_save_to_drafts_complete(GObject * source_object,GAsyncResult * result,gpointer user_data)915 composer_save_to_drafts_complete (GObject *source_object,
916                                   GAsyncResult *result,
917                                   gpointer user_data)
918 {
919 	EActivity *activity;
920 	AsyncContext *async_context;
921 	EHTMLEditor *editor;
922 	EContentEditor *cnt_editor;
923 	GError *local_error = NULL;
924 
925 	async_context = (AsyncContext *) user_data;
926 
927 	editor = e_msg_composer_get_editor (async_context->composer);
928 	cnt_editor = e_html_editor_get_content_editor (editor);
929 
930 	/* We don't really care if this failed.  If something other than
931 	 * cancellation happened, emit a runtime warning so the error is
932 	 * not completely lost. */
933 	e_mail_session_handle_draft_headers_finish (
934 		E_MAIL_SESSION (source_object), result, &local_error);
935 
936 	activity = async_context->activity;
937 
938 	if (e_activity_handle_cancellation (activity, local_error)) {
939 		e_content_editor_set_changed (cnt_editor, TRUE);
940 		g_error_free (local_error);
941 
942 	} else if (local_error != NULL) {
943 		e_content_editor_set_changed (cnt_editor, TRUE);
944 		g_warning ("%s", local_error->message);
945 		g_error_free (local_error);
946 	} else
947 		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
948 
949 	/* Encode the draft message we just saved into the EMsgComposer
950 	 * as X-Evolution-Draft headers.  The message will be marked for
951 	 * deletion if the user saves a newer draft message or sends the
952 	 * composed message. */
953 	e_msg_composer_set_draft_headers (
954 		async_context->composer,
955 		async_context->folder_uri,
956 		async_context->message_uid);
957 
958 	e_content_editor_set_changed (cnt_editor, FALSE);
959 
960 	async_context_free (async_context);
961 }
962 
963 static void
964 composer_save_to_drafts_append_mail (AsyncContext *async_context,
965                                      CamelFolder *drafts_folder);
966 
967 static void
composer_save_to_drafts_cleanup(GObject * source_object,GAsyncResult * result,gpointer user_data)968 composer_save_to_drafts_cleanup (GObject *source_object,
969                                  GAsyncResult *result,
970                                  gpointer user_data)
971 {
972 	CamelSession *session;
973 	EActivity *activity;
974 	EAlertSink *alert_sink;
975 	GCancellable *cancellable;
976 	EHTMLEditor *editor;
977 	EContentEditor *cnt_editor;
978 	AsyncContext *async_context;
979 	GError *local_error = NULL;
980 
981 	async_context = (AsyncContext *) user_data;
982 
983 	editor = e_msg_composer_get_editor (async_context->composer);
984 	cnt_editor = e_html_editor_get_content_editor (editor);
985 
986 	activity = async_context->activity;
987 	alert_sink = e_activity_get_alert_sink (activity);
988 	cancellable = e_activity_get_cancellable (activity);
989 
990 	e_mail_folder_append_message_finish (
991 		CAMEL_FOLDER (source_object), result,
992 		&async_context->message_uid, &local_error);
993 
994 	if (e_activity_handle_cancellation (activity, local_error)) {
995 		g_warn_if_fail (async_context->message_uid == NULL);
996 		e_content_editor_set_changed (cnt_editor, TRUE);
997 		async_context_free (async_context);
998 		g_error_free (local_error);
999 		return;
1000 
1001 	} else if (local_error != NULL) {
1002 		g_warn_if_fail (async_context->message_uid == NULL);
1003 
1004 		if (e_msg_composer_is_exiting (async_context->composer)) {
1005 			gint response;
1006 
1007 			/* If we can't retrieve the Drafts folder for the
1008 			 * selected account, ask the user if he wants to
1009 			 * save to the local Drafts folder instead. */
1010 			response = e_alert_run_dialog_for_args (
1011 				GTK_WINDOW (async_context->composer),
1012 				"mail:ask-default-drafts", local_error->message, NULL);
1013 			if (response != GTK_RESPONSE_YES) {
1014 				e_content_editor_set_changed (cnt_editor, TRUE);
1015 				async_context_free (async_context);
1016 			} else {
1017 				composer_save_to_drafts_append_mail (async_context, NULL);
1018 			}
1019 
1020 			g_error_free (local_error);
1021 			return;
1022 		}
1023 
1024 		e_alert_submit (
1025 			alert_sink,
1026 			"mail-composer:save-to-drafts-error",
1027 			local_error->message, NULL);
1028 		e_content_editor_set_changed (cnt_editor, TRUE);
1029 		async_context_free (async_context);
1030 		g_error_free (local_error);
1031 		return;
1032 	}
1033 
1034 	session = e_msg_composer_ref_session (async_context->composer);
1035 
1036 	/* Mark the previously saved draft message for deletion.
1037 	 * Note: This is just a nice-to-have; ignore failures. */
1038 	e_mail_session_handle_draft_headers (
1039 		E_MAIL_SESSION (session),
1040 		async_context->message,
1041 		G_PRIORITY_DEFAULT, cancellable,
1042 		composer_save_to_drafts_complete,
1043 		async_context);
1044 
1045 	g_object_unref (session);
1046 }
1047 
1048 static void
composer_save_to_drafts_append_mail(AsyncContext * async_context,CamelFolder * drafts_folder)1049 composer_save_to_drafts_append_mail (AsyncContext *async_context,
1050                                      CamelFolder *drafts_folder)
1051 {
1052 	CamelFolder *local_drafts_folder;
1053 	GCancellable *cancellable;
1054 	CamelMessageInfo *info;
1055 
1056 	local_drafts_folder =
1057 		e_mail_session_get_local_folder (
1058 		async_context->session, E_MAIL_LOCAL_FOLDER_DRAFTS);
1059 
1060 	if (drafts_folder == NULL)
1061 		drafts_folder = g_object_ref (local_drafts_folder);
1062 
1063 	cancellable = e_activity_get_cancellable (async_context->activity);
1064 
1065 	info = camel_message_info_new (NULL);
1066 
1067 	camel_message_info_set_flags (info, CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN |
1068 		(camel_mime_message_has_attachment (async_context->message) ? CAMEL_MESSAGE_ATTACHMENTS : 0), ~0);
1069 
1070 	camel_medium_remove_header (
1071 		CAMEL_MEDIUM (async_context->message),
1072 		"X-Evolution-Replace-Outbox-UID");
1073 
1074 	e_mail_folder_append_message (
1075 		drafts_folder, async_context->message,
1076 		info, G_PRIORITY_DEFAULT, cancellable,
1077 		composer_save_to_drafts_cleanup,
1078 		async_context);
1079 
1080 	g_clear_object (&info);
1081 
1082 	g_object_unref (drafts_folder);
1083 }
1084 
1085 static void
composer_save_to_drafts_got_folder(GObject * source_object,GAsyncResult * result,gpointer user_data)1086 composer_save_to_drafts_got_folder (GObject *source_object,
1087                                     GAsyncResult *result,
1088                                     gpointer user_data)
1089 {
1090 	EActivity *activity;
1091 	CamelFolder *drafts_folder;
1092 	EHTMLEditor *editor;
1093 	EContentEditor *cnt_editor;
1094 	AsyncContext *async_context;
1095 	GError *local_error = NULL;
1096 
1097 	async_context = (AsyncContext *) user_data;
1098 
1099 	activity = async_context->activity;
1100 
1101 	editor = e_msg_composer_get_editor (async_context->composer);
1102 	cnt_editor = e_html_editor_get_content_editor (editor);
1103 
1104 	drafts_folder = e_mail_session_uri_to_folder_finish (
1105 		E_MAIL_SESSION (source_object), result, &local_error);
1106 
1107 	/* Sanity check. */
1108 	g_return_if_fail (
1109 		((drafts_folder != NULL) && (local_error == NULL)) ||
1110 		((drafts_folder == NULL) && (local_error != NULL)));
1111 
1112 	if (e_activity_handle_cancellation (activity, local_error)) {
1113 		e_content_editor_set_changed (cnt_editor, TRUE);
1114 		async_context_free (async_context);
1115 		g_error_free (local_error);
1116 		return;
1117 
1118 	} else if (local_error != NULL) {
1119 		gint response;
1120 
1121 		/* If we can't retrieve the Drafts folder for the
1122 		 * selected account, ask the user if he wants to
1123 		 * save to the local Drafts folder instead. */
1124 		response = e_alert_run_dialog_for_args (
1125 			GTK_WINDOW (async_context->composer),
1126 			"mail:ask-default-drafts", local_error->message, NULL);
1127 
1128 		g_error_free (local_error);
1129 
1130 		if (response != GTK_RESPONSE_YES) {
1131 			e_content_editor_set_changed (cnt_editor, TRUE);
1132 			async_context_free (async_context);
1133 			return;
1134 		}
1135 	}
1136 
1137 	composer_save_to_drafts_append_mail (async_context, drafts_folder);
1138 }
1139 
1140 static void
em_utils_composer_save_to_drafts_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1141 em_utils_composer_save_to_drafts_cb (EMsgComposer *composer,
1142                                      CamelMimeMessage *message,
1143                                      EActivity *activity,
1144                                      EMailSession *session)
1145 {
1146 	AsyncContext *async_context;
1147 	EComposerHeaderTable *table;
1148 	ESource *source;
1149 	const gchar *local_drafts_folder_uri;
1150 	gchar *identity_uid;
1151 	gchar *drafts_folder_uri = NULL;
1152 
1153 	async_context = g_slice_new0 (AsyncContext);
1154 	async_context->message = g_object_ref (message);
1155 	async_context->session = g_object_ref (session);
1156 	async_context->composer = g_object_ref (composer);
1157 	async_context->activity = g_object_ref (activity);
1158 
1159 	table = e_msg_composer_get_header_table (composer);
1160 
1161 	identity_uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
1162 	source = e_composer_header_table_ref_source (table, identity_uid);
1163 
1164 	/* Get the selected identity's preferred Drafts folder. */
1165 	if (source != NULL) {
1166 		ESourceMailComposition *extension;
1167 		const gchar *extension_name;
1168 		gchar *uri;
1169 
1170 		extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
1171 		extension = e_source_get_extension (source, extension_name);
1172 		uri = e_source_mail_composition_dup_drafts_folder (extension);
1173 
1174 		drafts_folder_uri = uri;
1175 
1176 		g_object_unref (source);
1177 	}
1178 
1179 	local_drafts_folder_uri =
1180 		e_mail_session_get_local_folder_uri (
1181 		session, E_MAIL_LOCAL_FOLDER_DRAFTS);
1182 
1183 	if (drafts_folder_uri == NULL) {
1184 		async_context->folder_uri = g_strdup (local_drafts_folder_uri);
1185 		composer_save_to_drafts_append_mail (async_context, NULL);
1186 	} else {
1187 		GCancellable *cancellable;
1188 
1189 		cancellable = e_activity_get_cancellable (activity);
1190 		async_context->folder_uri = g_strdup (drafts_folder_uri);
1191 
1192 		e_mail_session_uri_to_folder (
1193 			session, drafts_folder_uri, 0,
1194 			G_PRIORITY_DEFAULT, cancellable,
1195 			composer_save_to_drafts_got_folder,
1196 			async_context);
1197 
1198 		g_free (drafts_folder_uri);
1199 	}
1200 
1201 	g_free (identity_uid);
1202 }
1203 
1204 static void
emcu_manage_flush_outbox(EMailSession * session)1205 emcu_manage_flush_outbox (EMailSession *session)
1206 {
1207 	GSettings *settings;
1208 
1209 	g_return_if_fail (E_IS_MAIL_SESSION (session));
1210 
1211 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
1212 	if (g_settings_get_boolean (settings, "composer-use-outbox")) {
1213 		gint delay_flush = g_settings_get_int (settings, "composer-delay-outbox-flush");
1214 
1215 		if (delay_flush == 0) {
1216 			e_mail_session_flush_outbox (session);
1217 		} else if (delay_flush > 0) {
1218 			e_mail_session_schedule_outbox_flush (session, delay_flush);
1219 		}
1220 	}
1221 	g_object_unref (settings);
1222 }
1223 
1224 static void
composer_save_to_outbox_completed(GObject * source_object,GAsyncResult * result,gpointer user_data)1225 composer_save_to_outbox_completed (GObject *source_object,
1226                                    GAsyncResult *result,
1227                                    gpointer user_data)
1228 {
1229 	EMailSession *session;
1230 	EActivity *activity;
1231 	EAlertSink *alert_sink;
1232 	GCancellable *cancellable;
1233 	AsyncContext *async_context;
1234 	GError *local_error = NULL;
1235 
1236 	session = E_MAIL_SESSION (source_object);
1237 	async_context = (AsyncContext *) user_data;
1238 
1239 	activity = async_context->activity;
1240 	alert_sink = e_activity_get_alert_sink (activity);
1241 	cancellable = e_activity_get_cancellable (activity);
1242 
1243 	e_mail_session_append_to_local_folder_finish (
1244 		session, result, NULL, &local_error);
1245 
1246 	if (e_activity_handle_cancellation (activity, local_error)) {
1247 		g_error_free (local_error);
1248 		goto exit;
1249 
1250 	} else if (local_error != NULL) {
1251 		e_alert_submit (
1252 			alert_sink,
1253 			"mail-composer:append-to-outbox-error",
1254 			local_error->message, NULL);
1255 		g_error_free (local_error);
1256 		goto exit;
1257 	}
1258 
1259 	/* special processing for Outbox folder */
1260 	manage_x_evolution_replace_outbox (
1261 		async_context->composer,
1262 		session,
1263 		async_context->message,
1264 		cancellable);
1265 
1266 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
1267 
1268 	/* Wait for the EActivity's completion message to
1269 	 * time out and then destroy the composer window. */
1270 	g_object_weak_ref (
1271 		G_OBJECT (activity), (GWeakNotify)
1272 		gtk_widget_destroy, async_context->composer);
1273 
1274 	emcu_manage_flush_outbox (session);
1275 
1276 exit:
1277 	async_context_free (async_context);
1278 }
1279 
1280 static void
em_utils_composer_save_to_outbox_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1281 em_utils_composer_save_to_outbox_cb (EMsgComposer *composer,
1282                                      CamelMimeMessage *message,
1283                                      EActivity *activity,
1284                                      EMailSession *session)
1285 {
1286 	AsyncContext *async_context;
1287 	CamelMessageInfo *info;
1288 	GCancellable *cancellable;
1289 
1290 	async_context = g_slice_new0 (AsyncContext);
1291 	async_context->message = g_object_ref (message);
1292 	async_context->composer = g_object_ref (composer);
1293 	async_context->activity = g_object_ref (activity);
1294 
1295 	cancellable = e_activity_get_cancellable (activity);
1296 
1297 	info = camel_message_info_new (NULL);
1298 	camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
1299 
1300 	e_mail_session_append_to_local_folder (
1301 		session, E_MAIL_LOCAL_FOLDER_OUTBOX,
1302 		message, info, G_PRIORITY_DEFAULT, cancellable,
1303 		composer_save_to_outbox_completed,
1304 		async_context);
1305 
1306 	g_clear_object (&info);
1307 }
1308 
1309 typedef struct _PrintAsyncContext {
1310 	GMainLoop *main_loop;
1311 	GError *error;
1312 } PrintAsyncContext;
1313 
1314 static void
em_composer_utils_print_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1315 em_composer_utils_print_done_cb (GObject *source_object,
1316 				 GAsyncResult *result,
1317 				 gpointer user_data)
1318 {
1319 	PrintAsyncContext *async_context = user_data;
1320 
1321 	g_return_if_fail (E_IS_MAIL_PRINTER (source_object));
1322 	g_return_if_fail (async_context != NULL);
1323 	g_return_if_fail (async_context->main_loop != NULL);
1324 
1325 	e_mail_printer_print_finish (E_MAIL_PRINTER (source_object), result, &(async_context->error));
1326 
1327 	g_main_loop_quit (async_context->main_loop);
1328 }
1329 
1330 static void
em_utils_composer_print_cb(EMsgComposer * composer,GtkPrintOperationAction action,CamelMimeMessage * message,EActivity * activity,EMailSession * session)1331 em_utils_composer_print_cb (EMsgComposer *composer,
1332                             GtkPrintOperationAction action,
1333                             CamelMimeMessage *message,
1334                             EActivity *activity,
1335                             EMailSession *session)
1336 {
1337 	EMailParser *parser;
1338 	EMailPartList *parts, *reserved_parts;
1339 	EMailPrinter *printer;
1340 	EMailBackend *mail_backend;
1341 	const gchar *message_id;
1342 	GCancellable *cancellable;
1343 	CamelObjectBag *parts_registry;
1344 	gchar *mail_uri;
1345 	PrintAsyncContext async_context;
1346 
1347 	mail_backend = E_MAIL_BACKEND (e_shell_get_backend_by_name (e_msg_composer_get_shell (composer), "mail"));
1348 	g_return_if_fail (mail_backend != NULL);
1349 
1350 	cancellable = e_activity_get_cancellable (activity);
1351 	parser = e_mail_parser_new (CAMEL_SESSION (session));
1352 
1353 	message_id = camel_mime_message_get_message_id (message);
1354 	parts = e_mail_parser_parse_sync (parser, NULL, message_id, message, cancellable);
1355 	if (!parts) {
1356 		g_clear_object (&parser);
1357 		return;
1358 	}
1359 
1360 	parts_registry = e_mail_part_list_get_registry ();
1361 
1362 	mail_uri = e_mail_part_build_uri (NULL, message_id, NULL, NULL);
1363 	reserved_parts = camel_object_bag_reserve (parts_registry, mail_uri);
1364 	g_clear_object (&reserved_parts);
1365 
1366 	camel_object_bag_add (parts_registry, mail_uri, parts);
1367 
1368 	printer = e_mail_printer_new (parts, e_mail_backend_get_remote_content (mail_backend));
1369 
1370 	async_context.error = NULL;
1371 	async_context.main_loop = g_main_loop_new (NULL, FALSE);
1372 
1373 	/* Cannot use EAsyncClosure here, it blocks the main context, which is not good here. */
1374 	e_mail_printer_print (printer, action, NULL, cancellable, em_composer_utils_print_done_cb, &async_context);
1375 
1376 	g_main_loop_run (async_context.main_loop);
1377 
1378 	camel_object_bag_remove (parts_registry, parts);
1379 	g_main_loop_unref (async_context.main_loop);
1380 	g_object_unref (printer);
1381 	g_object_unref (parts);
1382 	g_free (mail_uri);
1383 
1384 	if (e_activity_handle_cancellation (activity, async_context.error)) {
1385 		g_error_free (async_context.error);
1386 	} else if (async_context.error != NULL) {
1387 		e_alert_submit (
1388 			e_activity_get_alert_sink (activity),
1389 			"mail-composer:no-build-message",
1390 			async_context.error->message, NULL);
1391 		g_error_free (async_context.error);
1392 	}
1393 }
1394 
1395 static gint
compare_sources_with_uids_order_cb(gconstpointer a,gconstpointer b,gpointer user_data)1396 compare_sources_with_uids_order_cb (gconstpointer a,
1397                                     gconstpointer b,
1398                                     gpointer user_data)
1399 {
1400 	ESource *asource = (ESource *) a;
1401 	ESource *bsource = (ESource *) b;
1402 	GHashTable *uids_order = user_data;
1403 	gint aindex, bindex;
1404 
1405 	aindex = GPOINTER_TO_INT (g_hash_table_lookup (uids_order, e_source_get_uid (asource)));
1406 	bindex = GPOINTER_TO_INT (g_hash_table_lookup (uids_order, e_source_get_uid (bsource)));
1407 
1408 	if (aindex <= 0)
1409 		aindex = g_hash_table_size (uids_order);
1410 	if (bindex <= 0)
1411 		bindex = g_hash_table_size (uids_order);
1412 
1413 	return aindex - bindex;
1414 }
1415 
1416 static void
sort_sources_by_ui(GList ** psources,gpointer user_data)1417 sort_sources_by_ui (GList **psources,
1418                     gpointer user_data)
1419 {
1420 	EShell *shell = user_data;
1421 	EShellBackend *shell_backend;
1422 	EMailSession *mail_session;
1423 	EMailAccountStore *account_store;
1424 	GtkTreeModel *model;
1425 	GtkTreeIter iter;
1426 	GHashTable *uids_order;
1427 	gint index = 0;
1428 
1429 	g_return_if_fail (psources != NULL);
1430 	g_return_if_fail (E_IS_SHELL (shell));
1431 
1432 	/* nothing to sort */
1433 	if (!*psources || !g_list_next (*psources))
1434 		return;
1435 
1436 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
1437 	g_return_if_fail (shell_backend != NULL);
1438 
1439 	mail_session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
1440 	g_return_if_fail (mail_session != NULL);
1441 
1442 	account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session));
1443 	g_return_if_fail (account_store != NULL);
1444 
1445 	model = GTK_TREE_MODEL (account_store);
1446 	if (!gtk_tree_model_get_iter_first (model, &iter))
1447 		return;
1448 
1449 	uids_order = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1450 
1451 	do {
1452 		CamelService *service = NULL;
1453 
1454 		gtk_tree_model_get (model, &iter, E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, &service, -1);
1455 
1456 		if (service) {
1457 			index++;
1458 			g_hash_table_insert (uids_order, g_strdup (camel_service_get_uid (service)), GINT_TO_POINTER (index));
1459 			g_object_unref (service);
1460 		}
1461 	} while (gtk_tree_model_iter_next (model, &iter));
1462 
1463 	*psources = g_list_sort_with_data (*psources, compare_sources_with_uids_order_cb, uids_order);
1464 
1465 	g_hash_table_destroy (uids_order);
1466 }
1467 
1468 /* (trasfer full) */
1469 ESource *
em_composer_utils_guess_identity_source(EShell * shell,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,gchar ** out_identity_name,gchar ** out_identity_address)1470 em_composer_utils_guess_identity_source (EShell *shell,
1471 					 CamelMimeMessage *message,
1472 					 CamelFolder *folder,
1473 					 const gchar *message_uid,
1474 					 gchar **out_identity_name,
1475 					 gchar **out_identity_address)
1476 {
1477 	ESource *source;
1478 
1479 	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
1480 
1481 	/* Check send account override for the passed-in folder */
1482 	source = em_utils_check_send_account_override (shell, message, folder, out_identity_name, out_identity_address);
1483 
1484 	/* If not set and it's a search folder, then check the original folder */
1485 	if (!source && message_uid && CAMEL_IS_VEE_FOLDER (folder)) {
1486 		CamelMessageInfo *mi = camel_folder_get_message_info (folder, message_uid);
1487 		if (mi) {
1488 			CamelFolder *location;
1489 
1490 			location = camel_vee_folder_get_location (CAMEL_VEE_FOLDER (folder), (CamelVeeMessageInfo *) mi, NULL);
1491 			if (location)
1492 				source = em_utils_check_send_account_override (shell, message, location, out_identity_name, out_identity_address);
1493 			g_clear_object (&mi);
1494 		}
1495 	}
1496 
1497 	/* If no send account override, then guess */
1498 	if (!source) {
1499 		source = em_utils_guess_mail_identity_with_recipients_and_sort (e_shell_get_registry (shell),
1500 			message, folder, message_uid, out_identity_name, out_identity_address, sort_sources_by_ui, shell);
1501 	}
1502 
1503 	return source;
1504 }
1505 
1506 /* Composing messages... */
1507 
1508 static CamelMimeMessage *em_utils_get_composer_recipients_as_message (EMsgComposer *composer);
1509 
1510 static void
set_up_new_composer(EMsgComposer * composer,const gchar * subject,CamelFolder * folder,CamelMimeMessage * message,const gchar * message_uid,gboolean is_new_message)1511 set_up_new_composer (EMsgComposer *composer,
1512 		     const gchar *subject,
1513 		     CamelFolder *folder,
1514 		     CamelMimeMessage *message,
1515 		     const gchar *message_uid,
1516 		     gboolean is_new_message)
1517 {
1518 	EClientCache *client_cache;
1519 	ESourceRegistry *registry;
1520 	EComposerHeaderTable *table;
1521 	ESource *source = NULL;
1522 	gchar *identity = NULL, *identity_name = NULL, *identity_address = NULL;
1523 
1524 	table = e_msg_composer_get_header_table (composer);
1525 
1526 	client_cache = e_composer_header_table_ref_client_cache (table);
1527 	registry = e_client_cache_ref_registry (client_cache);
1528 
1529 	if (folder != NULL) {
1530 		gchar *folder_uri;
1531 		GList *list;
1532 
1533 		if (message) {
1534 			g_object_ref (message);
1535 		} else if (message_uid && !is_new_message) {
1536 			/* Do this only if it's not a new message, in case the default account
1537 			   has set "always CC/Bcc" address, which might match one of the configured
1538 			   accounts, which in turn forces to use the default account due to this
1539 			   read back from the composer. */
1540 			message = em_utils_get_composer_recipients_as_message (composer);
1541 		}
1542 
1543 		if (message) {
1544 			EShell *shell;
1545 
1546 			shell = e_msg_composer_get_shell (composer);
1547 
1548 			source = em_composer_utils_guess_identity_source (shell, message, folder, message_uid, &identity_name, &identity_address);
1549 		}
1550 
1551 		/* In case of search folder, try to guess the store from
1552 		   the internal folders of it. If they are all from the same
1553 		   store, then use that store. */
1554 		if (!source && CAMEL_IS_VEE_FOLDER (folder)) {
1555 			GHashTable *stores, *done_folders;
1556 			GSList *todo;
1557 
1558 			stores = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
1559 			done_folders = g_hash_table_new (g_direct_hash, g_direct_equal);
1560 
1561 			todo = g_slist_prepend (NULL, g_object_ref (folder));
1562 
1563 			while (todo) {
1564 				CamelVeeFolder *vfolder = todo->data;
1565 
1566 				todo = g_slist_remove (todo, vfolder);
1567 				if (!g_hash_table_contains (done_folders, vfolder)) {
1568 					GList *folders, *llink;
1569 
1570 					g_hash_table_insert (done_folders, vfolder, NULL);
1571 
1572 					folders = camel_vee_folder_ref_folders (vfolder);
1573 					for (llink = folders; llink; llink = g_list_next (llink)) {
1574 						CamelFolder *subfolder = llink->data;
1575 
1576 						if (!g_hash_table_contains (done_folders, subfolder)) {
1577 							if (CAMEL_IS_VEE_FOLDER (subfolder)) {
1578 								todo = g_slist_prepend (todo, g_object_ref (subfolder));
1579 							} else {
1580 								CamelStore *store = camel_folder_get_parent_store (subfolder);
1581 
1582 								g_hash_table_insert (done_folders, subfolder, NULL);
1583 
1584 								if (store) {
1585 									g_hash_table_insert (stores, g_object_ref (store), NULL);
1586 
1587 									if (g_hash_table_size (stores) > 1) {
1588 										g_slist_free_full (todo, g_object_unref);
1589 										todo = NULL;
1590 										break;
1591 									}
1592 								}
1593 							}
1594 						}
1595 					}
1596 
1597 					g_list_free_full (folders, g_object_unref);
1598 				}
1599 
1600 				g_object_unref (vfolder);
1601 			}
1602 
1603 			if (g_hash_table_size (stores) == 1) {
1604 				GHashTableIter iter;
1605 				gpointer store;
1606 
1607 				g_hash_table_iter_init (&iter, stores);
1608 				if (g_hash_table_iter_next (&iter, &store, NULL) && store)
1609 					source = em_utils_ref_mail_identity_for_store (registry, store);
1610 			}
1611 
1612 			g_slist_free_full (todo, g_object_unref);
1613 			g_hash_table_destroy (done_folders);
1614 			g_hash_table_destroy (stores);
1615 		}
1616 
1617 		g_clear_object (&message);
1618 
1619 		if (!source) {
1620 			CamelStore *store;
1621 
1622 			store = camel_folder_get_parent_store (folder);
1623 			source = em_utils_ref_mail_identity_for_store (registry, store);
1624 		}
1625 
1626 		folder_uri = e_mail_folder_uri_from_folder (folder);
1627 
1628 		list = g_list_prepend (NULL, folder_uri);
1629 		e_composer_header_table_set_post_to_list (table, list);
1630 		g_list_free (list);
1631 
1632 		g_free (folder_uri);
1633 	}
1634 
1635 	if (source != NULL) {
1636 		identity = e_source_dup_uid (source);
1637 		g_object_unref (source);
1638 	}
1639 
1640 	if (subject)
1641 		e_composer_header_table_set_subject (table, subject);
1642 	e_composer_header_table_set_identity_uid (table, identity, identity_name, identity_address);
1643 
1644 	em_utils_apply_send_account_override_to_composer (composer, folder);
1645 
1646 	g_free (identity);
1647 	g_free (identity_name);
1648 	g_free (identity_address);
1649 
1650 	g_object_unref (client_cache);
1651 	g_object_unref (registry);
1652 }
1653 
1654 /**
1655  * em_utils_compose_new_message:
1656  * @composer: an #EMsgComposer
1657  * @folder: (nullable): a #CamelFolder, or %NULL
1658  *
1659  * Sets up a new @composer window.
1660  *
1661  * See: em_utils_compose_new_message_with_selection()
1662  *
1663  * Since: 3.22
1664  **/
1665 void
em_utils_compose_new_message(EMsgComposer * composer,CamelFolder * folder)1666 em_utils_compose_new_message (EMsgComposer *composer,
1667                               CamelFolder *folder)
1668 {
1669 	em_utils_compose_new_message_with_selection (composer, folder, NULL);
1670 }
1671 
1672 /**
1673  * em_utils_compose_new_message_with_selection:
1674  * @composer: an #EMsgComposer
1675  * @folder: (nullable): a #CamelFolder, or %NULL
1676  * @message_uid: (nullable): a UID of the selected message, or %NULL
1677  *
1678  * Sets up a new @composer window, similar to em_utils_compose_new_message(),
1679  * but also tries to identify From account more precisely, when the @folder
1680  * is a search folder.
1681  *
1682  * Since: 3.28
1683  **/
1684 void
em_utils_compose_new_message_with_selection(EMsgComposer * composer,CamelFolder * folder,const gchar * message_uid)1685 em_utils_compose_new_message_with_selection (EMsgComposer *composer,
1686 					     CamelFolder *folder,
1687 					     const gchar *message_uid)
1688 {
1689 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
1690 
1691 	if (folder)
1692 		g_return_if_fail (CAMEL_IS_FOLDER (folder));
1693 
1694 	set_up_new_composer (composer, "", folder, NULL, message_uid, TRUE);
1695 	composer_set_no_change (composer);
1696 
1697 	gtk_widget_show (GTK_WIDGET (composer));
1698 }
1699 
1700 static CamelMimeMessage *
em_utils_get_composer_recipients_as_message(EMsgComposer * composer)1701 em_utils_get_composer_recipients_as_message (EMsgComposer *composer)
1702 {
1703 	CamelMimeMessage *message;
1704 	EComposerHeaderTable *table;
1705 	EComposerHeader *header;
1706 	EDestination **destv;
1707 	CamelInternetAddress *to_addr, *cc_addr, *bcc_addr, *dest_addr;
1708 	const gchar *text_addr;
1709 	gint ii;
1710 
1711 	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
1712 
1713 	table = e_msg_composer_get_header_table (composer);
1714 	header = e_composer_header_table_get_header (table, E_COMPOSER_HEADER_TO);
1715 
1716 	if (!e_composer_header_get_visible (header))
1717 		return NULL;
1718 
1719 	message = camel_mime_message_new ();
1720 
1721 	to_addr = camel_internet_address_new ();
1722 	cc_addr = camel_internet_address_new ();
1723 	bcc_addr = camel_internet_address_new ();
1724 
1725 	/* To */
1726 	dest_addr = to_addr;
1727 	destv = e_composer_header_table_get_destinations_to (table);
1728 	for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1729 		text_addr = e_destination_get_address (destv[ii]);
1730 		if (text_addr && *text_addr) {
1731 			if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1732 				camel_internet_address_add (dest_addr, "", text_addr);
1733 		}
1734 	}
1735 	e_destination_freev (destv);
1736 
1737 	/* CC */
1738 	dest_addr = cc_addr;
1739 	destv = e_composer_header_table_get_destinations_cc (table);
1740 	for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1741 		text_addr = e_destination_get_address (destv[ii]);
1742 		if (text_addr && *text_addr) {
1743 			if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1744 				camel_internet_address_add (dest_addr, "", text_addr);
1745 		}
1746 	}
1747 	e_destination_freev (destv);
1748 
1749 	/* Bcc */
1750 	dest_addr = bcc_addr;
1751 	destv = e_composer_header_table_get_destinations_bcc (table);
1752 	for (ii = 0; destv != NULL && destv[ii] != NULL; ii++) {
1753 		text_addr = e_destination_get_address (destv[ii]);
1754 		if (text_addr && *text_addr) {
1755 			if (camel_address_decode (CAMEL_ADDRESS (dest_addr), text_addr) <= 0)
1756 				camel_internet_address_add (dest_addr, "", text_addr);
1757 		}
1758 	}
1759 	e_destination_freev (destv);
1760 
1761 	if (camel_address_length (CAMEL_ADDRESS (to_addr)) > 0)
1762 		camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, to_addr);
1763 
1764 	if (camel_address_length (CAMEL_ADDRESS (cc_addr)) > 0)
1765 		camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_CC, cc_addr);
1766 
1767 	if (camel_address_length (CAMEL_ADDRESS (bcc_addr)) > 0)
1768 		camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_BCC, bcc_addr);
1769 
1770 	g_object_unref (to_addr);
1771 	g_object_unref (cc_addr);
1772 	g_object_unref (bcc_addr);
1773 
1774 	return message;
1775 }
1776 
1777 typedef struct _CreateComposerData {
1778 	CamelFolder *folder;
1779 	const gchar *message_uid; /* In the Camel string pool */
1780 	gchar *mailto;
1781 } CreateComposerData;
1782 
1783 static void
create_composer_data_free(gpointer ptr)1784 create_composer_data_free (gpointer ptr)
1785 {
1786 	CreateComposerData *ccd = ptr;
1787 
1788 	if (ccd) {
1789 		g_clear_object (&ccd->folder);
1790 		camel_pstring_free (ccd->message_uid);
1791 		g_free (ccd->mailto);
1792 		g_slice_free (CreateComposerData, ccd);
1793 	}
1794 }
1795 
1796 static void
msg_composer_created_with_mailto_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1797 msg_composer_created_with_mailto_cb (GObject *source_object,
1798 				     GAsyncResult *result,
1799 				     gpointer user_data)
1800 {
1801 	CreateComposerData *ccd = user_data;
1802 	EMsgComposer *composer;
1803 	GError *error = NULL;
1804 
1805 	g_return_if_fail (ccd != NULL);
1806 
1807 	composer = e_msg_composer_new_finish (result, &error);
1808 	if (error) {
1809 		g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
1810 		g_clear_error (&error);
1811 		create_composer_data_free (ccd);
1812 
1813 		return;
1814 	}
1815 
1816 	if (ccd->mailto)
1817 		e_msg_composer_setup_from_url (composer, ccd->mailto);
1818 
1819 	set_up_new_composer (composer, NULL, ccd->folder, NULL, ccd->message_uid, TRUE);
1820 
1821 	composer_set_no_change (composer);
1822 
1823 	gtk_window_present (GTK_WINDOW (composer));
1824 
1825 	create_composer_data_free (ccd);
1826 }
1827 
1828 /**
1829  * em_utils_compose_new_message_with_mailto:
1830  * @shell: an #EShell
1831  * @mailto: a mailto URL
1832  * @folder: a #CamelFolder, or %NULL
1833  *
1834  * Opens a new composer window as a child window of @parent's toplevel
1835  * window. If @mailto is non-NULL, the composer fields will be filled in
1836  * according to the values in the mailto URL.
1837  *
1838  * See: em_utils_compose_new_message_with_mailto_and_selection()
1839  **/
1840 void
em_utils_compose_new_message_with_mailto(EShell * shell,const gchar * mailto,CamelFolder * folder)1841 em_utils_compose_new_message_with_mailto (EShell *shell,
1842                                           const gchar *mailto,
1843                                           CamelFolder *folder)
1844 {
1845 	em_utils_compose_new_message_with_mailto_and_selection (shell, mailto, folder, NULL);
1846 }
1847 
1848 /**
1849  * em_utils_compose_new_message_with_mailto_and_selection:
1850  * @shell: an #EShell
1851  * @mailto: a mailto URL
1852  * @folder: a #CamelFolder, or %NULL
1853  * @message_uid: (nullable): a UID of the selected message, or %NULL
1854  *
1855  * similarly to em_utils_compose_new_message_with_mailto(), opens a new composer
1856  * window as a child window of @parent's toplevel window. If @mailto is non-NULL,
1857  * the composer fields will be filled in according to the values in the mailto URL.
1858  * It also tries to identify From account more precisely, when the @folder
1859  * is a search folder.
1860  *
1861  * Since: 3.28
1862  **/
1863 void
em_utils_compose_new_message_with_mailto_and_selection(EShell * shell,const gchar * mailto,CamelFolder * folder,const gchar * message_uid)1864 em_utils_compose_new_message_with_mailto_and_selection (EShell *shell,
1865 							const gchar *mailto,
1866 							CamelFolder *folder,
1867 							const gchar *message_uid)
1868 {
1869 	CreateComposerData *ccd;
1870 
1871 	g_return_if_fail (E_IS_SHELL (shell));
1872 
1873 	if (folder)
1874 		g_return_if_fail (CAMEL_IS_FOLDER (folder));
1875 
1876 	ccd = g_slice_new0 (CreateComposerData);
1877 	ccd->folder = folder ? g_object_ref (folder) : NULL;
1878 	ccd->message_uid = camel_pstring_strdup (message_uid);
1879 	ccd->mailto = g_strdup (mailto);
1880 
1881 	e_msg_composer_new (shell, msg_composer_created_with_mailto_cb, ccd);
1882 }
1883 
1884 static ESource *
emcu_ref_identity_source_from_composer(EMsgComposer * composer)1885 emcu_ref_identity_source_from_composer (EMsgComposer *composer)
1886 {
1887 	EComposerHeaderTable *table;
1888 	ESource *source = NULL;
1889 	gchar *identity_uid;
1890 
1891 	if (!composer)
1892 		return NULL;
1893 
1894 	table = e_msg_composer_get_header_table (composer);
1895 	identity_uid = e_composer_header_table_dup_identity_uid (table, NULL, NULL);
1896 	if (identity_uid)
1897 		source = e_composer_header_table_ref_source (table, identity_uid);
1898 	g_free (identity_uid);
1899 
1900 	return source;
1901 }
1902 
1903 static void
emcu_change_locale(const gchar * lc_messages,const gchar * lc_time,gchar ** out_lc_messages,gchar ** out_lc_time)1904 emcu_change_locale (const gchar *lc_messages,
1905 		    const gchar *lc_time,
1906 		    gchar **out_lc_messages,
1907 		    gchar **out_lc_time)
1908 {
1909 	gboolean success;
1910 	gchar *previous;
1911 
1912 	if (lc_messages) {
1913 		#if defined(LC_MESSAGES)
1914 		previous = g_strdup (setlocale (LC_MESSAGES, NULL));
1915 		success = setlocale (LC_MESSAGES, lc_messages) != NULL;
1916 		#else
1917 		previous = g_strdup (setlocale (LC_ALL, NULL));
1918 		success = setlocale (LC_ALL, lc_messages) != NULL;
1919 		#endif
1920 
1921 		if (out_lc_messages)
1922 			*out_lc_messages = success ? g_strdup (previous) : NULL;
1923 
1924 		g_free (previous);
1925 	}
1926 
1927 	if (lc_time) {
1928 		#if defined(LC_TIME)
1929 		previous = g_strdup (setlocale (LC_TIME, NULL));
1930 		success = setlocale (LC_TIME, lc_time) != NULL;
1931 		#elif defined(LC_MESSAGES)
1932 		previous = g_strdup (setlocale (LC_ALL, NULL));
1933 		success = setlocale (LC_ALL, lc_time) != NULL;
1934 		#else
1935 		previous = NULL;
1936 		success = FALSE;
1937 		#endif
1938 
1939 		if (out_lc_time)
1940 			*out_lc_time = success ? g_strdup (previous) : NULL;
1941 
1942 		g_free (previous);
1943 	}
1944 }
1945 
1946 static void
emcu_prepare_attribution_locale(ESource * identity_source,gchar ** out_lc_messages,gchar ** out_lc_time)1947 emcu_prepare_attribution_locale (ESource *identity_source,
1948 				 gchar **out_lc_messages,
1949 				 gchar **out_lc_time)
1950 {
1951 	gchar *lang = NULL;
1952 
1953 	g_return_if_fail (out_lc_messages != NULL);
1954 	g_return_if_fail (out_lc_time != NULL);
1955 
1956 	if (identity_source && e_source_has_extension (identity_source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
1957 		ESourceMailComposition *extension;
1958 
1959 		extension = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
1960 		lang = e_source_mail_composition_dup_language (extension);
1961 	}
1962 
1963 	if (!lang || !*lang) {
1964 		GSettings *settings;
1965 
1966 		g_free (lang);
1967 
1968 		settings = e_util_ref_settings ("org.gnome.evolution.mail");
1969 		lang = g_settings_get_string (settings, "composer-attribution-language");
1970 		g_object_unref (settings);
1971 
1972 		if (!lang || !*lang)
1973 			g_clear_pointer (&lang, g_free);
1974 	}
1975 
1976 	if (!lang) {
1977 		/* Set the locale always, even when using the user interface
1978 		   language, because gettext() can return wrong text (in the previously
1979 		   used language) */
1980 		#if defined(LC_MESSAGES)
1981 		lang = g_strdup (setlocale (LC_MESSAGES, NULL));
1982 		#else
1983 		lang = g_strdup (setlocale (LC_ALL, NULL));
1984 		#endif
1985 	}
1986 
1987 	if (lang) {
1988 		if (!g_str_equal (lang, "C") && !strchr (lang, '.')) {
1989 			gchar *tmp;
1990 
1991 			tmp = g_strconcat (lang, ".UTF-8", NULL);
1992 			g_free (lang);
1993 			lang = tmp;
1994 		}
1995 
1996 		emcu_change_locale (lang, lang, out_lc_messages, out_lc_time);
1997 
1998 		g_free (lang);
1999 	}
2000 }
2001 
2002 /* Takes ownership (frees) the 'restore_locale' */
2003 static void
emcu_restore_locale_after_attribution(gchar * restore_lc_messages,gchar * restore_lc_time)2004 emcu_restore_locale_after_attribution (gchar *restore_lc_messages,
2005 				       gchar *restore_lc_time)
2006 {
2007 	emcu_change_locale (restore_lc_messages, restore_lc_time, NULL, NULL);
2008 
2009 	g_free (restore_lc_messages);
2010 	g_free (restore_lc_time);
2011 }
2012 
2013 /* Editing messages... */
2014 
2015 typedef enum {
2016 	QUOTING_ATTRIBUTION,
2017 	QUOTING_FORWARD,
2018 	QUOTING_ORIGINAL
2019 } QuotingTextEnum;
2020 
2021 static struct {
2022 	const gchar * conf_key;
2023 	const gchar * message;
2024 } conf_messages[] = {
2025 	[QUOTING_ATTRIBUTION] =
2026 		{ "composer-message-attribution",
2027 		/* Note to translators: this is the attribution string used
2028 		 * when quoting messages.  Each ${Variable} gets replaced
2029 		 * with a value.  To see a full list of available variables,
2030 		 * see mail/em-composer-utils.c:attribvars array. */
2031 		  N_("On ${AbbrevWeekdayName}, ${Year}-${Month}-${Day} at "
2032 		     "${24Hour}:${Minute} ${TimeZone}, ${Sender} wrote:")
2033 		},
2034 
2035 	[QUOTING_FORWARD] =
2036 		{ "composer-message-forward",
2037 		  N_("-------- Forwarded Message --------")
2038 		},
2039 
2040 	[QUOTING_ORIGINAL] =
2041 		{ "composer-message-original",
2042 		  N_("-----Original Message-----")
2043 		}
2044 };
2045 
2046 static gchar *
quoting_text(QuotingTextEnum type,EMsgComposer * composer)2047 quoting_text (QuotingTextEnum type,
2048 	      EMsgComposer *composer)
2049 {
2050 	GSettings *settings;
2051 	gchar *restore_lc_messages = NULL, *restore_lc_time = NULL;
2052 	gchar *text;
2053 
2054 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
2055 	text = g_settings_get_string (settings, conf_messages[type].conf_key);
2056 	g_object_unref (settings);
2057 
2058 	if (text && *text)
2059 		return text;
2060 
2061 	g_free (text);
2062 
2063 	if (composer) {
2064 		ESource *identity_source;
2065 
2066 		identity_source = emcu_ref_identity_source_from_composer (composer);
2067 
2068 		emcu_prepare_attribution_locale (identity_source, &restore_lc_messages, &restore_lc_time);
2069 
2070 		g_clear_object (&identity_source);
2071 	}
2072 
2073 	text = g_strdup (_(conf_messages[type].message));
2074 
2075 	emcu_restore_locale_after_attribution (restore_lc_messages, restore_lc_time);
2076 
2077 	return text;
2078 }
2079 
2080 /**
2081  * em_composer_utils_get_forward_marker:
2082  * @composer: an #EMsgComposer from which to get the identity information
2083  *
2084  * Returns: (transfer full): a text marker which is used for inline forwarded messages.
2085  *   Free returned pointer with g_free(), when no longer needed.
2086  *
2087  * Since: 3.24
2088  **/
2089 gchar *
em_composer_utils_get_forward_marker(EMsgComposer * composer)2090 em_composer_utils_get_forward_marker (EMsgComposer *composer)
2091 {
2092 	return quoting_text (QUOTING_FORWARD, composer);
2093 }
2094 
2095 /**
2096  * em_composer_utils_get_original_marker:
2097  * @composer: an #EMsgComposer from which to get the identity information
2098  *
2099  * Returns: (transfer full): a text marker which is used for inline message replies.
2100  *   Free returned pointer with g_free(), when no longer needed.
2101  *
2102  * Since: 3.24
2103  **/
2104 gchar *
em_composer_utils_get_original_marker(EMsgComposer * composer)2105 em_composer_utils_get_original_marker (EMsgComposer *composer)
2106 {
2107 	return quoting_text (QUOTING_ORIGINAL, composer);
2108 }
2109 
2110 static gboolean
emcu_message_references_existing_account(CamelMimeMessage * message,EMsgComposer * composer)2111 emcu_message_references_existing_account (CamelMimeMessage *message,
2112 					  EMsgComposer *composer)
2113 {
2114 	ESource *source;
2115 	gchar *identity_uid;
2116 	gboolean res = FALSE;
2117 
2118 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
2119 	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
2120 
2121 	identity_uid = (gchar *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Identity");
2122 	if (!identity_uid) {
2123 		/* for backward compatibility */
2124 		identity_uid = (gchar *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Account");
2125 	}
2126 
2127 	if (!identity_uid)
2128 		return FALSE;
2129 
2130 	identity_uid = g_strstrip (g_strdup (identity_uid));
2131 	source = e_composer_header_table_ref_source (e_msg_composer_get_header_table (composer), identity_uid);
2132 
2133 	res = source != NULL;
2134 
2135 	g_clear_object (&source);
2136 	g_free (identity_uid);
2137 
2138 	return res;
2139 }
2140 
2141 typedef struct _OutboxData {
2142 	CamelSession *session;
2143 	CamelMessageInfo *info;
2144 } OutboxData;
2145 
2146 static void
outbox_data_free(gpointer ptr)2147 outbox_data_free (gpointer ptr)
2148 {
2149 	OutboxData *od = ptr;
2150 
2151 	if (od) {
2152 		if (od->info) {
2153 			g_object_set_data (G_OBJECT (od->info), MAIL_USER_KEY_EDITING, NULL);
2154 
2155 			if (od->session && !(camel_message_info_get_flags (od->info) & CAMEL_MESSAGE_DELETED)) {
2156 				emcu_manage_flush_outbox (E_MAIL_SESSION (od->session));
2157 			}
2158 		}
2159 
2160 		g_clear_object (&od->session);
2161 		g_clear_object (&od->info);
2162 		g_free (od);
2163 	}
2164 }
2165 
2166 /**
2167  * em_utils_edit_message:
2168  * @composer: an #EMsgComposer
2169  * @folder: a #CamelFolder
2170  * @message: a #CamelMimeMessage
2171  * @message_uid: UID of @message, or %NULL
2172  *
2173  * Sets up the @composer with the headers/mime-parts/etc of the @message.
2174  *
2175  * Since: 3.22
2176  **/
2177 void
em_utils_edit_message(EMsgComposer * composer,CamelFolder * folder,CamelMimeMessage * message,const gchar * message_uid,gboolean keep_signature)2178 em_utils_edit_message (EMsgComposer *composer,
2179                        CamelFolder *folder,
2180                        CamelMimeMessage *message,
2181                        const gchar *message_uid,
2182                        gboolean keep_signature)
2183 {
2184 	ESourceRegistry *registry;
2185 	ESource *source;
2186 	gboolean folder_is_sent;
2187 	gboolean folder_is_drafts;
2188 	gboolean folder_is_outbox;
2189 	gboolean folder_is_templates;
2190 	gchar *override_identity_uid = NULL, *override_alias_name = NULL, *override_alias_address = NULL;
2191 
2192 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2193 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2194 	if (folder)
2195 		g_return_if_fail (CAMEL_IS_FOLDER (folder));
2196 
2197 	registry = e_shell_get_registry (e_msg_composer_get_shell (composer));
2198 
2199 	if (folder) {
2200 		folder_is_sent = em_utils_folder_is_sent (registry, folder);
2201 		folder_is_drafts = em_utils_folder_is_drafts (registry, folder);
2202 		folder_is_outbox = em_utils_folder_is_outbox (registry, folder);
2203 		folder_is_templates = em_utils_folder_is_templates (registry, folder);
2204 	} else {
2205 		folder_is_sent = FALSE;
2206 		folder_is_drafts = FALSE;
2207 		folder_is_outbox = FALSE;
2208 		folder_is_templates = FALSE;
2209 	}
2210 	if (folder) {
2211 		if ((!folder_is_sent && !folder_is_drafts && !folder_is_outbox && !folder_is_templates) ||
2212 		    (!folder_is_outbox && !folder_is_templates && !emcu_message_references_existing_account (message, composer))) {
2213 			CamelStore *store;
2214 
2215 			store = camel_folder_get_parent_store (folder);
2216 			source = em_utils_ref_mail_identity_for_store (registry, store);
2217 
2218 			if (source) {
2219 				g_free (override_identity_uid);
2220 				override_identity_uid = e_source_dup_uid (source);
2221 				g_object_unref (source);
2222 			}
2223 		}
2224 
2225 		source = em_utils_check_send_account_override (e_msg_composer_get_shell (composer), message, folder, &override_alias_name, &override_alias_address);
2226 		if (source) {
2227 			g_free (override_identity_uid);
2228 			override_identity_uid = e_source_dup_uid (source);
2229 			g_object_unref (source);
2230 		}
2231 	}
2232 
2233 	/* Remember the source message headers when re-editing in Drafts or Outbox,
2234 	   thus Reply, Forward and such mark the source message properly. Do this
2235 	   before the setup_with_message(), because it modifies the message headers. */
2236 	if (folder_is_drafts || folder_is_outbox) {
2237 		CamelMedium *medium;
2238 		const gchar *hdr_folder;
2239 		const gchar *hdr_message;
2240 		const gchar *hdr_flags;
2241 
2242 		medium = CAMEL_MEDIUM (message);
2243 
2244 		hdr_folder = camel_medium_get_header (medium, "X-Evolution-Source-Folder");
2245 		hdr_message = camel_medium_get_header (medium, "X-Evolution-Source-Message");
2246 		hdr_flags = camel_medium_get_header (medium, "X-Evolution-Source-Flags");
2247 
2248 		if (hdr_folder && hdr_message && hdr_flags) {
2249 			e_msg_composer_set_header (composer, "X-Evolution-Source-Folder", hdr_folder);
2250 			e_msg_composer_set_header (composer, "X-Evolution-Source-Message", hdr_message);
2251 			e_msg_composer_set_header (composer, "X-Evolution-Source-Flags", hdr_flags);
2252 		}
2253 	}
2254 
2255 	e_msg_composer_setup_with_message (composer, message, keep_signature, override_identity_uid, override_alias_name, override_alias_address, NULL);
2256 
2257 	g_free (override_identity_uid);
2258 	g_free (override_alias_name);
2259 	g_free (override_alias_address);
2260 
2261 	/* Override PostTo header only if the folder is a regular folder */
2262 	if (folder && !folder_is_sent && !folder_is_drafts && !folder_is_outbox && !folder_is_templates) {
2263 		EComposerHeaderTable *table;
2264 		gchar *folder_uri;
2265 		GList *list;
2266 
2267 		table = e_msg_composer_get_header_table (composer);
2268 
2269 		folder_uri = e_mail_folder_uri_from_folder (folder);
2270 
2271 		list = g_list_prepend (NULL, folder_uri);
2272 		e_composer_header_table_set_post_to_list (table, list);
2273 		g_list_free (list);
2274 
2275 		g_free (folder_uri);
2276 	}
2277 
2278 	e_msg_composer_remove_header (
2279 		composer, "X-Evolution-Replace-Outbox-UID");
2280 
2281 	if (message_uid != NULL && folder_is_drafts && folder) {
2282 		gchar *folder_uri;
2283 
2284 		folder_uri = e_mail_folder_uri_from_folder (folder);
2285 
2286 		e_msg_composer_set_draft_headers (
2287 			composer, folder_uri, message_uid);
2288 
2289 		g_free (folder_uri);
2290 
2291 	} else if (message_uid != NULL && folder_is_outbox) {
2292 		CamelMessageInfo *info;
2293 
2294 		e_msg_composer_set_header (
2295 			composer, "X-Evolution-Replace-Outbox-UID",
2296 			message_uid);
2297 
2298 		info = camel_folder_get_message_info (folder, message_uid);
2299 		if (info) {
2300 			OutboxData *od;
2301 
2302 			/* This makes the message not to send it while it's being edited */
2303 			g_object_set_data (G_OBJECT (info), MAIL_USER_KEY_EDITING, GINT_TO_POINTER (1));
2304 
2305 			od = g_new0 (OutboxData, 1);
2306 			od->session = e_msg_composer_ref_session (composer);
2307 			od->info = info; /* takes ownership of it */
2308 
2309 			g_object_set_data_full (G_OBJECT (composer), MAIL_USER_KEY_EDITING, od, outbox_data_free);
2310 		}
2311 	}
2312 
2313 	composer_set_no_change (composer);
2314 
2315 	gtk_widget_show (GTK_WIDGET (composer));
2316 }
2317 
2318 static void
emu_update_composers_security(EMsgComposer * composer,guint32 validity_found)2319 emu_update_composers_security (EMsgComposer *composer,
2320                                guint32 validity_found)
2321 {
2322 	GtkAction *action;
2323 	GSettings *settings;
2324 	gboolean sign_reply;
2325 
2326 	g_return_if_fail (composer != NULL);
2327 
2328 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
2329 	sign_reply = (validity_found & E_MAIL_PART_VALIDITY_SIGNED) != 0 &&
2330 		     g_settings_get_boolean (settings, "composer-sign-reply-if-signed");
2331 	g_object_unref (settings);
2332 
2333 	/* Pre-set only for encrypted messages, not for signed */
2334 	if (sign_reply) {
2335 		action = NULL;
2336 
2337 		if (validity_found & E_MAIL_PART_VALIDITY_SMIME) {
2338 			if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer))) &&
2339 			    !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer))))
2340 				action = E_COMPOSER_ACTION_SMIME_SIGN (composer);
2341 		} else {
2342 			if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer))) &&
2343 			    !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer))))
2344 				action = E_COMPOSER_ACTION_PGP_SIGN (composer);
2345 		}
2346 
2347 		if (action)
2348 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2349 	}
2350 
2351 	if (validity_found & E_MAIL_PART_VALIDITY_ENCRYPTED) {
2352 		action = NULL;
2353 
2354 		if (validity_found & E_MAIL_PART_VALIDITY_SMIME) {
2355 			if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer))) &&
2356 			    !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer))))
2357 				action = E_COMPOSER_ACTION_SMIME_ENCRYPT (composer);
2358 		} else {
2359 			if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer))) &&
2360 			    !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer))))
2361 				action = E_COMPOSER_ACTION_PGP_ENCRYPT (composer);
2362 		}
2363 
2364 		if (action)
2365 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2366 	}
2367 }
2368 
2369 void
em_utils_get_real_folder_uri_and_message_uid(CamelFolder * folder,const gchar * uid,gchar ** folder_uri,gchar ** message_uid)2370 em_utils_get_real_folder_uri_and_message_uid (CamelFolder *folder,
2371                                               const gchar *uid,
2372                                               gchar **folder_uri,
2373                                               gchar **message_uid)
2374 {
2375 	g_return_if_fail (folder != NULL);
2376 	g_return_if_fail (uid != NULL);
2377 	g_return_if_fail (folder_uri != NULL);
2378 	g_return_if_fail (message_uid != NULL);
2379 
2380 	em_utils_get_real_folder_and_message_uid (folder, uid, NULL, folder_uri, message_uid);
2381 }
2382 
2383 static void
emu_add_composer_references_from_message(EMsgComposer * composer,CamelMimeMessage * message)2384 emu_add_composer_references_from_message (EMsgComposer *composer,
2385 					  CamelMimeMessage *message)
2386 {
2387 	const gchar *message_id_header;
2388 
2389 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2390 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2391 
2392 	message_id_header = camel_mime_message_get_message_id (message);
2393 	if (message_id_header && *message_id_header) {
2394 		GString *references = g_string_new ("");
2395 		gint ii = 0;
2396 		const gchar *value;
2397 		gchar *unfolded;
2398 
2399 		while (value = e_msg_composer_get_header (composer, "References", ii), value) {
2400 			ii++;
2401 
2402 			if (references->len)
2403 				g_string_append_c (references, ' ');
2404 			g_string_append (references, value);
2405 		}
2406 
2407 		if (references->len)
2408 			g_string_append_c (references, ' ');
2409 
2410 		if (*message_id_header != '<')
2411 			g_string_append_c (references, '<');
2412 
2413 		g_string_append (references, message_id_header);
2414 
2415 		if (*message_id_header != '<')
2416 			g_string_append_c (references, '>');
2417 
2418 		unfolded = camel_header_unfold (references->str);
2419 
2420 		e_msg_composer_set_header (composer, "References", unfolded);
2421 
2422 		g_string_free (references, TRUE);
2423 		g_free (unfolded);
2424 	}
2425 }
2426 
2427 static void
real_update_forwarded_flag(gpointer uid,gpointer folder)2428 real_update_forwarded_flag (gpointer uid,
2429                             gpointer folder)
2430 {
2431 	if (uid && folder)
2432 		camel_folder_set_message_flags (
2433 			folder, uid, CAMEL_MESSAGE_FORWARDED,
2434 			CAMEL_MESSAGE_FORWARDED);
2435 }
2436 
2437 static void
update_forwarded_flags_cb(EMsgComposer * composer,CamelMimeMessage * message,EActivity * activity,ForwardData * data)2438 update_forwarded_flags_cb (EMsgComposer *composer,
2439 			   CamelMimeMessage *message,
2440 			   EActivity *activity,
2441                            ForwardData *data)
2442 {
2443 	if (data && data->uids && data->folder)
2444 		g_ptr_array_foreach (
2445 			data->uids, real_update_forwarded_flag, data->folder);
2446 }
2447 
2448 static void
emu_set_source_headers(EMsgComposer * composer,CamelFolder * folder,const gchar * message_uid,guint32 flags)2449 emu_set_source_headers (EMsgComposer *composer,
2450 			CamelFolder *folder,
2451 			const gchar *message_uid,
2452 			guint32 flags)
2453 {
2454 	gchar *source_folder_uri = NULL;
2455 	gchar *source_message_uid = NULL;
2456 
2457 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2458 
2459 	if (!folder || !message_uid)
2460 		return;
2461 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
2462 
2463 	em_utils_get_real_folder_uri_and_message_uid (folder, message_uid,
2464 		&source_folder_uri, &source_message_uid);
2465 
2466 	if (!source_message_uid)
2467 		source_message_uid = g_strdup (message_uid);
2468 
2469 	if (source_folder_uri && source_message_uid)
2470 		e_msg_composer_set_source_headers (composer, source_folder_uri, source_message_uid, flags);
2471 
2472 	g_free (source_folder_uri);
2473 	g_free (source_message_uid);
2474 }
2475 
2476 static void
setup_forward_attached_callbacks(EMsgComposer * composer,CamelFolder * folder,GPtrArray * uids)2477 setup_forward_attached_callbacks (EMsgComposer *composer,
2478                                   CamelFolder *folder,
2479                                   GPtrArray *uids)
2480 {
2481 	ForwardData *data;
2482 
2483 	if (!composer || !folder || !uids || !uids->len)
2484 		return;
2485 
2486 	if (uids->len == 1) {
2487 		emu_set_source_headers (composer, folder, uids->pdata[0], CAMEL_MESSAGE_FORWARDED);
2488 		return;
2489 	}
2490 
2491 	data = g_slice_new0 (ForwardData);
2492 	data->folder = g_object_ref (folder);
2493 	data->uids = g_ptr_array_ref (uids);
2494 
2495 	g_signal_connect (
2496 		composer, "send",
2497 		G_CALLBACK (update_forwarded_flags_cb), data);
2498 	g_signal_connect (
2499 		composer, "save-to-drafts",
2500 		G_CALLBACK (update_forwarded_flags_cb), data);
2501 
2502 	g_object_set_data_full (
2503 		G_OBJECT (composer), "forward-data", data,
2504 		(GDestroyNotify) forward_data_free);
2505 }
2506 
2507 static void
forward_non_attached(EMsgComposer * composer,CamelFolder * folder,const gchar * uid,CamelMimeMessage * message,EMailForwardStyle style)2508 forward_non_attached (EMsgComposer *composer,
2509                       CamelFolder *folder,
2510                       const gchar *uid,
2511                       CamelMimeMessage *message,
2512                       EMailForwardStyle style)
2513 {
2514 	CamelSession *session;
2515 	EMailPartList *part_list = NULL;
2516 	gchar *text, *forward, *subject;
2517 	guint32 validity_found = 0;
2518 	guint32 flags;
2519 
2520 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2521 
2522 	session = e_msg_composer_ref_session (composer);
2523 
2524 	flags = E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS |
2525 		E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
2526 	if (style == E_MAIL_FORWARD_STYLE_QUOTED)
2527 		flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE;
2528 	if (!e_content_editor_get_html_mode (e_html_editor_get_content_editor (e_msg_composer_get_editor (composer))))
2529 		flags |= E_MAIL_FORMATTER_QUOTE_FLAG_NO_FORMATTING;
2530 
2531 	/* Setup composer's From account before calling quoting_text(),
2532 	   because quoting_text() relies on that account. */
2533 	subject = mail_tool_generate_forward_subject (message);
2534 	set_up_new_composer (composer, subject, folder, message, uid, FALSE);
2535 	g_free (subject);
2536 
2537 	forward = quoting_text (QUOTING_FORWARD, composer);
2538 	text = em_utils_message_to_html_ex (session, message, forward, flags, NULL, NULL, NULL, &validity_found, &part_list);
2539 
2540 	e_msg_composer_add_attachments_from_part_list (composer, part_list, FALSE);
2541 
2542 	if (text != NULL) {
2543 		e_msg_composer_set_body_text (composer, text, TRUE);
2544 
2545 		emu_add_composer_references_from_message (composer, message);
2546 
2547 		emu_set_source_headers (composer, folder, uid, CAMEL_MESSAGE_FORWARDED);
2548 
2549 		emu_update_composers_security (composer, validity_found);
2550 		composer_set_no_change (composer);
2551 		gtk_widget_show (GTK_WIDGET (composer));
2552 
2553 		g_free (text);
2554 	}
2555 
2556 	g_clear_object (&session);
2557 	g_clear_object (&part_list);
2558 	g_free (forward);
2559 }
2560 
2561 /**
2562  * em_utils_forward_message:
2563  * @composer: an #EMsgComposer
2564  * @message: a #CamelMimeMessage to forward
2565  * @style: the forward style to use
2566  * @folder: (nullable):  a #CamelFolder, or %NULL
2567  * @uid: (nullable): the UID of %message, or %NULL
2568  *
2569  * Forwards @message in the given @style.
2570  *
2571  * If @style is #E_MAIL_FORWARD_STYLE_ATTACHED, the new message is
2572  * created as follows.  If there is more than a single message in @uids,
2573  * a multipart/digest will be constructed and attached to a new composer
2574  * window preset with the appropriate header defaults for forwarding the
2575  * first message in the list.  If only one message is to be forwarded,
2576  * it is forwarded as a simple message/rfc822 attachment.
2577  *
2578  * If @style is #E_MAIL_FORWARD_STYLE_INLINE, each message is forwarded
2579  * in its own composer window in 'inline' form.
2580  *
2581  * If @style is #E_MAIL_FORWARD_STYLE_QUOTED, each message is forwarded
2582  * in its own composer window in 'quoted' form (each line starting with
2583  * a "> ").
2584  **/
2585 void
em_utils_forward_message(EMsgComposer * composer,CamelMimeMessage * message,EMailForwardStyle style,CamelFolder * folder,const gchar * uid)2586 em_utils_forward_message (EMsgComposer *composer,
2587                           CamelMimeMessage *message,
2588                           EMailForwardStyle style,
2589                           CamelFolder *folder,
2590                           const gchar *uid)
2591 {
2592 	CamelMimePart *part;
2593 	GPtrArray *uids = NULL;
2594 	gchar *subject;
2595 
2596 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2597 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2598 
2599 	e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2600 
2601 	switch (style) {
2602 		case E_MAIL_FORWARD_STYLE_ATTACHED:
2603 		default:
2604 			part = mail_tool_make_message_attachment (message);
2605 			subject = mail_tool_generate_forward_subject (message);
2606 
2607 			if (folder && uid) {
2608 				uids = g_ptr_array_new ();
2609 				g_ptr_array_add (uids, (gpointer) uid);
2610 			}
2611 
2612 			em_utils_forward_attachment (composer, part, subject, uids ? folder : NULL, uids);
2613 
2614 			g_object_unref (part);
2615 			g_free (subject);
2616 			break;
2617 
2618 		case E_MAIL_FORWARD_STYLE_INLINE:
2619 		case E_MAIL_FORWARD_STYLE_QUOTED:
2620 			forward_non_attached (composer, folder, uid, message, style);
2621 			break;
2622 	}
2623 
2624 	if (uids)
2625 		g_ptr_array_unref (uids);
2626 }
2627 
2628 void
em_utils_forward_attachment(EMsgComposer * composer,CamelMimePart * part,const gchar * subject,CamelFolder * folder,GPtrArray * uids)2629 em_utils_forward_attachment (EMsgComposer *composer,
2630                              CamelMimePart *part,
2631                              const gchar *subject,
2632                              CamelFolder *folder,
2633                              GPtrArray *uids)
2634 {
2635 	CamelDataWrapper *content;
2636 
2637 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2638 	g_return_if_fail (CAMEL_IS_MIME_PART (part));
2639 
2640 	if (folder != NULL)
2641 		g_return_if_fail (CAMEL_IS_FOLDER (folder));
2642 
2643 	e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2644 
2645 	set_up_new_composer (composer, subject, folder, NULL, NULL, FALSE);
2646 
2647 	e_msg_composer_attach (composer, part);
2648 
2649 	content = camel_medium_get_content (CAMEL_MEDIUM (part));
2650 	if (CAMEL_IS_MIME_MESSAGE (content)) {
2651 		emu_add_composer_references_from_message (composer, CAMEL_MIME_MESSAGE (content));
2652 	} else if (CAMEL_IS_MULTIPART (content)) {
2653 		gchar *mime_type;
2654 
2655 		mime_type = camel_data_wrapper_get_mime_type (content);
2656 		if (mime_type && g_ascii_strcasecmp (mime_type, "multipart/digest") == 0) {
2657 			/* This is the way evolution forwards multiple messages as attachment */
2658 			CamelMultipart *multipart;
2659 			guint ii, nparts;
2660 
2661 			multipart = CAMEL_MULTIPART (content);
2662 			nparts = camel_multipart_get_number (multipart);
2663 
2664 			for (ii = 0; ii < nparts; ii++) {
2665 				CamelMimePart *mpart;
2666 
2667 				g_free (mime_type);
2668 
2669 				mpart = camel_multipart_get_part (multipart, ii);
2670 				mime_type = camel_data_wrapper_get_mime_type (CAMEL_DATA_WRAPPER (mpart));
2671 
2672 				if (mime_type && g_ascii_strcasecmp (mime_type, "message/rfc822") == 0) {
2673 					content = camel_medium_get_content (CAMEL_MEDIUM (mpart));
2674 
2675 					if (CAMEL_IS_MIME_MESSAGE (content))
2676 						emu_add_composer_references_from_message (composer, CAMEL_MIME_MESSAGE (content));
2677 				}
2678 			}
2679 		}
2680 
2681 		g_free (mime_type);
2682 	}
2683 
2684 	if (uids != NULL)
2685 		setup_forward_attached_callbacks (composer, folder, uids);
2686 
2687 	composer_set_no_change (composer);
2688 
2689 	gtk_widget_show (GTK_WIDGET (composer));
2690 }
2691 
2692 /**
2693  * em_utils_redirect_message:
2694  * @composer: an #EMsgComposer
2695  * @message: message to redirect
2696  *
2697  * Sets up the @composer to redirect @message (Note: only headers will be
2698  * editable). Adds Resent-From/Resent-To/etc headers.
2699  *
2700  * Since: 3.22
2701  **/
2702 void
em_utils_redirect_message(EMsgComposer * composer,CamelMimeMessage * message)2703 em_utils_redirect_message (EMsgComposer *composer,
2704                            CamelMimeMessage *message)
2705 {
2706 	ESource *source;
2707 	EShell *shell;
2708 	CamelMedium *medium;
2709 	gchar *identity_uid = NULL, *alias_name = NULL, *alias_address = NULL;
2710 
2711 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2712 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2713 
2714 	shell = e_msg_composer_get_shell (composer);
2715 	medium = CAMEL_MEDIUM (message);
2716 
2717 	/* QMail will refuse to send a message if it finds one of
2718 	 * it's Delivered-To headers in the message, so remove all
2719 	 * Delivered-To headers. Fixes bug #23635. */
2720 	while (camel_medium_get_header (medium, "Delivered-To"))
2721 		camel_medium_remove_header (medium, "Delivered-To");
2722 
2723 	while (camel_medium_get_header (medium, "Bcc"))
2724 		camel_medium_remove_header (medium, "Bcc");
2725 
2726 	while (camel_medium_get_header (medium, "Resent-Bcc"))
2727 		camel_medium_remove_header (medium, "Resent-Bcc");
2728 
2729 	source = em_composer_utils_guess_identity_source (shell, message, NULL, NULL, &alias_name, &alias_address);
2730 
2731 	if (source != NULL) {
2732 		identity_uid = e_source_dup_uid (source);
2733 		g_object_unref (source);
2734 	}
2735 
2736 	e_msg_composer_setup_redirect (composer, message, identity_uid, alias_name, alias_address, NULL);
2737 
2738 	g_free (identity_uid);
2739 	g_free (alias_name);
2740 	g_free (alias_address);
2741 
2742 	gtk_widget_show (GTK_WIDGET (composer));
2743 
2744 	composer_set_no_change (composer);
2745 }
2746 
2747 /* Replying to messages... */
2748 
2749 EDestination **
em_utils_camel_address_to_destination(CamelInternetAddress * iaddr)2750 em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
2751 {
2752 	EDestination *dest, **destv;
2753 	gint n, i, j;
2754 
2755 	if (iaddr == NULL)
2756 		return NULL;
2757 
2758 	if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0)
2759 		return NULL;
2760 
2761 	destv = g_malloc (sizeof (EDestination *) * (n + 1));
2762 	for (i = 0, j = 0; i < n; i++) {
2763 		const gchar *name, *addr;
2764 
2765 		if (camel_internet_address_get (iaddr, i, &name, &addr)) {
2766 			dest = e_destination_new ();
2767 			e_destination_set_name (dest, name);
2768 			e_destination_set_email (dest, addr);
2769 
2770 			destv[j++] = dest;
2771 		}
2772 	}
2773 
2774 	if (j == 0) {
2775 		g_free (destv);
2776 		return NULL;
2777 	}
2778 
2779 	destv[j] = NULL;
2780 
2781 	return destv;
2782 }
2783 
2784 static gchar *
emcu_construct_reply_subject(const gchar * source_subject)2785 emcu_construct_reply_subject (const gchar *source_subject)
2786 {
2787 	gchar *res;
2788 
2789 	if (source_subject) {
2790 		GSettings *settings;
2791 		gboolean skip_len = -1;
2792 
2793 		if (em_utils_is_re_in_subject (source_subject, &skip_len, NULL, NULL) && skip_len > 0)
2794 			source_subject = source_subject + skip_len;
2795 
2796 		settings = e_util_ref_settings ("org.gnome.evolution.mail");
2797 		if (g_settings_get_boolean (settings, "composer-use-localized-fwd-re")) {
2798 			/* Translators: This is a reply attribution in the message reply subject. The %s is replaced with the subject of the original message. Both 'Re'-s in the 'reply-attribution' translation context should translate into the same string, the same as the ':' separator. */
2799 			res = g_strdup_printf (C_("reply-attribution", "Re: %s"), source_subject);
2800 		} else {
2801 			/* Do not localize this string */
2802 			res = g_strdup_printf ("Re: %s", source_subject);
2803 		}
2804 		g_clear_object (&settings);
2805 	} else {
2806 		res = g_strdup ("");
2807 	}
2808 
2809 	return res;
2810 }
2811 
2812 static void
reply_setup_composer_recipients(EMsgComposer * composer,CamelInternetAddress * to,CamelInternetAddress * cc,CamelFolder * folder,const gchar * message_uid,CamelNNTPAddress * postto)2813 reply_setup_composer_recipients (EMsgComposer *composer,
2814 				 CamelInternetAddress *to,
2815 				 CamelInternetAddress *cc,
2816 				 CamelFolder *folder,
2817 				 const gchar *message_uid,
2818 				 CamelNNTPAddress *postto)
2819 {
2820 	EComposerHeaderTable *table;
2821 	EDestination **tov, **ccv;
2822 
2823 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2824 	if (to != NULL)
2825 		g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
2826 
2827 	if (cc != NULL)
2828 		g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
2829 
2830 	/* Construct the tov/ccv */
2831 	tov = em_utils_camel_address_to_destination (to);
2832 	ccv = em_utils_camel_address_to_destination (cc);
2833 
2834 	table = e_msg_composer_get_header_table (composer);
2835 	e_composer_header_table_set_destinations_to (table, tov);
2836 
2837 	/* Add destinations instead of setting, so we don't remove
2838 	 * automatic CC addresses that have already been added. */
2839 	e_composer_header_table_add_destinations_cc (table, ccv);
2840 
2841 	e_destination_freev (tov);
2842 	e_destination_freev (ccv);
2843 
2844 	/* Add post-to, if necessary */
2845 	if (postto && camel_address_length ((CamelAddress *) postto)) {
2846 		CamelFolder *use_folder = folder, *temp_folder = NULL;
2847 		gchar *store_url = NULL;
2848 		gchar *post;
2849 
2850 		if (use_folder && CAMEL_IS_VEE_FOLDER (use_folder) && message_uid) {
2851 			em_utils_get_real_folder_and_message_uid (use_folder, message_uid, &temp_folder, NULL, NULL);
2852 
2853 			if (temp_folder)
2854 				use_folder = temp_folder;
2855 		}
2856 
2857 		if (use_folder) {
2858 			CamelStore *parent_store;
2859 			CamelService *service;
2860 			CamelURL *url;
2861 
2862 			parent_store = camel_folder_get_parent_store (use_folder);
2863 
2864 			service = CAMEL_SERVICE (parent_store);
2865 			url = camel_service_new_camel_url (service);
2866 
2867 			store_url = camel_url_to_string (
2868 				url, CAMEL_URL_HIDE_ALL);
2869 			if (store_url[strlen (store_url) - 1] == '/')
2870 				store_url[strlen (store_url) - 1] = '\0';
2871 
2872 			camel_url_free (url);
2873 		}
2874 
2875 		post = camel_address_encode ((CamelAddress *) postto);
2876 		e_composer_header_table_set_post_to_base (
2877 			table, store_url ? store_url : "", post);
2878 		g_free (post);
2879 		g_free (store_url);
2880 		g_clear_object (&temp_folder);
2881 	}
2882 }
2883 
2884 static void
reply_setup_composer(EMsgComposer * composer,CamelMimeMessage * message,const gchar * identity_uid,const gchar * identity_name,const gchar * identity_address,CamelInternetAddress * to,CamelInternetAddress * cc,CamelFolder * folder,const gchar * message_uid,CamelNNTPAddress * postto)2885 reply_setup_composer (EMsgComposer *composer,
2886 		      CamelMimeMessage *message,
2887 		      const gchar *identity_uid,
2888 		      const gchar *identity_name,
2889 		      const gchar *identity_address,
2890 		      CamelInternetAddress *to,
2891 		      CamelInternetAddress *cc,
2892 		      CamelFolder *folder,
2893 		      const gchar *message_uid,
2894 		      CamelNNTPAddress *postto)
2895 {
2896 	gchar *message_id, *references;
2897 	EComposerHeaderTable *table;
2898 	CamelMedium *medium;
2899 	gchar *subject;
2900 
2901 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
2902 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2903 
2904 	e_msg_composer_set_is_reply_or_forward (composer, TRUE);
2905 
2906 	if (to != NULL)
2907 		g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
2908 
2909 	if (cc != NULL)
2910 		g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
2911 
2912 	reply_setup_composer_recipients (composer, to, cc, folder, message_uid, postto);
2913 
2914 	/* Set the subject of the new message. */
2915 	subject = emcu_construct_reply_subject (camel_mime_message_get_subject (message));
2916 
2917 	table = e_msg_composer_get_header_table (composer);
2918 	e_composer_header_table_set_subject (table, subject);
2919 	e_composer_header_table_set_identity_uid (table, identity_uid, identity_name, identity_address);
2920 
2921 	g_free (subject);
2922 
2923 	/* Add In-Reply-To and References. */
2924 
2925 	medium = CAMEL_MEDIUM (message);
2926 	message_id = camel_header_unfold (camel_medium_get_header (medium, "Message-ID"));
2927 	references = camel_header_unfold (camel_medium_get_header (medium, "References"));
2928 
2929 	if (message_id != NULL) {
2930 		gchar *reply_refs;
2931 
2932 		e_msg_composer_add_header (
2933 			composer, "In-Reply-To", message_id);
2934 
2935 		if (references)
2936 			reply_refs = g_strdup_printf (
2937 				"%s %s", references, message_id);
2938 		else
2939 			reply_refs = g_strdup (message_id);
2940 
2941 		e_msg_composer_add_header (
2942 			composer, "References", reply_refs);
2943 		g_free (reply_refs);
2944 
2945 	} else if (references != NULL) {
2946 		e_msg_composer_add_header (
2947 			composer, "References", references);
2948 	}
2949 
2950 	g_free (message_id);
2951 	g_free (references);
2952 }
2953 
2954 static gboolean
get_reply_list(CamelMimeMessage * message,CamelInternetAddress * to)2955 get_reply_list (CamelMimeMessage *message,
2956                 CamelInternetAddress *to)
2957 {
2958 	const gchar *header, *p;
2959 	gchar *addr;
2960 
2961 	/* Examples:
2962 	 *
2963 	 * List-Post: <mailto:list@host.com>
2964 	 * List-Post: <mailto:moderator@host.com?subject=list%20posting>
2965 	 * List-Post: NO (posting not allowed on this list)
2966 	 */
2967 	if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post")))
2968 		return FALSE;
2969 
2970 	while (*header == ' ' || *header == '\t')
2971 		header++;
2972 
2973 	/* check for NO */
2974 	if (!g_ascii_strncasecmp (header, "NO", 2))
2975 		return FALSE;
2976 
2977 	/* Search for the first mailto angle-bracket enclosed URL.
2978 	 * (See rfc2369, Section 2, paragraph 3 for details) */
2979 	if (!(header = camel_strstrcase (header, "<mailto:")))
2980 		return FALSE;
2981 
2982 	header += 8;
2983 
2984 	p = header;
2985 	while (*p && !strchr ("?>", *p))
2986 		p++;
2987 
2988 	addr = g_strndup (header, p - header);
2989 	camel_internet_address_add (to, NULL, addr);
2990 	g_free (addr);
2991 
2992 	return TRUE;
2993 }
2994 
2995 gboolean
em_utils_is_munged_list_message(CamelMimeMessage * message)2996 em_utils_is_munged_list_message (CamelMimeMessage *message)
2997 {
2998 	CamelInternetAddress *reply_to, *list;
2999 	gboolean result = FALSE;
3000 
3001 	reply_to = camel_mime_message_get_reply_to (message);
3002 	if (reply_to) {
3003 		list = camel_internet_address_new ();
3004 
3005 		if (get_reply_list (message, list) &&
3006 		    camel_address_length (CAMEL_ADDRESS (list)) ==
3007 		    camel_address_length (CAMEL_ADDRESS (reply_to))) {
3008 			gint i;
3009 			const gchar *r_name, *r_addr;
3010 			const gchar *l_name, *l_addr;
3011 
3012 			for (i = 0; i < camel_address_length (CAMEL_ADDRESS (list)); i++) {
3013 				if (!camel_internet_address_get (reply_to, i, &r_name, &r_addr))
3014 					break;
3015 				if (!camel_internet_address_get (list, i, &l_name, &l_addr))
3016 					break;
3017 				if (strcmp (l_addr, r_addr))
3018 					break;
3019 			}
3020 			if (i == camel_address_length (CAMEL_ADDRESS (list)))
3021 				result = TRUE;
3022 		}
3023 		g_object_unref (list);
3024 	}
3025 	return result;
3026 }
3027 
3028 static CamelInternetAddress *
get_reply_to(CamelMimeMessage * message)3029 get_reply_to (CamelMimeMessage *message)
3030 {
3031 	CamelInternetAddress *reply_to;
3032 
3033 	reply_to = camel_mime_message_get_reply_to (message);
3034 	if (reply_to) {
3035 		GSettings *settings;
3036 		gboolean ignore_list_reply_to;
3037 
3038 		settings = e_util_ref_settings ("org.gnome.evolution.mail");
3039 		ignore_list_reply_to = g_settings_get_boolean (
3040 			settings, "composer-ignore-list-reply-to");
3041 		g_object_unref (settings);
3042 
3043 		if (ignore_list_reply_to && em_utils_is_munged_list_message (message))
3044 			reply_to = NULL;
3045 	}
3046 	if (!reply_to)
3047 		reply_to = camel_mime_message_get_from (message);
3048 
3049 	return reply_to;
3050 }
3051 
3052 static void
get_reply_sender(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3053 get_reply_sender (CamelMimeMessage *message,
3054                   CamelInternetAddress *to,
3055                   CamelNNTPAddress *postto)
3056 {
3057 	CamelInternetAddress *reply_to;
3058 	CamelMedium *medium;
3059 	const gchar *posthdr = NULL;
3060 
3061 	medium = CAMEL_MEDIUM (message);
3062 
3063 	/* check whether there is a 'Newsgroups: ' header in there */
3064 	if (postto != NULL && posthdr == NULL)
3065 		posthdr = camel_medium_get_header (medium, "Followup-To");
3066 
3067 	if (postto != NULL && posthdr == NULL)
3068 		posthdr = camel_medium_get_header (medium, "Newsgroups");
3069 
3070 	if (postto != NULL && posthdr != NULL) {
3071 		camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3072 		return;
3073 	}
3074 
3075 	reply_to = get_reply_to (message);
3076 
3077 	if (reply_to != NULL) {
3078 		const gchar *name;
3079 		const gchar *addr;
3080 		gint ii = 0;
3081 
3082 		while (camel_internet_address_get (reply_to, ii++, &name, &addr))
3083 			camel_internet_address_add (to, name, addr);
3084 	}
3085 }
3086 
3087 void
em_utils_get_reply_sender(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3088 em_utils_get_reply_sender (CamelMimeMessage *message,
3089                            CamelInternetAddress *to,
3090                            CamelNNTPAddress *postto)
3091 {
3092 	get_reply_sender (message, to, postto);
3093 }
3094 
3095 static void
get_reply_from(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto)3096 get_reply_from (CamelMimeMessage *message,
3097                 CamelInternetAddress *to,
3098                 CamelNNTPAddress *postto)
3099 {
3100 	CamelInternetAddress *from;
3101 	CamelMedium *medium;
3102 	const gchar *name, *addr;
3103 	const gchar *posthdr = NULL;
3104 
3105 	medium = CAMEL_MEDIUM (message);
3106 
3107 	/* check whether there is a 'Newsgroups: ' header in there */
3108 	if (postto != NULL && posthdr == NULL)
3109 		posthdr = camel_medium_get_header (medium, "Followup-To");
3110 
3111 	if (postto != NULL && posthdr == NULL)
3112 		posthdr = camel_medium_get_header (medium, "Newsgroups");
3113 
3114 	if (postto != NULL && posthdr != NULL) {
3115 		camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3116 		return;
3117 	}
3118 
3119 	from = camel_mime_message_get_from (message);
3120 
3121 	if (from != NULL) {
3122 		gint ii = 0;
3123 
3124 		while (camel_internet_address_get (from, ii++, &name, &addr))
3125 			camel_internet_address_add (to, name, addr);
3126 	}
3127 }
3128 
3129 static void
get_reply_recipient(CamelMimeMessage * message,CamelInternetAddress * to,CamelNNTPAddress * postto,CamelInternetAddress * address)3130 get_reply_recipient (CamelMimeMessage *message,
3131                      CamelInternetAddress *to,
3132                      CamelNNTPAddress *postto,
3133                      CamelInternetAddress *address)
3134 {
3135 	CamelMedium *medium;
3136 	const gchar *posthdr = NULL;
3137 
3138 	medium = CAMEL_MEDIUM (message);
3139 
3140 	/* check whether there is a 'Newsgroups: ' header in there */
3141 	if (postto != NULL && posthdr == NULL)
3142 		posthdr = camel_medium_get_header (medium, "Followup-To");
3143 
3144 	if (postto != NULL && posthdr == NULL)
3145 		 posthdr = camel_medium_get_header (medium, "Newsgroups");
3146 
3147 	if (postto != NULL && posthdr != NULL) {
3148 		camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3149 		return;
3150 	}
3151 
3152 	if (address != NULL) {
3153 		const gchar *name;
3154 		const gchar *addr;
3155 		gint ii = 0;
3156 
3157 		while (camel_internet_address_get (address, ii++, &name, &addr))
3158 			camel_internet_address_add (to, name, addr);
3159 	}
3160 
3161 }
3162 
3163 static void
concat_unique_addrs(CamelInternetAddress * dest,CamelInternetAddress * src,GHashTable * rcpt_hash)3164 concat_unique_addrs (CamelInternetAddress *dest,
3165                      CamelInternetAddress *src,
3166                      GHashTable *rcpt_hash)
3167 {
3168 	const gchar *name, *addr;
3169 	gint i;
3170 
3171 	for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) {
3172 		if (!g_hash_table_contains (rcpt_hash, addr)) {
3173 			camel_internet_address_add (dest, name, addr);
3174 			g_hash_table_insert (rcpt_hash, g_strdup (addr), NULL);
3175 		}
3176 	}
3177 }
3178 
3179 static void
add_source_to_recipient_hash(ESourceRegistry * registry,GHashTable * rcpt_hash,const gchar * address,ESource * source,gboolean source_is_default)3180 add_source_to_recipient_hash (ESourceRegistry *registry,
3181 			      GHashTable *rcpt_hash,
3182 			      const gchar *address,
3183 			      ESource *source,
3184 			      gboolean source_is_default)
3185 {
3186 	ESource *cached_source;
3187 	gboolean insert_source;
3188 
3189 	g_return_if_fail (rcpt_hash != NULL);
3190 	g_return_if_fail (E_IS_SOURCE (source));
3191 
3192 	if (!address || !*address)
3193 		return;
3194 
3195 	cached_source = g_hash_table_lookup (rcpt_hash, address);
3196 
3197 	insert_source = source_is_default || !cached_source;
3198 
3199 	if (insert_source)
3200 		g_hash_table_insert (rcpt_hash, g_strdup (address), g_object_ref (source));
3201 }
3202 
3203 static void
unref_nonull_object(gpointer ptr)3204 unref_nonull_object (gpointer ptr)
3205 {
3206 	if (ptr)
3207 		g_object_unref (ptr);
3208 }
3209 
3210 static GHashTable *
generate_recipient_hash(ESourceRegistry * registry)3211 generate_recipient_hash (ESourceRegistry *registry)
3212 {
3213 	GHashTable *rcpt_hash;
3214 	ESource *default_source;
3215 	GList *list, *link;
3216 	const gchar *extension_name;
3217 
3218 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
3219 
3220 	rcpt_hash = g_hash_table_new_full (
3221 		camel_strcase_hash,
3222 		camel_strcase_equal,
3223 		g_free, unref_nonull_object);
3224 
3225 	default_source = e_source_registry_ref_default_mail_identity (registry);
3226 
3227 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3228 	list = e_source_registry_list_sources (registry, extension_name);
3229 
3230 	for (link = list; link != NULL; link = g_list_next (link)) {
3231 		ESource *source = E_SOURCE (link->data);
3232 		ESourceMailIdentity *extension;
3233 		GHashTable *aliases;
3234 		const gchar *address;
3235 		gboolean source_is_default;
3236 
3237 		/* No default mail identity implies there are no mail
3238 		 * identities at all and so we should never get here. */
3239 		g_warn_if_fail (default_source != NULL);
3240 
3241 		if (!e_source_registry_check_enabled (registry, source))
3242 			continue;
3243 
3244 		source_is_default = e_source_equal (source, default_source);
3245 
3246 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3247 		extension = e_source_get_extension (source, extension_name);
3248 
3249 		address = e_source_mail_identity_get_address (extension);
3250 
3251 		add_source_to_recipient_hash (registry, rcpt_hash, address, source, source_is_default);
3252 
3253 		aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
3254 		if (aliases) {
3255 			GHashTableIter iter;
3256 			gpointer key;
3257 
3258 			g_hash_table_iter_init (&iter, aliases);
3259 			while (g_hash_table_iter_next (&iter, &key, NULL)) {
3260 				address = key;
3261 
3262 				add_source_to_recipient_hash (registry, rcpt_hash, address, source, source_is_default);
3263 			}
3264 
3265 			g_hash_table_destroy (aliases);
3266 		}
3267 	}
3268 
3269 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3270 
3271 	if (default_source != NULL)
3272 		g_object_unref (default_source);
3273 
3274 	return rcpt_hash;
3275 }
3276 
3277 void
em_utils_get_reply_all(ESourceRegistry * registry,CamelMimeMessage * message,CamelInternetAddress * to,CamelInternetAddress * cc,CamelNNTPAddress * postto)3278 em_utils_get_reply_all (ESourceRegistry *registry,
3279                         CamelMimeMessage *message,
3280                         CamelInternetAddress *to,
3281                         CamelInternetAddress *cc,
3282                         CamelNNTPAddress *postto)
3283 {
3284 	CamelInternetAddress *reply_to;
3285 	CamelInternetAddress *to_addrs;
3286 	CamelInternetAddress *cc_addrs;
3287 	CamelMedium *medium;
3288 	const gchar *name, *addr;
3289 	const gchar *posthdr = NULL;
3290 	GHashTable *rcpt_hash;
3291 
3292 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
3293 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
3294 	g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
3295 	g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
3296 
3297 	medium = CAMEL_MEDIUM (message);
3298 
3299 	/* check whether there is a 'Newsgroups: ' header in there */
3300 	if (postto != NULL && posthdr == NULL)
3301 		posthdr = camel_medium_get_header (medium, "Followup-To");
3302 
3303 	if (postto != NULL && posthdr == NULL)
3304 		posthdr = camel_medium_get_header (medium, "Newsgroups");
3305 
3306 	if (postto != NULL && posthdr != NULL)
3307 		camel_address_decode (CAMEL_ADDRESS (postto), posthdr);
3308 
3309 	rcpt_hash = generate_recipient_hash (registry);
3310 
3311 	reply_to = get_reply_to (message);
3312 	to_addrs = camel_mime_message_get_recipients (
3313 		message, CAMEL_RECIPIENT_TYPE_TO);
3314 	cc_addrs = camel_mime_message_get_recipients (
3315 		message, CAMEL_RECIPIENT_TYPE_CC);
3316 
3317 	if (reply_to != NULL) {
3318 		gint ii = 0;
3319 
3320 		while (camel_internet_address_get (reply_to, ii++, &name, &addr)) {
3321 			/* Ignore references to the Reply-To address
3322 			 * in the To and Cc lists. */
3323 			if (addr && !g_hash_table_contains (rcpt_hash, addr)) {
3324 				/* In the case we are doing a Reply-To-All,
3325 				 * we do not want to include the user's email
3326 				 * address because replying to oneself is
3327 				 * kinda silly. */
3328 				camel_internet_address_add (to, name, addr);
3329 				g_hash_table_insert (rcpt_hash, g_strdup (addr), NULL);
3330 			}
3331 		}
3332 	}
3333 
3334 	concat_unique_addrs (to, to_addrs, rcpt_hash);
3335 	concat_unique_addrs (cc, cc_addrs, rcpt_hash);
3336 
3337 	/* Set as the 'To' the first 'Reply-To' address, if such exists, when no address
3338 	   had been picked (like when all addresses are configured mail accounts). */
3339 	if (reply_to &&
3340 	    camel_address_length ((CamelAddress *) to) == 0 &&
3341 	    camel_internet_address_get (reply_to, 0, &name, &addr)) {
3342 		camel_internet_address_add (to, name, addr);
3343 	}
3344 
3345 	/* Promote the first Cc: address to To: if To: is empty. */
3346 	if (camel_address_length ((CamelAddress *) to) == 0 &&
3347 	    camel_address_length ((CamelAddress *) cc) > 0) {
3348 		if (camel_internet_address_get (cc, 0, &name, &addr))
3349 			camel_internet_address_add (to, name, addr);
3350 		camel_address_remove ((CamelAddress *) cc, 0);
3351 	}
3352 
3353 	/* If To: is still empty, may we removed duplicates (i.e. ourself),
3354 	 * so add the original To if it was set. */
3355 	if (camel_address_length ((CamelAddress *) to) == 0
3356 	    && (camel_internet_address_get (to_addrs, 0, &name, &addr)
3357 		|| camel_internet_address_get (cc_addrs, 0, &name, &addr))) {
3358 		camel_internet_address_add (to, name, addr);
3359 	}
3360 
3361 	g_hash_table_destroy (rcpt_hash);
3362 }
3363 
3364 enum {
3365 	ATTRIB_UNKNOWN,
3366 	ATTRIB_CUSTOM,
3367 	ATTRIB_TIMEZONE,
3368 	ATTRIB_STRFTIME,
3369 	ATTRIB_TM_SEC,
3370 	ATTRIB_TM_MIN,
3371 	ATTRIB_TM_24HOUR,
3372 	ATTRIB_TM_12HOUR,
3373 	ATTRIB_TM_MDAY,
3374 	ATTRIB_TM_MON,
3375 	ATTRIB_TM_YEAR,
3376 	ATTRIB_TM_2YEAR,
3377 	ATTRIB_TM_WDAY, /* not actually used */
3378 	ATTRIB_TM_YDAY
3379 };
3380 
3381 typedef void		(*AttribFormatter)	(GString *str,
3382 						 const gchar *attr,
3383 						 CamelMimeMessage *message);
3384 
3385 static void
format_sender(GString * str,const gchar * attr,CamelMimeMessage * message)3386 format_sender (GString *str,
3387                const gchar *attr,
3388                CamelMimeMessage *message)
3389 {
3390 	CamelInternetAddress *sender;
3391 	const gchar *name, *addr = NULL;
3392 	gchar *tmp = NULL;
3393 
3394 	sender = camel_mime_message_get_from (message);
3395 	if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) {
3396 		name = NULL;
3397 
3398 		if (camel_internet_address_get (sender, 0, &name, &addr)) {
3399 			if (name && !*name) {
3400 				name = NULL;
3401 			} else if (name && *name == '\"') {
3402 				gint len = strlen (name);
3403 
3404 				if (len == 1) {
3405 					name = NULL;
3406 				} else if (len > 1 && name[len - 1] == '\"') {
3407 					if (len == 2) {
3408 						name = NULL;
3409 					} else {
3410 						tmp = g_strndup (name + 1, len - 2);
3411 						name = tmp;
3412 					}
3413 				}
3414 			}
3415 		}
3416 	} else {
3417 		name = _("an unknown sender");
3418 	}
3419 
3420 	if (name && !strcmp (attr, "{SenderName}")) {
3421 		g_string_append (str, name);
3422 	} else if (addr && !strcmp (attr, "{SenderEMail}")) {
3423 		g_string_append (str, addr);
3424 	} else if (name && *name) {
3425 		g_string_append (str, name);
3426 	} else if (addr) {
3427 		g_string_append (str, addr);
3428 	}
3429 
3430 	g_free (tmp);
3431 }
3432 
3433 static struct {
3434 	const gchar *name;
3435 	gint type;
3436 	struct {
3437 		const gchar *format;         /* strftime or printf format */
3438 		AttribFormatter formatter;  /* custom formatter */
3439 	} v;
3440 } attribvars[] = {
3441 	{ "{Sender}", ATTRIB_CUSTOM, { NULL, format_sender } },
3442 	{ "{SenderName}", ATTRIB_CUSTOM, { NULL, format_sender } },
3443 	{ "{SenderEMail}", ATTRIB_CUSTOM, { NULL, format_sender } },
3444 	{ "{AbbrevWeekdayName}", ATTRIB_STRFTIME, { "%a", NULL } },
3445 	{ "{WeekdayName}", ATTRIB_STRFTIME, { "%A", NULL } },
3446 	{ "{AbbrevMonthName}", ATTRIB_STRFTIME, { "%b", NULL } },
3447 	{ "{MonthName}", ATTRIB_STRFTIME, { "%B", NULL } },
3448 	{ "{AmPmUpper}", ATTRIB_STRFTIME, { "%p", NULL } },
3449 	{ "{AmPmLower}", ATTRIB_STRFTIME, { "%P", NULL } },
3450 	{ "{Day}", ATTRIB_TM_MDAY, { "%02d", NULL } },  /* %d  01-31 */
3451 	{ "{ Day}", ATTRIB_TM_MDAY, { "% 2d", NULL } },  /* %e   1-31 */
3452 	{ "{24Hour}", ATTRIB_TM_24HOUR, { "%02d", NULL } },  /* %H  00-23 */
3453 	{ "{12Hour}", ATTRIB_TM_12HOUR, { "%02d", NULL } },  /* %I  00-12 */
3454 	{ "{DayOfYear}", ATTRIB_TM_YDAY, { "%d", NULL } },  /* %j  1-366 */
3455 	{ "{Month}", ATTRIB_TM_MON, { "%02d", NULL } },  /* %m  01-12 */
3456 	{ "{Minute}", ATTRIB_TM_MIN, { "%02d", NULL } },  /* %M  00-59 */
3457 	{ "{Seconds}", ATTRIB_TM_SEC, { "%02d", NULL } },  /* %S  00-61 */
3458 	{ "{2DigitYear}", ATTRIB_TM_2YEAR, { "%02d", NULL } },  /* %y */
3459 	{ "{Year}", ATTRIB_TM_YEAR, { "%04d", NULL } },  /* %Y */
3460 	{ "{TimeZone}", ATTRIB_TIMEZONE, { "%+05d", NULL } }
3461 };
3462 
3463 gchar *
em_composer_utils_get_reply_credits(ESource * identity_source,CamelMimeMessage * message)3464 em_composer_utils_get_reply_credits (ESource *identity_source,
3465 				     CamelMimeMessage *message)
3466 {
3467 	register const gchar *inptr;
3468 	const gchar *start;
3469 	gint tzone, len, i;
3470 	gchar buf[64];
3471 	GString *str;
3472 	struct tm tm;
3473 	time_t date;
3474 	gint type;
3475 	gchar *format, *restore_lc_messages = NULL, *restore_lc_time = NULL;
3476 
3477 	emcu_prepare_attribution_locale (identity_source, &restore_lc_messages, &restore_lc_time);
3478 
3479 	format = quoting_text (QUOTING_ATTRIBUTION, NULL);
3480 	str = g_string_new ("");
3481 
3482 	date = camel_mime_message_get_date (message, &tzone);
3483 
3484 	if (date == CAMEL_MESSAGE_DATE_CURRENT) {
3485 		/* The message has no Date: header, look at Received: */
3486 		date = camel_mime_message_get_date_received (message, &tzone);
3487 	}
3488 	if (date == CAMEL_MESSAGE_DATE_CURRENT) {
3489 		/* That didn't work either, use current time */
3490 		time (&date);
3491 		tzone = 0;
3492 	} else if (tzone == 0) {
3493 		GSettings *settings;
3494 
3495 		settings = e_util_ref_settings ("org.gnome.evolution.mail");
3496 
3497 		if (g_settings_get_boolean (settings, "composer-reply-credits-utc-to-localtime")) {
3498 			struct tm local;
3499 			gint offset = 0;
3500 
3501 			e_localtime_with_offset (date, &local, &offset);
3502 
3503 			tzone = offset / 3600;
3504 			tzone = (tzone * 100) + ((offset / 60) % 60);
3505 		}
3506 
3507 		g_clear_object (&settings);
3508 	}
3509 
3510 	/* Convert to UTC */
3511 	date += (tzone / 100) * 60 * 60;
3512 	date += (tzone % 100) * 60;
3513 
3514 	gmtime_r (&date, &tm);
3515 
3516 	inptr = format;
3517 	while (*inptr != '\0') {
3518 		start = inptr;
3519 		while (*inptr && strncmp (inptr, "${", 2) != 0)
3520 			inptr++;
3521 
3522 		g_string_append_len (str, start, inptr - start);
3523 
3524 		if (*inptr == '\0')
3525 			break;
3526 
3527 		start = ++inptr;
3528 		while (*inptr && *inptr != '}')
3529 			inptr++;
3530 
3531 		if (*inptr != '}') {
3532 			/* broken translation */
3533 			g_string_append_len (str, "${", 2);
3534 			inptr = start + 1;
3535 			continue;
3536 		}
3537 
3538 		inptr++;
3539 		len = inptr - start;
3540 		type = ATTRIB_UNKNOWN;
3541 		for (i = 0; i < G_N_ELEMENTS (attribvars); i++) {
3542 			if (!strncmp (attribvars[i].name, start, len)) {
3543 				type = attribvars[i].type;
3544 				break;
3545 			}
3546 		}
3547 
3548 		switch (type) {
3549 		case ATTRIB_CUSTOM:
3550 			attribvars[i].v.formatter (
3551 				str, attribvars[i].name, message);
3552 			break;
3553 		case ATTRIB_TIMEZONE:
3554 			g_string_append_printf (
3555 				str, attribvars[i].v.format, tzone);
3556 			break;
3557 		case ATTRIB_STRFTIME:
3558 			e_utf8_strftime_match_lc_messages (
3559 				buf, sizeof (buf), attribvars[i].v.format, &tm);
3560 			g_string_append (str, buf);
3561 			break;
3562 		case ATTRIB_TM_SEC:
3563 			g_string_append_printf (
3564 				str, attribvars[i].v.format, tm.tm_sec);
3565 			break;
3566 		case ATTRIB_TM_MIN:
3567 			g_string_append_printf (
3568 				str, attribvars[i].v.format, tm.tm_min);
3569 			break;
3570 		case ATTRIB_TM_24HOUR:
3571 			g_string_append_printf (
3572 				str, attribvars[i].v.format, tm.tm_hour);
3573 			break;
3574 		case ATTRIB_TM_12HOUR:
3575 			g_string_append_printf (
3576 				str, attribvars[i].v.format,
3577 				(tm.tm_hour + 1) % 13);
3578 			break;
3579 		case ATTRIB_TM_MDAY:
3580 			g_string_append_printf (
3581 				str, attribvars[i].v.format, tm.tm_mday);
3582 			break;
3583 		case ATTRIB_TM_MON:
3584 			g_string_append_printf (
3585 				str, attribvars[i].v.format, tm.tm_mon + 1);
3586 			break;
3587 		case ATTRIB_TM_YEAR:
3588 			g_string_append_printf (
3589 				str, attribvars[i].v.format, tm.tm_year + 1900);
3590 			break;
3591 		case ATTRIB_TM_2YEAR:
3592 			g_string_append_printf (
3593 				str, attribvars[i].v.format, tm.tm_year % 100);
3594 			break;
3595 		case ATTRIB_TM_WDAY:
3596 			/* not actually used */
3597 			g_string_append_printf (
3598 				str, attribvars[i].v.format, tm.tm_wday);
3599 			break;
3600 		case ATTRIB_TM_YDAY:
3601 			g_string_append_printf (
3602 				str, attribvars[i].v.format, tm.tm_yday + 1);
3603 			break;
3604 		default:
3605 			/* Misspelled variable?  Drop the
3606 			 * format argument and continue. */
3607 			break;
3608 		}
3609 	}
3610 
3611 	emcu_restore_locale_after_attribution (restore_lc_messages, restore_lc_time);
3612 
3613 	g_free (format);
3614 
3615 	return g_string_free (str, FALSE);
3616 }
3617 
3618 static void
composer_set_body(EMsgComposer * composer,CamelMimeMessage * message,EMailReplyStyle style,EMailPartList * parts_list,EMailPartList ** out_used_part_list)3619 composer_set_body (EMsgComposer *composer,
3620                    CamelMimeMessage *message,
3621                    EMailReplyStyle style,
3622                    EMailPartList *parts_list,
3623 		   EMailPartList **out_used_part_list)
3624 {
3625 	gchar *text, *credits, *original;
3626 	ESource *identity_source;
3627 	CamelMimePart *part;
3628 	CamelSession *session;
3629 	GSettings *settings;
3630 	guint32 validity_found = 0, keep_sig_flag = 0;
3631 
3632 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
3633 	if (g_settings_get_boolean (settings, "composer-reply-keep-signature"))
3634 		keep_sig_flag = E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
3635 	g_clear_object (&settings);
3636 
3637 	session = e_msg_composer_ref_session (composer);
3638 
3639 	switch (style) {
3640 	case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
3641 		/* do nothing */
3642 		break;
3643 	case E_MAIL_REPLY_STYLE_ATTACH:
3644 		/* attach the original message as an attachment */
3645 		part = mail_tool_make_message_attachment (message);
3646 		e_msg_composer_attach (composer, part);
3647 		g_object_unref (part);
3648 		break;
3649 	case E_MAIL_REPLY_STYLE_OUTLOOK:
3650 		original = quoting_text (QUOTING_ORIGINAL, composer);
3651 		text = em_utils_message_to_html_ex (
3652 			session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS | keep_sig_flag,
3653 			parts_list, NULL, NULL, &validity_found, out_used_part_list);
3654 		e_msg_composer_set_body_text (composer, text, TRUE);
3655 		g_free (text);
3656 		g_free (original);
3657 		emu_update_composers_security (composer, validity_found);
3658 		break;
3659 
3660 	case E_MAIL_REPLY_STYLE_QUOTED:
3661 	default:
3662 		identity_source = emcu_ref_identity_source_from_composer (composer);
3663 
3664 		/* do what any sane user would want when replying... */
3665 		credits = em_composer_utils_get_reply_credits (identity_source, message);
3666 
3667 		g_clear_object (&identity_source);
3668 
3669 		text = em_utils_message_to_html_ex (
3670 			session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE | keep_sig_flag,
3671 			parts_list, NULL, NULL, &validity_found, out_used_part_list);
3672 		g_free (credits);
3673 		e_msg_composer_set_body_text (composer, text, TRUE);
3674 		g_free (text);
3675 		emu_update_composers_security (composer, validity_found);
3676 		break;
3677 	}
3678 
3679 	g_object_unref (session);
3680 }
3681 
3682 static gboolean
emcu_folder_is_inbox(CamelFolder * folder)3683 emcu_folder_is_inbox (CamelFolder *folder)
3684 {
3685 	CamelSession *session;
3686 	CamelStore *store;
3687 	gboolean is_inbox = FALSE;
3688 
3689 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
3690 
3691 	store = camel_folder_get_parent_store (folder);
3692 	if (!store)
3693 		return FALSE;
3694 
3695 	session = camel_service_ref_session (CAMEL_SERVICE (store));
3696 	if (!session)
3697 		return FALSE;
3698 
3699 	if (E_IS_MAIL_SESSION (session)) {
3700 		MailFolderCache *folder_cache;
3701 		CamelFolderInfoFlags flags = 0;
3702 
3703 		folder_cache = e_mail_session_get_folder_cache (E_MAIL_SESSION (session));
3704 		if (folder_cache && mail_folder_cache_get_folder_info_flags (
3705 			folder_cache, store, camel_folder_get_full_name (folder), &flags)) {
3706 			is_inbox = (flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX;
3707 		}
3708 	}
3709 
3710 	g_object_unref (session);
3711 
3712 	return is_inbox;
3713 }
3714 
3715 typedef struct _AltReplyContext {
3716 	EShell *shell;
3717 	EAlertSink *alert_sink;
3718 	CamelMimeMessage *source_message;
3719 	CamelFolder *folder;
3720 	gchar *message_uid;
3721 	CamelMimeMessage *new_message; /* When processed with a template */
3722 	EMailPartList *source;
3723 	EMailReplyType type;
3724 	EMailReplyStyle style;
3725 	guint32 flags;
3726 	gboolean template_preserve_subject;
3727 	EMailPartValidityFlags validity_pgp_sum;
3728 	EMailPartValidityFlags validity_smime_sum;
3729 } AltReplyContext;
3730 
3731 static void
alt_reply_context_free(gpointer ptr)3732 alt_reply_context_free (gpointer ptr)
3733 {
3734 	AltReplyContext *context = ptr;
3735 
3736 	if (context) {
3737 		g_clear_object (&context->shell);
3738 		g_clear_object (&context->alert_sink);
3739 		g_clear_object (&context->source_message);
3740 		g_clear_object (&context->folder);
3741 		g_clear_object (&context->source);
3742 		g_clear_object (&context->new_message);
3743 		g_free (context->message_uid);
3744 		g_slice_free (AltReplyContext, context);
3745 	}
3746 }
3747 
3748 static guint32
get_composer_mark_read_on_reply_flag(void)3749 get_composer_mark_read_on_reply_flag (void)
3750 {
3751 	GSettings *settings;
3752 	guint32 res = 0;
3753 
3754 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
3755 
3756 	if (g_settings_get_boolean (settings, "composer-mark-read-on-reply"))
3757 		res = CAMEL_MESSAGE_SEEN;
3758 
3759 	g_object_unref (settings);
3760 
3761 	return res;
3762 }
3763 
3764 static void
alt_reply_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3765 alt_reply_composer_created_cb (GObject *source_object,
3766 			       GAsyncResult *result,
3767 			       gpointer user_data)
3768 {
3769 	AltReplyContext *context = user_data;
3770 	EMsgComposer *composer;
3771 	GError *error = NULL;
3772 
3773 	g_return_if_fail (context != NULL);
3774 
3775 	composer = e_msg_composer_new_finish (result, &error);
3776 
3777 	if (composer) {
3778 		EContentEditor *cnt_editor;
3779 
3780 		cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
3781 
3782 		if (context->new_message) {
3783 			CamelInternetAddress *to = NULL, *cc = NULL;
3784 			CamelNNTPAddress *postto = NULL;
3785 			gboolean need_reply_all = FALSE;
3786 
3787 			if ((context->flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
3788 				e_content_editor_set_html_mode (cnt_editor, (context->flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0);
3789 			}
3790 
3791 			em_utils_edit_message (composer, context->folder, context->new_message, context->message_uid, TRUE);
3792 
3793 			if (context->type == E_MAIL_REPLY_TO_SENDER) {
3794 				/* Reply to sender */
3795 				to = camel_internet_address_new ();
3796 				if (context->folder)
3797 					postto = camel_nntp_address_new ();
3798 				get_reply_sender (context->source_message, to, postto);
3799 			} else if (context->type == E_MAIL_REPLY_TO_LIST) {
3800 				/* Reply to list */
3801 				to = camel_internet_address_new ();
3802 
3803 				if (!get_reply_list (context->source_message, to)) {
3804 					need_reply_all = TRUE;
3805 					g_clear_object (&to);
3806 				}
3807 			} else if (context->type != E_MAIL_REPLY_TO_ALL) {
3808 				g_warn_if_reached ();
3809 			}
3810 
3811 			if (context->type == E_MAIL_REPLY_TO_ALL || need_reply_all) {
3812 				/* Reply to all */
3813 				to = camel_internet_address_new ();
3814 				cc = camel_internet_address_new ();
3815 
3816 				if (context->folder)
3817 					postto = camel_nntp_address_new ();
3818 
3819 				em_utils_get_reply_all (e_shell_get_registry (context->shell), context->source_message, to, cc, postto);
3820 			}
3821 
3822 			reply_setup_composer_recipients (composer, to, cc, context->folder, context->message_uid, postto);
3823 
3824 			composer_set_no_change (composer);
3825 
3826 			g_clear_object (&to);
3827 			g_clear_object (&cc);
3828 			g_clear_object (&postto);
3829 
3830 			if (context->folder && context->message_uid) {
3831 				emu_set_source_headers (composer, context->folder, context->message_uid,
3832 					CAMEL_MESSAGE_ANSWERED | get_composer_mark_read_on_reply_flag ());
3833 			}
3834 		} else {
3835 			em_utils_reply_to_message (composer, context->source_message,
3836 				context->folder, context->message_uid, context->type, context->style,
3837 				context->source, NULL, context->flags | E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY);
3838 		}
3839 
3840 		em_composer_utils_update_security (composer, context->validity_pgp_sum, context->validity_smime_sum);
3841 	} else {
3842 		e_alert_submit (context->alert_sink, "mail-composer:failed-create-composer",
3843 			error ? error->message : _("Unknown error"), NULL);
3844 	}
3845 
3846 	alt_reply_context_free (context);
3847 	g_clear_error (&error);
3848 }
3849 
3850 static void
alt_reply_template_applied_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)3851 alt_reply_template_applied_cb (GObject *source_object,
3852 			       GAsyncResult *result,
3853 			       gpointer user_data)
3854 {
3855 	AltReplyContext *context = user_data;
3856 	GError *error = NULL;
3857 
3858 	g_return_if_fail (context != NULL);
3859 
3860 	context->new_message = e_mail_templates_apply_finish (source_object, result, &error);
3861 
3862 	if (context->new_message) {
3863 		if (context->template_preserve_subject) {
3864 			gchar *subject;
3865 
3866 			subject = emcu_construct_reply_subject (camel_mime_message_get_subject (context->source_message));
3867 			camel_mime_message_set_subject (context->new_message, subject);
3868 			g_free (subject);
3869 		}
3870 
3871 		e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context);
3872 	} else {
3873 		e_alert_submit (context->alert_sink, "mail:no-retrieve-message",
3874 			error ? error->message : _("Unknown error"), NULL);
3875 		alt_reply_context_free (context);
3876 	}
3877 
3878 	g_clear_error (&error);
3879 }
3880 
3881 static void
emcu_three_state_toggled_cb(GtkToggleButton * widget,gpointer user_data)3882 emcu_three_state_toggled_cb (GtkToggleButton *widget,
3883 			     gpointer user_data)
3884 {
3885 	glong *phandlerid = user_data;
3886 
3887 	g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
3888 	g_return_if_fail (phandlerid != NULL);
3889 
3890 	g_signal_handler_block (widget, *phandlerid);
3891 
3892 	if (gtk_toggle_button_get_inconsistent (widget) &&
3893 	    gtk_toggle_button_get_active (widget)) {
3894 		gtk_toggle_button_set_active (widget, FALSE);
3895 		gtk_toggle_button_set_inconsistent (widget, FALSE);
3896 	} else if (!gtk_toggle_button_get_active (widget)) {
3897 		gtk_toggle_button_set_inconsistent (widget, TRUE);
3898 		gtk_toggle_button_set_active (widget, FALSE);
3899 	} else {
3900 	}
3901 
3902 	g_signal_handler_unblock (widget, *phandlerid);
3903 }
3904 
3905 static void
emcu_connect_three_state_changer(GtkToggleButton * toggle_button)3906 emcu_connect_three_state_changer (GtkToggleButton *toggle_button)
3907 {
3908 	glong *phandlerid;
3909 
3910 	g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
3911 
3912 	phandlerid = g_new0 (glong, 1);
3913 
3914 	*phandlerid = g_signal_connect_data (toggle_button, "toggled",
3915 		G_CALLBACK (emcu_three_state_toggled_cb),
3916 		phandlerid, (GClosureNotify) g_free, 0);
3917 }
3918 
3919 static void
emcu_three_state_set_value(GtkToggleButton * toggle_button,EThreeState value)3920 emcu_three_state_set_value (GtkToggleButton *toggle_button,
3921 			    EThreeState value)
3922 {
3923 	g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
3924 
3925 	if (value == E_THREE_STATE_OFF) {
3926 		gtk_toggle_button_set_active (toggle_button, FALSE);
3927 		gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
3928 	} else if (value == E_THREE_STATE_ON) {
3929 		gtk_toggle_button_set_active (toggle_button, TRUE);
3930 		gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
3931 	} else {
3932 		gtk_toggle_button_set_active (toggle_button, FALSE);
3933 		gtk_toggle_button_set_inconsistent (toggle_button, TRUE);
3934 	}
3935 }
3936 
3937 static EThreeState
emcu_three_state_get_value(GtkToggleButton * toggle_button)3938 emcu_three_state_get_value (GtkToggleButton *toggle_button)
3939 {
3940 	g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), E_THREE_STATE_INCONSISTENT);
3941 
3942 	if (gtk_toggle_button_get_inconsistent (toggle_button))
3943 		return E_THREE_STATE_INCONSISTENT;
3944 	else if (gtk_toggle_button_get_active (toggle_button))
3945 		return E_THREE_STATE_ON;
3946 
3947 	return E_THREE_STATE_OFF;
3948 }
3949 
3950 static GtkComboBox *
emcu_create_templates_combo(EShell * shell,const gchar * folder_uri,const gchar * message_uid)3951 emcu_create_templates_combo (EShell *shell,
3952 			     const gchar *folder_uri,
3953 			     const gchar *message_uid)
3954 {
3955 	GtkComboBox *combo;
3956 	GtkCellRenderer *renderer;
3957 	EShellBackend *shell_backend;
3958 	EMailSession *mail_session;
3959 	EMailTemplatesStore *templates_store;
3960 	GtkTreeStore *tree_store;
3961 	GtkTreeIter found_iter;
3962 	gboolean found_message = FALSE;
3963 
3964 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
3965 	g_return_val_if_fail (E_IS_MAIL_BACKEND (shell_backend), NULL);
3966 
3967 	mail_session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
3968 	templates_store = e_mail_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session)));
3969 
3970 	tree_store = e_mail_templates_store_build_model (templates_store, folder_uri, message_uid, &found_message, &found_iter);
3971 
3972 	combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_store)));
3973 
3974 	renderer = gtk_cell_renderer_text_new ();
3975 	g_object_set (G_OBJECT (renderer),
3976 		"ellipsize", PANGO_ELLIPSIZE_END,
3977 		"single-paragraph-mode", TRUE,
3978 		NULL);
3979 
3980 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
3981 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
3982 		"text", E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME,
3983 		NULL);
3984 
3985 	g_clear_object (&templates_store);
3986 	g_clear_object (&tree_store);
3987 
3988 	if (found_message) {
3989 		gtk_combo_box_set_active_iter (combo, &found_iter);
3990 	} else {
3991 		gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE);
3992 	}
3993 
3994 	return combo;
3995 }
3996 
3997 /**
3998  * em_utils_reply_alternative:
3999  * @parent: (nullable): a parent #GtkWindow for the question dialog
4000  * @shell: an #EShell instance used to create #EMsgComposer
4001  * @alert_sink: an #EAlertSink to put any errors to
4002  * @message: a #CamelMimeMessage
4003  * @folder: (nullable): a #CamelFolder, or %NULL
4004  * @message_uid: (nullable): the UID of @message, or %NULL
4005  * @style: the reply style to use
4006  * @source: (nullable): source to inherit view settings from
4007  * @validity_pgp_sum: a bit-or of #EMailPartValidityFlags for PGP from original message part list
4008  * @validity_smime_sum: a bit-or of #EMailPartValidityFlags for S/MIME from original message part list
4009  *
4010  * This is similar to em_utils_reply_to_message(), except it asks user to
4011  * change some settings before sending. It calls em_utils_reply_to_message()
4012  * at the end for non-templated replies.
4013  *
4014  * Since: 3.30
4015  **/
4016 void
em_utils_reply_alternative(GtkWindow * parent,EShell * shell,EAlertSink * alert_sink,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,EMailReplyStyle default_style,EMailPartList * source,EMailPartValidityFlags validity_pgp_sum,EMailPartValidityFlags validity_smime_sum)4017 em_utils_reply_alternative (GtkWindow *parent,
4018 			    EShell *shell,
4019 			    EAlertSink *alert_sink,
4020 			    CamelMimeMessage *message,
4021 			    CamelFolder *folder,
4022 			    const gchar *message_uid,
4023 			    EMailReplyStyle default_style,
4024 			    EMailPartList *source,
4025 			    EMailPartValidityFlags validity_pgp_sum,
4026 			    EMailPartValidityFlags validity_smime_sum)
4027 {
4028 	GtkWidget *dialog, *widget, *style_label;
4029 	GtkBox *hbox, *vbox;
4030 	GtkRadioButton *recip_sender, *recip_list, *recip_all;
4031 	GtkLabel *sender_label, *list_label, *all_label;
4032 	GtkRadioButton *style_default, *style_attach, *style_inline, *style_quote, *style_no_quote;
4033 	GtkToggleButton *html_format;
4034 	GtkToggleButton *bottom_posting;
4035 	GtkToggleButton *top_signature;
4036 	GtkCheckButton *apply_template;
4037 	GtkComboBox *templates;
4038 	GtkCheckButton *preserve_message_subject;
4039 	PangoAttrList *attr_list;
4040 	GSettings *settings;
4041 	gchar *last_tmpl_folder_uri, *last_tmpl_message_uid, *address, *text;
4042 	gboolean can_reply_list = FALSE;
4043 	CamelInternetAddress *to, *cc;
4044 	CamelNNTPAddress *postto = NULL;
4045 	gint n_addresses;
4046 
4047 	g_return_if_fail (E_IS_SHELL (shell));
4048 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
4049 
4050 	settings = e_util_ref_settings ("org.gnome.evolution.mail");
4051 
4052 	dialog = gtk_dialog_new_with_buttons (_("Alternative Reply"), parent,
4053 		GTK_DIALOG_DESTROY_WITH_PARENT,
4054 		_("_Cancel"), GTK_RESPONSE_CANCEL,
4055 		_("_Reply"), GTK_RESPONSE_OK,
4056 		NULL);
4057 
4058 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
4059 
4060 	vbox = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
4061 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
4062 	gtk_box_set_spacing (vbox, 2);
4063 
4064 	#define add_with_indent(x) \
4065 		gtk_widget_set_margin_left (GTK_WIDGET (x), 12); \
4066 		gtk_box_pack_start (vbox, GTK_WIDGET (x), FALSE, FALSE, 0);
4067 
4068 	widget = gtk_label_new (_("Recipients:"));
4069 	g_object_set (G_OBJECT (widget),
4070 		"hexpand", FALSE,
4071 		"halign", GTK_ALIGN_START,
4072 		NULL);
4073 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4074 
4075 	attr_list = pango_attr_list_new ();
4076 	pango_attr_list_insert (attr_list, pango_attr_style_new (PANGO_STYLE_ITALIC));
4077 
4078 	#define add_with_label(wgt, lbl) G_STMT_START { \
4079 		GtkWidget *divider_label = gtk_label_new (":"); \
4080 		gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); \
4081 		hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6)); \
4082 		gtk_box_pack_start (hbox, GTK_WIDGET (wgt), FALSE, FALSE, 0); \
4083 		gtk_box_pack_start (hbox, GTK_WIDGET (divider_label), FALSE, FALSE, 0); \
4084 		gtk_box_pack_start (hbox, GTK_WIDGET (lbl), FALSE, FALSE, 0); \
4085 		e_binding_bind_property ( \
4086 			wgt, "sensitive", \
4087 			divider_label, "visible", \
4088 			G_BINDING_SYNC_CREATE); \
4089 		e_binding_bind_property ( \
4090 			wgt, "sensitive", \
4091 			lbl, "visible", \
4092 			G_BINDING_SYNC_CREATE); \
4093 		add_with_indent (hbox); } G_STMT_END
4094 
4095 	recip_sender = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4096 		NULL, _("Reply to _Sender")));
4097 	sender_label = GTK_LABEL (gtk_label_new (""));
4098 	add_with_label (recip_sender, sender_label);
4099 
4100 	recip_list = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4101 		gtk_radio_button_get_group (recip_sender), _("Reply to _List")));
4102 	list_label = GTK_LABEL (gtk_label_new (""));
4103 	add_with_label (recip_list, list_label);
4104 
4105 	recip_all = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4106 		gtk_radio_button_get_group (recip_sender), _("Reply to _All")));
4107 	all_label = GTK_LABEL (gtk_label_new (""));
4108 	add_with_label (recip_all, all_label);
4109 
4110 	#undef add_with_label
4111 
4112 	pango_attr_list_unref (attr_list);
4113 
4114 	/* One line gap between sections */
4115 	widget = gtk_label_new (" ");
4116 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4117 
4118 	style_label = gtk_label_new (_("Reply style:"));
4119 	g_object_set (G_OBJECT (style_label),
4120 		"hexpand", FALSE,
4121 		"halign", GTK_ALIGN_START,
4122 		NULL);
4123 	gtk_box_pack_start (vbox, style_label, FALSE, FALSE, 0);
4124 
4125 	style_default = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4126 		NULL, _("_Default")));
4127 	add_with_indent (style_default);
4128 
4129 	style_attach = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4130 		gtk_radio_button_get_group (style_default), _("Attach_ment")));
4131 	add_with_indent (style_attach);
4132 
4133 	style_inline = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4134 		gtk_radio_button_get_group (style_default), _("Inline (_Outlook style)")));
4135 	add_with_indent (style_inline);
4136 
4137 	style_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4138 		gtk_radio_button_get_group (style_default), _("_Quote")));
4139 	add_with_indent (style_quote);
4140 
4141 	style_no_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4142 		gtk_radio_button_get_group (style_default), _("Do _Not Quote")));
4143 	add_with_indent (style_no_quote);
4144 
4145 	/* One line gap between sections */
4146 	widget = gtk_label_new (" ");
4147 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4148 
4149 	html_format = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Format message in HTML")));
4150 	gtk_box_pack_start (vbox, GTK_WIDGET (html_format), FALSE, FALSE, 0);
4151 
4152 	bottom_posting = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Start _typing at the bottom")));
4153 	gtk_box_pack_start (vbox, GTK_WIDGET (bottom_posting), FALSE, FALSE, 0);
4154 
4155 	top_signature = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Keep signature above the original message")));
4156 	gtk_box_pack_start (vbox, GTK_WIDGET (top_signature), FALSE, FALSE, 0);
4157 
4158 	/* One line gap between sections */
4159 	widget = gtk_label_new (" ");
4160 	gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
4161 
4162 	apply_template = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Apply t_emplate")));
4163 	gtk_box_pack_start (vbox, GTK_WIDGET (apply_template), FALSE, FALSE, 0);
4164 
4165 	last_tmpl_folder_uri = g_settings_get_string (settings, "alt-reply-template-folder-uri");
4166 	last_tmpl_message_uid = g_settings_get_string (settings, "alt-reply-template-message-uid");
4167 
4168 	templates = emcu_create_templates_combo (shell, last_tmpl_folder_uri, last_tmpl_message_uid);
4169 	add_with_indent (templates);
4170 
4171 	g_free (last_tmpl_folder_uri);
4172 	g_free (last_tmpl_message_uid);
4173 
4174 	preserve_message_subject = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Preserve original message S_ubject")));
4175 	add_with_indent (preserve_message_subject);
4176 
4177 	#undef add_with_indent
4178 
4179 	gtk_widget_show_all (GTK_WIDGET (vbox));
4180 
4181 	#define populate_label_with_text(lbl, txt) \
4182 		g_object_set (G_OBJECT (lbl), \
4183 			"ellipsize", PANGO_ELLIPSIZE_END, \
4184 			"max-width-chars", 50, \
4185 			"label", txt ? txt : "", \
4186 			"tooltip-text", txt ? txt : "", \
4187 			NULL);
4188 
4189 	/* Reply to sender */
4190 	to = camel_internet_address_new ();
4191 	if (folder)
4192 		postto = camel_nntp_address_new ();
4193 	get_reply_sender (message, to, postto);
4194 
4195 	if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) {
4196 		address = camel_address_format (CAMEL_ADDRESS (postto));
4197 	} else {
4198 		address = camel_address_format (CAMEL_ADDRESS (to));
4199 	}
4200 
4201 	populate_label_with_text (sender_label, address);
4202 
4203 	g_clear_object (&postto);
4204 	g_clear_object (&to);
4205 	g_free (address);
4206 
4207 	/* Reply to list */
4208 	to = camel_internet_address_new ();
4209 
4210 	if (!get_reply_list (message, to)) {
4211 		gtk_widget_set_sensitive (GTK_WIDGET (recip_list), FALSE);
4212 	} else {
4213 		can_reply_list = TRUE;
4214 
4215 		address = camel_address_format (CAMEL_ADDRESS (to));
4216 		populate_label_with_text (list_label, address);
4217 		g_free (address);
4218 	}
4219 
4220 	g_clear_object (&to);
4221 
4222 	/* Reply to all */
4223 	to = camel_internet_address_new ();
4224 	cc = camel_internet_address_new ();
4225 
4226 	if (folder)
4227 		postto = camel_nntp_address_new ();
4228 
4229 	em_utils_get_reply_all (e_shell_get_registry (shell), message, to, cc, postto);
4230 
4231 	if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) {
4232 		n_addresses = camel_address_length (CAMEL_ADDRESS (postto));
4233 		address = camel_address_format (CAMEL_ADDRESS (postto));
4234 	} else {
4235 		camel_address_cat (CAMEL_ADDRESS (to), CAMEL_ADDRESS (cc));
4236 		n_addresses = camel_address_length (CAMEL_ADDRESS (to));
4237 		address = camel_address_format (CAMEL_ADDRESS (to));
4238 	}
4239 
4240 	text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "one recipient", "%d recipients", n_addresses), n_addresses);
4241 	populate_label_with_text (all_label, text);
4242 	gtk_widget_set_tooltip_text (GTK_WIDGET (all_label), address);
4243 
4244 	g_clear_object (&to);
4245 	g_clear_object (&cc);
4246 	g_clear_object (&postto);
4247 	g_free (address);
4248 	g_free (text);
4249 
4250 	#undef populate_label_with_text
4251 
4252 	/* Prefer reply-to-list */
4253 	if (g_settings_get_boolean (settings, "composer-group-reply-to-list")) {
4254 		if (can_reply_list)
4255 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE);
4256 		else if (n_addresses > 1)
4257 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE);
4258 		else
4259 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE);
4260 
4261 	/* Prefer reply-to-all */
4262 	} else {
4263 		if (n_addresses > 1)
4264 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE);
4265 		else if (can_reply_list)
4266 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE);
4267 		else
4268 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE);
4269 	}
4270 
4271 	switch (g_settings_get_enum (settings, "alt-reply-style")) {
4272 	case E_MAIL_REPLY_STYLE_UNKNOWN:
4273 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_default), TRUE);
4274 		break;
4275 	case E_MAIL_REPLY_STYLE_QUOTED:
4276 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_quote), TRUE);
4277 		break;
4278 	case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE:
4279 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_no_quote), TRUE);
4280 		break;
4281 	case E_MAIL_REPLY_STYLE_ATTACH:
4282 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_attach), TRUE);
4283 		break;
4284 	case E_MAIL_REPLY_STYLE_OUTLOOK:
4285 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_inline), TRUE);
4286 		break;
4287 	}
4288 
4289 	emcu_three_state_set_value (html_format, g_settings_get_enum (settings, "alt-reply-html-format"));
4290 	emcu_three_state_set_value (bottom_posting, g_settings_get_enum (settings, "alt-reply-start-bottom"));
4291 	emcu_three_state_set_value (top_signature, g_settings_get_enum (settings, "alt-reply-top-signature"));
4292 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), g_settings_get_boolean (settings, "alt-reply-template-apply"));
4293 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preserve_message_subject), g_settings_get_boolean (settings, "alt-reply-template-preserve-subject"));
4294 
4295 	if (!gtk_widget_get_sensitive (GTK_WIDGET (templates)))
4296 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), FALSE);
4297 
4298 	emcu_connect_three_state_changer (html_format);
4299 	emcu_connect_three_state_changer (bottom_posting);
4300 	emcu_connect_three_state_changer (top_signature);
4301 
4302 	e_binding_bind_property (
4303 		apply_template, "active",
4304 		templates, "sensitive",
4305 		G_BINDING_SYNC_CREATE);
4306 
4307 	e_binding_bind_property (
4308 		apply_template, "active",
4309 		preserve_message_subject, "sensitive",
4310 		G_BINDING_SYNC_CREATE);
4311 
4312 	/* Enable the 'Reply Style' section only if not using Template */
4313 	e_binding_bind_property (
4314 		apply_template, "active",
4315 		style_label, "sensitive",
4316 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4317 
4318 	e_binding_bind_property (
4319 		apply_template, "active",
4320 		style_default, "sensitive",
4321 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4322 
4323 	e_binding_bind_property (
4324 		apply_template, "active",
4325 		style_attach, "sensitive",
4326 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4327 
4328 	e_binding_bind_property (
4329 		apply_template, "active",
4330 		style_inline, "sensitive",
4331 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4332 
4333 	e_binding_bind_property (
4334 		apply_template, "active",
4335 		style_quote, "sensitive",
4336 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4337 
4338 	e_binding_bind_property (
4339 		apply_template, "active",
4340 		style_no_quote, "sensitive",
4341 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4342 
4343 	/* Similarly with other options, which don't work when using Templates */
4344 	e_binding_bind_property (
4345 		apply_template, "active",
4346 		bottom_posting, "sensitive",
4347 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4348 
4349 	e_binding_bind_property (
4350 		apply_template, "active",
4351 		top_signature, "sensitive",
4352 		G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
4353 
4354 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
4355 		GtkTreeIter iter;
4356 		AltReplyContext *context;
4357 		EThreeState three_state;
4358 		CamelFolder *template_folder = NULL;
4359 		gchar *template_message_uid = NULL;
4360 
4361 		context = g_slice_new0 (AltReplyContext);
4362 		context->shell = g_object_ref (shell);
4363 		context->alert_sink = g_object_ref (alert_sink);
4364 		context->source_message = g_object_ref (message);
4365 		context->folder = folder ? g_object_ref (folder) : NULL;
4366 		context->source = source ? g_object_ref (source) : NULL;
4367 		context->message_uid = g_strdup (message_uid);
4368 		context->style = E_MAIL_REPLY_STYLE_UNKNOWN;
4369 		context->flags = E_MAIL_REPLY_FLAG_FORCE_STYLE;
4370 		context->validity_pgp_sum = validity_pgp_sum;
4371 		context->validity_smime_sum = validity_smime_sum;
4372 
4373 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_quote)))
4374 			context->style = E_MAIL_REPLY_STYLE_QUOTED;
4375 		else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_no_quote)))
4376 			context->style = E_MAIL_REPLY_STYLE_DO_NOT_QUOTE;
4377 		else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_attach)))
4378 			context->style = E_MAIL_REPLY_STYLE_ATTACH;
4379 		else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_inline)))
4380 			context->style = E_MAIL_REPLY_STYLE_OUTLOOK;
4381 		else
4382 			context->flags = context->flags & (~E_MAIL_REPLY_FLAG_FORCE_STYLE);
4383 
4384 		three_state = emcu_three_state_get_value (html_format);
4385 		g_settings_set_enum (settings, "alt-reply-html-format", three_state);
4386 
4387 		if (three_state == E_THREE_STATE_ON)
4388 			context->flags |= E_MAIL_REPLY_FLAG_FORMAT_HTML;
4389 		else if (three_state == E_THREE_STATE_OFF)
4390 			context->flags |= E_MAIL_REPLY_FLAG_FORMAT_PLAIN;
4391 
4392 		three_state = emcu_three_state_get_value (bottom_posting);
4393 		g_settings_set_enum (settings, "alt-reply-start-bottom", three_state);
4394 
4395 		if (three_state == E_THREE_STATE_ON)
4396 			context->flags |= E_MAIL_REPLY_FLAG_BOTTOM_POSTING;
4397 		else if (three_state == E_THREE_STATE_OFF)
4398 			context->flags |= E_MAIL_REPLY_FLAG_TOP_POSTING;
4399 
4400 		three_state = emcu_three_state_get_value (top_signature);
4401 		g_settings_set_enum (settings, "alt-reply-top-signature", three_state);
4402 
4403 		if (three_state == E_THREE_STATE_ON)
4404 			context->flags |= E_MAIL_REPLY_FLAG_TOP_SIGNATURE;
4405 		else if (three_state == E_THREE_STATE_OFF)
4406 			context->flags |= E_MAIL_REPLY_FLAG_BOTTOM_SIGNATURE;
4407 
4408 		g_settings_set_enum (settings, "alt-reply-style", context->style);
4409 		g_settings_set_boolean (settings, "alt-reply-template-apply", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template)));
4410 		g_settings_set_boolean (settings, "alt-reply-template-preserve-subject", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject)));
4411 
4412 		if (gtk_combo_box_get_active_iter (templates, &iter)) {
4413 			gtk_tree_model_get (gtk_combo_box_get_model (templates), &iter,
4414 				E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER, &template_folder,
4415 				E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID, &template_message_uid,
4416 				-1);
4417 		}
4418 
4419 		if (template_folder) {
4420 			gchar *folder_uri;
4421 
4422 			folder_uri = e_mail_folder_uri_from_folder (template_folder);
4423 			g_settings_set_string (settings, "alt-reply-template-folder-uri", folder_uri ? folder_uri : "");
4424 			g_free (folder_uri);
4425 		}
4426 
4427 		g_settings_set_string (settings, "alt-reply-template-message-uid", template_message_uid ? template_message_uid : "");
4428 
4429 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_sender)))
4430 			context->type = E_MAIL_REPLY_TO_SENDER;
4431 		else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_list)))
4432 			context->type = E_MAIL_REPLY_TO_LIST;
4433 		else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_all)))
4434 			context->type = E_MAIL_REPLY_TO_ALL;
4435 		else
4436 			g_warn_if_reached ();
4437 
4438 		if (context->style == E_MAIL_REPLY_STYLE_UNKNOWN)
4439 			context->style = default_style;
4440 
4441 		context->template_preserve_subject = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject));
4442 
4443 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template))) {
4444 			e_mail_templates_apply (context->source_message, context->folder, message_uid, template_folder, template_message_uid,
4445 				NULL, alt_reply_template_applied_cb, context);
4446 
4447 		} else {
4448 			e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context);
4449 		}
4450 
4451 		g_clear_object (&template_folder);
4452 		g_free (template_message_uid);
4453 	}
4454 
4455 	gtk_widget_destroy (dialog);
4456 	g_clear_object (&settings);
4457 }
4458 
4459 static void
em_utils_update_by_reply_flags(EContentEditor * cnt_editor,guint32 reply_flags)4460 em_utils_update_by_reply_flags (EContentEditor *cnt_editor,
4461 				guint32 reply_flags) /* EMailReplyFlags */
4462 {
4463 	if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_POSTING | E_MAIL_REPLY_FLAG_BOTTOM_POSTING)) != 0) {
4464 		e_content_editor_set_start_bottom (cnt_editor,
4465 			(reply_flags & E_MAIL_REPLY_FLAG_TOP_POSTING) != 0 ?
4466 			E_THREE_STATE_OFF : E_THREE_STATE_ON);
4467 	}
4468 
4469 	if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_SIGNATURE | E_MAIL_REPLY_FLAG_BOTTOM_SIGNATURE)) != 0) {
4470 		e_content_editor_set_top_signature (cnt_editor,
4471 			(reply_flags & E_MAIL_REPLY_FLAG_TOP_SIGNATURE) != 0 ?
4472 			E_THREE_STATE_ON : E_THREE_STATE_OFF);
4473 	}
4474 }
4475 
4476 /**
4477  * em_utils_reply_to_message:
4478  * @composer: an #EMsgComposer
4479  * @message: a #CamelMimeMessage
4480  * @folder: a #CamelFolder, or %NULL
4481  * @message_uid: the UID of @message, or %NULL
4482  * @type: the type of reply to create
4483  * @style: the reply style to use
4484  * @source: source to inherit view settings from
4485  * @address: used for E_MAIL_REPLY_TO_RECIPIENT @type
4486  * @reply_flags: bit-or of #EMailReplyFlags
4487  *
4488  * Creates a new composer ready to reply to @message.
4489  *
4490  * @folder and @message_uid may be supplied in order to update the message
4491  * flags once it has been replied to.
4492  **/
4493 void
em_utils_reply_to_message(EMsgComposer * composer,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid,EMailReplyType type,EMailReplyStyle style,EMailPartList * parts_list,CamelInternetAddress * address,EMailReplyFlags reply_flags)4494 em_utils_reply_to_message (EMsgComposer *composer,
4495                            CamelMimeMessage *message,
4496                            CamelFolder *folder,
4497                            const gchar *message_uid,
4498                            EMailReplyType type,
4499                            EMailReplyStyle style,
4500                            EMailPartList *parts_list,
4501 			   CamelInternetAddress *address,
4502 			   EMailReplyFlags reply_flags)
4503 {
4504 	ESourceRegistry *registry;
4505 	CamelInternetAddress *to, *cc;
4506 	CamelNNTPAddress *postto = NULL;
4507 	EShell *shell;
4508 	ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT;
4509 	ESource *source;
4510 	EMailPartList *used_part_list = NULL;
4511 	EContentEditor *cnt_editor;
4512 	gchar *identity_uid = NULL, *identity_name = NULL, *identity_address = NULL;
4513 	guint32 flags;
4514 
4515 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4516 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
4517 
4518 	cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
4519 
4520 	if ((reply_flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
4521 		e_content_editor_set_html_mode (cnt_editor, (reply_flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0);
4522 	}
4523 
4524 	em_utils_update_by_reply_flags (cnt_editor, reply_flags);
4525 
4526 	to = camel_internet_address_new ();
4527 	cc = camel_internet_address_new ();
4528 
4529 	shell = e_msg_composer_get_shell (composer);
4530 	registry = e_shell_get_registry (shell);
4531 
4532 	if (type == E_MAIL_REPLY_TO_SENDER &&
4533 	    !(reply_flags & E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY) &&
4534 	    em_utils_sender_is_user (registry, message, TRUE)) {
4535 		type = E_MAIL_REPLY_TO_ALL;
4536 	}
4537 
4538 	source = em_composer_utils_guess_identity_source (shell, message, folder, message_uid, &identity_name, &identity_address);
4539 
4540 	if (source != NULL) {
4541 		identity_uid = e_source_dup_uid (source);
4542 		if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) &&
4543 		    e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
4544 			ESourceMailComposition *extension;
4545 
4546 			extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
4547 			prefer_reply_style = e_source_mail_composition_get_reply_style (extension);
4548 		}
4549 
4550 		g_object_unref (source);
4551 	}
4552 
4553 	flags = CAMEL_MESSAGE_ANSWERED | get_composer_mark_read_on_reply_flag ();
4554 
4555 	if (!address && (type == E_MAIL_REPLY_TO_FROM || type == E_MAIL_REPLY_TO_SENDER) &&
4556 	    folder && !emcu_folder_is_inbox (folder) && em_utils_folder_is_sent (registry, folder))
4557 		type = E_MAIL_REPLY_TO_ALL;
4558 
4559 	switch (type) {
4560 	case E_MAIL_REPLY_TO_FROM:
4561 		if (folder)
4562 			postto = camel_nntp_address_new ();
4563 
4564 		get_reply_from (message, to, postto);
4565 		break;
4566 	case E_MAIL_REPLY_TO_RECIPIENT:
4567 		if (folder)
4568 			postto = camel_nntp_address_new ();
4569 
4570 		get_reply_recipient (message, to, postto, address);
4571 		break;
4572 	case E_MAIL_REPLY_TO_SENDER:
4573 		if (folder)
4574 			postto = camel_nntp_address_new ();
4575 
4576 		get_reply_sender (message, to, postto);
4577 		break;
4578 	case E_MAIL_REPLY_TO_LIST:
4579 		flags |= CAMEL_MESSAGE_ANSWERED_ALL;
4580 		if (get_reply_list (message, to))
4581 			break;
4582 		/* falls through */
4583 	case E_MAIL_REPLY_TO_ALL:
4584 		flags |= CAMEL_MESSAGE_ANSWERED_ALL;
4585 		if (folder)
4586 			postto = camel_nntp_address_new ();
4587 
4588 		em_utils_get_reply_all (registry, message, to, cc, postto);
4589 		break;
4590 	}
4591 
4592 	reply_setup_composer (composer, message, identity_uid, identity_name, identity_address, to, cc, folder, message_uid, postto);
4593 
4594 	if (postto)
4595 		g_object_unref (postto);
4596 	g_object_unref (to);
4597 	g_object_unref (cc);
4598 
4599 	/* If there was no send-account override */
4600 	if (!identity_uid) {
4601 		EComposerHeaderTable *header_table;
4602 		gchar *used_identity_uid;
4603 
4604 		header_table = e_msg_composer_get_header_table (composer);
4605 		used_identity_uid = e_composer_header_table_dup_identity_uid (header_table, NULL, NULL);
4606 
4607 		if (used_identity_uid) {
4608 			source = e_source_registry_ref_source (registry, used_identity_uid);
4609 			if (source) {
4610 				if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) &&
4611 				    e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
4612 					ESourceMailComposition *extension;
4613 
4614 					extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
4615 					prefer_reply_style = e_source_mail_composition_get_reply_style (extension);
4616 				}
4617 
4618 				g_object_unref (source);
4619 			}
4620 		}
4621 
4622 		g_free (used_identity_uid);
4623 	}
4624 
4625 	switch (prefer_reply_style) {
4626 		case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT:
4627 			/* Do nothing, keep the passed-in reply style. */
4628 			break;
4629 		case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_QUOTED:
4630 			style = E_MAIL_REPLY_STYLE_QUOTED;
4631 			break;
4632 		case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DO_NOT_QUOTE:
4633 			style = E_MAIL_REPLY_STYLE_DO_NOT_QUOTE;
4634 			break;
4635 		case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_ATTACH:
4636 			style = E_MAIL_REPLY_STYLE_ATTACH;
4637 			break;
4638 		case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_OUTLOOK:
4639 			style = E_MAIL_REPLY_STYLE_OUTLOOK;
4640 			break;
4641 	}
4642 
4643 	composer_set_body (composer, message, style, parts_list, &used_part_list);
4644 
4645 	e_msg_composer_add_attachments_from_part_list (composer, used_part_list, TRUE);
4646 	g_clear_object (&used_part_list);
4647 
4648 	if (folder)
4649 		emu_set_source_headers (composer, folder, message_uid, flags);
4650 
4651 	/* because some reply types can change recipients after the composer is populated */
4652 	em_utils_apply_send_account_override_to_composer (composer, folder);
4653 
4654 	/* This is required to be done (also) at the end */
4655 	em_utils_update_by_reply_flags (cnt_editor, reply_flags);
4656 
4657 	composer_set_no_change (composer);
4658 
4659 	gtk_widget_show (GTK_WIDGET (composer));
4660 
4661 	g_free (identity_uid);
4662 	g_free (identity_name);
4663 	g_free (identity_address);
4664 }
4665 
4666 static void
post_header_clicked_cb(EComposerPostHeader * header,EMailSession * session)4667 post_header_clicked_cb (EComposerPostHeader *header,
4668                         EMailSession *session)
4669 {
4670 	GtkTreeSelection *selection;
4671 	EMFolderSelector *selector;
4672 	EMFolderTreeModel *model;
4673 	EMFolderTree *folder_tree;
4674 	GtkWidget *dialog;
4675 	GList *list;
4676 	const gchar *caption;
4677 
4678 	/* FIXME Limit the folder tree to the NNTP account? */
4679 	model = em_folder_tree_model_get_default ();
4680 
4681 	dialog = em_folder_selector_new (
4682 		/* FIXME GTK_WINDOW (composer) */ NULL, model);
4683 
4684 	gtk_window_set_title (GTK_WINDOW (dialog), _("Posting destination"));
4685 
4686 	selector = EM_FOLDER_SELECTOR (dialog);
4687 	em_folder_selector_set_can_create (selector, TRUE);
4688 
4689 	caption = _("Choose folders to post the message to.");
4690 	em_folder_selector_set_caption (selector, caption);
4691 
4692 	folder_tree = em_folder_selector_get_folder_tree (selector);
4693 
4694 	em_folder_tree_set_excluded (
4695 		folder_tree,
4696 		EMFT_EXCLUDE_NOSELECT |
4697 		EMFT_EXCLUDE_VIRTUAL |
4698 		EMFT_EXCLUDE_VTRASH);
4699 
4700 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
4701 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
4702 
4703 	list = e_composer_post_header_get_folders (header);
4704 	em_folder_tree_set_selected_list (folder_tree, list, FALSE);
4705 	g_list_foreach (list, (GFunc) g_free, NULL);
4706 	g_list_free (list);
4707 
4708 	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) {
4709 		/* Prevent the header's "custom" flag from being reset,
4710 		 * which is what the default method will do next. */
4711 		g_signal_stop_emission_by_name (header, "clicked");
4712 		goto exit;
4713 	}
4714 
4715 	list = em_folder_tree_get_selected_uris (folder_tree);
4716 	e_composer_post_header_set_folders (header, list);
4717 	g_list_foreach (list, (GFunc) g_free, NULL);
4718 	g_list_free (list);
4719 
4720 exit:
4721 	gtk_widget_destroy (dialog);
4722 }
4723 
4724 /**
4725  * em_configure_new_composer:
4726  * @composer: a newly created #EMsgComposer
4727  *
4728  * Integrates a newly created #EMsgComposer into the mail backend.  The
4729  * composer can't link directly to the mail backend without introducing
4730  * circular library dependencies, so this function finishes configuring
4731  * things the #EMsgComposer instance can't do itself.
4732  **/
4733 void
em_configure_new_composer(EMsgComposer * composer,EMailSession * session)4734 em_configure_new_composer (EMsgComposer *composer,
4735                            EMailSession *session)
4736 {
4737 	EComposerHeaderTable *table;
4738 	EComposerHeaderType header_type;
4739 	EComposerHeader *header;
4740 
4741 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4742 	g_return_if_fail (E_IS_MAIL_SESSION (session));
4743 
4744 	header_type = E_COMPOSER_HEADER_POST_TO;
4745 	table = e_msg_composer_get_header_table (composer);
4746 	header = e_composer_header_table_get_header (table, header_type);
4747 
4748 	g_signal_connect (
4749 		composer, "presend",
4750 		G_CALLBACK (composer_presend_check_recipients), session);
4751 
4752 	g_signal_connect (
4753 		composer, "presend",
4754 		G_CALLBACK (composer_presend_check_identity), session);
4755 
4756 	g_signal_connect (
4757 		composer, "presend",
4758 		G_CALLBACK (composer_presend_check_plugins), session);
4759 
4760 	g_signal_connect (
4761 		composer, "presend",
4762 		G_CALLBACK (composer_presend_check_subject), session);
4763 
4764 	g_signal_connect (
4765 		composer, "presend",
4766 		G_CALLBACK (composer_presend_check_unwanted_html), session);
4767 
4768 	g_signal_connect (
4769 		composer, "send",
4770 		G_CALLBACK (em_utils_composer_send_cb), session);
4771 
4772 	g_signal_connect (
4773 		composer, "save-to-drafts",
4774 		G_CALLBACK (em_utils_composer_save_to_drafts_cb), session);
4775 
4776 	g_signal_connect (
4777 		composer, "save-to-outbox",
4778 		G_CALLBACK (em_utils_composer_save_to_outbox_cb), session);
4779 
4780 	g_signal_connect (
4781 		composer, "print",
4782 		G_CALLBACK (em_utils_composer_print_cb), session);
4783 
4784 	/* Handle "Post To:" button clicks, which displays a folder tree
4785 	 * widget.  The composer doesn't know about folder tree widgets,
4786 	 * so it can't handle this itself.
4787 	 *
4788 	 * Note: This is a G_SIGNAL_RUN_LAST signal, which allows us to
4789 	 *       stop the signal emission if the user cancels or closes
4790 	 *       the folder selector dialog.  See the handler function. */
4791 	g_signal_connect (
4792 		header, "clicked",
4793 		G_CALLBACK (post_header_clicked_cb), session);
4794 }
4795 
4796 /* free returned pointer with g_object_unref(), if not NULL */
4797 ESource *
em_utils_check_send_account_override(EShell * shell,CamelMimeMessage * message,CamelFolder * folder,gchar ** out_alias_name,gchar ** out_alias_address)4798 em_utils_check_send_account_override (EShell *shell,
4799                                       CamelMimeMessage *message,
4800                                       CamelFolder *folder,
4801 				      gchar **out_alias_name,
4802 				      gchar **out_alias_address)
4803 {
4804 	EMailBackend *mail_backend;
4805 	EMailSendAccountOverride *account_override;
4806 	CamelInternetAddress *to = NULL, *cc = NULL, *bcc = NULL;
4807 	gchar *folder_uri = NULL, *account_uid, *alias_name = NULL, *alias_address = NULL;
4808 	ESource *account_source = NULL;
4809 	ESourceRegistry *source_registry;
4810 
4811 	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
4812 
4813 	if (!message && !folder)
4814 		return NULL;
4815 
4816 	if (message) {
4817 		to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
4818 		cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
4819 		bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
4820 	}
4821 
4822 	mail_backend = E_MAIL_BACKEND (e_shell_get_backend_by_name (shell, "mail"));
4823 	g_return_val_if_fail (mail_backend != NULL, NULL);
4824 
4825 	if (folder)
4826 		folder_uri = e_mail_folder_uri_from_folder (folder);
4827 
4828 	source_registry = e_shell_get_registry (shell);
4829 	account_override = e_mail_backend_get_send_account_override (mail_backend);
4830 	account_uid = e_mail_send_account_override_get_account_uid (account_override, folder_uri, to, cc, bcc, &alias_name, &alias_address);
4831 
4832 	while (account_uid) {
4833 		account_source = e_source_registry_ref_source (source_registry, account_uid);
4834 		if (account_source)
4835 			break;
4836 
4837 		/* stored send account override settings contain a reference
4838 		 * to a dropped account, thus cleanup it now */
4839 		e_mail_send_account_override_remove_for_account_uid (account_override, account_uid, alias_name, alias_address);
4840 
4841 		g_free (account_uid);
4842 		g_free (alias_name);
4843 		g_free (alias_address);
4844 
4845 		alias_name = NULL;
4846 		alias_address = NULL;
4847 
4848 		account_uid = e_mail_send_account_override_get_account_uid (account_override, folder_uri, to, cc, bcc, &alias_name, &alias_address);
4849 	}
4850 
4851 	if (out_alias_name)
4852 		*out_alias_name = alias_name;
4853 	else
4854 		g_free (alias_name);
4855 
4856 	if (out_alias_address)
4857 		*out_alias_address = alias_address;
4858 	else
4859 		g_free (alias_address);
4860 
4861 	g_free (folder_uri);
4862 	g_free (account_uid);
4863 
4864 	return account_source;
4865 }
4866 
4867 void
em_utils_apply_send_account_override_to_composer(EMsgComposer * composer,CamelFolder * folder)4868 em_utils_apply_send_account_override_to_composer (EMsgComposer *composer,
4869                                                   CamelFolder *folder)
4870 {
4871 	CamelMimeMessage *message;
4872 	EComposerHeaderTable *header_table;
4873 	EShell *shell;
4874 	ESource *source;
4875 	gchar *alias_name = NULL, *alias_address = NULL;
4876 
4877 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4878 
4879 	shell = e_msg_composer_get_shell (composer);
4880 	message = em_utils_get_composer_recipients_as_message (composer);
4881 	source = em_utils_check_send_account_override (shell, message, folder, &alias_name, &alias_address);
4882 	g_clear_object (&message);
4883 
4884 	if (!source)
4885 		return;
4886 
4887 	header_table = e_msg_composer_get_header_table (composer);
4888 	e_composer_header_table_set_identity_uid (header_table, e_source_get_uid (source), alias_name, alias_address);
4889 
4890 	g_object_unref (source);
4891 	g_free (alias_name);
4892 	g_free (alias_address);
4893 }
4894 
4895 void
em_utils_add_installed_languages(GtkComboBoxText * combo)4896 em_utils_add_installed_languages (GtkComboBoxText *combo)
4897 {
4898 	const ESupportedLocales *supported_locales;
4899 	GHashTable *locales;
4900 	GList *langs = NULL, *link;
4901 	gboolean has_en_us = FALSE;
4902 	gint ii, n_langs = 0;
4903 
4904 	g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo));
4905 
4906 	supported_locales = e_util_get_supported_locales ();
4907 	locales = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4908 
4909 	for (ii = 0; supported_locales[ii].code; ii++) {
4910 		const gchar *locale = supported_locales[ii].locale;
4911 
4912 		if (locale) {
4913 			gchar *name;
4914 
4915 			name = e_util_get_language_name (locale);
4916 
4917 			if (name && *name) {
4918 				g_hash_table_insert (locales, name, (gpointer) locale);
4919 			} else {
4920 				g_free (name);
4921 				g_hash_table_insert (locales, g_strdup (locale), (gpointer) locale);
4922 			}
4923 
4924 			has_en_us = has_en_us || g_strcmp0 (locale, "en_US") == 0;
4925 		}
4926 	}
4927 
4928 	if (!has_en_us) {
4929 		const gchar *locale = "C";
4930 		gchar *name = e_util_get_language_name ("en_US");
4931 
4932 		if (name && *name) {
4933 			g_hash_table_insert (locales, name, (gpointer) locale);
4934 		} else {
4935 			g_free (name);
4936 			g_hash_table_insert (locales, g_strdup ("en_US"), (gpointer) locale);
4937 		}
4938 	}
4939 
4940 	langs = g_hash_table_get_keys (locales);
4941 	langs = g_list_sort (langs, (GCompareFunc) g_utf8_collate);
4942 
4943 	for (link = langs; link; link = g_list_next (link)) {
4944 		const gchar *lang_name = link->data;
4945 
4946 		if (lang_name) {
4947 			const gchar *locale = g_hash_table_lookup (locales, lang_name);
4948 
4949 			gtk_combo_box_text_append (combo, locale, lang_name);
4950 			n_langs++;
4951 		}
4952 	}
4953 
4954 	g_hash_table_destroy (locales);
4955 	g_list_free (langs);
4956 
4957 	if (n_langs > 10)
4958 		gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), 5);
4959 }
4960 
4961 void
em_composer_utils_update_security(EMsgComposer * composer,EMailPartValidityFlags validity_pgp_sum,EMailPartValidityFlags validity_smime_sum)4962 em_composer_utils_update_security (EMsgComposer *composer,
4963 				   EMailPartValidityFlags validity_pgp_sum,
4964 				   EMailPartValidityFlags validity_smime_sum)
4965 {
4966 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
4967 
4968 	if (validity_pgp_sum != 0 || validity_smime_sum != 0) {
4969 		GtkToggleAction *action;
4970 		GSettings *settings;
4971 		gboolean sign_reply;
4972 
4973 		settings = e_util_ref_settings ("org.gnome.evolution.mail");
4974 		sign_reply = g_settings_get_boolean (settings, "composer-sign-reply-if-signed");
4975 		g_object_unref (settings);
4976 
4977 		if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) {
4978 			if (sign_reply && (validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
4979 				action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer));
4980 				gtk_toggle_action_set_active (action, TRUE);
4981 			}
4982 
4983 			if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
4984 				action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer));
4985 				gtk_toggle_action_set_active (action, TRUE);
4986 			}
4987 		}
4988 
4989 		if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) {
4990 			if (sign_reply && (validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
4991 				action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer));
4992 				gtk_toggle_action_set_active (action, TRUE);
4993 			}
4994 
4995 			if ((validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
4996 				action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer));
4997 				gtk_toggle_action_set_active (action, TRUE);
4998 			}
4999 		}
5000 	}
5001 }
5002 
5003 void
em_composer_utils_update_security_from_part_list(EMsgComposer * composer,EMailPartList * part_list)5004 em_composer_utils_update_security_from_part_list (EMsgComposer *composer,
5005 						  EMailPartList *part_list)
5006 {
5007 	EMailPartValidityFlags validity_pgp_sum = 0;
5008 	EMailPartValidityFlags validity_smime_sum = 0;
5009 
5010 	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
5011 
5012 	if (!part_list)
5013 		return;
5014 
5015 	e_mail_part_list_sum_validity (part_list, &validity_pgp_sum, &validity_smime_sum);
5016 	em_composer_utils_update_security (composer, validity_pgp_sum, validity_smime_sum);
5017 }
5018