1 /*
2 * e-mail-parser.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 "evolution-config.h"
19
20 #include "e-mail-parser.h"
21
22 #include <string.h>
23
24 #include <libebackend/libebackend.h>
25
26 #include <shell/e-shell.h>
27 #include <shell/e-shell-window.h>
28
29 #include "e-mail-parser-extension.h"
30 #include "e-mail-part-attachment.h"
31 #include "e-mail-part-utils.h"
32
33 #define E_MAIL_PARSER_GET_PRIVATE(obj) \
34 (G_TYPE_INSTANCE_GET_PRIVATE \
35 ((obj), E_TYPE_MAIL_PARSER, EMailParserPrivate))
36
37 #define d(x)
38
39 struct _EMailParserPrivate {
40 GMutex mutex;
41
42 gint last_error;
43
44 CamelSession *session;
45 GHashTable *ongoing_part_lists; /* GCancellable * ~> EMailPartList * */
46 };
47
48 enum {
49 PROP_0,
50 PROP_SESSION
51 };
52
53 /* internal parser extensions */
54 GType e_mail_parser_application_mbox_get_type (void);
55 GType e_mail_parser_audio_get_type (void);
56 GType e_mail_parser_headers_get_type (void);
57 GType e_mail_parser_message_get_type (void);
58 GType e_mail_parser_secure_button_get_type (void);
59 GType e_mail_parser_source_get_type (void);
60 GType e_mail_parser_image_get_type (void);
61 GType e_mail_parser_inline_pgp_encrypted_get_type (void);
62 GType e_mail_parser_inline_pgp_signed_get_type (void);
63 GType e_mail_parser_message_delivery_status_get_type (void);
64 GType e_mail_parser_message_external_get_type (void);
65 GType e_mail_parser_message_rfc822_get_type (void);
66 GType e_mail_parser_multipart_alternative_get_type (void);
67 GType e_mail_parser_multipart_apple_double_get_type (void);
68 GType e_mail_parser_multipart_digest_get_type (void);
69 GType e_mail_parser_multipart_encrypted_get_type (void);
70 GType e_mail_parser_multipart_mixed_get_type (void);
71 GType e_mail_parser_multipart_related_get_type (void);
72 GType e_mail_parser_multipart_signed_get_type (void);
73 GType e_mail_parser_text_enriched_get_type (void);
74 GType e_mail_parser_text_html_get_type (void);
75 GType e_mail_parser_text_plain_get_type (void);
76 #ifdef ENABLE_SMIME
77 GType e_mail_parser_application_smime_get_type (void);
78 #endif
79
80 static gpointer parent_class;
81
82 static void
mail_parser_move_security_before_headers(GQueue * part_queue)83 mail_parser_move_security_before_headers (GQueue *part_queue)
84 {
85 GList *link, *last_headers = NULL;
86 GSList *headers_stack = NULL;
87
88 link = g_queue_peek_head_link (part_queue);
89 while (link) {
90 EMailPart *part = link->data;
91 const gchar *id;
92
93 if (!part) {
94 link = g_list_next (link);
95 continue;
96 }
97
98 id = e_mail_part_get_id (part);
99 if (!id) {
100 link = g_list_next (link);
101 continue;
102 }
103
104 if (g_str_has_suffix (id, ".rfc822")) {
105 headers_stack = g_slist_prepend (headers_stack, last_headers);
106 last_headers = NULL;
107 } else if (g_str_has_suffix (id, ".rfc822.end")) {
108 g_warn_if_fail (headers_stack != NULL);
109
110 if (headers_stack) {
111 last_headers = headers_stack->data;
112 headers_stack = g_slist_remove (headers_stack, last_headers);
113 } else {
114 last_headers = NULL;
115 }
116 }
117
118 if (g_strcmp0 (e_mail_part_get_mime_type (part), "application/vnd.evolution.headers") == 0) {
119 last_headers = link;
120 link = g_list_next (link);
121 } else if (g_strcmp0 (e_mail_part_get_mime_type (part), "application/vnd.evolution.secure-button") == 0) {
122 g_warn_if_fail (last_headers != NULL);
123
124 if (last_headers) {
125 GList *next = g_list_next (link);
126
127 g_warn_if_fail (g_queue_remove (part_queue, part));
128 g_queue_insert_before (part_queue, last_headers, part);
129
130 link = next;
131 } else {
132 link = g_list_next (link);
133 }
134 } else {
135 link = g_list_next (link);
136 }
137 }
138
139 g_warn_if_fail (headers_stack == NULL);
140 g_slist_free (headers_stack);
141 }
142
143 static void
mail_parser_run(EMailParser * parser,EMailPartList * part_list,GCancellable * cancellable)144 mail_parser_run (EMailParser *parser,
145 EMailPartList *part_list,
146 GCancellable *cancellable)
147 {
148 EMailExtensionRegistry *reg;
149 CamelMimeMessage *message;
150 EMailPart *mail_part;
151 GQueue *parsers;
152 GQueue mail_part_queue = G_QUEUE_INIT;
153 GList *iter;
154 GString *part_id;
155
156 if (cancellable)
157 g_object_ref (cancellable);
158 else
159 cancellable = g_cancellable_new ();
160
161 g_mutex_lock (&parser->priv->mutex);
162 g_hash_table_insert (parser->priv->ongoing_part_lists, cancellable, part_list);
163 g_mutex_unlock (&parser->priv->mutex);
164
165 message = e_mail_part_list_get_message (part_list);
166
167 reg = e_mail_parser_get_extension_registry (parser);
168
169 parsers = e_mail_extension_registry_get_for_mime_type (
170 reg, "application/vnd.evolution.message");
171
172 if (parsers == NULL)
173 parsers = e_mail_extension_registry_get_for_mime_type (
174 reg, "message/*");
175
176 /* No parsers means the internal Evolution parser
177 * extensions were not loaded. Something is terribly wrong! */
178 g_return_if_fail (parsers != NULL);
179
180 part_id = g_string_new (".message");
181
182 mail_part = e_mail_part_new (CAMEL_MIME_PART (message), ".message");
183 e_mail_part_list_add_part (part_list, mail_part);
184 g_object_unref (mail_part);
185
186 for (iter = parsers->head; iter; iter = iter->next) {
187 EMailParserExtension *extension;
188 gboolean message_handled;
189
190 if (g_cancellable_is_cancelled (cancellable))
191 break;
192
193 extension = iter->data;
194 if (!extension)
195 continue;
196
197 message_handled = e_mail_parser_extension_parse (
198 extension, parser,
199 CAMEL_MIME_PART (message),
200 part_id, cancellable, &mail_part_queue);
201
202 if (message_handled)
203 break;
204 }
205
206 mail_parser_move_security_before_headers (&mail_part_queue);
207
208 while (!g_queue_is_empty (&mail_part_queue)) {
209 mail_part = g_queue_pop_head (&mail_part_queue);
210 e_mail_part_list_add_part (part_list, mail_part);
211 g_object_unref (mail_part);
212 }
213
214 g_mutex_lock (&parser->priv->mutex);
215 g_hash_table_remove (parser->priv->ongoing_part_lists, cancellable);
216 g_mutex_unlock (&parser->priv->mutex);
217
218 g_clear_object (&cancellable);
219 g_string_free (part_id, TRUE);
220 }
221
222 static void
shell_gone_cb(gpointer user_data,GObject * gone_extension_registry)223 shell_gone_cb (gpointer user_data,
224 GObject *gone_extension_registry)
225 {
226 EMailParserClass *class = user_data;
227
228 g_return_if_fail (class != NULL);
229
230 g_clear_object (&class->extension_registry);
231 }
232
233 static void
mail_parser_set_session(EMailParser * parser,CamelSession * session)234 mail_parser_set_session (EMailParser *parser,
235 CamelSession *session)
236 {
237 g_return_if_fail (CAMEL_IS_SESSION (session));
238 g_return_if_fail (parser->priv->session == NULL);
239
240 parser->priv->session = g_object_ref (session);
241 }
242
243 static void
e_mail_parser_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)244 e_mail_parser_set_property (GObject *object,
245 guint property_id,
246 const GValue *value,
247 GParamSpec *pspec)
248 {
249 switch (property_id) {
250 case PROP_SESSION:
251 mail_parser_set_session (
252 E_MAIL_PARSER (object),
253 g_value_get_object (value));
254 return;
255 }
256
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
258 }
259
260 static void
e_mail_parser_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)261 e_mail_parser_get_property (GObject *object,
262 guint property_id,
263 GValue *value,
264 GParamSpec *pspec)
265 {
266 switch (property_id) {
267 case PROP_SESSION:
268 g_value_set_object (
269 value,
270 e_mail_parser_get_session (
271 E_MAIL_PARSER (object)));
272 return;
273 }
274
275 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
276 }
277
278 static void
e_mail_parser_finalize(GObject * object)279 e_mail_parser_finalize (GObject *object)
280 {
281 EMailParserPrivate *priv;
282
283 priv = E_MAIL_PARSER_GET_PRIVATE (object);
284
285 g_clear_object (&priv->session);
286 g_hash_table_destroy (priv->ongoing_part_lists);
287 g_mutex_clear (&priv->mutex);
288
289 /* Chain up to parent's finalize() method. */
290 G_OBJECT_CLASS (parent_class)->finalize (object);
291 }
292
293 static void
e_mail_parser_base_init(EMailParserClass * class)294 e_mail_parser_base_init (EMailParserClass *class)
295 {
296 EShell *shell;
297
298 /* Register internal extensions. */
299 g_type_ensure (e_mail_parser_application_mbox_get_type ());
300 /* This is currently disabled, because the WebKit player requires javascript,
301 which is disabled in Evolution. */
302 /* g_type_ensure (e_mail_parser_audio_get_type ()); */
303 g_type_ensure (e_mail_parser_headers_get_type ());
304 g_type_ensure (e_mail_parser_message_get_type ());
305 g_type_ensure (e_mail_parser_secure_button_get_type ());
306 g_type_ensure (e_mail_parser_source_get_type ());
307 g_type_ensure (e_mail_parser_image_get_type ());
308 g_type_ensure (e_mail_parser_inline_pgp_encrypted_get_type ());
309 g_type_ensure (e_mail_parser_inline_pgp_signed_get_type ());
310 g_type_ensure (e_mail_parser_message_delivery_status_get_type ());
311 g_type_ensure (e_mail_parser_message_external_get_type ());
312 g_type_ensure (e_mail_parser_message_rfc822_get_type ());
313 g_type_ensure (e_mail_parser_multipart_alternative_get_type ());
314 g_type_ensure (e_mail_parser_multipart_apple_double_get_type ());
315 g_type_ensure (e_mail_parser_multipart_digest_get_type ());
316 g_type_ensure (e_mail_parser_multipart_encrypted_get_type ());
317 g_type_ensure (e_mail_parser_multipart_mixed_get_type ());
318 g_type_ensure (e_mail_parser_multipart_related_get_type ());
319 g_type_ensure (e_mail_parser_multipart_signed_get_type ());
320 g_type_ensure (e_mail_parser_text_enriched_get_type ());
321 g_type_ensure (e_mail_parser_text_html_get_type ());
322 g_type_ensure (e_mail_parser_text_plain_get_type ());
323 #ifdef ENABLE_SMIME
324 g_type_ensure (e_mail_parser_application_smime_get_type ());
325 #endif
326
327 class->extension_registry = g_object_new (
328 E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL);
329
330 e_mail_parser_extension_registry_load (class->extension_registry);
331
332 e_extensible_load_extensions (E_EXTENSIBLE (class->extension_registry));
333
334 shell = e_shell_get_default ();
335 /* It can be NULL when creating developer documentation */
336 if (shell)
337 g_object_weak_ref (G_OBJECT (shell), shell_gone_cb, class);
338 }
339
340 static void
e_mail_parser_class_init(EMailParserClass * class)341 e_mail_parser_class_init (EMailParserClass *class)
342 {
343 GObjectClass *object_class;
344
345 parent_class = g_type_class_peek_parent (class);
346 g_type_class_add_private (class, sizeof (EMailParserPrivate));
347
348 object_class = G_OBJECT_CLASS (class);
349 object_class->finalize = e_mail_parser_finalize;
350 object_class->set_property = e_mail_parser_set_property;
351 object_class->get_property = e_mail_parser_get_property;
352
353 g_object_class_install_property (
354 object_class,
355 PROP_SESSION,
356 g_param_spec_object (
357 "session",
358 "Camel Session",
359 NULL,
360 CAMEL_TYPE_SESSION,
361 G_PARAM_READWRITE |
362 G_PARAM_CONSTRUCT_ONLY));
363 }
364
365 static void
e_mail_parser_init(EMailParser * parser)366 e_mail_parser_init (EMailParser *parser)
367 {
368 parser->priv = E_MAIL_PARSER_GET_PRIVATE (parser);
369 parser->priv->ongoing_part_lists = g_hash_table_new (g_direct_hash, g_direct_equal);
370
371 g_mutex_init (&parser->priv->mutex);
372 }
373
374 GType
e_mail_parser_get_type(void)375 e_mail_parser_get_type (void)
376 {
377 static GType type = 0;
378
379 if (G_UNLIKELY (type == 0)) {
380 static const GTypeInfo type_info = {
381 sizeof (EMailParserClass),
382 (GBaseInitFunc) e_mail_parser_base_init,
383 (GBaseFinalizeFunc) NULL,
384 (GClassInitFunc) e_mail_parser_class_init,
385 (GClassFinalizeFunc) NULL,
386 NULL, /* class_data */
387 sizeof (EMailParser),
388 0, /* n_preallocs */
389 (GInstanceInitFunc) e_mail_parser_init,
390 NULL /* value_table */
391 };
392
393 type = g_type_register_static (
394 G_TYPE_OBJECT, "EMailParser",
395 &type_info, 0);
396 }
397
398 return type;
399 }
400
401 EMailParser *
e_mail_parser_new(CamelSession * session)402 e_mail_parser_new (CamelSession *session)
403 {
404 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
405
406 return g_object_new (
407 E_TYPE_MAIL_PARSER,
408 "session", session, NULL);
409 }
410
411 /**
412 * e_mail_parser_parse_sync:
413 * @parser: an #EMailParser
414 * @folder: (allow none) a #CamelFolder containing the @message or %NULL
415 * @message_uid: (allow none) UID of the @message within the @folder or %NULL
416 * @message: a #CamelMimeMessage
417 * @cancellable: (allow-none) a #GCancellable
418 *
419 * Parses the @message synchronously. Returns a list of #EMailPart<!-- -->s which
420 * represents structure of the message and additional properties of each part.
421 *
422 * Note that this function can block for a while, so it's not a good idea to call
423 * it from main thread.
424 *
425 * Return Value: An #EMailPartsList
426 */
427 EMailPartList *
e_mail_parser_parse_sync(EMailParser * parser,CamelFolder * folder,const gchar * message_uid,CamelMimeMessage * message,GCancellable * cancellable)428 e_mail_parser_parse_sync (EMailParser *parser,
429 CamelFolder *folder,
430 const gchar *message_uid,
431 CamelMimeMessage *message,
432 GCancellable *cancellable)
433 {
434 EMailPartList *part_list;
435
436 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
437 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
438
439 part_list = e_mail_part_list_new (message, message_uid, folder);
440
441 mail_parser_run (parser, part_list, cancellable);
442
443 if (camel_debug_start ("emformat:parser")) {
444 GQueue queue = G_QUEUE_INIT;
445
446 printf (
447 "%s finished with EMailPartList:\n",
448 G_OBJECT_TYPE_NAME (parser));
449
450 e_mail_part_list_queue_parts (part_list, NULL, &queue);
451
452 while (!g_queue_is_empty (&queue)) {
453 EMailPart *part;
454
455 part = g_queue_pop_head (&queue);
456
457 printf (
458 " id: %s | cid: %s | mime_type: %s | "
459 "is_hidden: %d | is_attachment: %d | is_printable: %d\n",
460 e_mail_part_get_id (part),
461 e_mail_part_get_cid (part),
462 e_mail_part_get_mime_type (part),
463 part->is_hidden ? 1 : 0,
464 e_mail_part_get_is_attachment (part) ? 1 : 0,
465 e_mail_part_get_is_printable (part) ? 1 : 0);
466
467 g_object_unref (part);
468 }
469
470 camel_debug_end ();
471 }
472
473 return part_list;
474 }
475
476 static void
mail_parser_parse_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)477 mail_parser_parse_thread (GSimpleAsyncResult *simple,
478 GObject *source_object,
479 GCancellable *cancellable)
480 {
481 EMailPartList *part_list;
482
483 part_list = g_simple_async_result_get_op_res_gpointer (simple);
484
485 mail_parser_run (
486 E_MAIL_PARSER (source_object),
487 part_list, cancellable);
488 }
489
490 /**
491 * e_mail_parser_parse:
492 * @parser: an #EMailParser
493 * @message: a #CamelMimeMessage
494 * @callback: a #GAsyncReadyCallback
495 * @cancellable: (allow-none) a #GCancellable
496 * @user_data: (allow-none) user data passed to the callback
497 *
498 * Asynchronous version of e_mail_parser_parse_sync().
499 */
500 void
e_mail_parser_parse(EMailParser * parser,CamelFolder * folder,const gchar * message_uid,CamelMimeMessage * message,GAsyncReadyCallback callback,GCancellable * cancellable,gpointer user_data)501 e_mail_parser_parse (EMailParser *parser,
502 CamelFolder *folder,
503 const gchar *message_uid,
504 CamelMimeMessage *message,
505 GAsyncReadyCallback callback,
506 GCancellable *cancellable,
507 gpointer user_data)
508 {
509 GSimpleAsyncResult *simple;
510 EMailPartList *part_list;
511
512 g_return_if_fail (E_IS_MAIL_PARSER (parser));
513 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
514
515 part_list = e_mail_part_list_new (message, message_uid, folder);
516
517 simple = g_simple_async_result_new (
518 G_OBJECT (parser), callback,
519 user_data, e_mail_parser_parse);
520
521 g_simple_async_result_set_check_cancellable (simple, cancellable);
522
523 g_simple_async_result_set_op_res_gpointer (
524 simple, part_list, (GDestroyNotify) g_object_unref);
525
526 g_simple_async_result_run_in_thread (
527 simple, mail_parser_parse_thread,
528 G_PRIORITY_DEFAULT, cancellable);
529
530 g_object_unref (simple);
531 }
532
533 EMailPartList *
e_mail_parser_parse_finish(EMailParser * parser,GAsyncResult * result,GError ** error)534 e_mail_parser_parse_finish (EMailParser *parser,
535 GAsyncResult *result,
536 GError **error)
537 {
538 GSimpleAsyncResult *simple;
539 EMailPartList *part_list;
540
541 g_return_val_if_fail (
542 g_simple_async_result_is_valid (
543 result, G_OBJECT (parser), e_mail_parser_parse), NULL);
544
545 simple = G_SIMPLE_ASYNC_RESULT (result);
546 part_list = g_simple_async_result_get_op_res_gpointer (simple);
547
548 if (camel_debug_start ("emformat:parser")) {
549 GQueue queue = G_QUEUE_INIT;
550
551 printf (
552 "%s finished with EMailPartList:\n",
553 G_OBJECT_TYPE_NAME (parser));
554
555 e_mail_part_list_queue_parts (part_list, NULL, &queue);
556
557 while (!g_queue_is_empty (&queue)) {
558 EMailPart *part;
559
560 part = g_queue_pop_head (&queue);
561
562 printf (
563 " id: %s | cid: %s | mime_type: %s | "
564 "is_hidden: %d | is_attachment: %d | is_printable: %d\n",
565 e_mail_part_get_id (part),
566 e_mail_part_get_cid (part),
567 e_mail_part_get_mime_type (part),
568 part->is_hidden ? 1 : 0,
569 e_mail_part_get_is_attachment (part) ? 1 : 0,
570 e_mail_part_get_is_printable (part) ? 1 : 0);
571
572 g_object_unref (part);
573 }
574
575 camel_debug_end ();
576 }
577
578 return g_object_ref (part_list);
579 }
580
581 GQueue *
e_mail_parser_get_parsers_for_part(EMailParser * parser,CamelMimePart * part)582 e_mail_parser_get_parsers_for_part (EMailParser *parser,
583 CamelMimePart *part)
584 {
585 CamelContentType *ct;
586 gchar *mime_type;
587 GQueue *parsers;
588
589 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
590 g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
591
592 ct = camel_mime_part_get_content_type (part);
593 if (!ct) {
594 mime_type = (gchar *) "application/vnd.evolution.error";
595 } else {
596 gchar *tmp;
597 tmp = camel_content_type_simple (ct);
598 mime_type = g_ascii_strdown (tmp, -1);
599 g_free (tmp);
600 }
601
602 parsers = e_mail_parser_get_parsers (parser, mime_type);
603
604 if (ct)
605 g_free (mime_type);
606
607 return parsers;
608 }
609
610 GQueue *
e_mail_parser_get_parsers(EMailParser * parser,const gchar * mime_type)611 e_mail_parser_get_parsers (EMailParser *parser,
612 const gchar *mime_type)
613 {
614 EMailExtensionRegistry *reg;
615 EMailParserClass *parser_class;
616 gchar *as_mime_type;
617 GQueue *parsers;
618
619 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
620
621 parser_class = E_MAIL_PARSER_GET_CLASS (parser);
622 g_return_val_if_fail (parser_class != NULL, NULL);
623
624 if (mime_type)
625 as_mime_type = g_ascii_strdown (mime_type, -1);
626 else
627 as_mime_type = NULL;
628
629 reg = E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry);
630
631 parsers = e_mail_extension_registry_get_for_mime_type (reg, as_mime_type);
632 if (!parsers)
633 parsers = e_mail_extension_registry_get_fallback (reg, as_mime_type);
634
635 g_free (as_mime_type);
636
637 return parsers;
638 }
639
640 gboolean
e_mail_parser_parse_part(EMailParser * parser,CamelMimePart * part,GString * part_id,GCancellable * cancellable,GQueue * out_mail_parts)641 e_mail_parser_parse_part (EMailParser *parser,
642 CamelMimePart *part,
643 GString *part_id,
644 GCancellable *cancellable,
645 GQueue *out_mail_parts)
646 {
647 CamelContentType *ct;
648 gchar *mime_type;
649 gint handled;
650
651 ct = camel_mime_part_get_content_type (part);
652 if (!ct) {
653 mime_type = (gchar *) "application/vnd.evolution.error";
654 } else {
655 gchar *tmp;
656 tmp = camel_content_type_simple (ct);
657 mime_type = g_ascii_strdown (tmp, -1);
658 g_free (tmp);
659 }
660
661 handled = e_mail_parser_parse_part_as (
662 parser, part, part_id, mime_type,
663 cancellable, out_mail_parts);
664
665 if (ct) {
666 g_free (mime_type);
667 }
668
669 return handled;
670 }
671
672 gboolean
e_mail_parser_parse_part_as(EMailParser * parser,CamelMimePart * part,GString * part_id,const gchar * mime_type,GCancellable * cancellable,GQueue * out_mail_parts)673 e_mail_parser_parse_part_as (EMailParser *parser,
674 CamelMimePart *part,
675 GString *part_id,
676 const gchar *mime_type,
677 GCancellable *cancellable,
678 GQueue *out_mail_parts)
679 {
680 GQueue *parsers;
681 GList *iter;
682 gboolean mime_part_handled = FALSE;
683
684 parsers = e_mail_parser_get_parsers (parser, mime_type);
685
686 if (parsers == NULL) {
687 e_mail_parser_wrap_as_attachment (
688 parser, part, part_id, out_mail_parts);
689 return TRUE;
690 }
691
692 for (iter = parsers->head; iter; iter = iter->next) {
693 EMailParserExtension *extension;
694
695 extension = iter->data;
696 if (!extension)
697 continue;
698
699 mime_part_handled = e_mail_parser_extension_parse (
700 extension, parser, part, part_id,
701 cancellable, out_mail_parts);
702
703 if (mime_part_handled)
704 break;
705 }
706
707 return mime_part_handled;
708 }
709
710 void
e_mail_parser_error(EMailParser * parser,GQueue * out_mail_parts,const gchar * format,...)711 e_mail_parser_error (EMailParser *parser,
712 GQueue *out_mail_parts,
713 const gchar *format,
714 ...)
715 {
716 const gchar *mime_type = "application/vnd.evolution.error";
717 EMailPart *mail_part;
718 CamelMimePart *part;
719 gchar *errmsg;
720 gchar *uri;
721 va_list ap;
722
723 g_return_if_fail (E_IS_MAIL_PARSER (parser));
724 g_return_if_fail (out_mail_parts != NULL);
725 g_return_if_fail (format != NULL);
726
727 va_start (ap, format);
728 errmsg = g_strdup_vprintf (format, ap);
729
730 part = camel_mime_part_new ();
731 camel_mime_part_set_content (
732 part, errmsg, strlen (errmsg), mime_type);
733 g_free (errmsg);
734 va_end (ap);
735
736 g_mutex_lock (&parser->priv->mutex);
737 parser->priv->last_error++;
738 uri = g_strdup_printf (".error.%d", parser->priv->last_error);
739 g_mutex_unlock (&parser->priv->mutex);
740
741 mail_part = e_mail_part_new (part, uri);
742 e_mail_part_set_mime_type (mail_part, mime_type);
743 e_mail_part_set_is_printable (mail_part, FALSE);
744 mail_part->is_error = TRUE;
745
746 g_free (uri);
747 g_object_unref (part);
748
749 g_queue_push_tail (out_mail_parts, mail_part);
750 }
751
752 static void
attachment_loaded(EAttachment * attachment,GAsyncResult * res,gpointer user_data)753 attachment_loaded (EAttachment *attachment,
754 GAsyncResult *res,
755 gpointer user_data)
756 {
757 EShell *shell;
758 GtkWindow *window;
759
760 shell = e_shell_get_default ();
761 window = e_shell_get_active_window (shell);
762
763 e_attachment_load_handle_error (attachment, res, window);
764
765 g_object_unref (attachment);
766 }
767
768 /* Idle callback */
769 static gboolean
load_attachment_idle(EAttachment * attachment)770 load_attachment_idle (EAttachment *attachment)
771 {
772 e_attachment_load_async (
773 attachment,
774 (GAsyncReadyCallback) attachment_loaded, NULL);
775
776 return FALSE;
777 }
778
779 void
e_mail_parser_wrap_as_attachment(EMailParser * parser,CamelMimePart * part,GString * part_id,GQueue * parts_queue)780 e_mail_parser_wrap_as_attachment (EMailParser *parser,
781 CamelMimePart *part,
782 GString *part_id,
783 GQueue *parts_queue)
784 {
785 EMailPartAttachment *empa;
786 EAttachment *attachment;
787 EMailPart *first_part;
788 const gchar *snoop_mime_type;
789 GQueue *extensions;
790 CamelContentType *ct;
791 gchar *mime_type;
792 CamelDataWrapper *dw;
793 GByteArray *ba;
794 gsize size;
795 gint part_id_len;
796
797 ct = camel_mime_part_get_content_type (part);
798 extensions = NULL;
799 snoop_mime_type = NULL;
800 if (ct) {
801 EMailExtensionRegistry *reg;
802 mime_type = camel_content_type_simple (ct);
803
804 reg = e_mail_parser_get_extension_registry (parser);
805 extensions = e_mail_extension_registry_get_for_mime_type (
806 reg, mime_type);
807
808 if (camel_content_type_is (ct, "text", "*") ||
809 camel_content_type_is (ct, "message", "*"))
810 snoop_mime_type = mime_type;
811 else
812 g_free (mime_type);
813 }
814
815 if (!snoop_mime_type)
816 snoop_mime_type = e_mail_part_snoop_type (part);
817
818 if (!extensions) {
819 EMailExtensionRegistry *reg;
820
821 reg = e_mail_parser_get_extension_registry (parser);
822 extensions = e_mail_extension_registry_get_for_mime_type (
823 reg, snoop_mime_type);
824
825 if (!extensions) {
826 extensions = e_mail_extension_registry_get_fallback (
827 reg, snoop_mime_type);
828 }
829 }
830
831 part_id_len = part_id->len;
832 g_string_append (part_id, ".attachment");
833
834 empa = e_mail_part_attachment_new (part, part_id->str);
835 empa->shown = extensions && (!g_queue_is_empty (extensions) &&
836 e_mail_part_is_inline (part, extensions));
837 empa->snoop_mime_type = snoop_mime_type;
838
839 first_part = g_queue_peek_head (parts_queue);
840 if (first_part != NULL && !E_IS_MAIL_PART_ATTACHMENT (first_part)) {
841 const gchar *id = e_mail_part_get_id (first_part);
842 empa->part_id_with_attachment = g_strdup (id);
843 first_part->is_hidden = TRUE;
844 }
845
846 attachment = e_mail_part_attachment_ref_attachment (empa);
847
848 e_attachment_set_initially_shown (attachment, empa->shown);
849 e_attachment_set_can_show (
850 attachment,
851 extensions && !g_queue_is_empty (extensions));
852
853 /* Try to guess size of the attachments */
854 dw = camel_medium_get_content (CAMEL_MEDIUM (part));
855 ba = camel_data_wrapper_get_byte_array (dw);
856 if (ba) {
857 size = ba->len;
858
859 if (camel_mime_part_get_encoding (part) == CAMEL_TRANSFER_ENCODING_BASE64)
860 size = size / 1.37;
861 } else {
862 size = 0;
863 }
864
865 /* e_attachment_load_async must be called from main thread */
866 /* Prioritize ahead of GTK+ redraws. */
867 g_idle_add_full (
868 G_PRIORITY_HIGH_IDLE,
869 (GSourceFunc) load_attachment_idle,
870 g_object_ref (attachment),
871 NULL);
872
873 if (size != 0) {
874 GFileInfo *file_info;
875
876 file_info = e_attachment_ref_file_info (attachment);
877
878 if (file_info == NULL) {
879 file_info = g_file_info_new ();
880 g_file_info_set_content_type (
881 file_info, empa->snoop_mime_type);
882 }
883
884 g_file_info_set_size (file_info, size);
885 e_attachment_set_file_info (attachment, file_info);
886
887 g_object_unref (file_info);
888 }
889
890 g_object_unref (attachment);
891
892 g_string_truncate (part_id, part_id_len);
893
894 /* Push to head, not tail. */
895 g_queue_push_head (parts_queue, empa);
896 }
897
898 void
e_mail_parser_wrap_as_non_expandable_attachment(EMailParser * parser,CamelMimePart * part,GString * part_id,GQueue * out_parts_queue)899 e_mail_parser_wrap_as_non_expandable_attachment (EMailParser *parser,
900 CamelMimePart *part,
901 GString *part_id,
902 GQueue *out_parts_queue)
903 {
904 GQueue work_queue = G_QUEUE_INIT;
905 GList *head, *link;
906
907 g_return_if_fail (E_IS_MAIL_PARSER (parser));
908 g_return_if_fail (CAMEL_IS_MIME_PART (part));
909 g_return_if_fail (part_id != NULL);
910 g_return_if_fail (out_parts_queue != NULL);
911
912 e_mail_parser_wrap_as_attachment (parser, part, part_id, &work_queue);
913
914 head = g_queue_peek_head_link (&work_queue);
915
916 for (link = head; link; link = g_list_next (link)) {
917 EMailPartAttachment *empa = link->data;
918
919 if (E_IS_MAIL_PART_ATTACHMENT (empa)) {
920 EAttachment *attachment;
921 CamelMimePart *att_part;
922
923 empa->shown = FALSE;
924 e_mail_part_attachment_set_expandable (empa, FALSE);
925
926 attachment = e_mail_part_attachment_ref_attachment (empa);
927 e_attachment_set_initially_shown (attachment, FALSE);
928 e_attachment_set_can_show (attachment, FALSE);
929
930 att_part = e_attachment_ref_mime_part (attachment);
931 if (att_part)
932 camel_mime_part_set_disposition (att_part, NULL);
933
934 g_clear_object (&att_part);
935 g_clear_object (&attachment);
936 }
937 }
938
939 e_queue_transfer (&work_queue, out_parts_queue);
940 }
941
942 CamelSession *
e_mail_parser_get_session(EMailParser * parser)943 e_mail_parser_get_session (EMailParser *parser)
944 {
945 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
946
947 return parser->priv->session;
948 }
949
950 /* The 'operation' is not used as a GCancellable, but as an identificator
951 of an ongoing operation for which the part list is requested. */
952 EMailPartList *
e_mail_parser_ref_part_list_for_operation(EMailParser * parser,GCancellable * operation)953 e_mail_parser_ref_part_list_for_operation (EMailParser *parser,
954 GCancellable *operation)
955 {
956 EMailPartList *part_list;
957
958 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
959
960 g_mutex_lock (&parser->priv->mutex);
961 part_list = g_hash_table_lookup (parser->priv->ongoing_part_lists, operation);
962 if (part_list)
963 g_object_ref (part_list);
964 g_mutex_unlock (&parser->priv->mutex);
965
966 return part_list;
967 }
968
969 EMailExtensionRegistry *
e_mail_parser_get_extension_registry(EMailParser * parser)970 e_mail_parser_get_extension_registry (EMailParser *parser)
971 {
972 EMailParserClass *parser_class;
973
974 g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL);
975
976 parser_class = E_MAIL_PARSER_GET_CLASS (parser);
977 g_return_val_if_fail (parser_class != NULL, NULL);
978
979 return E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry);
980 }
981