No issues found
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 |
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 }