evolution-3.6.4/modules/prefer-plain/e-mail-parser-prefer-plain.c

No issues found

Incomplete coverage

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
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  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 }