1 /*
2  * e-mail-formatter.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 
18 #include "e-mail-formatter.h"
19 
20 #include <string.h>
21 
22 #include <gdk/gdk.h>
23 #include <libebackend/libebackend.h>
24 
25 #include <e-util/e-util.h>
26 #include <shell/e-shell.h>
27 
28 #include "e-mail-formatter-enumtypes.h"
29 #include "e-mail-formatter-extension.h"
30 #include "e-mail-formatter-utils.h"
31 #include "e-mail-part.h"
32 
33 #define d(x)
34 
35 #define E_MAIL_FORMATTER_GET_PRIVATE(obj) \
36 	(G_TYPE_INSTANCE_GET_PRIVATE \
37 	((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))
38 
39 #define STYLESHEET_URI "evo-file://$EVOLUTION_WEBKITDATADIR/webview.css"
40 
41 typedef struct _AsyncContext AsyncContext;
42 
43 struct _EMailFormatterPrivate {
44 	EImageLoadingPolicy image_loading_policy;
45 
46 	gboolean show_sender_photo;
47 	gboolean show_real_date;
48 	gboolean animate_images;
49 
50 	GMutex property_lock;
51 
52 	gchar *charset;
53 	gchar *default_charset;
54 };
55 
56 struct _AsyncContext {
57 	GOutputStream *stream;
58 	EMailPartList *part_list;
59 	EMailFormatterHeaderFlags flags;
60 	EMailFormatterMode mode;
61 };
62 
63 /* internal formatter extensions */
64 GType e_mail_formatter_attachment_get_type (void);
65 GType e_mail_formatter_audio_get_type (void);
66 GType e_mail_formatter_error_get_type (void);
67 GType e_mail_formatter_headers_get_type (void);
68 GType e_mail_formatter_image_get_type (void);
69 GType e_mail_formatter_message_rfc822_get_type (void);
70 GType e_mail_formatter_secure_button_get_type (void);
71 GType e_mail_formatter_source_get_type (void);
72 GType e_mail_formatter_text_enriched_get_type (void);
73 GType e_mail_formatter_text_html_get_type (void);
74 GType e_mail_formatter_text_plain_get_type (void);
75 
76 static gpointer e_mail_formatter_parent_class = 0;
77 
78 enum {
79 	PROP_0,
80 	PROP_ANIMATE_IMAGES,
81 	PROP_BODY_COLOR,
82 	PROP_CHARSET,
83 	PROP_CITATION_COLOR,
84 	PROP_CONTENT_COLOR,
85 	PROP_DEFAULT_CHARSET,
86 	PROP_FRAME_COLOR,
87 	PROP_HEADER_COLOR,
88 	PROP_IMAGE_LOADING_POLICY,
89 	PROP_MARK_CITATIONS,
90 	PROP_SHOW_REAL_DATE,
91 	PROP_SHOW_SENDER_PHOTO,
92 	PROP_TEXT_COLOR
93 };
94 
95 enum {
96 	NEED_REDRAW,
97 	CLAIM_ATTACHMENT,
98 	LAST_SIGNAL
99 };
100 
101 static gint signals[LAST_SIGNAL];
102 
103 static void
async_context_free(AsyncContext * async_context)104 async_context_free (AsyncContext *async_context)
105 {
106 	g_clear_object (&async_context->part_list);
107 	g_clear_object (&async_context->stream);
108 
109 	g_slice_free (AsyncContext, async_context);
110 }
111 
112 static EMailFormatterContext *
mail_formatter_create_context(EMailFormatter * formatter,EMailPartList * part_list,EMailFormatterMode mode,EMailFormatterHeaderFlags flags)113 mail_formatter_create_context (EMailFormatter *formatter,
114                                EMailPartList *part_list,
115                                EMailFormatterMode mode,
116                                EMailFormatterHeaderFlags flags)
117 {
118 	EMailFormatterClass *class;
119 	EMailFormatterContext *context;
120 
121 	class = E_MAIL_FORMATTER_GET_CLASS (formatter);
122 	g_return_val_if_fail (class != NULL, NULL);
123 
124 	g_warn_if_fail (class->context_size >= sizeof (EMailFormatterContext));
125 
126 	context = g_malloc0 (class->context_size);
127 	context->part_list = g_object_ref (part_list);
128 	context->mode = mode;
129 	context->flags = flags;
130 
131 	return context;
132 }
133 
134 static void
mail_formatter_free_context(EMailFormatterContext * context)135 mail_formatter_free_context (EMailFormatterContext *context)
136 {
137 	if (context->part_list != NULL)
138 		g_object_unref (context->part_list);
139 
140 	g_free (context);
141 }
142 
143 static void
shell_gone_cb(gpointer user_data,GObject * gone_extension_registry)144 shell_gone_cb (gpointer user_data,
145 	       GObject *gone_extension_registry)
146 {
147 	EMailFormatterClass *class = user_data;
148 
149 	g_return_if_fail (class != NULL);
150 
151 	g_clear_object (&class->extension_registry);
152 }
153 
154 static void
e_mail_formatter_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)155 e_mail_formatter_set_property (GObject *object,
156                                guint property_id,
157                                const GValue *value,
158                                GParamSpec *pspec)
159 {
160 	switch (property_id) {
161 		case PROP_ANIMATE_IMAGES:
162 			e_mail_formatter_set_animate_images (
163 				E_MAIL_FORMATTER (object),
164 				g_value_get_boolean (value));
165 			return;
166 
167 		case PROP_BODY_COLOR:
168 			e_mail_formatter_set_color (
169 				E_MAIL_FORMATTER (object),
170 				E_MAIL_FORMATTER_COLOR_BODY,
171 				g_value_get_boxed (value));
172 			return;
173 
174 		case PROP_CHARSET:
175 			e_mail_formatter_set_charset (
176 				E_MAIL_FORMATTER (object),
177 				g_value_get_string (value));
178 			return;
179 
180 		case PROP_CITATION_COLOR:
181 			e_mail_formatter_set_color (
182 				E_MAIL_FORMATTER (object),
183 				E_MAIL_FORMATTER_COLOR_CITATION,
184 				g_value_get_boxed (value));
185 			return;
186 
187 		case PROP_CONTENT_COLOR:
188 			e_mail_formatter_set_color (
189 				E_MAIL_FORMATTER (object),
190 				E_MAIL_FORMATTER_COLOR_CONTENT,
191 				g_value_get_boxed (value));
192 			return;
193 
194 		case PROP_DEFAULT_CHARSET:
195 			e_mail_formatter_set_default_charset (
196 				E_MAIL_FORMATTER (object),
197 				g_value_get_string (value));
198 			return;
199 
200 		case PROP_FRAME_COLOR:
201 			e_mail_formatter_set_color (
202 				E_MAIL_FORMATTER (object),
203 				E_MAIL_FORMATTER_COLOR_FRAME,
204 				g_value_get_boxed (value));
205 			return;
206 
207 		case PROP_HEADER_COLOR:
208 			e_mail_formatter_set_color (
209 				E_MAIL_FORMATTER (object),
210 				E_MAIL_FORMATTER_COLOR_HEADER,
211 				g_value_get_boxed (value));
212 			return;
213 
214 		case PROP_IMAGE_LOADING_POLICY:
215 			e_mail_formatter_set_image_loading_policy (
216 				E_MAIL_FORMATTER (object),
217 				g_value_get_enum (value));
218 			return;
219 
220 		case PROP_MARK_CITATIONS:
221 			e_mail_formatter_set_mark_citations (
222 				E_MAIL_FORMATTER (object),
223 				g_value_get_boolean (value));
224 			return;
225 
226 		case PROP_SHOW_REAL_DATE:
227 			e_mail_formatter_set_show_real_date (
228 				E_MAIL_FORMATTER (object),
229 				g_value_get_boolean (value));
230 			return;
231 
232 		case PROP_SHOW_SENDER_PHOTO:
233 			e_mail_formatter_set_show_sender_photo (
234 				E_MAIL_FORMATTER (object),
235 				g_value_get_boolean (value));
236 			return;
237 
238 		case PROP_TEXT_COLOR:
239 			e_mail_formatter_set_color (
240 				E_MAIL_FORMATTER (object),
241 				E_MAIL_FORMATTER_COLOR_TEXT,
242 				g_value_get_boxed (value));
243 			return;
244 	}
245 
246 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
247 }
248 
249 static void
e_mail_formatter_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)250 e_mail_formatter_get_property (GObject *object,
251                              guint property_id,
252                              GValue *value,
253                              GParamSpec *pspec)
254 {
255 	switch (property_id) {
256 		case PROP_ANIMATE_IMAGES:
257 			g_value_set_boolean (
258 				value,
259 				e_mail_formatter_get_animate_images (
260 				E_MAIL_FORMATTER (object)));
261 			return;
262 
263 		case PROP_BODY_COLOR:
264 			g_value_set_boxed (
265 				value,
266 				e_mail_formatter_get_color (
267 				E_MAIL_FORMATTER (object),
268 				E_MAIL_FORMATTER_COLOR_BODY));
269 			return;
270 
271 		case PROP_CHARSET:
272 			g_value_take_string (
273 				value,
274 				e_mail_formatter_dup_charset (
275 				E_MAIL_FORMATTER (object)));
276 			return;
277 
278 		case PROP_CITATION_COLOR:
279 			g_value_set_boxed (
280 				value,
281 				e_mail_formatter_get_color (
282 				E_MAIL_FORMATTER (object),
283 				E_MAIL_FORMATTER_COLOR_CITATION));
284 			return;
285 
286 		case PROP_CONTENT_COLOR:
287 			g_value_set_boxed (
288 				value,
289 				e_mail_formatter_get_color (
290 				E_MAIL_FORMATTER (object),
291 				E_MAIL_FORMATTER_COLOR_CONTENT));
292 			return;
293 
294 		case PROP_DEFAULT_CHARSET:
295 			g_value_take_string (
296 				value,
297 				e_mail_formatter_dup_default_charset (
298 				E_MAIL_FORMATTER (object)));
299 			return;
300 
301 		case PROP_FRAME_COLOR:
302 			g_value_set_boxed (
303 				value,
304 				e_mail_formatter_get_color (
305 				E_MAIL_FORMATTER (object),
306 				E_MAIL_FORMATTER_COLOR_FRAME));
307 			return;
308 
309 		case PROP_HEADER_COLOR:
310 			g_value_set_boxed (
311 				value,
312 				e_mail_formatter_get_color (
313 				E_MAIL_FORMATTER (object),
314 				E_MAIL_FORMATTER_COLOR_HEADER));
315 			return;
316 
317 		case PROP_IMAGE_LOADING_POLICY:
318 			g_value_set_enum (
319 				value,
320 				e_mail_formatter_get_image_loading_policy (
321 				E_MAIL_FORMATTER (object)));
322 			return;
323 
324 		case PROP_MARK_CITATIONS:
325 			g_value_set_boolean (
326 				value,
327 				e_mail_formatter_get_mark_citations (
328 				E_MAIL_FORMATTER (object)));
329 			return;
330 
331 		case PROP_SHOW_REAL_DATE:
332 			g_value_set_boolean (
333 				value,
334 				e_mail_formatter_get_show_real_date (
335 				E_MAIL_FORMATTER (object)));
336 			return;
337 
338 		case PROP_SHOW_SENDER_PHOTO:
339 			g_value_set_boolean (
340 				value,
341 				e_mail_formatter_get_show_sender_photo (
342 				E_MAIL_FORMATTER (object)));
343 			return;
344 
345 		case PROP_TEXT_COLOR:
346 			g_value_set_boxed (
347 				value,
348 				e_mail_formatter_get_color (
349 				E_MAIL_FORMATTER (object),
350 				E_MAIL_FORMATTER_COLOR_TEXT));
351 			return;
352 	}
353 
354 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
355 }
356 
357 static void
e_mail_formatter_finalize(GObject * object)358 e_mail_formatter_finalize (GObject *object)
359 {
360 	EMailFormatterPrivate *priv;
361 
362 	priv = E_MAIL_FORMATTER_GET_PRIVATE (object);
363 
364 	g_free (priv->charset);
365 	g_free (priv->default_charset);
366 
367 	g_mutex_clear (&priv->property_lock);
368 
369 	/* Chain up to parent's finalize() method. */
370 	G_OBJECT_CLASS (e_mail_formatter_parent_class)->finalize (object);
371 }
372 
373 static void
e_mail_formatter_constructed(GObject * object)374 e_mail_formatter_constructed (GObject *object)
375 {
376 	/* Chain up to parent's constructed() method. */
377 	G_OBJECT_CLASS (e_mail_formatter_parent_class)->constructed (object);
378 
379 	e_extensible_load_extensions (E_EXTENSIBLE (object));
380 }
381 
382 static void
mail_formatter_run(EMailFormatter * formatter,EMailFormatterContext * context,GOutputStream * stream,GCancellable * cancellable)383 mail_formatter_run (EMailFormatter *formatter,
384                     EMailFormatterContext *context,
385                     GOutputStream *stream,
386                     GCancellable *cancellable)
387 {
388 	GQueue queue = G_QUEUE_INIT;
389 	GList *head, *link;
390 	gchar *hdr;
391 	const gchar *string;
392 
393 	hdr = e_mail_formatter_get_html_header (formatter);
394 	g_output_stream_write_all (
395 		stream, hdr, strlen (hdr), NULL, cancellable, NULL);
396 	g_free (hdr);
397 
398 	e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
399 
400 	head = g_queue_peek_head_link (&queue);
401 
402 	for (link = head; link != NULL; link = g_list_next (link)) {
403 		EMailPart *part = link->data;
404 		const gchar *part_id;
405 		gboolean ok;
406 
407 		part_id = e_mail_part_get_id (part);
408 
409 		if (g_cancellable_is_cancelled (cancellable))
410 			break;
411 
412 		if (part->is_hidden && !part->is_error) {
413 			if (e_mail_part_id_has_suffix (part, ".rfc822")) {
414 				link = e_mail_formatter_find_rfc822_end_iter (link);
415 			}
416 
417 			if (link == NULL)
418 				break;
419 
420 			continue;
421 		}
422 
423 		if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING &&
424 		    !e_mail_part_get_is_printable (part))
425 			continue;
426 
427 		/* Force formatting as source if needed */
428 		if (context->mode != E_MAIL_FORMATTER_MODE_SOURCE) {
429 			const gchar *mime_type;
430 
431 			mime_type = e_mail_part_get_mime_type (part);
432 			if (mime_type == NULL)
433 				continue;
434 
435 			ok = e_mail_formatter_format_as (
436 				formatter, context, part, stream,
437 				mime_type, cancellable);
438 
439 			/* If the written part was message/rfc822 then
440 			 * jump to the end of the message, because content
441 			 * of the whole message has been formatted by
442 			 * message_rfc822 formatter */
443 			if (ok && e_mail_part_id_has_suffix (part, ".rfc822")) {
444 				link = e_mail_formatter_find_rfc822_end_iter (link);
445 
446 				if (link == NULL)
447 					break;
448 
449 				continue;
450 			}
451 
452 		} else {
453 			ok = FALSE;
454 		}
455 
456 		if (!ok) {
457 			/* We don't want to source these */
458 			if (e_mail_part_id_has_suffix (part, ".headers"))
459 				continue;
460 
461 			e_mail_formatter_format_as (
462 				formatter, context, part, stream,
463 				"application/vnd.evolution.source", cancellable);
464 
465 			/* .message is the entire message. There's nothing more
466 			 * to be written. */
467 			if (g_strcmp0 (part_id, ".message") == 0)
468 				break;
469 
470 			/* If we just wrote source of a rfc822 message, then jump
471 			 * behind the message (otherwise source of all parts
472 			 * would be rendered twice) */
473 			if (e_mail_part_id_has_suffix (part, ".rfc822")) {
474 
475 				do {
476 					part = link->data;
477 					if (e_mail_part_id_has_suffix (part, ".rfc822.end"))
478 						break;
479 
480 					link = g_list_next (link);
481 				} while (link != NULL);
482 
483 				if (link == NULL)
484 					break;
485 			}
486 		}
487 	}
488 
489 	while (!g_queue_is_empty (&queue))
490 		g_object_unref (g_queue_pop_head (&queue));
491 
492 	string = "</body></html>";
493 	g_output_stream_write_all (
494 		stream, string, strlen (string),
495 		NULL, cancellable, NULL);
496 }
497 
498 static void
mail_formatter_update_style(EMailFormatter * formatter,GtkStateFlags state)499 mail_formatter_update_style (EMailFormatter *formatter,
500                              GtkStateFlags state)
501 {
502 	GtkStyleContext *style_context;
503 	GtkWidgetPath *widget_path;
504 	GdkRGBA rgba;
505 
506 	g_object_freeze_notify (G_OBJECT (formatter));
507 
508 	style_context = gtk_style_context_new ();
509 	widget_path = gtk_widget_path_new ();
510 	gtk_widget_path_append_type (widget_path, GTK_TYPE_WINDOW);
511 	gtk_style_context_set_path (style_context, widget_path);
512 
513 	if (!gtk_style_context_lookup_color (style_context, "theme_bg_color", &rgba))
514 		gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_BG_COLOR);
515 
516 	e_mail_formatter_set_color (formatter, E_MAIL_FORMATTER_COLOR_BODY, &rgba);
517 
518 	rgba.red *= 0.8;
519 	rgba.green *= 0.8;
520 	rgba.blue *= 0.8;
521 	e_mail_formatter_set_color (
522 		formatter, E_MAIL_FORMATTER_COLOR_FRAME, &rgba);
523 
524 	if (!gtk_style_context_lookup_color (style_context, "theme_fg_color", &rgba))
525 		gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_FG_COLOR);
526 
527 	e_mail_formatter_set_color (formatter, E_MAIL_FORMATTER_COLOR_HEADER, &rgba);
528 
529 	if (!gtk_style_context_lookup_color (style_context, "theme_base_color", &rgba))
530 		gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_BASE_COLOR);
531 
532 	e_mail_formatter_set_color  (formatter, E_MAIL_FORMATTER_COLOR_CONTENT, &rgba);
533 
534 	if (!gtk_style_context_lookup_color (style_context, "theme_fg_color", &rgba))
535 		gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_FG_COLOR);
536 
537 	e_mail_formatter_set_color (formatter, E_MAIL_FORMATTER_COLOR_TEXT, &rgba);
538 
539 	gtk_widget_path_free (widget_path);
540 	g_object_unref (style_context);
541 
542 	g_object_thaw_notify (G_OBJECT (formatter));
543 }
544 
545 static void
e_mail_formatter_base_init(EMailFormatterClass * class)546 e_mail_formatter_base_init (EMailFormatterClass *class)
547 {
548 	EShell *shell;
549 
550 	/* Register internal extensions. */
551 	g_type_ensure (e_mail_formatter_attachment_get_type ());
552 	/* This is currently disabled, because the WebKit player requires javascript,
553 	   which is disabled in Evolution. */
554 	/* g_type_ensure (e_mail_formatter_audio_get_type ()); */
555 	g_type_ensure (e_mail_formatter_error_get_type ());
556 	g_type_ensure (e_mail_formatter_headers_get_type ());
557 	g_type_ensure (e_mail_formatter_image_get_type ());
558 	g_type_ensure (e_mail_formatter_message_rfc822_get_type ());
559 	g_type_ensure (e_mail_formatter_secure_button_get_type ());
560 	g_type_ensure (e_mail_formatter_source_get_type ());
561 	g_type_ensure (e_mail_formatter_text_enriched_get_type ());
562 	g_type_ensure (e_mail_formatter_text_html_get_type ());
563 	g_type_ensure (e_mail_formatter_text_plain_get_type ());
564 
565 	class->extension_registry = g_object_new (
566 		E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, NULL);
567 
568 	e_mail_formatter_extension_registry_load (
569 		class->extension_registry,
570 		E_TYPE_MAIL_FORMATTER_EXTENSION);
571 
572 	e_extensible_load_extensions (
573 		E_EXTENSIBLE (class->extension_registry));
574 
575 	class->text_html_flags =
576 		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
577 		CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
578 		CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
579 		CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES |
580 		CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
581 
582 	shell = e_shell_get_default ();
583 	/* It can be NULL when creating developer documentation */
584 	if (shell)
585 		g_object_weak_ref (G_OBJECT (shell), shell_gone_cb, class);
586 }
587 
588 static void
e_mail_formatter_class_init(EMailFormatterClass * class)589 e_mail_formatter_class_init (EMailFormatterClass *class)
590 {
591 	GObjectClass *object_class;
592 	GdkRGBA *rgba;
593 
594 	e_mail_formatter_parent_class = g_type_class_peek_parent (class);
595 	g_type_class_add_private (class, sizeof (EMailFormatterPrivate));
596 
597 	object_class = G_OBJECT_CLASS (class);
598 	object_class->set_property = e_mail_formatter_set_property;
599 	object_class->get_property = e_mail_formatter_get_property;
600 	object_class->finalize = e_mail_formatter_finalize;
601 	object_class->constructed = e_mail_formatter_constructed;
602 
603 	class->context_size = sizeof (EMailFormatterContext);
604 	class->run = mail_formatter_run;
605 	class->update_style = mail_formatter_update_style;
606 
607 	rgba = &class->colors[E_MAIL_FORMATTER_COLOR_BODY];
608 	gdk_rgba_parse (rgba, "#eeeeee");
609 
610 	rgba = &class->colors[E_MAIL_FORMATTER_COLOR_CONTENT];
611 	gdk_rgba_parse (rgba, "#ffffff");
612 
613 	rgba = &class->colors[E_MAIL_FORMATTER_COLOR_FRAME];
614 	gdk_rgba_parse (rgba, "#3f3f3f");
615 
616 	rgba = &class->colors[E_MAIL_FORMATTER_COLOR_HEADER];
617 	gdk_rgba_parse (rgba, "#000000");
618 
619 	rgba = &class->colors[E_MAIL_FORMATTER_COLOR_TEXT];
620 	gdk_rgba_parse (rgba, "#000000");
621 
622 	g_object_class_install_property (
623 		object_class,
624 		PROP_ANIMATE_IMAGES,
625 		g_param_spec_boolean (
626 			"animate-images",
627 			"Animate images",
628 			NULL,
629 			FALSE,
630 			G_PARAM_READWRITE |
631 			G_PARAM_STATIC_STRINGS));
632 
633 	g_object_class_install_property (
634 		object_class,
635 		PROP_BODY_COLOR,
636 		g_param_spec_boxed (
637 			"body-color",
638 			"Body Color",
639 			NULL,
640 			GDK_TYPE_RGBA,
641 			G_PARAM_READWRITE |
642 			G_PARAM_STATIC_STRINGS));
643 
644 	g_object_class_install_property (
645 		object_class,
646 		PROP_CHARSET,
647 		g_param_spec_string (
648 			"charset",
649 			NULL,
650 			NULL,
651 			NULL,
652 			G_PARAM_READWRITE |
653 			G_PARAM_STATIC_STRINGS));
654 
655 	g_object_class_install_property (
656 		object_class,
657 		PROP_CITATION_COLOR,
658 		g_param_spec_boxed (
659 			"citation-color",
660 			"Citation Color",
661 			NULL,
662 			GDK_TYPE_RGBA,
663 			G_PARAM_READWRITE |
664 			G_PARAM_STATIC_STRINGS));
665 
666 	g_object_class_install_property (
667 		object_class,
668 		PROP_CONTENT_COLOR,
669 		g_param_spec_boxed (
670 			"content-color",
671 			"Content Color",
672 			NULL,
673 			GDK_TYPE_RGBA,
674 			G_PARAM_READWRITE |
675 			G_PARAM_STATIC_STRINGS));
676 
677 	g_object_class_install_property (
678 		object_class,
679 		PROP_DEFAULT_CHARSET,
680 		g_param_spec_string (
681 			"default-charset",
682 			NULL,
683 			NULL,
684 			NULL,
685 			G_PARAM_READWRITE |
686 			G_PARAM_STATIC_STRINGS));
687 
688 	g_object_class_install_property (
689 		object_class,
690 		PROP_FRAME_COLOR,
691 		g_param_spec_boxed (
692 			"frame-color",
693 			"Frame Color",
694 			NULL,
695 			GDK_TYPE_RGBA,
696 			G_PARAM_READWRITE |
697 			G_PARAM_STATIC_STRINGS));
698 
699 	g_object_class_install_property (
700 		object_class,
701 		PROP_HEADER_COLOR,
702 		g_param_spec_boxed (
703 			"header-color",
704 			"Header Color",
705 			NULL,
706 			GDK_TYPE_RGBA,
707 			G_PARAM_READWRITE |
708 			G_PARAM_STATIC_STRINGS));
709 
710 	g_object_class_install_property (
711 		object_class,
712 		PROP_IMAGE_LOADING_POLICY,
713 		g_param_spec_enum (
714 			"image-loading-policy",
715 			"Image Loading Policy",
716 			NULL,
717 			E_TYPE_IMAGE_LOADING_POLICY,
718 			E_IMAGE_LOADING_POLICY_NEVER,
719 			G_PARAM_READWRITE |
720 			G_PARAM_STATIC_STRINGS));
721 
722 	g_object_class_install_property (
723 		object_class,
724 		PROP_MARK_CITATIONS,
725 		g_param_spec_boolean (
726 			"mark-citations",
727 			"Mark Citations",
728 			NULL,
729 			TRUE,
730 			G_PARAM_READWRITE |
731 			G_PARAM_STATIC_STRINGS));
732 
733 	g_object_class_install_property (
734 		object_class,
735 		PROP_SHOW_REAL_DATE,
736 		g_param_spec_boolean (
737 			"show-real-date",
738 			"Show real Date header value",
739 			NULL,
740 			TRUE,
741 			G_PARAM_READWRITE |
742 			G_PARAM_CONSTRUCT |
743 			G_PARAM_STATIC_STRINGS));
744 
745 	g_object_class_install_property (
746 		object_class,
747 		PROP_SHOW_SENDER_PHOTO,
748 		g_param_spec_boolean (
749 			"show-sender-photo",
750 			"Show Sender Photo",
751 			NULL,
752 			FALSE,
753 			G_PARAM_READWRITE |
754 			G_PARAM_CONSTRUCT |
755 			G_PARAM_STATIC_STRINGS));
756 
757 	g_object_class_install_property (
758 		object_class,
759 		PROP_TEXT_COLOR,
760 		g_param_spec_boxed (
761 			"text-color",
762 			"Text Color",
763 			NULL,
764 			GDK_TYPE_COLOR,
765 			G_PARAM_READWRITE |
766 			G_PARAM_STATIC_STRINGS));
767 
768 	signals[CLAIM_ATTACHMENT] = g_signal_new (
769 		"claim-attachment",
770 		E_TYPE_MAIL_FORMATTER,
771 		G_SIGNAL_RUN_FIRST,
772 		G_STRUCT_OFFSET (EMailFormatterClass, claim_attachment),
773 		NULL, NULL, NULL,
774 		G_TYPE_NONE, 1, E_TYPE_ATTACHMENT);
775 
776 	signals[NEED_REDRAW] = g_signal_new (
777 		"need-redraw",
778 		E_TYPE_MAIL_FORMATTER,
779 		G_SIGNAL_RUN_FIRST,
780 		G_STRUCT_OFFSET (EMailFormatterClass, need_redraw),
781 		NULL, NULL, NULL,
782 		G_TYPE_NONE, 0);
783 }
784 
785 static void
e_mail_formatter_init(EMailFormatter * formatter)786 e_mail_formatter_init (EMailFormatter *formatter)
787 {
788 	formatter->priv = E_MAIL_FORMATTER_GET_PRIVATE (formatter);
789 
790 	g_mutex_init (&formatter->priv->property_lock);
791 }
792 
793 static void
e_mail_formatter_extensible_interface_init(EExtensibleInterface * iface)794 e_mail_formatter_extensible_interface_init (EExtensibleInterface *iface)
795 {
796 
797 }
798 
799 EMailFormatter *
e_mail_formatter_new(void)800 e_mail_formatter_new (void)
801 {
802 	return g_object_new (E_TYPE_MAIL_FORMATTER, NULL);
803 }
804 
805 GType
e_mail_formatter_get_type(void)806 e_mail_formatter_get_type (void)
807 {
808 	static GType type = 0;
809 
810 	if (G_UNLIKELY (type == 0)) {
811 		const GTypeInfo type_info = {
812 			sizeof (EMailFormatterClass),
813 			(GBaseInitFunc) e_mail_formatter_base_init,
814 			(GBaseFinalizeFunc) NULL,
815 			(GClassInitFunc) e_mail_formatter_class_init,
816 			(GClassFinalizeFunc) NULL,
817 			NULL,	/* class_data */
818 			sizeof (EMailFormatter),
819 			0,	/* n_preallocs */
820 			(GInstanceInitFunc) e_mail_formatter_init,
821 			NULL	/* value_table */
822 		};
823 
824 		const GInterfaceInfo e_extensible_interface_info = {
825 			(GInterfaceInitFunc) e_mail_formatter_extensible_interface_init
826 		};
827 
828 		type = g_type_register_static (
829 			G_TYPE_OBJECT,
830 			"EMailFormatter", &type_info, 0);
831 
832 		g_type_add_interface_static (
833 			type, E_TYPE_EXTENSIBLE, &e_extensible_interface_info);
834 	}
835 
836 	return type;
837 }
838 
839 void
e_mail_formatter_claim_attachment(EMailFormatter * formatter,EAttachment * attachment)840 e_mail_formatter_claim_attachment (EMailFormatter *formatter,
841 				   EAttachment *attachment)
842 {
843 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
844 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
845 
846 	g_signal_emit (formatter, signals[CLAIM_ATTACHMENT], 0, attachment);
847 }
848 
849 void
e_mail_formatter_format_sync(EMailFormatter * formatter,EMailPartList * part_list,GOutputStream * stream,EMailFormatterHeaderFlags flags,EMailFormatterMode mode,GCancellable * cancellable)850 e_mail_formatter_format_sync (EMailFormatter *formatter,
851                               EMailPartList *part_list,
852                               GOutputStream *stream,
853                               EMailFormatterHeaderFlags flags,
854                               EMailFormatterMode mode,
855                               GCancellable *cancellable)
856 {
857 	EMailFormatterContext *context;
858 	EMailFormatterClass *class;
859 
860 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
861 	/* EMailPartList can be NULL. */
862 	g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
863 
864 	class = E_MAIL_FORMATTER_GET_CLASS (formatter);
865 	g_return_if_fail (class != NULL);
866 	g_return_if_fail (class->run != NULL);
867 
868 	context = mail_formatter_create_context (
869 		formatter, part_list, mode, flags);
870 
871 	class->run (formatter, context, stream, cancellable);
872 
873 	mail_formatter_free_context (context);
874 }
875 
876 static void
mail_formatter_format_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)877 mail_formatter_format_thread (GSimpleAsyncResult *simple,
878                               GObject *source_object,
879                               GCancellable *cancellable)
880 {
881 	AsyncContext *async_context;
882 
883 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
884 
885 	e_mail_formatter_format_sync (
886 		E_MAIL_FORMATTER (source_object),
887 		async_context->part_list,
888 		async_context->stream,
889 		async_context->flags,
890 		async_context->mode,
891 		cancellable);
892 }
893 
894 void
e_mail_formatter_format(EMailFormatter * formatter,EMailPartList * part_list,GOutputStream * stream,EMailFormatterHeaderFlags flags,EMailFormatterMode mode,GAsyncReadyCallback callback,GCancellable * cancellable,gpointer user_data)895 e_mail_formatter_format (EMailFormatter *formatter,
896                          EMailPartList *part_list,
897                          GOutputStream *stream,
898                          EMailFormatterHeaderFlags flags,
899                          EMailFormatterMode mode,
900                          GAsyncReadyCallback callback,
901                          GCancellable *cancellable,
902                          gpointer user_data)
903 {
904 	GSimpleAsyncResult *simple;
905 	AsyncContext *async_context;
906 	EMailFormatterClass *class;
907 
908 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
909 	/* EMailPartList can be NULL. */
910 	g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
911 
912 	class = E_MAIL_FORMATTER_GET_CLASS (formatter);
913 	g_return_if_fail (class != NULL);
914 	g_return_if_fail (class->run != NULL);
915 
916 	async_context = g_slice_new0 (AsyncContext);
917 	async_context->stream = g_object_ref (stream);
918 	async_context->flags = flags;
919 	async_context->mode = mode;
920 
921 	simple = g_simple_async_result_new (
922 		G_OBJECT (formatter), callback,
923 		user_data, e_mail_formatter_format);
924 
925 	g_simple_async_result_set_check_cancellable (simple, cancellable);
926 
927 	g_simple_async_result_set_op_res_gpointer (
928 		simple, async_context, (GDestroyNotify) async_context_free);
929 
930 	if (part_list != NULL) {
931 		async_context->part_list = g_object_ref (part_list);
932 
933 		g_simple_async_result_run_in_thread (
934 			simple, mail_formatter_format_thread,
935 			G_PRIORITY_DEFAULT, cancellable);
936 	} else {
937 		g_simple_async_result_complete_in_idle (simple);
938 	}
939 
940 	g_object_unref (simple);
941 }
942 
943 gboolean
e_mail_formatter_format_finish(EMailFormatter * formatter,GAsyncResult * result,GError ** error)944 e_mail_formatter_format_finish (EMailFormatter *formatter,
945                                 GAsyncResult *result,
946                                 GError **error)
947 {
948 	GSimpleAsyncResult *simple;
949 
950 	g_return_val_if_fail (
951 		g_simple_async_result_is_valid (
952 		result, G_OBJECT (formatter),
953 		e_mail_formatter_format), FALSE);
954 
955 	simple = G_SIMPLE_ASYNC_RESULT (result);
956 
957 	/* Assume success unless a GError is set. */
958 	return !g_simple_async_result_propagate_error (simple, error);
959 }
960 
961 /**
962  * e_mail_formatter_format_as:
963  * @formatter: an #EMailFormatter
964  * @context: an #EMailFormatterContext
965  * @part: an #EMailPart
966  * @stream: a #GOutputStream
967  * @as_mime_type: (allow-none) mime-type to use for formatting, or %NULL
968  * @cancellable: (allow-none) an optional #GCancellable
969  *
970  * Formats given @part using a @formatter extension for given mime type. When
971  * the mime type is %NULL, the function will try to lookup the best formatter
972  * for given @part by it's default mime type.
973  *
974  * Return Value: %TRUE on success, %FALSE when no suitable formatter is found or
975  * when it fails to format the part.
976  */
977 gboolean
e_mail_formatter_format_as(EMailFormatter * formatter,EMailFormatterContext * context,EMailPart * part,GOutputStream * stream,const gchar * as_mime_type,GCancellable * cancellable)978 e_mail_formatter_format_as (EMailFormatter *formatter,
979                             EMailFormatterContext *context,
980                             EMailPart *part,
981                             GOutputStream *stream,
982                             const gchar *as_mime_type,
983                             GCancellable *cancellable)
984 {
985 	EMailExtensionRegistry *extension_registry;
986 	GQueue *formatters;
987 	gboolean ok;
988 	d (
989 		gint _call_i;
990 		static gint _call = 0;
991 		G_LOCK_DEFINE_STATIC (_call);
992 		G_LOCK (_call);
993 		_call++;
994 		_call_i = _call;
995 		G_UNLOCK (_call)
996 	);
997 
998 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
999 	g_return_val_if_fail (part != NULL, FALSE);
1000 	g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
1001 
1002 	if (as_mime_type == NULL || *as_mime_type == '\0')
1003 		as_mime_type = e_mail_part_get_mime_type (part);
1004 
1005 	if (as_mime_type == NULL || *as_mime_type == '\0')
1006 		return FALSE;
1007 
1008 	extension_registry =
1009 		e_mail_formatter_get_extension_registry (formatter);
1010 	formatters = e_mail_extension_registry_get_for_mime_type (
1011 		extension_registry, as_mime_type);
1012 	if (formatters == NULL)
1013 		formatters = e_mail_extension_registry_get_fallback (
1014 			extension_registry, as_mime_type);
1015 
1016 	ok = FALSE;
1017 
1018 	d (
1019 		printf ("(%d) Formatting for part %s of type %s (found %d formatters)\n",
1020 		_call_i, e_mail_part_get_id (part), as_mime_type,
1021 		formatters ? g_queue_get_length (formatters) : 0));
1022 
1023 	if (formatters != NULL) {
1024 		GList *head, *link;
1025 
1026 		head = g_queue_peek_head_link (formatters);
1027 
1028 		for (link = head; link != NULL; link = g_list_next (link)) {
1029 			EMailFormatterExtension *extension;
1030 
1031 			extension = link->data;
1032 			if (extension == NULL)
1033 				continue;
1034 
1035 			ok = e_mail_formatter_extension_format (
1036 				extension, formatter, context,
1037 				part, stream, cancellable);
1038 
1039 			d (
1040 				printf (
1041 					"\t(%d) trying %s...%s\n", _call_i,
1042 					G_OBJECT_TYPE_NAME (extension),
1043 					ok ? "OK" : "failed"));
1044 
1045 			if (ok)
1046 				break;
1047 		}
1048 	}
1049 
1050 	return ok;
1051 }
1052 
1053 /**
1054  * em_format_format_text:
1055  * @part: an #EMailPart to decode
1056  * @formatter: an #EMailFormatter
1057  * @stream: Where to write the converted text
1058  * @cancellable: optional #GCancellable object, or %NULL
1059  *
1060  * Decode/output a part's content to @stream.
1061  **/
1062 void
e_mail_formatter_format_text(EMailFormatter * formatter,EMailPart * part,GOutputStream * stream,GCancellable * cancellable)1063 e_mail_formatter_format_text (EMailFormatter *formatter,
1064                               EMailPart *part,
1065                               GOutputStream *stream,
1066                               GCancellable *cancellable)
1067 {
1068 	CamelMimeFilter *filter;
1069 	const gchar *charset = NULL;
1070 	CamelMimeFilter *windows = NULL;
1071 	CamelMimePart *mime_part;
1072 	CamelContentType *mime_type;
1073 
1074 	if (g_cancellable_is_cancelled (cancellable))
1075 		return;
1076 
1077 	mime_part = e_mail_part_ref_mime_part (part);
1078 	mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mime_part));
1079 
1080 	if (formatter->priv->charset != NULL) {
1081 		charset = formatter->priv->charset;
1082 	} else if (mime_type != NULL
1083 		   && (charset = camel_content_type_param (mime_type, "charset"))
1084 		   && g_ascii_strncasecmp (charset, "iso-8859-", 9) == 0) {
1085 		GOutputStream *null_stream;
1086 		GOutputStream *filter_stream;
1087 
1088 		/* Since a few Windows mailers like to claim they sent
1089 		 * out iso-8859-# encoded text when they really sent
1090 		 * out windows-cp125#, do some simple sanity checking
1091 		 * before we move on... */
1092 
1093 		null_stream = camel_null_output_stream_new ();
1094 		windows = camel_mime_filter_windows_new (charset);
1095 		filter_stream = camel_filter_output_stream_new (
1096 			null_stream, windows);
1097 		g_filter_output_stream_set_close_base_stream (
1098 			G_FILTER_OUTPUT_STREAM (filter_stream), FALSE);
1099 
1100 		camel_data_wrapper_decode_to_output_stream_sync (
1101 			CAMEL_DATA_WRAPPER (mime_part),
1102 			filter_stream, cancellable, NULL);
1103 		g_output_stream_flush (filter_stream, cancellable, NULL);
1104 
1105 		g_object_unref (filter_stream);
1106 		g_object_unref (null_stream);
1107 
1108 		charset = camel_mime_filter_windows_real_charset (
1109 			CAMEL_MIME_FILTER_WINDOWS (windows));
1110 	} else if (charset == NULL) {
1111 		charset = formatter->priv->default_charset;
1112 	}
1113 
1114 	filter = camel_mime_filter_charset_new (charset, "UTF-8");
1115 	if (filter != NULL) {
1116 		e_mail_part_set_converted_to_utf8 (part, TRUE);
1117 
1118 		stream = camel_filter_output_stream_new (stream, filter);
1119 		g_filter_output_stream_set_close_base_stream (
1120 			G_FILTER_OUTPUT_STREAM (stream), FALSE);
1121 		g_object_unref (filter);
1122 	} else {
1123 		g_object_ref (stream);
1124 	}
1125 
1126 	camel_data_wrapper_decode_to_output_stream_sync (
1127 		camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
1128 		stream, cancellable, NULL);
1129 	g_output_stream_flush (stream, cancellable, NULL);
1130 
1131 	g_object_unref (stream);
1132 	g_clear_object (&windows);
1133 	g_clear_object (&mime_part);
1134 }
1135 
1136 const gchar *
e_mail_formatter_get_sub_html_header(EMailFormatter * formatter)1137 e_mail_formatter_get_sub_html_header (EMailFormatter *formatter)
1138 {
1139 	return  "<!DOCTYPE HTML>\n"
1140 		"<html>\n"
1141 		"<head>\n"
1142 		"<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
1143 		"<meta name=\"color-scheme\" content=\"light dark\">\n"
1144 		"<title>Evolution Mail Display</title>\n"
1145 		"<link type=\"text/css\" rel=\"stylesheet\" "
1146 		" href=\"" STYLESHEET_URI "\"/>\n"
1147 		"<style type=\"text/css\">\n"
1148 		" table th { font-weight: bold; }\n"
1149 		"</style>\n"
1150 		"</head>"
1151 		"<body class=\"-e-web-view-background-color -e-web-view-text-color\">";
1152 }
1153 
1154 gchar *
e_mail_formatter_get_html_header(EMailFormatter * formatter)1155 e_mail_formatter_get_html_header (EMailFormatter *formatter)
1156 {
1157 	return g_strdup (
1158 		"<!DOCTYPE HTML>\n"
1159 		"<html>\n"
1160 		"<head>\n"
1161 		"<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
1162 		"<meta name=\"color-scheme\" content=\"light dark\">\n"
1163 		"<title>Evolution Mail Display</title>\n"
1164 		"<link type=\"text/css\" rel=\"stylesheet\" "
1165 		" href=\"" STYLESHEET_URI "\"/>\n"
1166 		"<style type=\"text/css\">\n"
1167 		" table th { font-weight: bold; }\n"
1168 		"</style>\n"
1169 		"</head>"
1170 		"<body class=\"-e-mail-formatter-body-color "
1171 		"-e-web-view-background-color -e-web-view-text-color\">");
1172 }
1173 
1174 EMailExtensionRegistry *
e_mail_formatter_get_extension_registry(EMailFormatter * formatter)1175 e_mail_formatter_get_extension_registry (EMailFormatter *formatter)
1176 {
1177 	EMailFormatterClass *klass;
1178 
1179 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1180 
1181 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1182 	g_return_val_if_fail (klass != NULL, NULL);
1183 
1184 	return E_MAIL_EXTENSION_REGISTRY (klass->extension_registry);
1185 }
1186 
1187 CamelMimeFilterToHTMLFlags
e_mail_formatter_get_text_format_flags(EMailFormatter * formatter)1188 e_mail_formatter_get_text_format_flags (EMailFormatter *formatter)
1189 {
1190 	EMailFormatterClass *klass;
1191 
1192 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
1193 
1194 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1195 	g_return_val_if_fail (klass != NULL, 0);
1196 
1197 	return klass->text_html_flags;
1198 }
1199 
1200 const GdkRGBA *
e_mail_formatter_get_color(EMailFormatter * formatter,EMailFormatterColor type)1201 e_mail_formatter_get_color (EMailFormatter *formatter,
1202                             EMailFormatterColor type)
1203 {
1204 	EMailFormatterClass *klass;
1205 
1206 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1207 	g_return_val_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES, NULL);
1208 
1209 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1210 	g_return_val_if_fail (klass != NULL, NULL);
1211 
1212 	return &(klass->colors[type]);
1213 }
1214 
1215 void
e_mail_formatter_set_color(EMailFormatter * formatter,EMailFormatterColor type,const GdkRGBA * color)1216 e_mail_formatter_set_color (EMailFormatter *formatter,
1217                             EMailFormatterColor type,
1218                             const GdkRGBA *color)
1219 {
1220 	EMailFormatterClass *klass;
1221 	GdkRGBA *format_color;
1222 	const gchar *property_name;
1223 
1224 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1225 	g_return_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES);
1226 	g_return_if_fail (color != NULL);
1227 
1228 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1229 	g_return_if_fail (klass != NULL);
1230 
1231 	format_color = &(klass->colors[type]);
1232 
1233 	if (gdk_rgba_equal (color, format_color))
1234 		return;
1235 
1236 	format_color->red = color->red;
1237 	format_color->green = color->green;
1238 	format_color->blue = color->blue;
1239 
1240 	switch (type) {
1241 		case E_MAIL_FORMATTER_COLOR_BODY:
1242 			property_name = "body-color";
1243 			break;
1244 		case E_MAIL_FORMATTER_COLOR_CITATION:
1245 			property_name = "citation-color";
1246 			break;
1247 		case E_MAIL_FORMATTER_COLOR_CONTENT:
1248 			property_name = "content-color";
1249 			break;
1250 		case E_MAIL_FORMATTER_COLOR_FRAME:
1251 			property_name = "frame-color";
1252 			break;
1253 		case E_MAIL_FORMATTER_COLOR_HEADER:
1254 			property_name = "header-color";
1255 			break;
1256 		case E_MAIL_FORMATTER_COLOR_TEXT:
1257 			property_name = "text-color";
1258 			break;
1259 		default:
1260 			g_return_if_reached ();
1261 	}
1262 
1263 	g_object_notify (G_OBJECT (formatter), property_name);
1264 }
1265 
1266 void
e_mail_formatter_update_style(EMailFormatter * formatter,GtkStateFlags state)1267 e_mail_formatter_update_style (EMailFormatter *formatter,
1268                                GtkStateFlags state)
1269 {
1270 	EMailFormatterClass *class;
1271 
1272 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1273 
1274 	class = E_MAIL_FORMATTER_GET_CLASS (formatter);
1275 	g_return_if_fail (class != NULL);
1276 	g_return_if_fail (class->update_style != NULL);
1277 
1278 	class->update_style (formatter, state);
1279 }
1280 
1281 EImageLoadingPolicy
e_mail_formatter_get_image_loading_policy(EMailFormatter * formatter)1282 e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
1283 {
1284 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
1285 
1286 	return formatter->priv->image_loading_policy;
1287 }
1288 
1289 void
e_mail_formatter_set_image_loading_policy(EMailFormatter * formatter,EImageLoadingPolicy policy)1290 e_mail_formatter_set_image_loading_policy (EMailFormatter *formatter,
1291                                            EImageLoadingPolicy policy)
1292 {
1293 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1294 
1295 	if (policy == formatter->priv->image_loading_policy)
1296 		return;
1297 
1298 	formatter->priv->image_loading_policy = policy;
1299 
1300 	g_object_notify (G_OBJECT (formatter), "image-loading-policy");
1301 }
1302 
1303 gboolean
e_mail_formatter_get_mark_citations(EMailFormatter * formatter)1304 e_mail_formatter_get_mark_citations (EMailFormatter *formatter)
1305 {
1306 	EMailFormatterClass *klass;
1307 	guint32 flags;
1308 
1309 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
1310 
1311 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1312 	g_return_val_if_fail (klass != NULL, FALSE);
1313 
1314 	flags = klass->text_html_flags;
1315 
1316 	return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0);
1317 }
1318 
1319 void
e_mail_formatter_set_mark_citations(EMailFormatter * formatter,gboolean mark_citations)1320 e_mail_formatter_set_mark_citations (EMailFormatter *formatter,
1321                                      gboolean mark_citations)
1322 {
1323 	EMailFormatterClass *klass;
1324 
1325 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1326 
1327 	klass = E_MAIL_FORMATTER_GET_CLASS (formatter);
1328 	g_return_if_fail (klass != NULL);
1329 
1330 	if (mark_citations)
1331 		klass->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
1332 	else
1333 		klass->text_html_flags &= ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
1334 
1335 	g_object_notify (G_OBJECT (formatter), "mark-citations");
1336 }
1337 
1338 gboolean
e_mail_formatter_get_show_sender_photo(EMailFormatter * formatter)1339 e_mail_formatter_get_show_sender_photo (EMailFormatter *formatter)
1340 {
1341 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
1342 
1343 	return formatter->priv->show_sender_photo;
1344 }
1345 
1346 void
e_mail_formatter_set_show_sender_photo(EMailFormatter * formatter,gboolean show_sender_photo)1347 e_mail_formatter_set_show_sender_photo (EMailFormatter *formatter,
1348                                         gboolean show_sender_photo)
1349 {
1350 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1351 
1352 	if (formatter->priv->show_sender_photo == show_sender_photo)
1353 		return;
1354 
1355 	formatter->priv->show_sender_photo = show_sender_photo;
1356 
1357 	g_object_notify (G_OBJECT (formatter), "show-sender-photo");
1358 }
1359 
1360 gboolean
e_mail_formatter_get_show_real_date(EMailFormatter * formatter)1361 e_mail_formatter_get_show_real_date (EMailFormatter *formatter)
1362 {
1363 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
1364 
1365 	return formatter->priv->show_real_date;
1366 }
1367 
1368 void
e_mail_formatter_set_show_real_date(EMailFormatter * formatter,gboolean show_real_date)1369 e_mail_formatter_set_show_real_date (EMailFormatter *formatter,
1370                                      gboolean show_real_date)
1371 {
1372 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1373 
1374 	if (formatter->priv->show_real_date == show_real_date)
1375 		return;
1376 
1377 	formatter->priv->show_real_date = show_real_date;
1378 
1379 	g_object_notify (G_OBJECT (formatter), "show-real-date");
1380 }
1381 
1382 gboolean
e_mail_formatter_get_animate_images(EMailFormatter * formatter)1383 e_mail_formatter_get_animate_images (EMailFormatter *formatter)
1384 {
1385 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
1386 
1387 	return formatter->priv->animate_images;
1388 }
1389 
1390 void
e_mail_formatter_set_animate_images(EMailFormatter * formatter,gboolean animate_images)1391 e_mail_formatter_set_animate_images (EMailFormatter *formatter,
1392                                      gboolean animate_images)
1393 {
1394 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1395 
1396 	if (formatter->priv->animate_images == animate_images)
1397 		return;
1398 
1399 	formatter->priv->animate_images = animate_images;
1400 
1401 	g_object_notify (G_OBJECT (formatter), "animate-images");
1402 }
1403 
1404 const gchar *
e_mail_formatter_get_charset(EMailFormatter * formatter)1405 e_mail_formatter_get_charset (EMailFormatter *formatter)
1406 {
1407 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1408 
1409 	return formatter->priv->charset;
1410 }
1411 
1412 gchar *
e_mail_formatter_dup_charset(EMailFormatter * formatter)1413 e_mail_formatter_dup_charset (EMailFormatter *formatter)
1414 {
1415 	const gchar *protected;
1416 	gchar *duplicate;
1417 
1418 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1419 
1420 	g_mutex_lock (&formatter->priv->property_lock);
1421 
1422 	protected = e_mail_formatter_get_charset (formatter);
1423 	duplicate = g_strdup (protected);
1424 
1425 	g_mutex_unlock (&formatter->priv->property_lock);
1426 
1427 	return duplicate;
1428 }
1429 
1430 void
e_mail_formatter_set_charset(EMailFormatter * formatter,const gchar * charset)1431 e_mail_formatter_set_charset (EMailFormatter *formatter,
1432                               const gchar *charset)
1433 {
1434 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1435 
1436 	g_mutex_lock (&formatter->priv->property_lock);
1437 
1438 	if (g_strcmp0 (formatter->priv->charset, charset) == 0) {
1439 		g_mutex_unlock (&formatter->priv->property_lock);
1440 		return;
1441 	}
1442 
1443 	g_free (formatter->priv->charset);
1444 	formatter->priv->charset = g_strdup (charset);
1445 
1446 	g_mutex_unlock (&formatter->priv->property_lock);
1447 
1448 	g_object_notify (G_OBJECT (formatter), "charset");
1449 }
1450 
1451 const gchar *
e_mail_formatter_get_default_charset(EMailFormatter * formatter)1452 e_mail_formatter_get_default_charset (EMailFormatter *formatter)
1453 {
1454 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1455 
1456 	return formatter->priv->default_charset;
1457 }
1458 
1459 gchar *
e_mail_formatter_dup_default_charset(EMailFormatter * formatter)1460 e_mail_formatter_dup_default_charset (EMailFormatter *formatter)
1461 {
1462 	const gchar *protected;
1463 	gchar *duplicate;
1464 
1465 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
1466 
1467 	g_mutex_lock (&formatter->priv->property_lock);
1468 
1469 	protected = e_mail_formatter_get_default_charset (formatter);
1470 	duplicate = g_strdup (protected);
1471 
1472 	g_mutex_unlock (&formatter->priv->property_lock);
1473 
1474 	return duplicate;
1475 }
1476 
1477 void
e_mail_formatter_set_default_charset(EMailFormatter * formatter,const gchar * default_charset)1478 e_mail_formatter_set_default_charset (EMailFormatter *formatter,
1479                                       const gchar *default_charset)
1480 {
1481 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
1482 	g_return_if_fail (default_charset && *default_charset);
1483 
1484 	g_mutex_lock (&formatter->priv->property_lock);
1485 
1486 	if (g_strcmp0 (formatter->priv->default_charset, default_charset) == 0) {
1487 		g_mutex_unlock (&formatter->priv->property_lock);
1488 		return;
1489 	}
1490 
1491 	g_free (formatter->priv->default_charset);
1492 	formatter->priv->default_charset = g_strdup (default_charset);
1493 
1494 	g_mutex_unlock (&formatter->priv->property_lock);
1495 
1496 	g_object_notify (G_OBJECT (formatter), "default-charset");
1497 }
1498 
1499