1 /*
2  * e-mail-display.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  *
17  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18  *
19  */
20 
21 #include "evolution-config.h"
22 
23 #include <glib/gi18n.h>
24 #include <glib/gstdio.h>
25 
26 #include <gdk/gdk.h>
27 #include <camel/camel.h>
28 
29 #include <em-format/e-mail-extension-registry.h>
30 #include <em-format/e-mail-formatter-enumtypes.h>
31 #include <em-format/e-mail-formatter-extension.h>
32 #include <em-format/e-mail-formatter-print.h>
33 #include <em-format/e-mail-part-attachment.h>
34 #include <em-format/e-mail-part-utils.h>
35 
36 #include "shell/e-shell-utils.h"
37 
38 #include "e-cid-request.h"
39 #include "e-http-request.h"
40 #include "e-mail-display-popup-extension.h"
41 #include "e-mail-notes.h"
42 #include "e-mail-request.h"
43 #include "e-mail-ui-session.h"
44 #include "em-composer-utils.h"
45 #include "em-utils.h"
46 
47 #include "e-mail-display.h"
48 
49 #define d(x)
50 
51 #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
52 	(G_TYPE_INSTANCE_GET_PRIVATE \
53 	((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))
54 
55 typedef enum {
56 	E_ATTACHMENT_FLAG_VISIBLE	= (1 << 0),
57 	E_ATTACHMENT_FLAG_ZOOMED_TO_100	= (1 << 1)
58 } EAttachmentFlags;
59 
60 typedef enum {
61 	E_MAGIC_SPACEBAR_CAN_GO_BOTTOM	= (1 << 0),
62 	E_MAGIC_SPACEBAR_CAN_GO_TOP	= (1 << 1)
63 } EMagicSpacebarFlags;
64 
65 struct _EMailDisplayPrivate {
66 	EAttachmentStore *attachment_store;
67 	EAttachmentView *attachment_view;
68 	GHashTable *attachment_flags; /* EAttachment * ~> guint bit-or of EAttachmentFlags */
69 	guint attachment_inline_ui_id;
70 
71 	GtkActionGroup *attachment_inline_group;
72 	GtkActionGroup *attachment_accel_action_group;
73 	GtkAccelGroup *attachment_accel_group;
74 
75 	EMailPartList *part_list;
76 	EMailFormatterMode mode;
77 	EMailFormatter *formatter;
78 
79 	gboolean headers_collapsable;
80 	gboolean headers_collapsed;
81 	gboolean force_image_load;
82 
83 	GSettings *settings;
84 
85 	guint scheduled_reload;
86 
87 	GHashTable *old_settings;
88 
89 	GMutex remote_content_lock;
90 	EMailRemoteContent *remote_content;
91 	GHashTable *skipped_remote_content_sites;
92 
93 	guint32 magic_spacebar_state; /* bit-or of EMagicSpacebarFlags */
94 };
95 
96 enum {
97 	PROP_0,
98 	PROP_ATTACHMENT_STORE,
99 	PROP_ATTACHMENT_VIEW,
100 	PROP_FORMATTER,
101 	PROP_HEADERS_COLLAPSABLE,
102 	PROP_HEADERS_COLLAPSED,
103 	PROP_MODE,
104 	PROP_PART_LIST,
105 	PROP_REMOTE_CONTENT
106 };
107 
108 enum {
109 	REMOTE_CONTENT_CLICKED,
110 	LAST_SIGNAL
111 };
112 
113 static guint signals[LAST_SIGNAL];
114 static CamelDataCache *emd_global_http_cache = NULL;
115 
116 static void e_mail_display_cid_resolver_init (ECidResolverInterface *iface);
117 
118 G_DEFINE_TYPE_WITH_CODE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW,
119 	G_IMPLEMENT_INTERFACE (E_TYPE_CID_RESOLVER, e_mail_display_cid_resolver_init))
120 
121 static const gchar *ui =
122 "<ui>"
123 "  <popup name='context'>"
124 "    <placeholder name='custom-actions-1'>"
125 "      <menuitem action='add-to-address-book'/>"
126 "      <menuitem action='send-reply'/>"
127 "    </placeholder>"
128 "    <placeholder name='custom-actions-3'>"
129 "      <menu action='search-folder-menu'>"
130 "        <menuitem action='search-folder-recipient'/>"
131 "        <menuitem action='search-folder-sender'/>"
132 "      </menu>"
133 "    </placeholder>"
134 "  </popup>"
135 "</ui>";
136 
137 static GtkActionEntry mailto_entries[] = {
138 
139 	{ "add-to-address-book",
140 	  "contact-new",
141 	  N_("_Add to Address Book…"),
142 	  NULL,
143 	  NULL,  /* XXX Add a tooltip! */
144 	  NULL   /* Handled by EMailReader */ },
145 
146 	{ "search-folder-recipient",
147 	  NULL,
148 	  N_("_To This Address"),
149 	  NULL,
150 	  NULL,  /* XXX Add a tooltip! */
151 	  NULL   /* Handled by EMailReader */ },
152 
153 	{ "search-folder-sender",
154 	  NULL,
155 	  N_("_From This Address"),
156 	  NULL,
157 	  NULL,  /* XXX Add a tooltip! */
158 	  NULL   /* Handled by EMailReader */ },
159 
160 	{ "send-reply",
161 	  NULL,
162 	  N_("Send _Reply To…"),
163 	  NULL,
164 	  N_("Send a reply message to this address"),
165 	  NULL   /* Handled by EMailReader */ },
166 
167 	/*** Menus ***/
168 
169 	{ "search-folder-menu",
170 	  "folder-saved-search",
171 	  N_("Create Search _Folder"),
172 	  NULL,
173 	  NULL,
174 	  NULL }
175 };
176 
177 static const gchar *attachment_popup_ui =
178 "<ui>"
179 "  <popup name='context'>"
180 "    <placeholder name='inline-actions'>"
181 "      <menuitem action='zoom-to-100'/>"
182 "      <menuitem action='zoom-to-window'/>"
183 "      <menuitem action='show'/>"
184 "      <menuitem action='show-all'/>"
185 "      <separator/>"
186 "      <menuitem action='hide'/>"
187 "      <menuitem action='hide-all'/>"
188 "    </placeholder>"
189 "  </popup>"
190 "</ui>";
191 
192 static void
e_mail_display_claim_skipped_uri(EMailDisplay * mail_display,const gchar * uri)193 e_mail_display_claim_skipped_uri (EMailDisplay *mail_display,
194 				  const gchar *uri)
195 {
196 	SoupURI *soup_uri;
197 	const gchar *site;
198 
199 	g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display));
200 	g_return_if_fail (uri != NULL);
201 
202 	/* Do not store anything if the user doesn't want to see the notification */
203 	if (!g_settings_get_boolean (mail_display->priv->settings, "notify-remote-content"))
204 		return;
205 
206 	soup_uri = soup_uri_new (uri);
207 	if (!soup_uri)
208 		return;
209 
210 	site = soup_uri_get_host (soup_uri);
211 	if (site && *site) {
212 		g_mutex_lock (&mail_display->priv->remote_content_lock);
213 
214 		if (!g_hash_table_contains (mail_display->priv->skipped_remote_content_sites, site)) {
215 			g_hash_table_insert (mail_display->priv->skipped_remote_content_sites, g_strdup (site), NULL);
216 		}
217 
218 		g_mutex_unlock (&mail_display->priv->remote_content_lock);
219 	}
220 
221 	soup_uri_free (soup_uri);
222 }
223 
224 static void
e_mail_display_cleanup_skipped_uris(EMailDisplay * mail_display)225 e_mail_display_cleanup_skipped_uris (EMailDisplay *mail_display)
226 {
227 	g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display));
228 
229 	g_mutex_lock (&mail_display->priv->remote_content_lock);
230 	g_hash_table_remove_all (mail_display->priv->skipped_remote_content_sites);
231 	g_mutex_unlock (&mail_display->priv->remote_content_lock);
232 }
233 
234 static gboolean
e_mail_display_can_download_uri(EMailDisplay * mail_display,const gchar * uri)235 e_mail_display_can_download_uri (EMailDisplay *mail_display,
236 				 const gchar *uri)
237 {
238 	SoupURI *soup_uri;
239 	const gchar *site;
240 	gboolean can_download = FALSE;
241 	EMailRemoteContent *remote_content;
242 
243 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), FALSE);
244 	g_return_val_if_fail (uri != NULL, FALSE);
245 
246 	remote_content = e_mail_display_ref_remote_content (mail_display);
247 	if (!remote_content)
248 		return FALSE;
249 
250 	soup_uri = soup_uri_new (uri);
251 	if (!soup_uri) {
252 		g_object_unref (remote_content);
253 		return FALSE;
254 	}
255 
256 	site = soup_uri_get_host (soup_uri);
257 	if (site && *site)
258 		can_download = e_mail_remote_content_has_site (remote_content, site);
259 
260 	soup_uri_free (soup_uri);
261 
262 	if (!can_download && mail_display->priv->part_list) {
263 		CamelMimeMessage *message;
264 
265 		message = e_mail_part_list_get_message (mail_display->priv->part_list);
266 		if (message) {
267 			CamelInternetAddress *from;
268 
269 			from = camel_mime_message_get_from (message);
270 			if (from) {
271 				gint ii, len;
272 
273 				len = camel_address_length (CAMEL_ADDRESS (from));
274 				for (ii = 0; ii < len && !can_download; ii++) {
275 					const gchar *mail = NULL;
276 
277 					if (!camel_internet_address_get	(from, ii, NULL, &mail))
278 						break;
279 
280 					if (mail && *mail)
281 						can_download = e_mail_remote_content_has_mail (remote_content, mail);
282 				}
283 			}
284 		}
285 	}
286 
287 	g_object_unref (remote_content);
288 
289 	return can_download;
290 }
291 
292 static void
formatter_image_loading_policy_changed_cb(GObject * object,GParamSpec * pspec,gpointer user_data)293 formatter_image_loading_policy_changed_cb (GObject *object,
294                                            GParamSpec *pspec,
295                                            gpointer user_data)
296 {
297 	EMailDisplay *display = user_data;
298 	EMailFormatter *formatter = E_MAIL_FORMATTER (object);
299 	EImageLoadingPolicy policy;
300 
301 	policy = e_mail_formatter_get_image_loading_policy (formatter);
302 
303 	if (policy == E_IMAGE_LOADING_POLICY_ALWAYS)
304 		e_mail_display_load_images (display);
305 	else
306 		e_mail_display_reload (display);
307 }
308 
309 static void
mail_display_update_formatter_colors(EMailDisplay * display)310 mail_display_update_formatter_colors (EMailDisplay *display)
311 {
312 	EMailFormatter *formatter;
313 	GtkStateFlags state_flags;
314 
315 	formatter = display->priv->formatter;
316 	state_flags = gtk_widget_get_state_flags (GTK_WIDGET (display));
317 
318 	if (formatter != NULL)
319 		e_mail_formatter_update_style (formatter, state_flags);
320 }
321 
322 static gboolean
mail_display_process_mailto(EWebView * web_view,const gchar * mailto_uri,gpointer user_data)323 mail_display_process_mailto (EWebView *web_view,
324                              const gchar *mailto_uri,
325                              gpointer user_data)
326 {
327 	gboolean handled = FALSE;
328 
329 	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
330 	g_return_val_if_fail (mailto_uri != NULL, FALSE);
331 
332 	if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) {
333 		EShell *shell;
334 		EMailPartList *part_list;
335 
336 		part_list = E_MAIL_DISPLAY (web_view)->priv->part_list;
337 
338 		shell = e_shell_get_default ();
339 		em_utils_compose_new_message_with_mailto_and_selection (shell, mailto_uri,
340 			e_mail_part_list_get_folder (part_list),
341 			e_mail_part_list_get_message_uid (part_list));
342 
343 		handled = TRUE;
344 	}
345 
346 	return handled;
347 }
348 
349 static gboolean
decide_policy_cb(WebKitWebView * web_view,WebKitPolicyDecision * decision,WebKitPolicyDecisionType type)350 decide_policy_cb (WebKitWebView *web_view,
351                   WebKitPolicyDecision *decision,
352                   WebKitPolicyDecisionType type)
353 {
354 	WebKitNavigationPolicyDecision *navigation_decision;
355 	WebKitNavigationAction *navigation_action;
356 	WebKitURIRequest *request;
357 	const gchar *uri;
358 
359 	if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION)
360 		return FALSE;
361 
362 	navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
363 	navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
364 	request = webkit_navigation_action_get_request (navigation_action);
365 
366 	uri = webkit_uri_request_get_uri (request);
367 
368 	if (!uri || !*uri) {
369 		webkit_policy_decision_ignore (decision);
370 		return TRUE;
371 	}
372 
373 	if (g_str_has_prefix (uri, "file://")) {
374 		gchar *filename;
375 
376 		filename = g_filename_from_uri (uri, NULL, NULL);
377 
378 		if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
379 			webkit_policy_decision_ignore (decision);
380 			/* FIXME WK2 Not sure if the request will be changed there */
381 			webkit_uri_request_set_uri (request, "about:blank");
382 			g_free (filename);
383 			return TRUE;
384 		}
385 
386 		g_free (filename);
387 	}
388 
389 	if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
390 		/* do nothing, function handled the "mailto:" uri already */
391 		webkit_policy_decision_ignore (decision);
392 		return TRUE;
393 
394 	} else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
395 		/* ignore */
396 		webkit_policy_decision_ignore (decision);
397 		return TRUE;
398 
399 	} else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
400 		/* ignore */
401 		webkit_policy_decision_ignore (decision);
402 		return TRUE;
403 
404 	}
405 
406 	/* Let WebKit handle it. */
407 	return FALSE;
408 }
409 
410 static void
add_color_css_rule_for_web_view(EWebView * web_view,const gchar * iframe_id,const gchar * color_name,const gchar * color_value)411 add_color_css_rule_for_web_view (EWebView *web_view,
412 				 const gchar *iframe_id,
413 				 const gchar *color_name,
414 				 const gchar *color_value)
415 {
416 	gchar *selector;
417 	gchar *style;
418 
419 	selector = g_strconcat (".-e-mail-formatter-", color_name, NULL);
420 
421 	if (g_strstr_len (color_name, -1, "header")) {
422 		style = g_strconcat (
423 			"color: ", color_value, " !important;", NULL);
424 	} else if (g_strstr_len (color_name, -1, "frame")) {
425 		style = g_strconcat (
426 			"border-color: ", color_value, NULL);
427 	} else {
428 		style = g_strconcat (
429 			"background-color: ", color_value, " !important;", NULL);
430 	}
431 
432 	e_web_view_jsc_add_rule_into_style_sheet (
433 		WEBKIT_WEB_VIEW (web_view),
434 		iframe_id,
435 		"-e-mail-formatter-style-sheet",
436 		selector,
437 		style,
438 		e_web_view_get_cancellable (web_view));
439 
440 	g_free (style);
441 	g_free (selector);
442 }
443 
444 static void
initialize_web_view_colors(EMailDisplay * display,const gchar * iframe_id)445 initialize_web_view_colors (EMailDisplay *display,
446 			    const gchar *iframe_id)
447 {
448 	EMailFormatter *formatter;
449 	GtkTextDirection direction;
450 	const gchar *style;
451 	gint ii;
452 
453 	const gchar *color_names[] = {
454 		"body-color",
455 		"citation-color",
456 		"frame-color",
457 		"header-color",
458 		NULL
459 	};
460 
461 	formatter = e_mail_display_get_formatter (display);
462 
463 	for (ii = 0; color_names[ii]; ii++) {
464 		GdkRGBA *color = NULL;
465 		gchar *color_value;
466 
467 		g_object_get (formatter, color_names[ii], &color, NULL);
468 		color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color));
469 
470 		add_color_css_rule_for_web_view (
471 			E_WEB_VIEW (display),
472 			iframe_id,
473 			color_names[ii],
474 			color_value);
475 
476 		gdk_rgba_free (color);
477 		g_free (color_value);
478 	}
479 
480 	e_web_view_jsc_add_rule_into_style_sheet (
481 		WEBKIT_WEB_VIEW (display),
482 		iframe_id,
483 		"-e-mail-formatter-style-sheet",
484 		".-e-mail-formatter-frame-security-none",
485 		"border-width: 1px; border-style: solid",
486 		e_web_view_get_cancellable (E_WEB_VIEW (display)));
487 
488 	/* the rgba values below were copied from e-formatter-secure-button */
489 	direction = gtk_widget_get_default_direction ();
490 
491 	if (direction == GTK_TEXT_DIR_RTL)
492 		style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)";
493 	else
494 		style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(53%, 73%, 53%, 1.0)";
495 	e_web_view_jsc_add_rule_into_style_sheet (
496 		WEBKIT_WEB_VIEW (display),
497 		iframe_id,
498 		"-e-mail-formatter-style-sheet",
499 		".-e-mail-formatter-frame-security-good",
500 		style,
501 		e_web_view_get_cancellable (E_WEB_VIEW (display)));
502 
503 	if (direction == GTK_TEXT_DIR_RTL)
504 		style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)";
505 	else
506 		style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(73%, 53%, 53%, 1.0)";
507 	e_web_view_jsc_add_rule_into_style_sheet (
508 		WEBKIT_WEB_VIEW (display),
509 		iframe_id,
510 		"-e-mail-formatter-style-sheet",
511 		".-e-mail-formatter-frame-security-bad",
512 		style,
513 		e_web_view_get_cancellable (E_WEB_VIEW (display)));
514 
515 	if (direction == GTK_TEXT_DIR_RTL)
516 		style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)";
517 	else
518 		style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)";
519 	e_web_view_jsc_add_rule_into_style_sheet (
520 		WEBKIT_WEB_VIEW (display),
521 		iframe_id,
522 		"-e-mail-formatter-style-sheet",
523 		".-e-mail-formatter-frame-security-unknown",
524 		style,
525 		e_web_view_get_cancellable (E_WEB_VIEW (display)));
526 
527 	if (direction == GTK_TEXT_DIR_RTL)
528 		style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)";
529 	else
530 		style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 13%, 1.0)";
531 	e_web_view_jsc_add_rule_into_style_sheet (
532 		WEBKIT_WEB_VIEW (display),
533 		iframe_id,
534 		"-e-mail-formatter-style-sheet",
535 		".-e-mail-formatter-frame-security-need-key",
536 		style,
537 		e_web_view_get_cancellable (E_WEB_VIEW (display)));
538 }
539 
540 static void
mail_display_change_one_attachment_visibility(EMailDisplay * display,EAttachment * attachment,gboolean show,gboolean flip)541 mail_display_change_one_attachment_visibility (EMailDisplay *display,
542 					       EAttachment *attachment,
543 					       gboolean show,
544 					       gboolean flip)
545 {
546 	gchar *element_id;
547 	gchar *uri;
548 	guint flags;
549 
550 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
551 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
552 	g_return_if_fail (g_hash_table_contains (display->priv->attachment_flags, attachment));
553 
554 	flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
555 	if (flip)
556 		show = !(flags & E_ATTACHMENT_FLAG_VISIBLE);
557 
558 	if ((((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0) ? 1 : 0) == (show ? 1 : 0))
559 		return;
560 
561 	if (show)
562 		flags = flags | E_ATTACHMENT_FLAG_VISIBLE;
563 	else
564 		flags = flags & (~E_ATTACHMENT_FLAG_VISIBLE);
565 	g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
566 
567 	element_id = g_strdup_printf ("attachment-wrapper-%p", attachment);
568 	e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (display), e_web_view_get_cancellable (E_WEB_VIEW (display)),
569 		"Evo.MailDisplayShowAttachment(%s,%x);",
570 		element_id, show);
571 	g_free (element_id);
572 
573 	element_id = g_strdup_printf ("attachment-expander-img-%p", attachment);
574 	uri = g_strdup_printf ("gtk-stock://%s?size=%d", show ? "go-down" : "go-next", GTK_ICON_SIZE_BUTTON);
575 
576 	e_web_view_set_element_attribute (E_WEB_VIEW (display), element_id, NULL, "src", uri);
577 
578 	g_free (element_id);
579 	g_free (uri);
580 }
581 
582 static void
mail_display_change_attachment_visibility(EMailDisplay * display,gboolean all,gboolean show)583 mail_display_change_attachment_visibility (EMailDisplay *display,
584 					   gboolean all,
585 					   gboolean show)
586 {
587 	EAttachmentView *view;
588 	GList *attachments, *link;
589 
590 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
591 
592 	view = e_mail_display_get_attachment_view (display);
593 	g_return_if_fail (view != NULL);
594 
595 	if (all)
596 		attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
597 	else
598 		attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
599 
600 	for (link = attachments; link; link = g_list_next (link)) {
601 		EAttachment *attachment = link->data;
602 
603 		if (e_attachment_get_can_show (attachment))
604 			mail_display_change_one_attachment_visibility (display, attachment, show, FALSE);
605 	}
606 
607 	g_list_free_full (attachments, g_object_unref);
608 }
609 
610 static void
mail_attachment_change_zoom(EMailDisplay * display,gboolean to_100_percent)611 mail_attachment_change_zoom (EMailDisplay *display,
612 			     gboolean to_100_percent)
613 {
614 	EAttachmentView *view;
615 	GList *attachments, *link;
616 
617 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
618 
619 	view = e_mail_display_get_attachment_view (display);
620 	g_return_if_fail (view != NULL);
621 
622 	attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
623 
624 	for (link = attachments; link; link = g_list_next (link)) {
625 		EAttachment *attachment = link->data;
626 		gchar *element_id;
627 		const gchar *max_width;
628 		guint flags;
629 
630 		if (!E_IS_ATTACHMENT (attachment) ||
631 		    !g_hash_table_contains (display->priv->attachment_flags, attachment))
632 			continue;
633 
634 		flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
635 		if ((((flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0) ? 1 : 0) == (to_100_percent ? 1 : 0))
636 			continue;
637 
638 		if (to_100_percent)
639 			flags = flags | E_ATTACHMENT_FLAG_ZOOMED_TO_100;
640 		else
641 			flags = flags & (~E_ATTACHMENT_FLAG_ZOOMED_TO_100);
642 		g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
643 
644 		if (to_100_percent)
645 			max_width = NULL;
646 		else
647 			max_width = "100%";
648 
649 		element_id = g_strdup_printf ("attachment-wrapper-%p::child", attachment);
650 
651 		e_web_view_set_element_style_property (E_WEB_VIEW (display), element_id, "max-width", max_width);
652 
653 		g_free (element_id);
654 	}
655 
656 	g_list_free_full (attachments, g_object_unref);
657 }
658 
659 static void
action_attachment_show_cb(GtkAction * action,EMailDisplay * display)660 action_attachment_show_cb (GtkAction *action,
661 			   EMailDisplay *display)
662 {
663 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
664 
665 	mail_display_change_attachment_visibility (display, FALSE, TRUE);
666 }
667 
668 static void
action_attachment_show_all_cb(GtkAction * action,EMailDisplay * display)669 action_attachment_show_all_cb (GtkAction *action,
670 			       EMailDisplay *display)
671 {
672 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
673 
674 	mail_display_change_attachment_visibility (display, TRUE, TRUE);
675 }
676 
677 static void
action_attachment_hide_cb(GtkAction * action,EMailDisplay * display)678 action_attachment_hide_cb (GtkAction *action,
679 			   EMailDisplay *display)
680 {
681 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
682 
683 	mail_display_change_attachment_visibility (display, FALSE, FALSE);
684 }
685 
686 static void
action_attachment_hide_all_cb(GtkAction * action,EMailDisplay * display)687 action_attachment_hide_all_cb (GtkAction *action,
688 			       EMailDisplay *display)
689 {
690 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
691 
692 	mail_display_change_attachment_visibility (display, TRUE, FALSE);
693 }
694 
695 static void
action_attachment_zoom_to_100_cb(GtkAction * action,EMailDisplay * display)696 action_attachment_zoom_to_100_cb (GtkAction *action,
697 				  EMailDisplay *display)
698 {
699 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
700 
701 	mail_attachment_change_zoom (display, TRUE);
702 }
703 
704 static void
action_attachment_zoom_to_window_cb(GtkAction * action,EMailDisplay * display)705 action_attachment_zoom_to_window_cb (GtkAction *action,
706 				     EMailDisplay *display)
707 {
708 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
709 
710 	mail_attachment_change_zoom (display, FALSE);
711 }
712 
713 static GtkActionEntry attachment_inline_entries[] = {
714 
715 	{ "hide",
716 	  NULL,
717 	  N_("_Hide"),
718 	  NULL,
719 	  NULL,  /* XXX Add a tooltip! */
720 	  G_CALLBACK (action_attachment_hide_cb) },
721 
722 	{ "hide-all",
723 	  NULL,
724 	  N_("Hid_e All"),
725 	  NULL,
726 	  NULL,  /* XXX Add a tooltip! */
727 	  G_CALLBACK (action_attachment_hide_all_cb) },
728 
729 	{ "show",
730 	  NULL,
731 	  N_("_View Inline"),
732 	  NULL,
733 	  NULL,  /* XXX Add a tooltip! */
734 	  G_CALLBACK (action_attachment_show_cb) },
735 
736 	{ "show-all",
737 	  NULL,
738 	  N_("Vie_w All Inline"),
739 	  NULL,
740 	  NULL,  /* XXX Add a tooltip! */
741 	  G_CALLBACK (action_attachment_show_all_cb) },
742 
743 	{ "zoom-to-100",
744 	  NULL,
745 	  N_("_Zoom to 100%"),
746 	  NULL,
747 	  N_("Zoom the image to its natural size"),
748 	  G_CALLBACK (action_attachment_zoom_to_100_cb) },
749 
750 	{ "zoom-to-window",
751 	  NULL,
752 	  N_("_Zoom to window"),
753 	  NULL,
754 	  N_("Zoom large images to not be wider than the window width"),
755 	  G_CALLBACK (action_attachment_zoom_to_window_cb) }
756 };
757 
758 static void
call_attachment_save_handle_error(GObject * source_object,GAsyncResult * result,gpointer user_data)759 call_attachment_save_handle_error (GObject *source_object,
760 				   GAsyncResult *result,
761 				   gpointer user_data)
762 {
763 	GtkWindow *window = user_data;
764 
765 	g_return_if_fail (E_IS_ATTACHMENT (source_object));
766 	g_return_if_fail (!window || GTK_IS_WINDOW (window));
767 
768 	e_attachment_save_handle_error (E_ATTACHMENT (source_object), result, window);
769 
770 	g_clear_object (&window);
771 }
772 
773 static void
mail_display_open_attachment(EMailDisplay * display,EAttachment * attachment)774 mail_display_open_attachment (EMailDisplay *display,
775 			      EAttachment *attachment)
776 {
777 	GAppInfo *default_app;
778 	gpointer parent;
779 
780 	parent = gtk_widget_get_toplevel (GTK_WIDGET (display));
781 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
782 
783 	/* Either open in the default application... */
784 	default_app = e_attachment_ref_default_app (attachment);
785 	if (default_app || e_util_is_running_flatpak ()) {
786 		e_attachment_open_async (
787 			attachment, default_app, (GAsyncReadyCallback)
788 			e_attachment_open_handle_error, parent);
789 
790 		g_object_unref (default_app);
791 	} else {
792 		/* ...or save it */
793 		GList *attachments;
794 		EAttachmentStore *store;
795 		GFile *destination;
796 
797 		store = e_mail_display_get_attachment_store (display);
798 		attachments = g_list_prepend (NULL, attachment);
799 
800 		destination = e_attachment_store_run_save_dialog (store, attachments, parent);
801 		if (destination) {
802 			e_attachment_save_async (
803 				attachment, destination, (GAsyncReadyCallback)
804 				call_attachment_save_handle_error, parent ? g_object_ref (parent) : NULL);
805 
806 			g_object_unref (destination);
807 		}
808 
809 		g_list_free (attachments);
810 	}
811 }
812 
813 static void
action_attachment_toggle_cb(GtkAction * action,EMailDisplay * display)814 action_attachment_toggle_cb (GtkAction *action,
815 			     EMailDisplay *display)
816 {
817 	EAttachmentStore *store;
818 	GList *attachments, *link;
819 	const gchar *name;
820 	guint index = ~0;
821 	guint len, ii;
822 
823 	name = gtk_action_get_name (action);
824 	g_return_if_fail (name != NULL);
825 
826 	len = strlen (name);
827 
828 	g_return_if_fail (len > 0);
829 
830 	if (name[len - 1] >= '1' && name[len - 1] <= '9') {
831 		index = name[len - 1] - '1';
832 	}
833 
834 	store = e_mail_display_get_attachment_store (display);
835 
836 	if (index != ~0 && index >= e_attachment_store_get_num_attachments (store))
837 		return;
838 
839 	attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
840 
841 	if (index == ~0) {
842 		guint n_shown = 0, n_can = 0, flags;
843 
844 		for (link = attachments; link; link = g_list_next (link)) {
845 			EAttachment *attachment = link->data;
846 
847 			if (e_attachment_get_can_show (attachment)) {
848 				n_can++;
849 
850 				flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
851 
852 				if ((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0)
853 					n_shown++;
854 
855 				if (n_can != n_shown)
856 					break;
857 			}
858 		}
859 
860 		mail_display_change_attachment_visibility (display, TRUE, n_shown != n_can);
861 	} else {
862 		for (link = attachments, ii = 0; link; ii++, link = g_list_next (link)) {
863 			EAttachment *attachment = link->data;
864 
865 			if (index == ii) {
866 				if (e_attachment_get_can_show (attachment))
867 					mail_display_change_one_attachment_visibility (display, attachment, FALSE, TRUE);
868 				else
869 					mail_display_open_attachment (display, attachment);
870 				break;
871 			}
872 		}
873 	}
874 
875 	g_list_free_full (attachments, g_object_unref);
876 }
877 
878 static GtkActionEntry accel_entries[] = {
879 
880 	{ "attachment-toggle-all",
881 	  NULL,
882 	  NULL,
883 	  "<Primary><Alt>0",
884 	  NULL,
885 	  G_CALLBACK (action_attachment_toggle_cb) },
886 
887 	{ "attachment-toggle-1",
888 	  NULL,
889 	  NULL,
890 	  "<Primary><Alt>1",
891 	  NULL,
892 	  G_CALLBACK (action_attachment_toggle_cb) },
893 
894 	{ "attachment-toggle-2",
895 	  NULL,
896 	  NULL,
897 	  "<Primary><Alt>2",
898 	  NULL,
899 	  G_CALLBACK (action_attachment_toggle_cb) },
900 
901 	{ "attachment-toggle-3",
902 	  NULL,
903 	  NULL,
904 	  "<Primary><Alt>3",
905 	  NULL,
906 	  G_CALLBACK (action_attachment_toggle_cb) },
907 
908 	{ "attachment-toggle-4",
909 	  NULL,
910 	  NULL,
911 	  "<Primary><Alt>4",
912 	  NULL,
913 	  G_CALLBACK (action_attachment_toggle_cb) },
914 
915 	{ "attachment-toggle-5",
916 	  NULL,
917 	  NULL,
918 	  "<Primary><Alt>5",
919 	  NULL,
920 	  G_CALLBACK (action_attachment_toggle_cb) },
921 
922 	{ "attachment-toggle-6",
923 	  NULL,
924 	  NULL,
925 	  "<Primary><Alt>6",
926 	  NULL,
927 	  G_CALLBACK (action_attachment_toggle_cb) },
928 
929 	{ "attachment-toggle-7",
930 	  NULL,
931 	  NULL,
932 	  "<Primary><Alt>7",
933 	  NULL,
934 	  G_CALLBACK (action_attachment_toggle_cb) },
935 
936 	{ "attachment-toggle-8",
937 	  NULL,
938 	  NULL,
939 	  "<Primary><Alt>8",
940 	  NULL,
941 	  G_CALLBACK (action_attachment_toggle_cb) },
942 
943 	{ "attachment-toggle-9",
944 	  NULL,
945 	  NULL,
946 	  "<Primary><Alt>9",
947 	  NULL,
948 	  G_CALLBACK (action_attachment_toggle_cb) }
949 };
950 
951 static EAttachment *
mail_display_ref_attachment_from_element(EMailDisplay * display,const gchar * element_value)952 mail_display_ref_attachment_from_element (EMailDisplay *display,
953 					  const gchar *element_value)
954 {
955 	EAttachment *attachment = NULL;
956 	GQueue queue = G_QUEUE_INIT;
957 	GList *head, *link;
958 
959 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
960 	g_return_val_if_fail (element_value != NULL, NULL);
961 
962 	e_mail_part_list_queue_parts (display->priv->part_list, NULL, &queue);
963 	head = g_queue_peek_head_link (&queue);
964 
965 	for (link = head; link != NULL; link = g_list_next (link)) {
966 		EMailPart *part = E_MAIL_PART (link->data);
967 
968 		if (E_IS_MAIL_PART_ATTACHMENT (part)) {
969 			EAttachment *adept;
970 			gboolean can_use;
971 			gchar *tmp;
972 
973 			adept = e_mail_part_attachment_ref_attachment (E_MAIL_PART_ATTACHMENT (part));
974 
975 			tmp = g_strdup_printf ("%p", adept);
976 			can_use = g_strcmp0 (tmp, element_value) == 0;
977 			g_free (tmp);
978 
979 			if (can_use) {
980 				attachment = adept;
981 				break;
982 			}
983 
984 			g_clear_object (&adept);
985 		}
986 	}
987 
988 	while (!g_queue_is_empty (&queue))
989 		g_object_unref (g_queue_pop_head (&queue));
990 
991 	return attachment;
992 }
993 
994 static void
mail_display_attachment_expander_clicked_cb(EWebView * web_view,const gchar * iframe_id,const gchar * element_id,const gchar * element_class,const gchar * element_value,const GtkAllocation * element_position,gpointer user_data)995 mail_display_attachment_expander_clicked_cb (EWebView *web_view,
996 					     const gchar *iframe_id,
997 					     const gchar *element_id,
998 					     const gchar *element_class,
999 					     const gchar *element_value,
1000 					     const GtkAllocation *element_position,
1001 					     gpointer user_data)
1002 {
1003 	EMailDisplay *display;
1004 	EAttachment *attachment;
1005 
1006 	g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
1007 	g_return_if_fail (element_class != NULL);
1008 	g_return_if_fail (element_value != NULL);
1009 	g_return_if_fail (element_position != NULL);
1010 
1011 	display = E_MAIL_DISPLAY (web_view);
1012 	attachment = mail_display_ref_attachment_from_element (display, element_value);
1013 
1014 	if (attachment) {
1015 		if (e_attachment_get_can_show (attachment)) {
1016 			/* Flip the current 'visible' state */
1017 			mail_display_change_one_attachment_visibility (display, attachment, FALSE, TRUE);
1018 		} else {
1019 			mail_display_open_attachment (display, attachment);
1020 		}
1021 	}
1022 
1023 	g_clear_object (&attachment);
1024 }
1025 
1026 static void
mail_display_attachment_inline_update_actions(EMailDisplay * display)1027 mail_display_attachment_inline_update_actions (EMailDisplay *display)
1028 {
1029 	GtkActionGroup *action_group;
1030 	GtkAction *action;
1031 	GList *attachments, *link;
1032 	EAttachmentView *view;
1033 	guint n_shown = 0;
1034 	guint n_hidden = 0;
1035 	gboolean can_show = FALSE;
1036 	gboolean shown = FALSE;
1037 	gboolean is_image = FALSE;
1038 	gboolean zoomed_to_100 = FALSE;
1039 	gboolean visible;
1040 
1041 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1042 
1043 	action_group = display->priv->attachment_inline_group;
1044 	g_return_if_fail (action_group != NULL);
1045 
1046 	attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
1047 
1048 	for (link = attachments; link; link = g_list_next (link)) {
1049 		EAttachment *attachment = link->data;
1050 		guint32 flags;
1051 
1052 		if (!e_attachment_get_can_show (attachment))
1053 			continue;
1054 
1055 		flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
1056 		if ((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0)
1057 			n_shown++;
1058 		else
1059 			n_hidden++;
1060 	}
1061 
1062 	g_list_free_full (attachments, g_object_unref);
1063 
1064 	view = e_mail_display_get_attachment_view (display);
1065 	attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
1066 
1067 	if (attachments && attachments->data && !attachments->next) {
1068 		EAttachment *attachment;
1069 		gchar *mime_type;
1070 		guint32 flags;
1071 
1072 		attachment = attachments->data;
1073 		mime_type = e_attachment_dup_mime_type (attachment);
1074 		can_show = e_attachment_get_can_show (attachment);
1075 		is_image = can_show && mime_type && g_ascii_strncasecmp (mime_type, "image/", 6) == 0;
1076 
1077 		flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
1078 		shown = (flags & E_ATTACHMENT_FLAG_VISIBLE) != 0;
1079 		zoomed_to_100 = (flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0;
1080 
1081 		g_free (mime_type);
1082 	}
1083 	g_list_free_full (attachments, g_object_unref);
1084 
1085 	action = gtk_action_group_get_action (action_group, "show");
1086 	gtk_action_set_visible (action, can_show && !shown);
1087 
1088 	/* Show this action if there are multiple viewable
1089 	 * attachments, and at least one of them is hidden. */
1090 	visible = (n_shown + n_hidden > 1) && (n_hidden > 0);
1091 	action = gtk_action_group_get_action (action_group, "show-all");
1092 	gtk_action_set_visible (action, visible);
1093 
1094 	action = gtk_action_group_get_action (action_group, "hide");
1095 	gtk_action_set_visible (action, can_show && shown);
1096 
1097 	/* Show this action if there are multiple viewable
1098 	 * attachments, and at least one of them is shown. */
1099 	visible = (n_shown + n_hidden > 1) && (n_shown > 0);
1100 	action = gtk_action_group_get_action (action_group, "hide-all");
1101 	gtk_action_set_visible (action, visible);
1102 
1103 	action = gtk_action_group_get_action (action_group, "zoom-to-100");
1104 	gtk_action_set_visible (action, can_show && shown && is_image && !zoomed_to_100);
1105 
1106 	action = gtk_action_group_get_action (action_group, "zoom-to-window");
1107 	gtk_action_set_visible (action, can_show && shown && is_image && zoomed_to_100);
1108 }
1109 
1110 static void
mail_display_attachment_menu_deactivate_cb(GtkMenuShell * menu,gpointer user_data)1111 mail_display_attachment_menu_deactivate_cb (GtkMenuShell *menu,
1112 					    gpointer user_data)
1113 {
1114 	EMailDisplay *display = user_data;
1115 
1116 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1117 
1118 	gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE);
1119 
1120 	g_signal_handlers_disconnect_by_func (menu,
1121 		G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display);
1122 }
1123 
1124 static void
mail_display_attachment_select_path(EAttachmentView * view,EAttachment * attachment)1125 mail_display_attachment_select_path (EAttachmentView *view,
1126 				     EAttachment *attachment)
1127 {
1128 	GtkTreePath *path;
1129 	GtkTreeIter iter;
1130 	EAttachmentStore *store;
1131 
1132 	g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
1133 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1134 
1135 	store = e_attachment_view_get_store (view);
1136 	g_return_if_fail (e_attachment_store_find_attachment_iter (store, attachment, &iter));
1137 
1138 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1139 
1140 	e_attachment_view_unselect_all (view);
1141 	e_attachment_view_select_path (view, path);
1142 
1143 	gtk_tree_path_free (path);
1144 }
1145 
1146 static void
mail_display_attachment_menu_clicked_cb(EWebView * web_view,const gchar * iframe_id,const gchar * element_id,const gchar * element_class,const gchar * element_value,const GtkAllocation * element_position,gpointer user_data)1147 mail_display_attachment_menu_clicked_cb (EWebView *web_view,
1148 					 const gchar *iframe_id,
1149 					 const gchar *element_id,
1150 					 const gchar *element_class,
1151 					 const gchar *element_value,
1152 					 const GtkAllocation *element_position,
1153 					 gpointer user_data)
1154 {
1155 	EMailDisplay *display;
1156 	EAttachmentView *view;
1157 	EAttachment *attachment;
1158 
1159 	g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
1160 	g_return_if_fail (element_class != NULL);
1161 	g_return_if_fail (element_value != NULL);
1162 	g_return_if_fail (element_position != NULL);
1163 
1164 	display = E_MAIL_DISPLAY (web_view);
1165 	view = e_mail_display_get_attachment_view (display);
1166 	attachment = mail_display_ref_attachment_from_element (display, element_value);
1167 
1168 	if (view && attachment) {
1169 		GtkWidget *popup_menu;
1170 
1171 		popup_menu = e_attachment_view_get_popup_menu (view);
1172 
1173 		g_signal_connect (
1174 			popup_menu, "deactivate",
1175 			G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display);
1176 
1177 		mail_display_attachment_select_path (view, attachment);
1178 		mail_display_attachment_inline_update_actions (display);
1179 		gtk_action_group_set_visible (display->priv->attachment_inline_group, TRUE);
1180 
1181 		e_attachment_view_update_actions (view);
1182 		popup_menu = e_attachment_view_get_popup_menu (view);
1183 
1184 		g_object_set (GTK_MENU (popup_menu),
1185 		              "anchor-hints", (GDK_ANCHOR_FLIP_Y |
1186 		                               GDK_ANCHOR_SLIDE |
1187 		                               GDK_ANCHOR_RESIZE),
1188 		              NULL);
1189 
1190 		gtk_menu_popup_at_rect (GTK_MENU (popup_menu),
1191 		                        gtk_widget_get_parent_window (GTK_WIDGET (display)),
1192 		                        element_position,
1193 		                        GDK_GRAVITY_SOUTH_WEST,
1194 		                        GDK_GRAVITY_NORTH_WEST,
1195 		                        NULL);
1196 	}
1197 
1198 	g_clear_object (&attachment);
1199 }
1200 
1201 static void
mail_display_attachment_added_cb(EAttachmentStore * store,EAttachment * attachment,gpointer user_data)1202 mail_display_attachment_added_cb (EAttachmentStore *store,
1203 				  EAttachment *attachment,
1204 				  gpointer user_data)
1205 {
1206 	EMailDisplay *display = user_data;
1207 	guint flags;
1208 
1209 	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
1210 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1211 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1212 
1213 	flags = e_attachment_get_initially_shown (attachment) ? E_ATTACHMENT_FLAG_VISIBLE : 0;
1214 
1215 	g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
1216 }
1217 
1218 static void
mail_display_attachment_removed_cb(EAttachmentStore * store,EAttachment * attachment,gpointer user_data)1219 mail_display_attachment_removed_cb (EAttachmentStore *store,
1220 				    EAttachment *attachment,
1221 				    gpointer user_data)
1222 {
1223 	EMailDisplay *display = user_data;
1224 
1225 	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
1226 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1227 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1228 
1229 	g_hash_table_remove (display->priv->attachment_flags, attachment);
1230 }
1231 
1232 static void
mail_display_remote_content_clicked_cb(EWebView * web_view,const gchar * iframe_id,const gchar * element_id,const gchar * element_class,const gchar * element_value,const GtkAllocation * element_position,gpointer user_data)1233 mail_display_remote_content_clicked_cb (EWebView *web_view,
1234 					const gchar *iframe_id,
1235 					const gchar *element_id,
1236 					const gchar *element_class,
1237 					const gchar *element_value,
1238 					const GtkAllocation *element_position,
1239 					gpointer user_data)
1240 {
1241 	g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
1242 
1243 	g_signal_emit (web_view, signals[REMOTE_CONTENT_CLICKED], 0, element_position, NULL);
1244 }
1245 
1246 static void
mail_display_load_changed_cb(WebKitWebView * wk_web_view,WebKitLoadEvent load_event,gpointer user_data)1247 mail_display_load_changed_cb (WebKitWebView *wk_web_view,
1248 			      WebKitLoadEvent load_event,
1249 			      gpointer user_data)
1250 {
1251 	EMailDisplay *display;
1252 
1253 	g_return_if_fail (E_IS_MAIL_DISPLAY (wk_web_view));
1254 
1255 	display = E_MAIL_DISPLAY (wk_web_view);
1256 
1257 	if (load_event == WEBKIT_LOAD_STARTED) {
1258 		display->priv->magic_spacebar_state = 0;
1259 		e_mail_display_cleanup_skipped_uris (display);
1260 		e_attachment_store_remove_all (display->priv->attachment_store);
1261 	}
1262 }
1263 
1264 static void
mail_display_content_loaded_cb(EWebView * web_view,const gchar * iframe_id,gpointer user_data)1265 mail_display_content_loaded_cb (EWebView *web_view,
1266 				const gchar *iframe_id,
1267 				gpointer user_data)
1268 {
1269 	EMailDisplay *mail_display;
1270 	gchar *citation_color = NULL;
1271 
1272 	g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
1273 
1274 	mail_display = E_MAIL_DISPLAY (web_view);
1275 
1276 	initialize_web_view_colors (mail_display, iframe_id);
1277 
1278 	if (!iframe_id || !*iframe_id) {
1279 		e_web_view_register_element_clicked (web_view, "attachment-expander",
1280 			mail_display_attachment_expander_clicked_cb, NULL);
1281 		e_web_view_register_element_clicked (web_view, "attachment-menu",
1282 			mail_display_attachment_menu_clicked_cb, NULL);
1283 		e_web_view_register_element_clicked (web_view, "__evo-remote-content-img",
1284 			mail_display_remote_content_clicked_cb, NULL);
1285 	}
1286 
1287 	if (g_settings_get_boolean (mail_display->priv->settings, "mark-citations")) {
1288 		GdkRGBA rgba;
1289 
1290 		citation_color = g_settings_get_string (mail_display->priv->settings, "citation-color");
1291 
1292 		if (!citation_color || !gdk_rgba_parse (&rgba, citation_color)) {
1293 			g_free (citation_color);
1294 			citation_color = NULL;
1295 		} else {
1296 			g_free (citation_color);
1297 			citation_color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
1298 		}
1299 	}
1300 
1301 	e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (web_view), e_web_view_get_cancellable (web_view),
1302 		"Evo.MailDisplayBindDOM(%s, %s);", iframe_id, citation_color);
1303 
1304 	g_free (citation_color);
1305 
1306 	if (mail_display->priv->part_list) {
1307 		if (!iframe_id || !*iframe_id) {
1308 			GQueue queue = G_QUEUE_INIT;
1309 			GList *head, *link;
1310 
1311 			e_mail_part_list_queue_parts (mail_display->priv->part_list, NULL, &queue);
1312 			head = g_queue_peek_head_link (&queue);
1313 
1314 			for (link = head; link; link = g_list_next (link)) {
1315 				EMailPart *part = E_MAIL_PART (link->data);
1316 
1317 				e_mail_part_content_loaded (part, web_view, NULL);
1318 			}
1319 
1320 			while (!g_queue_is_empty (&queue))
1321 				g_object_unref (g_queue_pop_head (&queue));
1322 		} else {
1323 			EMailPart *part;
1324 
1325 			part = e_mail_part_list_ref_part (mail_display->priv->part_list, iframe_id);
1326 
1327 			if (part)
1328 				e_mail_part_content_loaded (part, web_view, iframe_id);
1329 
1330 			g_clear_object (&part);
1331 		}
1332 	}
1333 
1334 	if (e_mail_display_has_skipped_remote_content_sites (mail_display)) {
1335 		e_web_view_jsc_set_element_hidden (WEBKIT_WEB_VIEW (web_view),
1336 			"", "__evo-remote-content-img-small", FALSE,
1337 			e_web_view_get_cancellable (web_view));
1338 
1339 		e_web_view_jsc_set_element_hidden (WEBKIT_WEB_VIEW (web_view),
1340 			"", "__evo-remote-content-img-large", FALSE,
1341 			e_web_view_get_cancellable (web_view));
1342 	}
1343 
1344 	/* Re-grab the focus, which is needed for the caret mode to show the cursor */
1345 	if (e_web_view_get_caret_mode (web_view) &&
1346 	    gtk_widget_has_focus (GTK_WIDGET (web_view))) {
1347 		GtkWidget *toplevel, *widget = GTK_WIDGET (web_view);
1348 
1349 		toplevel = gtk_widget_get_toplevel (widget);
1350 
1351 		if (GTK_IS_WINDOW (toplevel)) {
1352 			gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
1353 			gtk_widget_grab_focus (widget);
1354 		}
1355 	}
1356 }
1357 
1358 static void
mail_display_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1359 mail_display_set_property (GObject *object,
1360                            guint property_id,
1361                            const GValue *value,
1362                            GParamSpec *pspec)
1363 {
1364 	switch (property_id) {
1365 		case PROP_HEADERS_COLLAPSABLE:
1366 			e_mail_display_set_headers_collapsable (
1367 				E_MAIL_DISPLAY (object),
1368 				g_value_get_boolean (value));
1369 			return;
1370 
1371 		case PROP_HEADERS_COLLAPSED:
1372 			e_mail_display_set_headers_collapsed (
1373 				E_MAIL_DISPLAY (object),
1374 				g_value_get_boolean (value));
1375 			return;
1376 
1377 		case PROP_MODE:
1378 			e_mail_display_set_mode (
1379 				E_MAIL_DISPLAY (object),
1380 				g_value_get_enum (value));
1381 			return;
1382 
1383 		case PROP_PART_LIST:
1384 			e_mail_display_set_part_list (
1385 				E_MAIL_DISPLAY (object),
1386 				g_value_get_pointer (value));
1387 			return;
1388 
1389 		case PROP_REMOTE_CONTENT:
1390 			e_mail_display_set_remote_content (
1391 				E_MAIL_DISPLAY (object),
1392 				g_value_get_object (value));
1393 			return;
1394 	}
1395 
1396 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1397 }
1398 
1399 static void
mail_display_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1400 mail_display_get_property (GObject *object,
1401                            guint property_id,
1402                            GValue *value,
1403                            GParamSpec *pspec)
1404 {
1405 	switch (property_id) {
1406 		case PROP_ATTACHMENT_STORE:
1407 			g_value_set_object (
1408 				value,
1409 				e_mail_display_get_attachment_store (
1410 				E_MAIL_DISPLAY (object)));
1411 			return;
1412 
1413 		case PROP_ATTACHMENT_VIEW:
1414 			g_value_set_object (
1415 				value,
1416 				e_mail_display_get_attachment_view (
1417 				E_MAIL_DISPLAY (object)));
1418 			return;
1419 
1420 		case PROP_FORMATTER:
1421 			g_value_set_object (
1422 				value,
1423 				e_mail_display_get_formatter (
1424 				E_MAIL_DISPLAY (object)));
1425 			return;
1426 
1427 		case PROP_HEADERS_COLLAPSABLE:
1428 			g_value_set_boolean (
1429 				value,
1430 				e_mail_display_get_headers_collapsable (
1431 				E_MAIL_DISPLAY (object)));
1432 			return;
1433 
1434 		case PROP_HEADERS_COLLAPSED:
1435 			g_value_set_boolean (
1436 				value,
1437 				e_mail_display_get_headers_collapsed (
1438 				E_MAIL_DISPLAY (object)));
1439 			return;
1440 
1441 		case PROP_MODE:
1442 			g_value_set_enum (
1443 				value,
1444 				e_mail_display_get_mode (
1445 				E_MAIL_DISPLAY (object)));
1446 			return;
1447 
1448 		case PROP_PART_LIST:
1449 			g_value_set_pointer (
1450 				value,
1451 				e_mail_display_get_part_list (
1452 				E_MAIL_DISPLAY (object)));
1453 			return;
1454 
1455 		case PROP_REMOTE_CONTENT:
1456 			g_value_take_object (
1457 				value,
1458 				e_mail_display_ref_remote_content (
1459 				E_MAIL_DISPLAY (object)));
1460 			return;
1461 	}
1462 
1463 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1464 }
1465 
1466 static void
mail_display_dispose(GObject * object)1467 mail_display_dispose (GObject *object)
1468 {
1469 	EMailDisplayPrivate *priv;
1470 
1471 	priv = E_MAIL_DISPLAY_GET_PRIVATE (object);
1472 
1473 	if (priv->scheduled_reload > 0) {
1474 		g_source_remove (priv->scheduled_reload);
1475 		priv->scheduled_reload = 0;
1476 	}
1477 
1478 	if (priv->settings != NULL) {
1479 		g_signal_handlers_disconnect_matched (
1480 			priv->settings, G_SIGNAL_MATCH_DATA,
1481 			0, 0, NULL, NULL, object);
1482 	}
1483 
1484 	if (priv->attachment_store) {
1485 		/* To have called the mail_display_attachment_removed_cb() before it's disconnected */
1486 		e_attachment_store_remove_all (priv->attachment_store);
1487 
1488 		g_signal_handlers_disconnect_by_func (priv->attachment_store,
1489 			G_CALLBACK (mail_display_attachment_added_cb), object);
1490 
1491 		g_signal_handlers_disconnect_by_func (priv->attachment_store,
1492 			G_CALLBACK (mail_display_attachment_removed_cb), object);
1493 	}
1494 
1495 	g_clear_object (&priv->part_list);
1496 	g_clear_object (&priv->formatter);
1497 	g_clear_object (&priv->settings);
1498 	g_clear_object (&priv->attachment_store);
1499 	g_clear_object (&priv->attachment_view);
1500 	g_clear_object (&priv->attachment_inline_group);
1501 	g_clear_object (&priv->attachment_accel_action_group);
1502 	g_clear_object (&priv->attachment_accel_group);
1503 
1504 	/* Chain up to parent's dispose() method. */
1505 	G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
1506 }
1507 
1508 static void
mail_display_finalize(GObject * object)1509 mail_display_finalize (GObject *object)
1510 {
1511 	EMailDisplayPrivate *priv;
1512 
1513 	priv = E_MAIL_DISPLAY_GET_PRIVATE (object);
1514 	g_clear_pointer (&priv->old_settings, g_hash_table_destroy);
1515 
1516 	g_mutex_lock (&priv->remote_content_lock);
1517 	g_clear_pointer (&priv->skipped_remote_content_sites, g_hash_table_destroy);
1518 	g_hash_table_destroy (priv->attachment_flags);
1519 	g_clear_object (&priv->remote_content);
1520 	g_mutex_unlock (&priv->remote_content_lock);
1521 	g_mutex_clear (&priv->remote_content_lock);
1522 
1523 	/* Chain up to parent's finalize() method. */
1524 	G_OBJECT_CLASS (e_mail_display_parent_class)->finalize (object);
1525 }
1526 
1527 static void
mail_display_get_font_settings(GSettings * settings,PangoFontDescription ** monospace,PangoFontDescription ** variable)1528 mail_display_get_font_settings (GSettings *settings,
1529                                 PangoFontDescription **monospace,
1530                                 PangoFontDescription **variable)
1531 {
1532 	gboolean use_custom_font;
1533 	gchar *monospace_font;
1534 	gchar *variable_font;
1535 
1536 	use_custom_font = g_settings_get_boolean (settings, "use-custom-font");
1537 
1538 	if (!use_custom_font) {
1539 		if (monospace)
1540 			*monospace = NULL;
1541 		if (variable)
1542 			*variable = NULL;
1543 		return;
1544 	}
1545 
1546 	monospace_font = g_settings_get_string (settings, "monospace-font");
1547 	variable_font = g_settings_get_string (settings, "variable-width-font");
1548 
1549 	if (monospace)
1550 		*monospace = (monospace_font != NULL) ? pango_font_description_from_string (monospace_font) : NULL;
1551 	if (variable)
1552 		*variable = (variable_font != NULL) ? pango_font_description_from_string (variable_font) : NULL;
1553 
1554 	g_free (monospace_font);
1555 	g_free (variable_font);
1556 }
1557 
1558 static void
mail_display_set_fonts(EWebView * web_view,PangoFontDescription ** monospace,PangoFontDescription ** variable)1559 mail_display_set_fonts (EWebView *web_view,
1560                         PangoFontDescription **monospace,
1561                         PangoFontDescription **variable)
1562 {
1563 	EMailDisplay *display = E_MAIL_DISPLAY (web_view);
1564 
1565 	mail_display_get_font_settings (display->priv->settings, monospace, variable);
1566 }
1567 
1568 static void
mail_display_headers_collapsed_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * js_result,gpointer user_data)1569 mail_display_headers_collapsed_cb (WebKitUserContentManager *manager,
1570 				   WebKitJavascriptResult *js_result,
1571 				   gpointer user_data)
1572 {
1573 	EMailDisplay *mail_display = user_data;
1574 	JSCValue *jsc_value;
1575 
1576 	g_return_if_fail (mail_display != NULL);
1577 	g_return_if_fail (js_result != NULL);
1578 
1579 	jsc_value = webkit_javascript_result_get_js_value (js_result);
1580 	g_return_if_fail (jsc_value_is_boolean (jsc_value));
1581 
1582 	e_mail_display_set_headers_collapsed (mail_display, jsc_value_to_boolean (jsc_value));
1583 }
1584 
1585 static void
mail_display_magic_spacebar_state_changed_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * js_result,gpointer user_data)1586 mail_display_magic_spacebar_state_changed_cb (WebKitUserContentManager *manager,
1587 					      WebKitJavascriptResult *js_result,
1588 					      gpointer user_data)
1589 {
1590 	EMailDisplay *mail_display = user_data;
1591 	JSCValue *jsc_value;
1592 
1593 	g_return_if_fail (mail_display != NULL);
1594 	g_return_if_fail (js_result != NULL);
1595 
1596 	jsc_value = webkit_javascript_result_get_js_value (js_result);
1597 	g_return_if_fail (jsc_value_is_number (jsc_value));
1598 
1599 	mail_display->priv->magic_spacebar_state = jsc_value_to_int32 (jsc_value);
1600 }
1601 
1602 static void
mail_display_constructed(GObject * object)1603 mail_display_constructed (GObject *object)
1604 {
1605 	EContentRequest *content_request;
1606 	WebKitUserContentManager *manager;
1607 	EWebView *web_view;
1608 	EMailDisplay *display;
1609 	GtkUIManager *ui_manager;
1610 
1611 	/* Chain up to parent's constructed() method. */
1612 	G_OBJECT_CLASS (e_mail_display_parent_class)->constructed (object);
1613 
1614 	g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (object)),
1615 		"enable-frame-flattening", TRUE,
1616 		NULL);
1617 
1618 	display = E_MAIL_DISPLAY (object);
1619 	web_view = E_WEB_VIEW (object);
1620 
1621 	e_web_view_update_fonts (web_view);
1622 
1623 	content_request = e_http_request_new ();
1624 	e_web_view_register_content_request_for_scheme (web_view, "evo-http", content_request);
1625 	e_web_view_register_content_request_for_scheme (web_view, "evo-https", content_request);
1626 	g_object_unref (content_request);
1627 
1628 	content_request = e_mail_request_new ();
1629 	e_binding_bind_property (display, "scale-factor",
1630 		content_request, "scale-factor",
1631 		G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1632 	e_web_view_register_content_request_for_scheme (web_view, "mail", content_request);
1633 	g_object_unref (content_request);
1634 
1635 	content_request = e_cid_request_new ();
1636 	e_web_view_register_content_request_for_scheme (web_view, "cid", content_request);
1637 	g_object_unref (content_request);
1638 
1639 	display->priv->attachment_view = E_ATTACHMENT_VIEW (g_object_ref_sink (e_attachment_bar_new (display->priv->attachment_store)));
1640 
1641 	ui_manager = e_attachment_view_get_ui_manager (display->priv->attachment_view);
1642 	if (ui_manager) {
1643 		GError *error = NULL;
1644 
1645 		gtk_ui_manager_insert_action_group (ui_manager, display->priv->attachment_inline_group, -1);
1646 
1647 		display->priv->attachment_inline_ui_id = gtk_ui_manager_add_ui_from_string (ui_manager,
1648 			attachment_popup_ui, -1, &error);
1649 
1650 		if (error) {
1651 			g_warning ("%s: Failed to read attachment_popup_ui: %s", G_STRFUNC, error->message);
1652 			g_clear_error (&error);
1653 		}
1654 	}
1655 
1656 	manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (object));
1657 
1658 	g_signal_connect_object (manager, "script-message-received::mailDisplayHeadersCollapsed",
1659 		G_CALLBACK (mail_display_headers_collapsed_cb), display, 0);
1660 
1661 	g_signal_connect_object (manager, "script-message-received::mailDisplayMagicSpacebarStateChanged",
1662 		G_CALLBACK (mail_display_magic_spacebar_state_changed_cb), display, 0);
1663 
1664 	webkit_user_content_manager_register_script_message_handler (manager, "mailDisplayHeadersCollapsed");
1665 	webkit_user_content_manager_register_script_message_handler (manager, "mailDisplayMagicSpacebarStateChanged");
1666 
1667 	e_extensible_load_extensions (E_EXTENSIBLE (object));
1668 }
1669 
1670 static void
mail_display_realize(GtkWidget * widget)1671 mail_display_realize (GtkWidget *widget)
1672 {
1673 	/* Chain up to parent's realize() method. */
1674 	GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget);
1675 
1676 	mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
1677 }
1678 
1679 static void
mail_display_style_updated(GtkWidget * widget)1680 mail_display_style_updated (GtkWidget *widget)
1681 {
1682 	EMailDisplay *display = E_MAIL_DISPLAY (widget);
1683 
1684 	mail_display_update_formatter_colors (display);
1685 
1686 	/* Chain up to parent's style_updated() method. */
1687 	GTK_WIDGET_CLASS (e_mail_display_parent_class)->
1688 		style_updated (widget);
1689 }
1690 
1691 static void
mail_display_before_popup_event(EWebView * web_view,const gchar * uri)1692 mail_display_before_popup_event (EWebView *web_view,
1693 				 const gchar *uri)
1694 {
1695 	gchar *popup_iframe_src = NULL, *popup_iframe_id = NULL;
1696 	GList *list, *link;
1697 
1698 	e_web_view_get_last_popup_place (web_view, &popup_iframe_src, &popup_iframe_id, NULL, NULL);
1699 
1700 	list = e_extensible_list_extensions (E_EXTENSIBLE (web_view), E_TYPE_EXTENSION);
1701 
1702 	for (link = list; link; link = g_list_next (link)) {
1703 		EExtension *extension = link->data;
1704 
1705 		if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension))
1706 			continue;
1707 
1708 		e_mail_display_popup_extension_update_actions (E_MAIL_DISPLAY_POPUP_EXTENSION (extension), popup_iframe_src, popup_iframe_id);
1709 	}
1710 
1711 	g_free (popup_iframe_src);
1712 	g_free (popup_iframe_id);
1713 	g_list_free (list);
1714 
1715 	/* Chain up to parent's method. */
1716 	E_WEB_VIEW_CLASS (e_mail_display_parent_class)->before_popup_event (web_view, uri);
1717 }
1718 
1719 static gboolean
mail_display_image_exists_in_cache(const gchar * image_uri)1720 mail_display_image_exists_in_cache (const gchar *image_uri)
1721 {
1722 	gchar *filename;
1723 	gchar *hash;
1724 	gboolean exists = FALSE;
1725 
1726 	if (!emd_global_http_cache)
1727 		return FALSE;
1728 
1729 	hash = e_http_request_util_compute_uri_checksum (image_uri);
1730 	filename = camel_data_cache_get_filename (
1731 		emd_global_http_cache, "http", hash);
1732 
1733 	if (filename != NULL) {
1734 		struct stat st;
1735 
1736 		exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1737 		if (exists && g_stat (filename, &st) == 0) {
1738 			exists = st.st_size != 0;
1739 		} else {
1740 			exists = FALSE;
1741 		}
1742 		g_free (filename);
1743 	}
1744 
1745 	g_free (hash);
1746 
1747 	return exists;
1748 }
1749 
1750 static void
mail_display_uri_requested_cb(EWebView * web_view,const gchar * uri,gchar ** redirect_to_uri)1751 mail_display_uri_requested_cb (EWebView *web_view,
1752 			       const gchar *uri,
1753 			       gchar **redirect_to_uri)
1754 {
1755 	EMailDisplay *display;
1756 	EMailPartList *part_list;
1757 	gboolean uri_is_http;
1758 
1759 	display = E_MAIL_DISPLAY (web_view);
1760 	part_list = e_mail_display_get_part_list (display);
1761 
1762 	if (part_list == NULL)
1763 		return;
1764 
1765 	uri_is_http =
1766 		g_str_has_prefix (uri, "http:") ||
1767 		g_str_has_prefix (uri, "https:") ||
1768 		g_str_has_prefix (uri, "evo-http:") ||
1769 		g_str_has_prefix (uri, "evo-https:");
1770 
1771 	/* Redirect http(s) request to evo-http(s) protocol.
1772 	 * See EMailRequest for further details about this. */
1773 	if (uri_is_http) {
1774 		CamelFolder *folder;
1775 		const gchar *message_uid;
1776 		gchar *new_uri, *mail_uri;
1777 		SoupURI *soup_uri;
1778 		GHashTable *query;
1779 		gboolean can_download_uri;
1780 		EImageLoadingPolicy image_policy;
1781 
1782 		can_download_uri = e_mail_display_can_download_uri (display, uri);
1783 		if (!can_download_uri) {
1784 			/* Check Evolution's cache */
1785 			can_download_uri = mail_display_image_exists_in_cache (
1786 				uri + (g_str_has_prefix (uri, "evo-") ? 4 : 0));
1787 		}
1788 
1789 		/* If the URI is not cached and we are not allowed to load it
1790 		 * then redirect to invalid URI, so that webkit would display
1791 		 * a native placeholder for it. */
1792 		image_policy = e_mail_formatter_get_image_loading_policy (
1793 			display->priv->formatter);
1794 		if (!can_download_uri && !display->priv->force_image_load &&
1795 		    (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
1796 			e_mail_display_claim_skipped_uri (display, uri);
1797 			g_free (*redirect_to_uri);
1798 			*redirect_to_uri = g_strdup ("");
1799 			return;
1800 		}
1801 
1802 		folder = e_mail_part_list_get_folder (part_list);
1803 		message_uid = e_mail_part_list_get_message_uid (part_list);
1804 
1805 		if (g_str_has_prefix (uri, "evo-")) {
1806 			soup_uri = soup_uri_new (uri);
1807 		} else {
1808 			new_uri = g_strconcat ("evo-", uri, NULL);
1809 			soup_uri = soup_uri_new (new_uri);
1810 
1811 			g_free (new_uri);
1812 		}
1813 
1814 		mail_uri = e_mail_part_build_uri (
1815 			folder, message_uid, NULL, NULL);
1816 
1817 		query = g_hash_table_new_full (
1818 			g_str_hash, g_str_equal,
1819 			g_free, g_free);
1820 
1821 		if (soup_uri->query) {
1822 			GHashTable *uri_query;
1823 			GHashTableIter iter;
1824 			gpointer key, value;
1825 
1826 			/* It's required to copy the hash table, because it's uncertain
1827 			   which of the key/value pair is freed and which not, while the code
1828 			   below expects to have freed both. */
1829 			uri_query = soup_form_decode (soup_uri->query);
1830 
1831 			g_hash_table_iter_init (&iter, uri_query);
1832 			while (g_hash_table_iter_next (&iter, &key, &value)) {
1833 				g_hash_table_insert (query, g_strdup (key), g_strdup (value));
1834 			}
1835 
1836 			g_hash_table_unref (uri_query);
1837 		}
1838 
1839 		g_hash_table_insert (query, g_strdup ("__evo-mail"), soup_uri_encode (mail_uri, NULL));
1840 
1841 		/* Required, because soup_uri_set_query_from_form() can change
1842 		   order of arguments, then the URL checksum doesn't match. */
1843 		g_hash_table_insert (query, g_strdup ("__evo-original-uri"), g_strdup (uri));
1844 
1845 		if (display->priv->force_image_load || can_download_uri) {
1846 			g_hash_table_insert (
1847 				query,
1848 				g_strdup ("__evo-load-images"),
1849 				g_strdup ("true"));
1850 		} else if (image_policy != E_IMAGE_LOADING_POLICY_ALWAYS) {
1851 			e_mail_display_claim_skipped_uri (display, uri);
1852 		}
1853 
1854 		soup_uri_set_query_from_form (soup_uri, query);
1855 
1856 		new_uri = soup_uri_to_string (soup_uri, FALSE);
1857 
1858 		soup_uri_free (soup_uri);
1859 		g_hash_table_unref (query);
1860 		g_free (mail_uri);
1861 
1862 		g_free (*redirect_to_uri);
1863 		*redirect_to_uri = new_uri;
1864 	}
1865 }
1866 
1867 static CamelMimePart *
camel_mime_part_from_cid(EMailDisplay * display,const gchar * uri)1868 camel_mime_part_from_cid (EMailDisplay *display,
1869                           const gchar *uri)
1870 {
1871 	EMailPartList *part_list;
1872 	CamelMimeMessage *message;
1873 	CamelMimePart *mime_part;
1874 
1875 	if (!g_str_has_prefix (uri, "cid:"))
1876 		return NULL;
1877 
1878 	part_list = e_mail_display_get_part_list (display);
1879 	if (!part_list)
1880 		return NULL;
1881 
1882 	message = e_mail_part_list_get_message (part_list);
1883 	if (!message)
1884 		return NULL;
1885 
1886 	mime_part = camel_mime_message_get_part_by_content_id (
1887 		message, uri + 4);
1888 
1889 	return mime_part;
1890 }
1891 
1892 static gchar *
mail_display_suggest_filename(EWebView * web_view,const gchar * uri)1893 mail_display_suggest_filename (EWebView *web_view,
1894                                const gchar *uri)
1895 {
1896 	EMailDisplay *display;
1897 	CamelMimePart *mime_part;
1898 	SoupURI *suri;
1899 
1900 	/* Note, this assumes the URI comes
1901 	 * from the currently loaded message. */
1902 	display = E_MAIL_DISPLAY (web_view);
1903 
1904 	mime_part = camel_mime_part_from_cid (display, uri);
1905 
1906 	if (mime_part)
1907 		return g_strdup (camel_mime_part_get_filename (mime_part));
1908 
1909 	suri = soup_uri_new (uri);
1910 	if (suri) {
1911 		gchar *filename = NULL;
1912 
1913 		if (suri->query) {
1914 			GHashTable *uri_query;
1915 
1916 			uri_query = soup_form_decode (suri->query);
1917 			if (uri_query && g_hash_table_contains (uri_query, "filename"))
1918 				filename = g_strdup (g_hash_table_lookup (uri_query, "filename"));
1919 
1920 			if (uri_query)
1921 				g_hash_table_destroy (uri_query);
1922 		}
1923 
1924 		soup_uri_free (suri);
1925 
1926 		if (filename && *filename)
1927 			return filename;
1928 
1929 		g_free (filename);
1930 	}
1931 
1932 	/* Chain up to parent's suggest_filename() method. */
1933 	return E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
1934 		suggest_filename (web_view, uri);
1935 }
1936 
1937 static void
mail_display_save_part_for_drop(CamelMimePart * mime_part,GtkSelectionData * data)1938 mail_display_save_part_for_drop (CamelMimePart *mime_part,
1939 				 GtkSelectionData *data)
1940 {
1941 	gchar *tmp, *path, *filename;
1942 	const gchar *part_filename;
1943 	CamelDataWrapper *dw;
1944 
1945 	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1946 	g_return_if_fail (data != NULL);
1947 
1948 	tmp = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
1949 	path = e_mkdtemp (tmp);
1950 	g_free (tmp);
1951 
1952 	g_return_if_fail (path != NULL);
1953 
1954 	part_filename = camel_mime_part_get_filename (mime_part);
1955 	if (!part_filename || !*part_filename) {
1956 		CamelDataWrapper *content;
1957 
1958 		content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1959 
1960 		if (CAMEL_IS_MIME_MESSAGE (content))
1961 			part_filename = camel_mime_message_get_subject (CAMEL_MIME_MESSAGE (content));
1962 	}
1963 
1964 	if (!part_filename || !*part_filename)
1965 		part_filename = "mail-part";
1966 
1967 	tmp = g_strdup (part_filename);
1968 	e_util_make_safe_filename (tmp);
1969 
1970 	filename = g_build_filename (path, tmp, NULL);
1971 	g_free (tmp);
1972 
1973 	dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1974 	g_warn_if_fail (dw);
1975 
1976 	if (dw) {
1977 		CamelStream *stream;
1978 
1979 		stream = camel_stream_fs_new_with_name (filename, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
1980 		if (stream) {
1981 			if (camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL)) {
1982 				tmp = g_filename_to_uri (filename, NULL, NULL);
1983 				if (tmp) {
1984 					gtk_selection_data_set (
1985 						data,
1986 						gtk_selection_data_get_data_type (data),
1987 						gtk_selection_data_get_format (data),
1988 						(const guchar *) tmp, strlen (tmp));
1989 					g_free (tmp);
1990 				}
1991 			}
1992 
1993 			camel_stream_close (stream, NULL, NULL);
1994 			g_object_unref (stream);
1995 		}
1996 	}
1997 
1998 	g_free (filename);
1999 	g_free (path);
2000 }
2001 
2002 static void
mail_display_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * data,guint info,guint time,EMailDisplay * display)2003 mail_display_drag_data_get (GtkWidget *widget,
2004                             GdkDragContext *context,
2005                             GtkSelectionData *data,
2006                             guint info,
2007                             guint time,
2008                             EMailDisplay *display)
2009 {
2010 	CamelDataWrapper *dw;
2011 	CamelMimePart *mime_part;
2012 	CamelStream *stream;
2013 	gchar *src, *base64_encoded, *mime_type, *uri;
2014 	const gchar *filename;
2015 	const guchar *data_from_webkit;
2016 	gint length;
2017 	GByteArray *byte_array;
2018 
2019 	data_from_webkit = gtk_selection_data_get_data (data);
2020 	length = gtk_selection_data_get_length (data);
2021 
2022 	uri = g_strndup ((const gchar *) data_from_webkit, length);
2023 
2024 	mime_part = camel_mime_part_from_cid (display, uri);
2025 
2026 	if (!mime_part && g_str_has_prefix (uri, "mail:")) {
2027 		SoupURI *soup_uri;
2028 		const gchar *soup_query;
2029 
2030 		soup_uri = soup_uri_new (uri);
2031 		if (soup_uri) {
2032 			soup_query = soup_uri_get_query (soup_uri);
2033 			if (soup_query) {
2034 				GHashTable *query;
2035 				const gchar *part_id_raw;
2036 
2037 				query = soup_form_decode (soup_query);
2038 				part_id_raw = query ? g_hash_table_lookup (query, "part_id") : NULL;
2039 				if (part_id_raw && *part_id_raw) {
2040 					EMailPartList *part_list;
2041 					EMailPart *mail_part;
2042 
2043 					part_list = e_mail_display_get_part_list (display);
2044 					if (part_list) {
2045 						gchar *part_id = soup_uri_decode (part_id_raw);
2046 
2047 						mail_part = e_mail_part_list_ref_part (part_list, part_id);
2048 						g_free (part_id);
2049 
2050 						if (mail_part) {
2051 							CamelMimePart *part;
2052 
2053 							part = e_mail_part_ref_mime_part (mail_part);
2054 							if (part) {
2055 								mail_display_save_part_for_drop (part, data);
2056 							}
2057 
2058 							g_clear_object (&part);
2059 							g_object_unref (mail_part);
2060 						}
2061 					}
2062 				}
2063 
2064 				if (query)
2065 					g_hash_table_unref (query);
2066 			}
2067 
2068 			soup_uri_free (soup_uri);
2069 		}
2070 	}
2071 
2072 	if (!mime_part)
2073 		goto out;
2074 
2075 	stream = camel_stream_mem_new ();
2076 	dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
2077 	g_return_if_fail (dw);
2078 
2079 	mime_type = camel_data_wrapper_get_mime_type (dw);
2080 	camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
2081 	camel_stream_close (stream, NULL, NULL);
2082 
2083 	byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
2084 
2085 	if (!byte_array->data) {
2086 		g_object_unref (stream);
2087 		g_free (mime_type);
2088 		goto out;
2089 	}
2090 
2091 	base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
2092 
2093 	filename = camel_mime_part_get_filename (mime_part);
2094 	/* Insert filename before base64 data */
2095 	src = g_strconcat (filename, ";data:", mime_type, ";base64,", base64_encoded, NULL);
2096 
2097 	gtk_selection_data_set (
2098 		data,
2099 		gtk_selection_data_get_data_type (data),
2100 		gtk_selection_data_get_format (data),
2101 		(const guchar *) src, strlen (src));
2102 
2103 	g_free (src);
2104 	g_free (base64_encoded);
2105 	g_free (mime_type);
2106 	g_object_unref (stream);
2107  out:
2108 	g_free (uri);
2109 }
2110 
2111 static void
e_mail_display_test_change_and_update_fonts_cb(EMailDisplay * mail_display,const gchar * key,GSettings * settings)2112 e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display,
2113 						const gchar *key,
2114 						GSettings *settings)
2115 {
2116 	GVariant *new_value, *old_value;
2117 
2118 	new_value = g_settings_get_value (settings, key);
2119 	old_value = g_hash_table_lookup (mail_display->priv->old_settings, key);
2120 
2121 	if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
2122 		if (new_value)
2123 			g_hash_table_insert (mail_display->priv->old_settings, g_strdup (key), new_value);
2124 		else
2125 			g_hash_table_remove (mail_display->priv->old_settings, key);
2126 
2127 		e_web_view_update_fonts (E_WEB_VIEW (mail_display));
2128 	} else if (new_value) {
2129 		g_variant_unref (new_value);
2130 	}
2131 }
2132 
2133 static void
mail_display_web_process_crashed_cb(EMailDisplay * display)2134 mail_display_web_process_crashed_cb (EMailDisplay *display)
2135 {
2136 	EAlertSink *alert_sink;
2137 
2138 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2139 
2140 	/* Cannot use the EWebView, because it places the alerts inside itself */
2141 	alert_sink = e_shell_utils_find_alternate_alert_sink (GTK_WIDGET (display));
2142 	if (alert_sink)
2143 		e_alert_submit (alert_sink, "mail:webkit-web-process-crashed", NULL);
2144 }
2145 
2146 static EMailPart *
e_mail_display_ref_mail_part(EMailDisplay * mail_display,const gchar * uri)2147 e_mail_display_ref_mail_part (EMailDisplay *mail_display,
2148 			      const gchar *uri)
2149 {
2150 	EMailPartList *part_list;
2151 
2152 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), NULL);
2153 	g_return_val_if_fail (uri != NULL, NULL);
2154 
2155 	part_list = e_mail_display_get_part_list (mail_display);
2156 	if (!part_list)
2157 		return NULL;
2158 
2159 	return e_mail_part_list_ref_part (part_list, uri);
2160 }
2161 
2162 static CamelMimePart *
e_mail_display_cid_resolver_ref_part(ECidResolver * resolver,const gchar * uri)2163 e_mail_display_cid_resolver_ref_part (ECidResolver *resolver,
2164 				      const gchar *uri)
2165 {
2166 	EMailPart *mail_part;
2167 	CamelMimePart *mime_part;
2168 
2169 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (resolver), NULL);
2170 	g_return_val_if_fail (uri != NULL, NULL);
2171 
2172 	mail_part = e_mail_display_ref_mail_part (E_MAIL_DISPLAY (resolver), uri);
2173 	if (!mail_part)
2174 		return NULL;
2175 
2176 	mime_part = e_mail_part_ref_mime_part (mail_part);
2177 
2178 	g_object_unref (mail_part);
2179 
2180 	return mime_part;
2181 }
2182 
2183 static gchar *
e_mail_display_cid_resolver_dup_mime_type(ECidResolver * resolver,const gchar * uri)2184 e_mail_display_cid_resolver_dup_mime_type (ECidResolver *resolver,
2185 					   const gchar *uri)
2186 {
2187 	EMailPart *mail_part;
2188 	gchar *mime_type;
2189 
2190 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (resolver), NULL);
2191 	g_return_val_if_fail (uri != NULL, NULL);
2192 
2193 	mail_part = e_mail_display_ref_mail_part (E_MAIL_DISPLAY (resolver), uri);
2194 	if (!mail_part)
2195 		return NULL;
2196 
2197 	mime_type = g_strdup (e_mail_part_get_mime_type (mail_part));
2198 
2199 	g_object_unref (mail_part);
2200 
2201 	return mime_type;
2202 }
2203 
2204 static void
e_mail_display_cid_resolver_init(ECidResolverInterface * iface)2205 e_mail_display_cid_resolver_init (ECidResolverInterface *iface)
2206 {
2207 	iface->ref_part = e_mail_display_cid_resolver_ref_part;
2208 	iface->dup_mime_type = e_mail_display_cid_resolver_dup_mime_type;
2209 }
2210 
2211 static void
e_mail_display_class_init(EMailDisplayClass * class)2212 e_mail_display_class_init (EMailDisplayClass *class)
2213 {
2214 	GObjectClass *object_class;
2215 	EWebViewClass *web_view_class;
2216 	GtkWidgetClass *widget_class;
2217 
2218 	g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
2219 
2220 	object_class = G_OBJECT_CLASS (class);
2221 	object_class->constructed = mail_display_constructed;
2222 	object_class->set_property = mail_display_set_property;
2223 	object_class->get_property = mail_display_get_property;
2224 	object_class->dispose = mail_display_dispose;
2225 	object_class->finalize = mail_display_finalize;
2226 
2227 	widget_class = GTK_WIDGET_CLASS (class);
2228 	widget_class->realize = mail_display_realize;
2229 	widget_class->style_updated = mail_display_style_updated;
2230 
2231 	web_view_class = E_WEB_VIEW_CLASS (class);
2232 	web_view_class->suggest_filename = mail_display_suggest_filename;
2233 	web_view_class->set_fonts = mail_display_set_fonts;
2234 	web_view_class->before_popup_event = mail_display_before_popup_event;
2235 
2236 	g_object_class_install_property (
2237 		object_class,
2238 		PROP_ATTACHMENT_STORE,
2239 		g_param_spec_object (
2240 			"attachment-store",
2241 			"Attachment Store",
2242 			NULL,
2243 			E_TYPE_ATTACHMENT_STORE,
2244 			G_PARAM_READABLE |
2245 			G_PARAM_STATIC_STRINGS));
2246 
2247 	g_object_class_install_property (
2248 		object_class,
2249 		PROP_ATTACHMENT_VIEW,
2250 		g_param_spec_object (
2251 			"attachment-view",
2252 			"Attachment View",
2253 			NULL,
2254 			E_TYPE_ATTACHMENT_VIEW,
2255 			G_PARAM_READABLE |
2256 			G_PARAM_STATIC_STRINGS));
2257 
2258 	g_object_class_install_property (
2259 		object_class,
2260 		PROP_FORMATTER,
2261 		g_param_spec_pointer (
2262 			"formatter",
2263 			"Mail Formatter",
2264 			NULL,
2265 			G_PARAM_READABLE |
2266 			G_PARAM_STATIC_STRINGS));
2267 
2268 	g_object_class_install_property (
2269 		object_class,
2270 		PROP_HEADERS_COLLAPSABLE,
2271 		g_param_spec_boolean (
2272 			"headers-collapsable",
2273 			"Headers Collapsable",
2274 			NULL,
2275 			FALSE,
2276 			G_PARAM_READWRITE |
2277 			G_PARAM_STATIC_STRINGS));
2278 
2279 	g_object_class_install_property (
2280 		object_class,
2281 		PROP_HEADERS_COLLAPSED,
2282 		g_param_spec_boolean (
2283 			"headers-collapsed",
2284 			"Headers Collapsed",
2285 			NULL,
2286 			FALSE,
2287 			G_PARAM_READWRITE |
2288 			G_PARAM_STATIC_STRINGS));
2289 
2290 	g_object_class_install_property (
2291 		object_class,
2292 		PROP_MODE,
2293 		g_param_spec_enum (
2294 			"mode",
2295 			"Mode",
2296 			NULL,
2297 			E_TYPE_MAIL_FORMATTER_MODE,
2298 			E_MAIL_FORMATTER_MODE_NORMAL,
2299 			G_PARAM_READWRITE |
2300 			G_PARAM_STATIC_STRINGS));
2301 
2302 	g_object_class_install_property (
2303 		object_class,
2304 		PROP_PART_LIST,
2305 		g_param_spec_pointer (
2306 			"part-list",
2307 			"Part List",
2308 			NULL,
2309 			G_PARAM_READWRITE |
2310 			G_PARAM_STATIC_STRINGS));
2311 
2312 	g_object_class_install_property (
2313 		object_class,
2314 		PROP_REMOTE_CONTENT,
2315 		g_param_spec_object (
2316 			"remote-content",
2317 			"Mail Remote Content",
2318 			NULL,
2319 			E_TYPE_MAIL_REMOTE_CONTENT,
2320 			G_PARAM_READWRITE |
2321 			G_PARAM_STATIC_STRINGS));
2322 
2323 	signals[REMOTE_CONTENT_CLICKED] = g_signal_new (
2324 		"remote-content-clicked",
2325 		G_TYPE_FROM_CLASS (class),
2326 		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
2327 		0,
2328 		NULL, NULL,
2329 		g_cclosure_marshal_VOID__BOXED,
2330 		G_TYPE_NONE, 1,
2331 		GDK_TYPE_RECTANGLE);
2332 }
2333 
2334 static void
e_mail_display_init(EMailDisplay * display)2335 e_mail_display_init (EMailDisplay *display)
2336 {
2337 	GtkUIManager *ui_manager;
2338 	GtkActionGroup *actions;
2339 	GList *acts_list, *link;
2340 
2341 	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
2342 
2343 	display->priv->attachment_store = E_ATTACHMENT_STORE (e_attachment_store_new ());
2344 	display->priv->attachment_flags = g_hash_table_new (g_direct_hash, g_direct_equal);
2345 	display->priv->attachment_inline_group = gtk_action_group_new ("e-mail-display-attachment-inline");
2346 	display->priv->attachment_accel_action_group = gtk_action_group_new ("e-mail-display-attachment-accel");
2347 	display->priv->attachment_accel_group = gtk_accel_group_new ();
2348 
2349 	gtk_action_group_add_actions (
2350 		display->priv->attachment_inline_group, attachment_inline_entries,
2351 		G_N_ELEMENTS (attachment_inline_entries), display);
2352 	gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE);
2353 
2354 	gtk_action_group_set_accel_group (display->priv->attachment_accel_action_group,
2355 		display->priv->attachment_accel_group);
2356 
2357 	gtk_action_group_add_actions (
2358 		display->priv->attachment_accel_action_group, accel_entries,
2359 		G_N_ELEMENTS (accel_entries), display);
2360 
2361 	acts_list = gtk_action_group_list_actions (display->priv->attachment_accel_action_group);
2362 
2363 	for (link = acts_list; link; link = g_list_next (link)) {
2364 		GtkAction *action = link->data;
2365 
2366 		gtk_action_connect_accelerator (action);
2367 	}
2368 
2369 	g_list_free (acts_list);
2370 
2371 	g_signal_connect (display->priv->attachment_store, "attachment-added",
2372 		G_CALLBACK (mail_display_attachment_added_cb), display);
2373 	g_signal_connect (display->priv->attachment_store, "attachment-removed",
2374 		G_CALLBACK (mail_display_attachment_removed_cb), display);
2375 
2376 	display->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
2377 
2378 	/* Set invalid mode so that MODE property initialization is run
2379 	 * completely (see e_mail_display_set_mode) */
2380 	display->priv->mode = E_MAIL_FORMATTER_MODE_INVALID;
2381 	e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_NORMAL);
2382 	display->priv->force_image_load = FALSE;
2383 	display->priv->scheduled_reload = 0;
2384 
2385 	g_signal_connect (
2386 		display, "web-process-crashed",
2387 		G_CALLBACK (mail_display_web_process_crashed_cb), NULL);
2388 
2389 	g_signal_connect (
2390 		display, "decide-policy",
2391 		G_CALLBACK (decide_policy_cb), NULL);
2392 
2393 	g_signal_connect (
2394 		display, "process-mailto",
2395 		G_CALLBACK (mail_display_process_mailto), NULL);
2396 
2397 	g_signal_connect_after (
2398 		display, "drag-data-get",
2399 		G_CALLBACK (mail_display_drag_data_get), display);
2400 
2401 	display->priv->settings = e_util_ref_settings ("org.gnome.evolution.mail");
2402 	g_signal_connect_swapped (
2403 		display->priv->settings , "changed::monospace-font",
2404 		G_CALLBACK (e_mail_display_test_change_and_update_fonts_cb), display);
2405 	g_signal_connect_swapped (
2406 		display->priv->settings , "changed::variable-width-font",
2407 		G_CALLBACK (e_mail_display_test_change_and_update_fonts_cb), display);
2408 	g_signal_connect_swapped (
2409 		display->priv->settings , "changed::use-custom-font",
2410 		G_CALLBACK (e_mail_display_test_change_and_update_fonts_cb), display);
2411 
2412 	g_signal_connect (
2413 		display, "load-changed",
2414 		G_CALLBACK (mail_display_load_changed_cb), NULL);
2415 
2416 	g_signal_connect (
2417 		display, "content-loaded",
2418 		G_CALLBACK (mail_display_content_loaded_cb), NULL);
2419 
2420 	actions = e_web_view_get_action_group (E_WEB_VIEW (display), "mailto");
2421 	gtk_action_group_add_actions (
2422 		actions, mailto_entries,
2423 		G_N_ELEMENTS (mailto_entries), display);
2424 	ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
2425 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
2426 
2427 	g_mutex_init (&display->priv->remote_content_lock);
2428 	display->priv->remote_content = NULL;
2429 	display->priv->skipped_remote_content_sites = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
2430 
2431 	g_signal_connect (display, "uri-requested", G_CALLBACK (mail_display_uri_requested_cb), NULL);
2432 
2433 	if (emd_global_http_cache == NULL) {
2434 		const gchar *user_cache_dir;
2435 		GError *error = NULL;
2436 
2437 		user_cache_dir = e_get_user_cache_dir ();
2438 		emd_global_http_cache = camel_data_cache_new (user_cache_dir, &error);
2439 
2440 		if (emd_global_http_cache) {
2441 			/* cache expiry - 2 hour access, 1 day max */
2442 			camel_data_cache_set_expire_age (
2443 				emd_global_http_cache, 24 * 60 * 60);
2444 			camel_data_cache_set_expire_access (
2445 				emd_global_http_cache, 2 * 60 * 60);
2446 		} else {
2447 			e_alert_submit (
2448 				E_ALERT_SINK (display), "mail:folder-open",
2449 				error ? error->message : _("Unknown error"), NULL);
2450 			g_clear_error (&error);
2451 		}
2452 	}
2453 }
2454 
2455 static void
e_mail_display_update_colors(EMailDisplay * display,GParamSpec * param_spec,EMailFormatter * formatter)2456 e_mail_display_update_colors (EMailDisplay *display,
2457                               GParamSpec *param_spec,
2458                               EMailFormatter *formatter)
2459 {
2460 	GdkRGBA *color = NULL;
2461 	gchar *color_value;
2462 
2463 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2464 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
2465 
2466 	g_object_get (formatter, param_spec->name, &color, NULL);
2467 
2468 	color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color));
2469 
2470 	add_color_css_rule_for_web_view (
2471 		E_WEB_VIEW (display),
2472 		"*",
2473 		param_spec->name,
2474 		color_value);
2475 
2476 	gdk_rgba_free (color);
2477 	g_free (color_value);
2478 }
2479 
2480 static void
e_mail_display_claim_attachment(EMailFormatter * formatter,EAttachment * attachment,gpointer user_data)2481 e_mail_display_claim_attachment (EMailFormatter *formatter,
2482 				 EAttachment *attachment,
2483 				 gpointer user_data)
2484 {
2485 	EMailDisplay *display = user_data;
2486 	GList *attachments;
2487 
2488 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
2489 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
2490 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2491 
2492 	attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
2493 
2494 	if (!g_list_find (attachments, attachment)) {
2495 		e_attachment_store_add_attachment (display->priv->attachment_store, attachment);
2496 
2497 		if (e_attachment_is_mail_note (attachment)) {
2498 			CamelFolder *folder;
2499 			const gchar *message_uid;
2500 
2501 			folder = e_mail_part_list_get_folder (display->priv->part_list);
2502 			message_uid = e_mail_part_list_get_message_uid (display->priv->part_list);
2503 
2504 			if (folder && message_uid) {
2505 				CamelMessageInfo *info;
2506 
2507 				info = camel_folder_get_message_info (folder, message_uid);
2508 				if (info) {
2509 					if (!camel_message_info_get_user_flag (info, E_MAIL_NOTES_USER_FLAG))
2510 						camel_message_info_set_user_flag (info, E_MAIL_NOTES_USER_FLAG, TRUE);
2511 					g_clear_object (&info);
2512 				}
2513 			}
2514 		}
2515 	}
2516 
2517 	g_list_free_full (attachments, g_object_unref);
2518 }
2519 
2520 GtkWidget *
e_mail_display_new(EMailRemoteContent * remote_content)2521 e_mail_display_new (EMailRemoteContent *remote_content)
2522 {
2523 	return g_object_new (E_TYPE_MAIL_DISPLAY,
2524 		"remote-content", remote_content,
2525 		NULL);
2526 }
2527 
2528 EAttachmentStore *
e_mail_display_get_attachment_store(EMailDisplay * display)2529 e_mail_display_get_attachment_store (EMailDisplay *display)
2530 {
2531 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2532 
2533 	return display->priv->attachment_store;
2534 }
2535 
2536 EAttachmentView *
e_mail_display_get_attachment_view(EMailDisplay * display)2537 e_mail_display_get_attachment_view (EMailDisplay *display)
2538 {
2539 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2540 
2541 	return display->priv->attachment_view;
2542 }
2543 
2544 EMailFormatterMode
e_mail_display_get_mode(EMailDisplay * display)2545 e_mail_display_get_mode (EMailDisplay *display)
2546 {
2547 	g_return_val_if_fail (
2548 		E_IS_MAIL_DISPLAY (display),
2549 		E_MAIL_FORMATTER_MODE_INVALID);
2550 
2551 	return display->priv->mode;
2552 }
2553 
2554 void
e_mail_display_set_mode(EMailDisplay * display,EMailFormatterMode mode)2555 e_mail_display_set_mode (EMailDisplay *display,
2556                          EMailFormatterMode mode)
2557 {
2558 	EMailFormatter *formatter;
2559 
2560 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2561 
2562 	if (display->priv->mode == mode)
2563 		return;
2564 
2565 	display->priv->mode = mode;
2566 
2567 	if (display->priv->mode == E_MAIL_FORMATTER_MODE_PRINTING)
2568 		formatter = e_mail_formatter_print_new ();
2569 	else
2570 		formatter = e_mail_formatter_new ();
2571 
2572 	g_clear_object (&display->priv->formatter);
2573 	display->priv->formatter = formatter;
2574 	mail_display_update_formatter_colors (display);
2575 
2576 	e_signal_connect_notify (
2577 		formatter, "notify::image-loading-policy",
2578 		G_CALLBACK (formatter_image_loading_policy_changed_cb),
2579 		display);
2580 
2581 	e_signal_connect_notify_object (
2582 		formatter, "notify::charset",
2583 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2584 
2585 	e_signal_connect_notify_object (
2586 		formatter, "notify::image-loading-policy",
2587 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2588 
2589 	e_signal_connect_notify_object (
2590 		formatter, "notify::mark-citations",
2591 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2592 
2593 	e_signal_connect_notify_object (
2594 		formatter, "notify::show-sender-photo",
2595 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2596 
2597 	e_signal_connect_notify_object (
2598 		formatter, "notify::show-real-date",
2599 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2600 
2601 	e_signal_connect_notify_object (
2602 		formatter, "notify::animate-images",
2603 		G_CALLBACK (e_mail_display_reload), display, G_CONNECT_SWAPPED);
2604 
2605 	e_signal_connect_notify_object (
2606 		formatter, "notify::body-color",
2607 		G_CALLBACK (e_mail_display_update_colors), display, G_CONNECT_SWAPPED);
2608 
2609 	e_signal_connect_notify_object (
2610 		formatter, "notify::citation-color",
2611 		G_CALLBACK (e_mail_display_update_colors), display, G_CONNECT_SWAPPED);
2612 
2613 	e_signal_connect_notify_object (
2614 		formatter, "notify::frame-color",
2615 		G_CALLBACK (e_mail_display_update_colors), display, G_CONNECT_SWAPPED);
2616 
2617 	e_signal_connect_notify_object (
2618 		formatter, "notify::header-color",
2619 		G_CALLBACK (e_mail_display_update_colors), display, G_CONNECT_SWAPPED);
2620 
2621 	g_object_connect (formatter,
2622 		"swapped-object-signal::need-redraw",
2623 			G_CALLBACK (e_mail_display_reload), display,
2624 		NULL);
2625 
2626 	g_signal_connect (formatter, "claim-attachment", G_CALLBACK (e_mail_display_claim_attachment), display);
2627 
2628 	e_mail_display_reload (display);
2629 
2630 	g_object_notify (G_OBJECT (display), "mode");
2631 }
2632 
2633 EMailFormatter *
e_mail_display_get_formatter(EMailDisplay * display)2634 e_mail_display_get_formatter (EMailDisplay *display)
2635 {
2636 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2637 
2638 	return display->priv->formatter;
2639 }
2640 
2641 EMailPartList *
e_mail_display_get_part_list(EMailDisplay * display)2642 e_mail_display_get_part_list (EMailDisplay *display)
2643 {
2644 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2645 
2646 	return display->priv->part_list;
2647 }
2648 
2649 void
e_mail_display_set_part_list(EMailDisplay * display,EMailPartList * part_list)2650 e_mail_display_set_part_list (EMailDisplay *display,
2651                               EMailPartList *part_list)
2652 {
2653 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2654 
2655 	if (display->priv->part_list == part_list)
2656 		return;
2657 
2658 	if (part_list != NULL) {
2659 		g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
2660 		g_object_ref (part_list);
2661 	}
2662 
2663 	if (display->priv->part_list != NULL)
2664 		g_object_unref (display->priv->part_list);
2665 
2666 	display->priv->part_list = part_list;
2667 
2668 	g_object_notify (G_OBJECT (display), "part-list");
2669 }
2670 
2671 gboolean
e_mail_display_get_headers_collapsable(EMailDisplay * display)2672 e_mail_display_get_headers_collapsable (EMailDisplay *display)
2673 {
2674 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
2675 
2676 	return display->priv->headers_collapsable;
2677 }
2678 
2679 void
e_mail_display_set_headers_collapsable(EMailDisplay * display,gboolean collapsable)2680 e_mail_display_set_headers_collapsable (EMailDisplay *display,
2681                                         gboolean collapsable)
2682 {
2683 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2684 
2685 	if (display->priv->headers_collapsable == collapsable)
2686 		return;
2687 
2688 	display->priv->headers_collapsable = collapsable;
2689 	e_mail_display_reload (display);
2690 
2691 	g_object_notify (G_OBJECT (display), "headers-collapsable");
2692 }
2693 
2694 gboolean
e_mail_display_get_headers_collapsed(EMailDisplay * display)2695 e_mail_display_get_headers_collapsed (EMailDisplay *display)
2696 {
2697 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
2698 
2699 	if (display->priv->headers_collapsable)
2700 		return display->priv->headers_collapsed;
2701 
2702 	return FALSE;
2703 }
2704 
2705 void
e_mail_display_set_headers_collapsed(EMailDisplay * display,gboolean collapsed)2706 e_mail_display_set_headers_collapsed (EMailDisplay *display,
2707                                       gboolean collapsed)
2708 {
2709 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2710 
2711 	if (display->priv->headers_collapsed == collapsed)
2712 		return;
2713 
2714 	display->priv->headers_collapsed = collapsed;
2715 
2716 	g_object_notify (G_OBJECT (display), "headers-collapsed");
2717 }
2718 
2719 void
e_mail_display_load(EMailDisplay * display,const gchar * msg_uri)2720 e_mail_display_load (EMailDisplay *display,
2721                      const gchar *msg_uri)
2722 {
2723 	EMailPartList *part_list;
2724 	CamelFolder *folder;
2725 	const gchar *message_uid;
2726 	const gchar *default_charset, *charset;
2727 	gchar *uri;
2728 
2729 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2730 
2731 	e_mail_display_set_force_load_images (display, FALSE);
2732 
2733 	part_list = display->priv->part_list;
2734 	if (part_list == NULL) {
2735 		e_web_view_clear (E_WEB_VIEW (display));
2736 		return;
2737 	}
2738 
2739 	folder = e_mail_part_list_get_folder (part_list);
2740 	message_uid = e_mail_part_list_get_message_uid (part_list);
2741 	default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
2742 	charset = e_mail_formatter_get_charset (display->priv->formatter);
2743 
2744 	if (!default_charset)
2745 		default_charset = "";
2746 	if (!charset)
2747 		charset = "";
2748 
2749 	uri = e_mail_part_build_uri (
2750 		folder, message_uid,
2751 		"mode", G_TYPE_INT, display->priv->mode,
2752 		"headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable,
2753 		"headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed,
2754 		"formatter_default_charset", G_TYPE_STRING, default_charset,
2755 		"formatter_charset", G_TYPE_STRING, charset,
2756 		NULL);
2757 
2758 	e_web_view_load_uri (E_WEB_VIEW (display), uri);
2759 
2760 	g_free (uri);
2761 }
2762 
2763 static gboolean
do_reload_display(EMailDisplay * display)2764 do_reload_display (EMailDisplay *display)
2765 {
2766 	EWebView *web_view;
2767 	gchar *uri, *query;
2768 	GHashTable *table;
2769 	SoupURI *soup_uri;
2770 	gchar *mode, *collapsable, *collapsed;
2771 	const gchar *default_charset, *charset;
2772 
2773 	web_view = E_WEB_VIEW (display);
2774 	uri = (gchar *) webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view));
2775 
2776 	display->priv->scheduled_reload = 0;
2777 
2778 	if (!uri || !*uri || g_ascii_strcasecmp (uri, "about:blank") == 0)
2779 		return FALSE;
2780 
2781 	if (strstr (uri, "?") == NULL) {
2782 		e_web_view_reload (web_view);
2783 		return FALSE;
2784 	}
2785 
2786 	soup_uri = soup_uri_new (uri);
2787 
2788 	mode = g_strdup_printf ("%d", display->priv->mode);
2789 	collapsable = g_strdup_printf ("%d", display->priv->headers_collapsable);
2790 	collapsed = g_strdup_printf ("%d", display->priv->headers_collapsed);
2791 	default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
2792 	charset = e_mail_formatter_get_charset (display->priv->formatter);
2793 
2794 	if (!default_charset)
2795 		default_charset = "";
2796 	if (!charset)
2797 		charset = "";
2798 
2799 	table = soup_form_decode (soup_uri->query);
2800 	g_hash_table_replace (
2801 		table, g_strdup ("mode"), mode);
2802 	g_hash_table_replace (
2803 		table, g_strdup ("headers_collapsable"), collapsable);
2804 	g_hash_table_replace (
2805 		table, g_strdup ("headers_collapsed"), collapsed);
2806 	g_hash_table_replace (
2807 		table, g_strdup ("formatter_default_charset"), (gpointer) default_charset);
2808 	g_hash_table_replace (
2809 		table, g_strdup ("formatter_charset"), (gpointer) charset);
2810 
2811 	query = soup_form_encode_hash (table);
2812 
2813 	/* The hash table does not free custom values upon destruction */
2814 	g_free (mode);
2815 	g_free (collapsable);
2816 	g_free (collapsed);
2817 	g_hash_table_destroy (table);
2818 
2819 	soup_uri_set_query (soup_uri, query);
2820 	g_free (query);
2821 
2822 	uri = soup_uri_to_string (soup_uri, FALSE);
2823 	e_web_view_load_uri (web_view, uri);
2824 	g_free (uri);
2825 	soup_uri_free (soup_uri);
2826 
2827 	return FALSE;
2828 }
2829 
2830 void
e_mail_display_reload(EMailDisplay * display)2831 e_mail_display_reload (EMailDisplay *display)
2832 {
2833 	const gchar *uri;
2834 
2835 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2836 
2837 	uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (display));
2838 
2839 	if (!uri || !*uri || g_ascii_strcasecmp (uri, "about:blank") == 0 ||
2840 	    display->priv->scheduled_reload > 0)
2841 		return;
2842 
2843 	/* Schedule reloading if neccessary.
2844 	 * Prioritize ahead of GTK+ redraws. */
2845 	display->priv->scheduled_reload = g_idle_add_full (
2846 		G_PRIORITY_HIGH_IDLE,
2847 		(GSourceFunc) do_reload_display, display, NULL);
2848 }
2849 
2850 GtkAction *
e_mail_display_get_action(EMailDisplay * display,const gchar * action_name)2851 e_mail_display_get_action (EMailDisplay *display,
2852                            const gchar *action_name)
2853 {
2854 	GtkAction *action;
2855 
2856 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2857 	g_return_val_if_fail (action_name != NULL, NULL);
2858 
2859 	action = e_web_view_get_action (E_WEB_VIEW (display), action_name);
2860 
2861 	return action;
2862 }
2863 
2864 void
e_mail_display_set_status(EMailDisplay * display,const gchar * status)2865 e_mail_display_set_status (EMailDisplay *display,
2866                            const gchar *status)
2867 {
2868 	gchar *str;
2869 
2870 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2871 
2872 	str = g_strdup_printf (
2873 		"<!DOCTYPE HTML>\n"
2874 		"<html>\n"
2875 		"<head>\n"
2876 		"<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
2877 		"<meta name=\"color-scheme\" content=\"light dark\">\n"
2878 		"<title>Evolution Mail Display</title>\n"
2879 		"</head>\n"
2880 		"<body class=\"-e-web-view-background-color e-web-view-text-color\">"
2881 		"  <style>html, body { height: 100%%; }</style>\n"
2882 		"  <table border=\"0\" width=\"100%%\" height=\"100%%\">\n"
2883 		"    <tr height=\"100%%\" valign=\"middle\">\n"
2884 		"      <td width=\"100%%\" align=\"center\">\n"
2885 		"        <strong>%s</strong>\n"
2886 		"      </td>\n"
2887 		"    </tr>\n"
2888 		"  </table>\n"
2889 		"</body>\n"
2890 		"</html>\n",
2891 		status);
2892 
2893 	e_web_view_load_string (E_WEB_VIEW (display), str);
2894 
2895 	g_free (str);
2896 }
2897 
2898 void
e_mail_display_load_images(EMailDisplay * display)2899 e_mail_display_load_images (EMailDisplay *display)
2900 {
2901 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2902 
2903 	e_mail_display_set_force_load_images (display, TRUE);
2904 	e_web_view_reload (E_WEB_VIEW (display));
2905 }
2906 
2907 void
e_mail_display_set_force_load_images(EMailDisplay * display,gboolean force_load_images)2908 e_mail_display_set_force_load_images (EMailDisplay *display,
2909                                       gboolean force_load_images)
2910 {
2911 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2912 
2913 	if ((display->priv->force_image_load ? 1 : 0) == (force_load_images ? 1 : 0))
2914 		return;
2915 
2916 	display->priv->force_image_load = force_load_images;
2917 }
2918 
2919 gboolean
e_mail_display_has_skipped_remote_content_sites(EMailDisplay * display)2920 e_mail_display_has_skipped_remote_content_sites (EMailDisplay *display)
2921 {
2922 	gboolean has_any;
2923 
2924 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
2925 
2926 	g_mutex_lock (&display->priv->remote_content_lock);
2927 
2928 	has_any = g_hash_table_size (display->priv->skipped_remote_content_sites) > 0;
2929 
2930 	g_mutex_unlock (&display->priv->remote_content_lock);
2931 
2932 	return has_any;
2933 }
2934 
2935 /* Free with g_list_free_full (uris, g_free); */
2936 GList *
e_mail_display_get_skipped_remote_content_sites(EMailDisplay * display)2937 e_mail_display_get_skipped_remote_content_sites (EMailDisplay *display)
2938 {
2939 	GList *uris, *link;
2940 
2941 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2942 
2943 	g_mutex_lock (&display->priv->remote_content_lock);
2944 
2945 	uris = g_hash_table_get_keys (display->priv->skipped_remote_content_sites);
2946 
2947 	for (link = uris; link; link = g_list_next (link)) {
2948 		link->data = g_strdup (link->data);
2949 	}
2950 
2951 	g_mutex_unlock (&display->priv->remote_content_lock);
2952 
2953 	return uris;
2954 }
2955 
2956 EMailRemoteContent *
e_mail_display_ref_remote_content(EMailDisplay * display)2957 e_mail_display_ref_remote_content (EMailDisplay *display)
2958 {
2959 	EMailRemoteContent *remote_content;
2960 
2961 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2962 
2963 	g_mutex_lock (&display->priv->remote_content_lock);
2964 
2965 	remote_content = display->priv->remote_content;
2966 	if (remote_content)
2967 		g_object_ref (remote_content);
2968 
2969 	g_mutex_unlock (&display->priv->remote_content_lock);
2970 
2971 	return remote_content;
2972 }
2973 
2974 void
e_mail_display_set_remote_content(EMailDisplay * display,EMailRemoteContent * remote_content)2975 e_mail_display_set_remote_content (EMailDisplay *display,
2976 				   EMailRemoteContent *remote_content)
2977 {
2978 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2979 	if (remote_content)
2980 		g_return_if_fail (E_IS_MAIL_REMOTE_CONTENT (remote_content));
2981 
2982 	g_mutex_lock (&display->priv->remote_content_lock);
2983 
2984 	if (display->priv->remote_content == remote_content) {
2985 		g_mutex_unlock (&display->priv->remote_content_lock);
2986 		return;
2987 	}
2988 
2989 	g_clear_object (&display->priv->remote_content);
2990 	display->priv->remote_content = remote_content ? g_object_ref (remote_content) : NULL;
2991 
2992 	g_mutex_unlock (&display->priv->remote_content_lock);
2993 }
2994 
2995 gboolean
e_mail_display_process_magic_spacebar(EMailDisplay * display,gboolean towards_bottom)2996 e_mail_display_process_magic_spacebar (EMailDisplay *display,
2997 				       gboolean towards_bottom)
2998 {
2999 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
3000 
3001 	if ((towards_bottom && !(display->priv->magic_spacebar_state & E_MAGIC_SPACEBAR_CAN_GO_BOTTOM)) ||
3002 	    (!towards_bottom && !(display->priv->magic_spacebar_state & E_MAGIC_SPACEBAR_CAN_GO_TOP)))
3003 		return FALSE;
3004 
3005 	e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (display), e_web_view_get_cancellable (E_WEB_VIEW (display)),
3006 		"Evo.MailDisplayProcessMagicSpacebar(%x);",
3007 		towards_bottom);
3008 
3009 	return TRUE;
3010 }
3011 
3012 gboolean
e_mail_display_need_key_event(EMailDisplay * mail_display,const GdkEventKey * event)3013 e_mail_display_need_key_event (EMailDisplay *mail_display,
3014 			       const GdkEventKey *event)
3015 {
3016 	GtkAccelGroup *accel_group;
3017 	GdkModifierType accel_mods;
3018 	GQuark accel_quark;
3019 	gchar *accel_name;
3020 
3021 	if (!event)
3022 		return FALSE;
3023 
3024 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), FALSE);
3025 
3026 	accel_group = gtk_action_group_get_accel_group (mail_display->priv->attachment_accel_action_group);
3027 
3028 	if (!accel_group)
3029 		return FALSE;
3030 
3031 	accel_mods = event->state & gtk_accelerator_get_default_mod_mask ();
3032 	accel_name = gtk_accelerator_name (event->keyval, accel_mods);
3033 	accel_quark = g_quark_from_string (accel_name);
3034 	g_free (accel_name);
3035 
3036 	return gtk_accel_group_activate (accel_group, accel_quark, G_OBJECT (mail_display),
3037 		event->keyval, accel_mods);
3038 }
3039