No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-parser-prefer-plain.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-parser-prefer-plain.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19
20 #include <string.h>
21 #include <gtk/gtk.h>
22 #include <glib/gi18n.h>
23
24 #include "e-mail-parser-prefer-plain.h"
25
26 #include <em-format/e-mail-extension-registry.h>
27 #include <em-format/e-mail-parser-extension.h>
28 #include <em-format/e-mail-part.h>
29 #include <em-format/e-mail-part-utils.h>
30
31 #include <libebackend/libebackend.h>
32
33 #define d(x)
34
35 typedef struct _EMailParserPreferPlain EMailParserPreferPlain;
36 typedef struct _EMailParserPreferPlainClass EMailParserPreferPlainClass;
37
38 struct _EMailParserPreferPlain {
39 EExtension parent;
40
41 GSettings *settings;
42 gint mode;
43 gboolean show_suppressed;
44 };
45
46 struct _EMailParserPreferPlainClass {
47 EExtensionClass parent_class;
48 };
49
50 GType e_mail_parser_prefer_plain_get_type (void);
51 static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface);
52 static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface);
53
54 enum {
55 PREFER_HTML,
56 PREFER_PLAIN,
57 ONLY_PLAIN
58 };
59
60 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
61 EMailParserPreferPlain,
62 e_mail_parser_prefer_plain,
63 E_TYPE_EXTENSION,
64 0,
65 G_IMPLEMENT_INTERFACE_DYNAMIC (
66 E_TYPE_MAIL_EXTENSION,
67 e_mail_parser_mail_extension_interface_init)
68 G_IMPLEMENT_INTERFACE_DYNAMIC (
69 E_TYPE_MAIL_PARSER_EXTENSION,
70 e_mail_parser_parser_extension_interface_init));
71
72 static const gchar * parser_mime_types[] = { "multipart/alternative",
73 "text/html",
74 NULL };
75
76 static struct {
77 const gchar *key;
78 const gchar *label;
79 const gchar *description;
80 } epp_options[] = {
81 { "normal",
82 N_("Show HTML if present"),
83 N_("Let Evolution choose the best part to show.") },
84
85 { "prefer_plain",
86 N_("Show plain text if present"),
87 N_("Show plain text part, if present, otherwise "
88 "let Evolution choose the best part to show.") },
89
90 { "only_plain",
91 N_("Only ever show plain text"),
92 N_("Always show plain text part and make attachments "
93 "from other parts, if requested.") },
94 };
95
96 enum {
97 PROP_0,
98 PROP_MODE,
99 PROP_SHOW_SUPPRESSED
100 };
101
102 static GSList *
103 make_part_attachment (EMailParser *parser,
104 CamelMimePart *part,
105 GString *part_id,
106 gboolean force_html,
107 GCancellable *cancellable)
108 {
109 GSList *parts;
110
111 if (camel_content_type_is (camel_mime_part_get_content_type (part), "text", "html")) {
112 EMailPart *mail_part;
113 gint len;
114 /* always show HTML as attachments and not inline */
115 camel_mime_part_set_disposition (part, "attachment");
116
117 if (!camel_mime_part_get_filename (part)) {
118 gchar *str = g_strdup_printf ("%s.html", _("attachment"));
119 camel_mime_part_set_filename (part, str);
120 g_free (str);
121 }
122
123 len = part_id->len;
124 g_string_append (part_id, ".text_html");
125 mail_part = e_mail_part_new (part, part_id->str);
126 mail_part->mime_type = g_strdup ("text/html");
127 g_string_truncate (part_id, len);
128
129 parts = e_mail_parser_wrap_as_attachment (
130 parser, part, g_slist_append (NULL, mail_part),
131 part_id, cancellable);
132
133 } else if (force_html && CAMEL_IS_MIME_MESSAGE (part)) {
134 /* message was asked to be formatted as text/html;
135 * might be for cases where message itself is a text/html part */
136 CamelMimePart *new_part;
137 CamelDataWrapper *content;
138
139 content = camel_medium_get_content (CAMEL_MEDIUM (part));
140 g_return_val_if_fail (content != NULL, NULL);
141
142 new_part = camel_mime_part_new ();
143 camel_medium_set_content (CAMEL_MEDIUM (new_part), content);
144
145 parts = e_mail_parser_parse_part (
146 parser, new_part, part_id, cancellable);
147
148 g_object_unref (new_part);
149 } else {
150 parts = e_mail_parser_parse_part (
151 parser, part, part_id, cancellable);
152 }
153
154 return parts;
155 }
156
157 static void
158 hide_parts (GSList *parts)
159 {
160 GSList *iter;
161
162 for (iter = parts; iter; iter = g_slist_next (iter)) {
163 EMailPart *p = iter->data;
164
165 if (!p)
166 continue;
167
168 p->is_hidden = TRUE;
169 }
170 }
171
172 static GSList *
173 empe_prefer_plain_parse (EMailParserExtension *extension,
174 EMailParser *parser,
175 CamelMimePart *part,
176 GString *part_id,
177 GCancellable *cancellable)
178 {
179 EMailParserPreferPlain *emp_pp;
180 CamelMultipart *mp;
181 gint i, nparts, partidlen;
182 GSList *parts;
183 CamelContentType *ct;
184 gboolean has_calendar = FALSE;
185 GSList *plain_text_parts = NULL;
186
187 emp_pp = (EMailParserPreferPlain *) extension;
188
189 ct = camel_mime_part_get_content_type (part);
190
191 /* We can actually parse HTML, but just to discard it when
192 * "Only ever show plain text" mode is set */
193 if (camel_content_type_is (ct, "text", "html")) {
194
195 /* Prevent recursion, fall back to next (real text/html) parser */
196 if (strstr (part_id->str, ".alternative-prefer-plain.") != NULL)
197 return NULL;
198
199 /* Not enforcing text/plain, so use real parser */
200 if (emp_pp->mode != ONLY_PLAIN)
201 return NULL;
202
203 /* Enforcing text/plain, but got only HTML part, so add it
204 * as attachment, to not show empty message preview, which
205 * is confusing. */
206 return make_part_attachment (
207 parser, part, part_id,
208 FALSE, cancellable);
209 }
210
211 parts = NULL;
212 partidlen = part_id->len;
213
214 mp = (CamelMultipart *) camel_medium_get_content (CAMEL_MEDIUM (part));
215
216 if (!CAMEL_IS_MULTIPART (mp)) {
217 return e_mail_parser_parse_part_as (
218 parser, part, part_id,
219 "application/vnd.evolution.source", cancellable);
220 }
221
222 nparts = camel_multipart_get_number (mp);
223 for (i = 0; i < nparts; i++) {
224
225 CamelMimePart *sp;
226 GSList *sparts = NULL;
227
228 sp = camel_multipart_get_part (mp, i);
229 ct = camel_mime_part_get_content_type (sp);
230
231 g_string_truncate (part_id, partidlen);
232 g_string_append_printf (part_id, ".alternative-prefer-plain.%d", i);
233
234 if (camel_content_type_is (ct, "text", "html")) {
235
236 if (emp_pp->mode != PREFER_HTML) {
237 if (emp_pp->show_suppressed) {
238 sparts = make_part_attachment (
239 parser, sp, part_id,
240 FALSE, cancellable);
241 }
242 } else {
243 sparts = e_mail_parser_parse_part (
244 parser, sp, part_id, cancellable);
245 }
246
247 parts = g_slist_concat (parts, sparts);
248 continue;
249 }
250
251 if (camel_content_type_is (ct, "text", "plain")) {
252
253 sparts = e_mail_parser_parse_part (
254 parser, sp, part_id, cancellable);
255
256 plain_text_parts = g_slist_concat (plain_text_parts, sparts);
257 continue;
258 }
259
260 /* Always show calendar part! */
261 if (camel_content_type_is (ct, "text", "calendar") ||
262 camel_content_type_is (ct, "text", "x-calendar")) {
263
264 /* Hide everything else, displaying native calendar part only */
265 hide_parts (parts);
266
267 sparts = e_mail_parser_parse_part (
268 parser, sp, part_id, cancellable);
269
270 parts = g_slist_concat (parts, sparts);
271 has_calendar = TRUE;
272 continue;
273 }
274
275 /* Multiparts can represent a text/html with inline images or so */
276 if (camel_content_type_is (ct, "multipart", "*")) {
277 GSList *iter;
278 gboolean has_html = FALSE;
279
280 sparts = e_mail_parser_parse_part (
281 parser, sp, part_id, cancellable);
282
283 /* Check whether the multipart contains a text/html part */
284 for (iter = sparts; iter; iter = g_slist_next (iter)) {
285 EMailPart *p = iter->data;
286 if (!p)
287 continue;
288
289 if (strstr (p->id, ".text_html") != NULL) {
290 has_html = TRUE;
291 break;
292 }
293 }
294
295 if (has_html && (emp_pp->mode != PREFER_HTML)) {
296 if (emp_pp->show_suppressed) {
297 sparts = e_mail_parser_wrap_as_attachment (
298 parser, sp, sparts, part_id,
299 cancellable);
300 } else {
301 hide_parts (sparts);
302 }
303 }
304
305 parts = g_slist_concat (parts, sparts);
306 continue;
307 }
308
309 /* Parse everything else as an attachment */
310 sparts = e_mail_parser_parse_part (
311 parser, sp, part_id, cancellable);
312 parts = g_slist_concat (
313 parts,
314 e_mail_parser_wrap_as_attachment (
315 parser, sp, sparts, part_id,
316 cancellable));
317 }
318
319 /* Don't hide the plain text if there's nothing else to display */
320 if (has_calendar || (nparts > 1 && emp_pp->mode == PREFER_HTML)) {
321 hide_parts (plain_text_parts);
322 }
323
324 if (plain_text_parts) {
325 if (parts && nparts > 1) {
326 /* a text/html part is hidden, but not marked as attachment,
327 thus do that now, when there exists a text/plain part */
328 GSList *piter;
329
330 for (piter = parts; piter; piter = g_slist_next (piter)) {
331 EMailPart *mpart = piter->data;
332
333 if (mpart && mpart->is_hidden && g_strcmp0 (mpart->mime_type, "text/html") == 0) {
334 mpart->is_attachment = TRUE;
335 }
336 }
337 }
338
339 /* plain_text parts should be always first */
340 parts = g_slist_concat (plain_text_parts, parts);
341 }
342
343 g_string_truncate (part_id, partidlen);
344
345 return parts;
346 }
347
348 static const gchar **
349 empe_mime_types (EMailExtension *extension)
350 {
351 return parser_mime_types;
352 }
353
354 void
355 e_mail_parser_prefer_plain_type_register (GTypeModule *type_module)
356 {
357 e_mail_parser_prefer_plain_register_type (type_module);
358 }
359
360 static void
361 e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface)
362 {
363 iface->mime_types = empe_mime_types;
364 }
365
366 static void
367 e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface)
368 {
369 iface->parse = empe_prefer_plain_parse;
370 }
371
372 static void
373 e_mail_parser_prefer_plain_constructed (GObject *object)
374 {
375 EExtensible *extensible;
376 EMailExtensionRegistry *reg;
377
378 extensible = e_extension_get_extensible (E_EXTENSION (object));
379 reg = E_MAIL_EXTENSION_REGISTRY (extensible);
380
381 e_mail_extension_registry_add_extension (reg, E_MAIL_EXTENSION (object));
382 }
383
384 static void
385 e_mail_parser_prefer_plain_get_property (GObject *object,
386 guint property_id,
387 GValue *value,
388 GParamSpec *pspec)
389 {
390 EMailParserPreferPlain *parser;
391
392 parser = (EMailParserPreferPlain *) object;
393
394 switch (property_id) {
395 case PROP_MODE:
396 g_value_set_int (value, parser->mode);
397 return;
398 case PROP_SHOW_SUPPRESSED:
399 g_value_set_boolean (value, parser->show_suppressed);
400 return;
401 }
402
403 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
404 }
405
406 static void
407 e_mail_parser_prefer_plain_set_property (GObject *object,
408 guint property_id,
409 const GValue *value,
410 GParamSpec *pspec)
411 {
412 EMailParserPreferPlain *parser;
413
414 parser = (EMailParserPreferPlain *) object;
415
416 switch (property_id) {
417 case PROP_MODE:
418 parser->mode = g_value_get_int (value);
419 return;
420 case PROP_SHOW_SUPPRESSED:
421 parser->show_suppressed = g_value_get_boolean (value);
422 return;
423 }
424
425 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
426 }
427
428 static void
429 e_mail_parser_prefer_plain_dispose (GObject *object)
430 {
431 EMailParserPreferPlain *parser;
432
433 parser = (EMailParserPreferPlain *) object;
434
435 g_clear_object (&parser->settings);
436
437 /* Chain up to parent's dispose() method. */
438 G_OBJECT_CLASS (e_mail_parser_prefer_plain_parent_class)->
439 dispose (object);
440 }
441
442 static void
443 e_mail_parser_prefer_plain_class_init (EMailParserPreferPlainClass *class)
444 {
445 GObjectClass *object_class;
446 EExtensionClass *extension_class;
447
448 object_class = G_OBJECT_CLASS (class);
449 object_class->constructed = e_mail_parser_prefer_plain_constructed;
450 object_class->get_property = e_mail_parser_prefer_plain_get_property;
451 object_class->set_property = e_mail_parser_prefer_plain_set_property;
452 object_class->dispose = e_mail_parser_prefer_plain_dispose;
453
454 extension_class = E_EXTENSION_CLASS (class);
455 extension_class->extensible_type = E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY;
456
457 g_object_class_install_property (
458 object_class,
459 PROP_MODE,
460 g_param_spec_int (
461 "mode",
462 "Mode",
463 NULL,
464 PREFER_HTML,
465 ONLY_PLAIN,
466 PREFER_HTML,
467 G_PARAM_READABLE | G_PARAM_WRITABLE));
468
469 g_object_class_install_property (
470 object_class,
471 PROP_SHOW_SUPPRESSED,
472 g_param_spec_boolean (
473 "show-suppressed",
474 "Show Suppressed",
475 NULL,
476 FALSE,
477 G_PARAM_READABLE | G_PARAM_WRITABLE));
478 }
479
480 void
481 e_mail_parser_prefer_plain_class_finalize (EMailParserPreferPlainClass *class)
482 {
483 }
484
485 static gboolean
486 parser_mode_get_mapping (GValue *value,
487 GVariant *variant,
488 gpointer user_data)
489 {
490 gint i;
491
492 const gchar *key = g_variant_get_string (variant, NULL);
493 if (key) {
494 for (i = 0; i < G_N_ELEMENTS (epp_options); i++) {
495 if (!strcmp (epp_options[i].key, key)) {
496 g_value_set_int (value, i);
497 return TRUE;
498 }
499 }
500 } else {
501 g_value_set_int (value, 0);
502 }
503
504 return TRUE;
505 }
506
507 static GVariant *
508 parser_mode_set_mapping (const GValue *value,
509 const GVariantType *expected_type,
510 gpointer user_data)
511 {
512 return g_variant_new_string (epp_options[g_value_get_int (value)].key);
513 }
514
515 static void
516 e_mail_parser_prefer_plain_init (EMailParserPreferPlain *parser)
517 {
518 gchar *key;
519 gint i;
520
521 parser->settings = g_settings_new ("org.gnome.evolution.plugin.prefer-plain");
522 g_settings_bind_with_mapping (
523 parser->settings, "mode",
524 parser, "mode", G_SETTINGS_BIND_DEFAULT,
525 parser_mode_get_mapping,
526 parser_mode_set_mapping,
527 NULL, NULL);
528 g_settings_bind (
529 parser->settings, "show-suppressed",
530 parser, "show-suppressed", G_SETTINGS_BIND_DEFAULT);
531
532 /* Initialize the settings */
533 key = g_settings_get_string (parser->settings, "mode");
534 if (key) {
535 for (i = 0; i < G_N_ELEMENTS (epp_options); i++) {
536 if (!strcmp (epp_options[i].key, key)) {
537 parser->mode = i;
538 break;
539 }
540 }
541 g_free (key);
542 } else {
543 parser->mode = 0;
544 }
545
546 parser->show_suppressed = g_settings_get_boolean (parser->settings, "show-suppressed");
547 }