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