evolution-3.6.4/em-format/e-mail-parser.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-parser.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-parser.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  * e-mail-parser.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  */
 18 
 19 #include "e-mail-parser.h"
 20 #include "e-mail-parser-extension.h"
 21 #include "e-mail-format-extensions.h"
 22 #include "e-mail-part-attachment.h"
 23 #include "e-mail-part-utils.h"
 24 
 25 #include <camel/camel.h>
 26 #include <libebackend/libebackend.h>
 27 
 28 #include <e-util/e-util.h>
 29 
 30 #include <shell/e-shell.h>
 31 #include <shell/e-shell-window.h>
 32 
 33 #include <widgets/misc/e-attachment.h>
 34 
 35 #include <string.h>
 36 
 37 #define E_MAIL_PARSER_GET_PRIVATE(obj) \
 38 	(G_TYPE_INSTANCE_GET_PRIVATE \
 39 	((obj), E_TYPE_MAIL_PARSER, EMailParserPrivate))
 40 
 41 #define d(x)
 42 
 43 struct _EMailParserPrivate {
 44 	GMutex *mutex;
 45 
 46 	gint last_error;
 47 
 48 	CamelSession *session;
 49 };
 50 
 51 enum {
 52 	PROP_0,
 53 	PROP_SESSION
 54 };
 55 
 56 static gpointer parent_class;
 57 
 58 static void
 59 mail_parser_run (EMailParser *parser,
 60                  EMailPartList *part_list,
 61                  GCancellable *cancellable)
 62 {
 63 	EMailExtensionRegistry *reg;
 64 	EMailPart *part;
 65 	GQueue *parsers;
 66 	GList *iter;
 67 	GString *part_id;
 68 
 69 	reg = e_mail_parser_get_extension_registry (parser);
 70 
 71 	parsers = e_mail_extension_registry_get_for_mime_type (
 72 		reg, "application/vnd.evolution.message");
 73 
 74 	if (parsers == NULL)
 75 		parsers = e_mail_extension_registry_get_for_mime_type (
 76 			reg, "message/*");
 77 
 78 	/* parsers == NULL means, that the internal Evolution parser
 79 	 * extensions were not loaded. Something is terribly wrong. */
 80 	g_return_if_fail (parsers != NULL);
 81 
 82 	part_id = g_string_new (".message");
 83 
 84 	for (iter = parsers->head; iter; iter = iter->next) {
 85 
 86 		EMailParserExtension *extension;
 87 
 88 		if (g_cancellable_is_cancelled (cancellable))
 89 			break;
 90 
 91 		extension = iter->data;
 92 		if (!extension)
 93 			continue;
 94 
 95 		part_list->list = e_mail_parser_extension_parse (
 96 			extension, parser,
 97 			CAMEL_MIME_PART (part_list->message),
 98 			part_id, cancellable);
 99 
100 		if (part_list->list != NULL)
101 			break;
102 	}
103 
104 	part = e_mail_part_new (
105 		CAMEL_MIME_PART (part_list->message), ".message");
106 	part_list->list = g_slist_prepend (part_list->list, part);
107 
108 	g_string_free (part_id, TRUE);
109 }
110 
111 static void
112 mail_parser_set_session (EMailParser *parser,
113                          CamelSession *session)
114 {
115 	g_return_if_fail (CAMEL_IS_SESSION (session));
116 	g_return_if_fail (parser->priv->session == NULL);
117 
118 	parser->priv->session = g_object_ref (session);
119 }
120 
121 static void
122 e_mail_parser_set_property (GObject *object,
123                           guint property_id,
124                           const GValue *value,
125                           GParamSpec *pspec)
126 {
127 	switch (property_id) {
128 		case PROP_SESSION:
129 			mail_parser_set_session (
130 				E_MAIL_PARSER (object),
131 				g_value_get_object (value));
132 			return;
133 	}
134 
135 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
136 }
137 
138 static void
139 e_mail_parser_get_property (GObject *object,
140                           guint property_id,
141                           GValue *value,
142                           GParamSpec *pspec)
143 {
144 	switch (property_id) {
145 		case PROP_SESSION:
146 			g_value_set_object (
147 				value,
148 				e_mail_parser_get_session (
149 				E_MAIL_PARSER (object)));
150 			return;
151 	}
152 
153 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
154 }
155 
156 static void
157 e_mail_parser_finalize (GObject *object)
158 {
159 	EMailParserPrivate *priv;
160 
161 	priv = E_MAIL_PARSER_GET_PRIVATE (object);
162 
163 	if (priv->mutex) {
164 		g_mutex_free (priv->mutex);
165 		priv->mutex = NULL;
166 	}
167 
168 	/* Chain up to parent's finalize() method. */
169 	G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171 
172 static void
173 e_mail_parser_base_init (EMailParserClass *class)
174 {
175 	class->extension_registry = g_object_new (
176 		E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL);
177 
178 	e_mail_parser_internal_extensions_load (
179 		E_MAIL_EXTENSION_REGISTRY (class->extension_registry));
180 
181 	e_extensible_load_extensions (E_EXTENSIBLE (class->extension_registry));
182 }
183 
184 static void
185 e_mail_parser_base_finalize (EMailParserClass *class)
186 {
187 	g_object_unref (class->extension_registry);
188 }
189 
190 static void
191 e_mail_parser_class_init (EMailParserClass *class)
192 {
193 	GObjectClass *object_class;
194 
195 	parent_class = g_type_class_peek_parent (class);
196 	g_type_class_add_private (class, sizeof (EMailParserPrivate));
197 
198 	object_class = G_OBJECT_CLASS (class);
199 	object_class->finalize = e_mail_parser_finalize;
200 	object_class->set_property = e_mail_parser_set_property;
201 	object_class->get_property = e_mail_parser_get_property;
202 
203 	g_object_class_install_property (
204 		object_class,
205 		PROP_SESSION,
206 		g_param_spec_object (
207 			"session",
208 			"Camel Session",
209 			NULL,
210 			CAMEL_TYPE_SESSION,
211 			G_PARAM_READWRITE |
212 			G_PARAM_CONSTRUCT_ONLY));
213 }
214 
215 static void
216 e_mail_parser_init (EMailParser *parser)
217 {
218 	parser->priv = E_MAIL_PARSER_GET_PRIVATE (parser);
219 
220 	parser->priv->mutex = g_mutex_new ();
221 }
222 
223 GType
224 e_mail_parser_get_type (void)
225 {
226 	static GType type = 0;
227 
228 	if (G_UNLIKELY (type == 0)) {
229 		static const GTypeInfo type_info = {
230 			sizeof (EMailParserClass),
231 			(GBaseInitFunc) e_mail_parser_base_init,
232 			(GBaseFinalizeFunc) e_mail_parser_base_finalize,
233 			(GClassInitFunc) e_mail_parser_class_init,
234 			(GClassFinalizeFunc) NULL,
235 			NULL,  /* class_data */
236 			sizeof (EMailParser),
237 			0,     /* n_preallocs */
238 			(GInstanceInitFunc) e_mail_parser_init,
239 			NULL   /* value_table */
240 		};
241 
242 		type = g_type_register_static (
243 			G_TYPE_OBJECT, "EMailParser",
244 			&type_info, 0);
245 	}
246 
247 	return type;
248 }
249 
250 EMailParser *
251 e_mail_parser_new (CamelSession *session)
252 {
253 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
254 
255 	return g_object_new (
256 		E_TYPE_MAIL_PARSER,
257 		"session", session, NULL);
258 }
259 
260 /**
261  * e_mail_parser_parse_sync:
262  * @parser: an #EMailParser
263  * @folder: (allow none) a #CamelFolder containing the @message or %NULL
264  * @message_uid: (allow none) UID of the @message within the @folder or %NULL
265  * @message: a #CamelMimeMessage
266  * @cancellable: (allow-none) a #GCancellable
267  *
268  * Parses the @message synchronously. Returns a list of #EMailPart<!-//>s which
269  * represents structure of the message and additional properties of each part.
270  *
271  * Note that this function can block for a while, so it's not a good idea to call
272  * it from main thread.
273  *
274  * Return Value: An #EMailPartsList
275  */
276 EMailPartList *
277 e_mail_parser_parse_sync (EMailParser *parser,
278                           CamelFolder *folder,
279                           const gchar *message_uid,
280                           CamelMimeMessage *message,
281                           GCancellable *cancellable)
282 {
283 	EMailPartList *part_list;
284 
285 	g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
286 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
287 
288 	part_list = e_mail_part_list_new ();
289 
290 	if (folder != NULL)
291 		part_list->folder = g_object_ref (folder);
292 
293 	part_list->message_uid = g_strdup (message_uid);
294 	part_list->message = g_object_ref (message);
295 
296 	mail_parser_run (parser, part_list, cancellable);
297 
298 	if (camel_debug_start ("emformat:parser")) {
299 		GSList *iter;
300 
301 		printf (
302 			"%s finished with EMailPartList:\n",
303 			G_OBJECT_TYPE_NAME (parser));
304 
305 		for (iter = part_list->list; iter; iter = iter->next) {
306 			EMailPart *part = iter->data;
307 			if (!part) continue;
308 			printf (
309 				"	id: %s | cid: %s | mime_type: %s | "
310 				"is_hidden: %d | is_attachment: %d\n",
311 				part->id, part->cid, part->mime_type,
312 				part->is_hidden ? 1 : 0,
313 				part->is_attachment ? 1 : 0);
314 		}
315 
316 		camel_debug_end ();
317 	}
318 
319 	return part_list;
320 }
321 
322 static void
323 mail_parser_parse_thread (GSimpleAsyncResult *simple,
324                           GObject *source_object,
325                           GCancellable *cancellable)
326 {
327 	EMailPartList *part_list;
328 
329 	part_list = g_simple_async_result_get_op_res_gpointer (simple);
330 
331 	mail_parser_run (
332 		E_MAIL_PARSER (source_object),
333 		part_list, cancellable);
334 }
335 
336 /**
337  * e_mail_parser_parse:
338  * @parser: an #EMailParser
339  * @message: a #CamelMimeMessage
340  * @callback: a #GAsyncReadyCallback
341  * @cancellable: (allow-none) a #GCancellable
342  * @user_data: (allow-none) user data passed to the callback
343  *
344  * Asynchronous version of e_mail_parser_parse_sync().
345  */
346 void
347 e_mail_parser_parse (EMailParser *parser,
348                      CamelFolder *folder,
349                      const gchar *message_uid,
350                      CamelMimeMessage *message,
351                      GAsyncReadyCallback callback,
352                      GCancellable *cancellable,
353                      gpointer user_data)
354 {
355 	GSimpleAsyncResult *simple;
356 	EMailPartList *part_list;
357 
358 	g_return_if_fail (E_IS_MAIL_PARSER (parser));
359 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
360 
361 	part_list = e_mail_part_list_new ();
362 	part_list->message = g_object_ref (message);
363 	part_list->message_uid = g_strdup (message_uid);
364 	if (folder != NULL)
365 		part_list->folder = g_object_ref (folder);
366 
367 	simple = g_simple_async_result_new (
368 		G_OBJECT (parser), callback,
369 		user_data, e_mail_parser_parse);
370 
371 	g_simple_async_result_set_check_cancellable (simple, cancellable);
372 
373 	g_simple_async_result_set_op_res_gpointer (
374 		simple, part_list, (GDestroyNotify) g_object_unref);
375 
376 	g_simple_async_result_run_in_thread (
377 		simple, mail_parser_parse_thread,
378 		G_PRIORITY_DEFAULT, cancellable);
379 
380 	g_object_unref (simple);
381 }
382 
383 EMailPartList *
384 e_mail_parser_parse_finish (EMailParser *parser,
385                             GAsyncResult *result,
386                             GError **error)
387 {
388 	GSimpleAsyncResult *simple;
389 	EMailPartList *part_list;
390 
391 	g_return_val_if_fail (
392 		g_simple_async_result_is_valid (
393 		result, G_OBJECT (parser), e_mail_parser_parse), NULL);
394 
395 	simple = G_SIMPLE_ASYNC_RESULT (result);
396 	part_list = g_simple_async_result_get_op_res_gpointer (simple);
397 
398 	if (camel_debug_start ("emformat:parser")) {
399 		GSList *iter;
400 
401 		printf (
402 			"%s finished with EMailPartList:\n",
403 			G_OBJECT_TYPE_NAME (parser));
404 
405 		for (iter = part_list->list; iter; iter = iter->next) {
406 			EMailPart *part = iter->data;
407 			if (!part) continue;
408 			printf (
409 				"	id: %s | cid: %s | mime_type: %s | "
410 				"is_hidden: %d | is_attachment: %d\n",
411 				part->id, part->cid, part->mime_type,
412 				part->is_hidden ? 1 : 0,
413 				part->is_attachment ? 1 : 0);
414 		}
415 
416 		camel_debug_end ();
417 	}
418 
419 	return g_object_ref (part_list);
420 }
421 
422 GSList *
423 e_mail_parser_parse_part (EMailParser *parser,
424                           CamelMimePart *part,
425                           GString *part_id,
426                           GCancellable *cancellable)
427 {
428 	CamelContentType *ct;
429 	gchar *mime_type;
430 	GSList *list;
431 
432 	ct = camel_mime_part_get_content_type (part);
433 	if (!ct) {
434 		mime_type = (gchar *) "application/vnd.evolution.error";
435 	} else {
436 		gchar *tmp;
437 		tmp = camel_content_type_simple (ct);
438 		mime_type = g_ascii_strdown (tmp, -1);
439 		g_free (tmp);
440 	}
441 
442 	list = e_mail_parser_parse_part_as (
443 		parser, part, part_id, mime_type, cancellable);
444 
445 	if (ct) {
446 		g_free (mime_type);
447 	}
448 
449 	return list;
450 }
451 
452 GSList *
453 e_mail_parser_parse_part_as (EMailParser *parser,
454                              CamelMimePart *part,
455                              GString *part_id,
456                              const gchar *mime_type,
457                              GCancellable *cancellable)
458 {
459 	GQueue *parsers;
460 	GList *iter;
461 	EMailExtensionRegistry *reg;
462 	EMailParserClass *parser_class;
463 	GSList *part_list;
464 	gchar *as_mime_type;
465 
466 	if (g_cancellable_is_cancelled (cancellable))
467 		return NULL;
468 
469 	if (mime_type)
470 		as_mime_type = g_ascii_strdown (mime_type, -1);
471 	else
472 		as_mime_type = NULL;
473 
474 	parser_class = E_MAIL_PARSER_GET_CLASS (parser);
475 	reg = E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry);
476 
477 	parsers = e_mail_extension_registry_get_for_mime_type (reg, as_mime_type);
478 	if (!parsers) {
479 		parsers = e_mail_extension_registry_get_fallback (reg, as_mime_type);
480 	}
481 
482 	if (as_mime_type)
483 		g_free (as_mime_type);
484 
485 	if (!parsers) {
486 		return e_mail_parser_wrap_as_attachment (
487 			parser, part, NULL, part_id, cancellable);
488 	}
489 
490 	for (iter = parsers->head; iter; iter = iter->next) {
491 		EMailParserExtension *extension;
492 
493 		extension = iter->data;
494 		if (!extension)
495 			continue;
496 
497 		part_list = e_mail_parser_extension_parse (
498 			extension, parser, part, part_id, cancellable);
499 
500 		if (part_list)
501 			break;
502 	}
503 
504 	return part_list;
505 }
506 
507 GSList *
508 e_mail_parser_error (EMailParser *parser,
509                      GCancellable *cancellable,
510                      const gchar *format,
511                      ...)
512 {
513 	EMailPart *mail_part;
514 	CamelMimePart *part;
515 	gchar *errmsg;
516 	gchar *uri;
517 	va_list ap;
518 
519 	g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
520 	g_return_val_if_fail (format != NULL, NULL);
521 
522 	va_start (ap, format);
523 	errmsg = g_strdup_vprintf (format, ap);
524 
525 	part = camel_mime_part_new ();
526 	camel_mime_part_set_content (
527 		part,
528 		errmsg, strlen (errmsg),
529 		"application/vnd.evolution.error");
530 	g_free (errmsg);
531 	va_end (ap);
532 
533 	g_mutex_lock (parser->priv->mutex);
534 	parser->priv->last_error++;
535 	uri = g_strdup_printf (".error.%d", parser->priv->last_error);
536 	g_mutex_unlock (parser->priv->mutex);
537 
538 	mail_part = e_mail_part_new (part, uri);
539 	mail_part->mime_type = g_strdup ("application/vnd.evolution.error");
540 	mail_part->is_error = TRUE;
541 
542 	g_free (uri);
543 	g_object_unref (part);
544 
545 	return g_slist_append (NULL, mail_part);
546 }
547 
548 static void
549 attachment_loaded (EAttachment *attachment,
550                    GAsyncResult *res,
551                    gpointer user_data)
552 {
553 	EShell *shell;
554 	GtkWindow *window;
555 
556 	shell = e_shell_get_default ();
557 	window = e_shell_get_active_window (shell);
558 
559 	e_attachment_load_handle_error (attachment, res, window);
560 
561 	g_object_unref (attachment);
562 }
563 
564 /* Idle callback */
565 static gboolean
566 load_attachment_idle (EAttachment *attachment)
567 {
568 	e_attachment_load_async (
569 		attachment,
570 		(GAsyncReadyCallback) attachment_loaded, NULL);
571 
572 	return FALSE;
573 }
574 
575 GSList *
576 e_mail_parser_wrap_as_attachment (EMailParser *parser,
577                                   CamelMimePart *part,
578                                   GSList *parts,
579                                   GString *part_id,
580                                   GCancellable *cancellable)
581 {
582 	EMailPartAttachment *empa;
583 	const gchar *snoop_mime_type, *cid;
584 	GQueue *extensions;
585 	CamelContentType *ct;
586 	gchar *mime_type;
587 	CamelDataWrapper *dw;
588 	GByteArray *ba;
589 	gsize size;
590 	gint part_id_len;
591 
592 	ct = camel_mime_part_get_content_type (part);
593 	extensions = NULL;
594 	snoop_mime_type = NULL;
595 	if (ct) {
596 		EMailExtensionRegistry *reg;
597 		mime_type = camel_content_type_simple (ct);
598 
599 		reg = e_mail_parser_get_extension_registry (parser);
600 		extensions = e_mail_extension_registry_get_for_mime_type (
601 			reg, mime_type);
602 
603 		if (camel_content_type_is (ct, "text", "*") ||
604 		    camel_content_type_is (ct, "message", "*"))
605 			snoop_mime_type = mime_type;
606 		else
607 			g_free (mime_type);
608 	}
609 
610 	if (!snoop_mime_type)
611 		snoop_mime_type = e_mail_part_snoop_type (part);
612 
613 	if (!extensions) {
614 		EMailExtensionRegistry *reg;
615 
616 		reg = e_mail_parser_get_extension_registry (parser);
617 		extensions = e_mail_extension_registry_get_for_mime_type (
618 			reg, snoop_mime_type);
619 
620 		if (!extensions) {
621 			extensions = e_mail_extension_registry_get_fallback (
622 				reg, snoop_mime_type);
623 		}
624 	}
625 
626 	part_id_len = part_id->len;
627 	g_string_append (part_id, ".attachment");
628 
629 	empa = (EMailPartAttachment *) e_mail_part_subclass_new (
630 		part, part_id->str, sizeof (EMailPartAttachment),
631 		(GFreeFunc) e_mail_part_attachment_free);
632 	empa->parent.mime_type = g_strdup ("application/vnd.evolution.attachment");
633 	empa->parent.is_attachment = TRUE;
634 	empa->shown = extensions && (!g_queue_is_empty (extensions) &&
635 		e_mail_part_is_inline (part, extensions));
636 	empa->snoop_mime_type = snoop_mime_type;
637 	empa->attachment = e_attachment_new ();
638 	empa->attachment_view_part_id = parts ? g_strdup (E_MAIL_PART (parts->data)->id) : NULL;
639 
640 	cid = camel_mime_part_get_content_id (part);
641 	if (cid)
642 		empa->parent.cid = g_strdup_printf ("cid:%s", cid);
643 
644 	e_attachment_set_mime_part (empa->attachment, part);
645 	e_attachment_set_shown (empa->attachment, empa->shown);
646 	e_attachment_set_can_show (
647 		empa->attachment,
648 		extensions && !g_queue_is_empty (extensions));
649 
650 	/* Try to guess size of the attachments */
651 	dw = camel_medium_get_content (CAMEL_MEDIUM (part));
652 	ba = camel_data_wrapper_get_byte_array (dw);
653 	if (ba) {
654 		size = ba->len;
655 
656 		if (camel_mime_part_get_encoding (part) == CAMEL_TRANSFER_ENCODING_BASE64)
657 			size = size / 1.37;
658 	} else {
659 		size = 0;
660 	}
661 
662 	/* e_attachment_load_async must be called from main thread */
663 	/* Prioritize ahead of GTK+ redraws. */
664 	g_idle_add_full (
665 		G_PRIORITY_HIGH_IDLE,
666 		(GSourceFunc) load_attachment_idle,
667 		g_object_ref (empa->attachment),
668 		NULL);
669 
670 	if (size != 0) {
671 		GFileInfo *fileinfo;
672 
673 		fileinfo = e_attachment_get_file_info (empa->attachment);
674 
675 		if (!fileinfo) {
676 			fileinfo = g_file_info_new ();
677 			g_file_info_set_content_type (
678 				fileinfo, empa->snoop_mime_type);
679 		} else {
680 			g_object_ref (fileinfo);
681 		}
682 
683 		g_file_info_set_size (fileinfo, size);
684 		e_attachment_set_file_info (empa->attachment, fileinfo);
685 
686 		g_object_unref (fileinfo);
687 	}
688 
689 	if (parts && parts->data) {
690 		E_MAIL_PART (parts->data)->is_hidden = TRUE;
691 	}
692 
693 	g_string_truncate (part_id, part_id_len);
694 
695 	return g_slist_prepend (parts, empa);
696 }
697 
698 CamelSession *
699 e_mail_parser_get_session (EMailParser *parser)
700 {
701 	g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
702 
703 	return parser->priv->session;
704 }
705 
706 EMailExtensionRegistry *
707 e_mail_parser_get_extension_registry (EMailParser *parser)
708 {
709 	EMailParserClass *parser_class;
710 
711 	g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
712 
713 	parser_class = E_MAIL_PARSER_GET_CLASS (parser);
714 	return E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry);
715 }