1 /*
2  * e-mail-parser-application-mbox.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 <string.h>
21 #include <glib/gi18n-lib.h>
22 
23 #include <e-util/e-util.h>
24 
25 #include "e-mail-parser-extension.h"
26 #include "e-mail-part-utils.h"
27 
28 typedef EMailParserExtension EMailParserApplicationMBox;
29 typedef EMailParserExtensionClass EMailParserApplicationMBoxClass;
30 
31 GType e_mail_parser_application_mbox_get_type (void);
32 
33 G_DEFINE_TYPE (
34 	EMailParserApplicationMBox,
35 	e_mail_parser_application_mbox,
36 	E_TYPE_MAIL_PARSER_EXTENSION)
37 
38 static const gchar *parser_mime_types[] = {
39 	"application/mbox",
40 	NULL
41 };
42 
43 static void
empe_app_mbox_add_message(EMailParser * parser,CamelMimeMessage * message,gint nth_message,GString * part_id,GCancellable * cancellable,GQueue * out_mail_parts)44 empe_app_mbox_add_message (EMailParser *parser,
45 			   CamelMimeMessage *message,
46 			   gint nth_message,
47 			   GString *part_id,
48 			   GCancellable *cancellable,
49 			   GQueue *out_mail_parts)
50 {
51 	GQueue work_queue = G_QUEUE_INIT;
52 	CamelMimePart *opart;
53 	gint old_len;
54 
55 	old_len = part_id->len;
56 
57 	g_string_append_printf (part_id, ".mbox.%d", nth_message);
58 
59 	opart = camel_mime_part_new ();
60 	camel_medium_set_content (CAMEL_MEDIUM (opart), CAMEL_DATA_WRAPPER (message));
61 	camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (opart), "message/rfc822");
62 
63 	e_mail_parser_parse_part_as (
64 		parser, opart, part_id, "message/rfc822",
65 		cancellable, &work_queue);
66 
67 	/* Wrap every message as attachment */
68 	e_mail_parser_wrap_as_attachment (parser, opart, part_id, &work_queue);
69 
70 	/* Inline all messages in mbox */
71 	if (!g_queue_is_empty (&work_queue)) {
72 		EMailPart *pp = g_queue_peek_head (&work_queue);
73 
74 		pp->force_inline = TRUE;
75 	}
76 
77 	e_queue_transfer (&work_queue, out_mail_parts);
78 
79 	g_string_truncate (part_id, old_len);
80 
81 	g_object_unref (opart);
82 }
83 
84 static gboolean
empe_app_mbox_parse(EMailParserExtension * extension,EMailParser * parser,CamelMimePart * part,GString * part_id,GCancellable * cancellable,GQueue * out_mail_parts)85 empe_app_mbox_parse (EMailParserExtension *extension,
86                      EMailParser *parser,
87                      CamelMimePart *part,
88                      GString *part_id,
89                      GCancellable *cancellable,
90                      GQueue *out_mail_parts)
91 {
92 	CamelMimeParser *mime_parser;
93 	CamelStream *mem_stream;
94 	CamelMimeParserState state;
95 	gint messages;
96 	GError *error = NULL;
97 
98 	/* Extract messages from the application/mbox part and
99 	 * render them as a flat list of messages. */
100 
101 	/* XXX If the mbox has multiple messages, maybe render them
102 	 *     as a multipart/digest so each message can be expanded
103 	 *     or collapsed individually.
104 	 *
105 	 *     See attachment_handler_mail_x_uid_list() for example. */
106 
107 	/* XXX This is based on em_utils_read_messages_from_stream().
108 	 *     Perhaps refactor that function to return an array of
109 	 *     messages instead of assuming we want to append them
110 	 *     to a folder? */
111 
112 	mime_parser = camel_mime_parser_new ();
113 	camel_mime_parser_scan_from (mime_parser, TRUE);
114 
115 	mem_stream = camel_stream_mem_new ();
116 	camel_data_wrapper_decode_to_stream_sync (
117 		camel_medium_get_content (CAMEL_MEDIUM (part)),
118 		mem_stream, NULL, NULL);
119 	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, cancellable, NULL);
120 
121 	camel_mime_parser_init_with_stream (mime_parser, mem_stream, &error);
122 	if (error != NULL) {
123 		e_mail_parser_error (
124 			parser, out_mail_parts,
125 			_("Error parsing MBOX part: %s"),
126 			error->message);
127 		g_object_unref (mem_stream);
128 		g_object_unref (mime_parser);
129 		g_error_free (error);
130 		return TRUE;
131 	}
132 
133 	/* Extract messages from the mbox. */
134 	messages = 0;
135 	state = camel_mime_parser_step (mime_parser, NULL, NULL);
136 
137 	while (state == CAMEL_MIME_PARSER_STATE_FROM) {
138 		CamelMimeMessage *message;
139 
140 		message = camel_mime_message_new ();
141 
142 		if (!camel_mime_part_construct_from_parser_sync (
143 			CAMEL_MIME_PART (message), mime_parser, NULL, NULL)) {
144 			g_object_unref (message);
145 			break;
146 		}
147 
148 		empe_app_mbox_add_message (parser, message, messages, part_id, cancellable, out_mail_parts);
149 		messages++;
150 
151 		g_object_unref (message);
152 
153 		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
154 		camel_mime_parser_step (mime_parser, NULL, NULL);
155 
156 		state = camel_mime_parser_step (mime_parser, NULL, NULL);
157 	}
158 
159 	if (!messages) {
160 		CamelMimeMessage *message;
161 
162 		g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, cancellable, NULL);
163 
164 		message = camel_mime_message_new ();
165 
166 		if (camel_data_wrapper_construct_from_stream_sync (CAMEL_DATA_WRAPPER (message), mem_stream, NULL, NULL)) {
167 			empe_app_mbox_add_message (parser, message, messages, part_id, cancellable, out_mail_parts);
168 			messages++;
169 		}
170 
171 		g_object_unref (message);
172 	}
173 
174 	g_object_unref (mime_parser);
175 	g_object_unref (mem_stream);
176 
177 	return messages > 0;
178 }
179 
180 static void
e_mail_parser_application_mbox_class_init(EMailParserExtensionClass * class)181 e_mail_parser_application_mbox_class_init (EMailParserExtensionClass *class)
182 {
183 	class->mime_types = parser_mime_types;
184 	class->priority = G_PRIORITY_LOW;
185 	class->flags =
186 		E_MAIL_PARSER_EXTENSION_INLINE |
187 		E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE;
188 	class->parse = empe_app_mbox_parse;
189 }
190 
191 static void
e_mail_parser_application_mbox_init(EMailParserExtension * extension)192 e_mail_parser_application_mbox_init (EMailParserExtension *extension)
193 {
194 }
195