1 /*
2  * e-mail-formatter-quote.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-quote.h"
19 
20 #include <camel/camel.h>
21 
22 #include "e-mail-formatter-utils.h"
23 #include "e-mail-part.h"
24 #include "e-mail-part-attachment.h"
25 #include "e-mail-part-utils.h"
26 
27 #include <libebackend/libebackend.h>
28 #include <gdk/gdk.h>
29 #include <glib/gi18n.h>
30 
31 #define E_MAIL_FORMATTER_QUOTE_GET_PRIVATE(obj) \
32 	(G_TYPE_INSTANCE_GET_PRIVATE \
33 	((obj), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuotePrivate))
34 
35 struct _EMailFormatterQuotePrivate {
36 	gchar *credits;
37 	EMailFormatterQuoteFlags flags;
38 };
39 
40 /* internal formatter extensions */
41 GType e_mail_formatter_quote_headers_get_type (void);
42 GType e_mail_formatter_quote_message_rfc822_get_type (void);
43 GType e_mail_formatter_quote_text_enriched_get_type (void);
44 GType e_mail_formatter_quote_text_html_get_type (void);
45 GType e_mail_formatter_quote_text_plain_get_type (void);
46 
47 static gpointer e_mail_formatter_quote_parent_class = 0;
48 
49 static void
mail_formatter_quote_run(EMailFormatter * formatter,EMailFormatterContext * context,GOutputStream * stream,GCancellable * cancellable)50 mail_formatter_quote_run (EMailFormatter *formatter,
51                           EMailFormatterContext *context,
52                           GOutputStream *stream,
53                           GCancellable *cancellable)
54 {
55 	EMailFormatterQuote *qf;
56 	EMailFormatterQuoteContext *qf_context;
57 	GQueue queue = G_QUEUE_INIT;
58 	GList *head, *link;
59 	const gchar *string;
60 
61 	if (g_cancellable_is_cancelled (cancellable))
62 		return;
63 
64 	qf = E_MAIL_FORMATTER_QUOTE (formatter);
65 
66 	qf_context = (EMailFormatterQuoteContext *) context;
67 	qf_context->qf_flags = qf->priv->flags;
68 
69 	if ((qf_context->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_NO_FORMATTING) != 0)
70 		context->flags |= E_MAIL_FORMATTER_HEADER_FLAG_NO_FORMATTING;
71 
72 	g_seekable_seek (
73 		G_SEEKABLE (stream),
74 		0, G_SEEK_SET, NULL, NULL);
75 
76 	e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
77 
78 	head = g_queue_peek_head_link (&queue);
79 
80 	for (link = head; link != NULL; link = g_list_next (link)) {
81 		EMailPart *part = E_MAIL_PART (link->data);
82 		const gchar *mime_type;
83 
84 		if (e_mail_part_id_has_suffix (part, ".headers") &&
85 		   !(qf_context->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS)) {
86 			continue;
87 		}
88 
89 		if (e_mail_part_id_has_suffix (part, ".rfc822")) {
90 			link = e_mail_formatter_find_rfc822_end_iter (link);
91 			continue;
92 		}
93 
94 		if (part->is_hidden)
95 			continue;
96 
97 		if (e_mail_part_get_is_attachment (part))
98 			continue;
99 
100 		mime_type = e_mail_part_get_mime_type (part);
101 
102 		/* Skip error messages in the quoted part */
103 		if (g_strcmp0 (mime_type, "application/vnd.evolution.error") == 0)
104 			continue;
105 
106 		e_mail_formatter_format_as (
107 			formatter, context, part, stream,
108 			mime_type, cancellable);
109 	}
110 
111 	while (!g_queue_is_empty (&queue))
112 		g_object_unref (g_queue_pop_head (&queue));
113 
114 	/* Before we were inserting the BR elements and the credits in front of
115 	 * the actual HTML code of the message. But this was wrong as when WebKit
116 	 * was loading the given HTML code that looked like
117 	 * <br>CREDITS<html>MESSAGE_CODE</html> WebKit parsed it like
118 	 * <html><br>CREDITS</html><html>MESSAGE_CODE</html>. As no elements are
119 	 * allowed outside of the HTML root element WebKit wrapped them into
120 	 * another HTML root element. Afterwards the first root element was
121 	 * treated as the primary one and all the elements from the second's root
122 	 * HEAD and BODY elements were moved to the first one.
123 	 * Thus the HTML that was loaded into composer contained the i.e. META
124 	 * or STYLE definitions in the body.
125 	 * So if we want to put something into the message we have to put it into
126 	 * the special span element and it will be moved to body in EHTMLEditorView */
127 	if (qf->priv->credits && *qf->priv->credits) {
128 		gchar *credits = g_markup_printf_escaped (
129 			"<span class=\"-x-evo-to-body\" data-credits=\"%s\"></span>",
130 			qf->priv->credits);
131 		g_output_stream_write_all (
132 			stream, credits, strlen (credits), NULL, cancellable, NULL);
133 		g_free (credits);
134 	}
135 
136 	/* If we want to cite the message we have to append the special span element
137 	 * after the message and cite it in EHTMLEditorView because of reasons
138 	 * mentioned above */
139 	if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) {
140 		string = "<span class=\"-x-evo-cite-body\"></span>";
141 		g_output_stream_write_all (
142 			stream, string, strlen (string), NULL, cancellable, NULL);
143 	}
144 }
145 
146 static void
e_mail_formatter_quote_init(EMailFormatterQuote * formatter)147 e_mail_formatter_quote_init (EMailFormatterQuote *formatter)
148 {
149 	formatter->priv = E_MAIL_FORMATTER_QUOTE_GET_PRIVATE (formatter);
150 }
151 
152 static void
e_mail_formatter_quote_finalize(GObject * object)153 e_mail_formatter_quote_finalize (GObject *object)
154 {
155 	EMailFormatterQuote *formatter;
156 
157 	formatter = E_MAIL_FORMATTER_QUOTE (object);
158 
159 	g_free (formatter->priv->credits);
160 	formatter->priv->credits = NULL;
161 
162 	/* Chain up to parent's finalize() */
163 	G_OBJECT_CLASS (e_mail_formatter_quote_parent_class)->finalize (object);
164 }
165 
166 static void
e_mail_formatter_quote_base_init(EMailFormatterQuoteClass * class)167 e_mail_formatter_quote_base_init (EMailFormatterQuoteClass *class)
168 {
169 	/* Register internal extensions. */
170 	g_type_ensure (e_mail_formatter_quote_headers_get_type ());
171 	g_type_ensure (e_mail_formatter_quote_message_rfc822_get_type ());
172 	g_type_ensure (e_mail_formatter_quote_text_enriched_get_type ());
173 	g_type_ensure (e_mail_formatter_quote_text_html_get_type ());
174 	g_type_ensure (e_mail_formatter_quote_text_plain_get_type ());
175 
176 	e_mail_formatter_extension_registry_load (
177 		E_MAIL_FORMATTER_CLASS (class)->extension_registry,
178 		E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION);
179 
180 	E_MAIL_FORMATTER_CLASS (class)->text_html_flags =
181 		CAMEL_MIME_FILTER_TOHTML_PRE |
182 		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
183 		CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
184 }
185 
186 static void
e_mail_formatter_quote_class_init(EMailFormatterQuoteClass * class)187 e_mail_formatter_quote_class_init (EMailFormatterQuoteClass *class)
188 {
189 	GObjectClass *object_class;
190 	EMailFormatterClass *formatter_class;
191 
192 	e_mail_formatter_quote_parent_class = g_type_class_peek_parent (class);
193 	g_type_class_add_private (class, sizeof (EMailFormatterQuotePrivate));
194 
195 	formatter_class = E_MAIL_FORMATTER_CLASS (class);
196 	formatter_class->context_size = sizeof (EMailFormatterQuoteContext);
197 	formatter_class->run = mail_formatter_quote_run;
198 
199 	object_class = G_OBJECT_CLASS (class);
200 	object_class->finalize = e_mail_formatter_quote_finalize;
201 }
202 
203 GType
e_mail_formatter_quote_get_type(void)204 e_mail_formatter_quote_get_type (void)
205 {
206 	static GType type = 0;
207 
208 	if (G_UNLIKELY (type == 0)) {
209 		const GTypeInfo type_info = {
210 			sizeof (EMailFormatterClass),
211 			(GBaseInitFunc) e_mail_formatter_quote_base_init,
212 			(GBaseFinalizeFunc) NULL,
213 			(GClassInitFunc) e_mail_formatter_quote_class_init,
214 			(GClassFinalizeFunc) NULL,
215 			NULL,	/* class_data */
216 			sizeof (EMailFormatterQuote),
217 			0,	/* n_preallocs */
218 			(GInstanceInitFunc) e_mail_formatter_quote_init,
219 			NULL	/* value_table */
220 		};
221 
222 		type = g_type_register_static (
223 			E_TYPE_MAIL_FORMATTER,
224 			"EMailFormatterQuote", &type_info, 0);
225 	}
226 
227 	return type;
228 }
229 
230 EMailFormatter *
e_mail_formatter_quote_new(const gchar * credits,EMailFormatterQuoteFlags flags)231 e_mail_formatter_quote_new (const gchar *credits,
232                             EMailFormatterQuoteFlags flags)
233 {
234 	EMailFormatterQuote *formatter;
235 	formatter = g_object_new (E_TYPE_MAIL_FORMATTER_QUOTE, NULL);
236 
237 	formatter->priv->credits = g_strdup (credits);
238 	formatter->priv->flags = flags;
239 
240 	return (EMailFormatter *) formatter;
241 }
242 
243 /* ------------------------------------------------------------------------- */
244 
G_DEFINE_ABSTRACT_TYPE(EMailFormatterQuoteExtension,e_mail_formatter_quote_extension,E_TYPE_MAIL_FORMATTER_EXTENSION)245 G_DEFINE_ABSTRACT_TYPE (
246 	EMailFormatterQuoteExtension,
247 	e_mail_formatter_quote_extension,
248 	E_TYPE_MAIL_FORMATTER_EXTENSION)
249 
250 static void
251 e_mail_formatter_quote_extension_class_init (EMailFormatterQuoteExtensionClass *class)
252 {
253 }
254 
255 static void
e_mail_formatter_quote_extension_init(EMailFormatterQuoteExtension * extension)256 e_mail_formatter_quote_extension_init (EMailFormatterQuoteExtension *extension)
257 {
258 }
259 
260