evolution-3.6.4/mail/importers/mail-importer.c

No issues found

  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  * Authors:
 17  *		Iain Holmes <iain@ximian.com>
 18  *      Michael Zucchi <notzed@ximian.com>
 19  *
 20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 21  *
 22  */
 23 
 24 #ifdef HAVE_CONFIG_H
 25 #include <config.h>
 26 #endif
 27 
 28 #include <errno.h>
 29 #include <string.h>
 30 #include <sys/types.h>
 31 #include <sys/types.h>
 32 #include <sys/stat.h>
 33 #include <unistd.h>
 34 #include <fcntl.h>
 35 
 36 #include <glib/gstdio.h>
 37 #include <glib/gi18n.h>
 38 
 39 #include "e-util/e-util-private.h"
 40 #include "shell/e-shell-backend.h"
 41 
 42 #include "libemail-utils/mail-mt.h"
 43 #include "libemail-engine/mail-tools.h"
 44 #include "libemail-engine/e-mail-session.h"
 45 
 46 #include "mail-importer.h"
 47 
 48 struct _import_mbox_msg {
 49 	MailMsg base;
 50 
 51 	EMailSession *session;
 52 	gchar *path;
 53 	gchar *uri;
 54 	GCancellable *cancellable;
 55 
 56 	void (*done)(gpointer data, GError **error);
 57 	gpointer done_data;
 58 };
 59 
 60 static gchar *
 61 import_mbox_desc (struct _import_mbox_msg *m)
 62 {
 63 	return g_strdup (_("Importing mailbox"));
 64 }
 65 
 66 static struct {
 67 	gchar tag;
 68 	guint32 mozflag;
 69 	guint32 flag;
 70 } status_flags[] = {
 71 	{ 'F', MSG_FLAG_MARKED, CAMEL_MESSAGE_FLAGGED },
 72 	{ 'A', MSG_FLAG_REPLIED, CAMEL_MESSAGE_ANSWERED },
 73 	{ 'D', MSG_FLAG_EXPUNGED, CAMEL_MESSAGE_DELETED },
 74 	{ 'R', MSG_FLAG_READ, CAMEL_MESSAGE_SEEN },
 75 };
 76 
 77 static guint32
 78 decode_status (const gchar *status)
 79 {
 80 	const gchar *p;
 81 	guint32 flags = 0;
 82 	gint i;
 83 
 84 	p = status;
 85 	while ((*p++)) {
 86 		for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
 87 			if (status_flags[i].tag == *p)
 88 				flags |= status_flags[i].flag;
 89 	}
 90 
 91 	return flags;
 92 }
 93 
 94 static guint32
 95 decode_mozilla_status (const gchar *tmp)
 96 {
 97 	gulong status = strtoul (tmp, NULL, 16);
 98 	guint32 flags = 0;
 99 	gint i;
100 
101 	for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
102 		if (status_flags[i].mozflag & status)
103 			flags |= status_flags[i].flag;
104 	return flags;
105 }
106 
107 static void
108 import_mbox_exec (struct _import_mbox_msg *m,
109                   GCancellable *cancellable,
110                   GError **error)
111 {
112 	CamelFolder *folder;
113 	CamelMimeParser *mp = NULL;
114 	struct stat st;
115 	gint fd;
116 	CamelMessageInfo *info;
117 
118 	if (g_stat (m->path, &st) == -1) {
119 		g_warning (
120 			"cannot find source file to import '%s': %s",
121 			m->path, g_strerror (errno));
122 		return;
123 	}
124 
125 	if (m->uri == NULL || m->uri[0] == 0)
126 		folder = e_mail_session_get_local_folder (
127 			m->session, E_MAIL_LOCAL_FOLDER_INBOX);
128 	else
129 		folder = e_mail_session_uri_to_folder_sync (
130 			m->session, m->uri, CAMEL_STORE_FOLDER_CREATE,
131 			cancellable, error);
132 
133 	if (folder == NULL)
134 		return;
135 
136 	if (S_ISREG (st.st_mode)) {
137 		fd = g_open (m->path, O_RDONLY | O_BINARY, 0);
138 		if (fd == -1) {
139 			g_warning (
140 				"cannot find source file to import '%s': %s",
141 				m->path, g_strerror (errno));
142 			goto fail1;
143 		}
144 
145 		mp = camel_mime_parser_new ();
146 		camel_mime_parser_scan_from (mp, TRUE);
147 		if (camel_mime_parser_init_with_fd (mp, fd) == -1) {
148 			/* will never happen - 0 is unconditionally returned */
149 			goto fail2;
150 		}
151 
152 		camel_operation_push_message (
153 			m->cancellable, _("Importing '%s'"),
154 			camel_folder_get_display_name (folder));
155 		camel_folder_freeze (folder);
156 		while (camel_mime_parser_step (mp, NULL, NULL) ==
157 				CAMEL_MIME_PARSER_STATE_FROM) {
158 
159 			CamelMimeMessage *msg;
160 			const gchar *tmp;
161 			gint pc = 0;
162 			guint32 flags = 0;
163 
164 			if (st.st_size > 0)
165 				pc = (gint) (100.0 * ((gdouble)
166 					camel_mime_parser_tell (mp) /
167 					(gdouble) st.st_size));
168 			camel_operation_progress (m->cancellable, pc);
169 
170 			msg = camel_mime_message_new ();
171 			if (!camel_mime_part_construct_from_parser_sync (
172 				(CamelMimePart *) msg, mp, NULL, NULL)) {
173 				/* set exception? */
174 				g_object_unref (msg);
175 				break;
176 			}
177 
178 			info = camel_message_info_new (NULL);
179 
180 			tmp = camel_medium_get_header ((CamelMedium *) msg, "X-Mozilla-Status");
181 			if (tmp)
182 				flags |= decode_mozilla_status (tmp);
183 			tmp = camel_medium_get_header ((CamelMedium *) msg, "Status");
184 			if (tmp)
185 				flags |= decode_status (tmp);
186 			tmp = camel_medium_get_header ((CamelMedium *) msg, "X-Status");
187 			if (tmp)
188 				flags |= decode_status (tmp);
189 
190 			camel_message_info_set_flags (info, flags, ~0);
191 			camel_folder_append_message_sync (
192 				folder, msg, info, NULL,
193 				cancellable, error);
194 			camel_message_info_free (info);
195 			g_object_unref (msg);
196 
197 			if (error && *error != NULL)
198 				break;
199 
200 			camel_mime_parser_step (mp, NULL, NULL);
201 		}
202 		/* FIXME Not passing a GCancellable or GError here. */
203 		camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
204 		camel_folder_thaw (folder);
205 		camel_operation_pop_message (m->cancellable);
206 	fail2:
207 		g_object_unref (mp);
208 	}
209 fail1:
210 	/* FIXME Not passing a GCancellable or GError here. */
211 	camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
212 	g_object_unref (folder);
213 }
214 
215 static void
216 import_mbox_done (struct _import_mbox_msg *m)
217 {
218 	if (m->done)
219 		m->done (m->done_data, &m->base.error);
220 }
221 
222 static void
223 import_mbox_free (struct _import_mbox_msg *m)
224 {
225 	g_object_unref (m->session);
226 	if (m->cancellable)
227 		g_object_unref (m->cancellable);
228 	g_free (m->uri);
229 	g_free (m->path);
230 }
231 
232 static MailMsgInfo import_mbox_info = {
233 	sizeof (struct _import_mbox_msg),
234 	(MailMsgDescFunc) import_mbox_desc,
235 	(MailMsgExecFunc) import_mbox_exec,
236 	(MailMsgDoneFunc) import_mbox_done,
237 	(MailMsgFreeFunc) import_mbox_free
238 };
239 
240 gint
241 mail_importer_import_mbox (EMailSession *session,
242                            const gchar *path,
243                            const gchar *folderuri,
244                            GCancellable *cancellable,
245                            void (*done) (gpointer data,
246                                          GError **error),
247                            gpointer data)
248 {
249 	struct _import_mbox_msg *m;
250 	gint id;
251 
252 	m = mail_msg_new (&import_mbox_info);
253 	m->session = g_object_ref (session);
254 	m->path = g_strdup (path);
255 	m->uri = g_strdup (folderuri);
256 	m->done = done;
257 	m->done_data = data;
258 	if (cancellable)
259 		m->cancellable = g_object_ref (cancellable);
260 
261 	id = m->base.seq;
262 	mail_msg_fast_ordered_push (m);
263 
264 	return id;
265 }
266 
267 void
268 mail_importer_import_mbox_sync (EMailSession *session,
269                                 const gchar *path,
270                                 const gchar *folderuri,
271                                 GCancellable *cancellable)
272 {
273 	struct _import_mbox_msg *m;
274 
275 	m = mail_msg_new (&import_mbox_info);
276 	m->session = g_object_ref (session);
277 	m->path = g_strdup (path);
278 	m->uri = g_strdup (folderuri);
279 	if (cancellable)
280 		m->base.cancellable = cancellable;
281 
282 	cancellable = m->base.cancellable;
283 
284 	import_mbox_exec (m, cancellable, &m->base.error);
285 	import_mbox_done (m);
286 	mail_msg_unref (m);
287 }
288 
289 struct _import_folders_data {
290 	MailImporterSpecial *special_folders;
291 	EMailSession *session;
292 	GCancellable *cancellable;
293 
294 	guint elmfmt : 1;
295 };
296 
297 static void
298 import_folders_rec (struct _import_folders_data *m,
299                     const gchar *filepath,
300                     const gchar *folderparent)
301 {
302 	GDir *dir;
303 	const gchar *d;
304 	struct stat st;
305 	const gchar *data_dir;
306 	gchar *filefull, *foldersub, *uri, *utf8_filename;
307 	const gchar *folder;
308 
309 	dir = g_dir_open (filepath, 0, NULL);
310 	if (dir == NULL)
311 		return;
312 
313 	data_dir = mail_session_get_data_dir ();
314 
315 	utf8_filename = g_filename_to_utf8 (filepath, -1, NULL, NULL, NULL);
316 	camel_operation_push_message (m->cancellable, _("Scanning %s"), utf8_filename);
317 	g_free (utf8_filename);
318 
319 	while ((d = g_dir_read_name (dir))) {
320 		if (d[0] == '.')
321 			continue;
322 
323 		filefull = g_build_filename (filepath, d, NULL);
324 
325 		/* skip non files and directories, and skip directories in mozilla mode */
326 		if (g_stat (filefull, &st) == -1
327 		    || !(S_ISREG (st.st_mode)
328 			 || (m->elmfmt && S_ISDIR (st.st_mode)))) {
329 			g_free (filefull);
330 			continue;
331 		}
332 
333 		folder = d;
334 		if (folderparent == NULL) {
335 			gint i;
336 
337 			for (i = 0; m->special_folders[i].orig; i++)
338 				if (strcmp (m->special_folders[i].orig, folder) == 0) {
339 					folder = m->special_folders[i].new;
340 					break;
341 				}
342 			/* FIXME: need a better way to get default store location */
343 			uri = g_strdup_printf (
344 				"mbox:%s/local#%s", data_dir, folder);
345 		} else {
346 			uri = g_strdup_printf (
347 				"mbox:%s/local#%s/%s",
348 				data_dir, folderparent, folder);
349 		}
350 
351 		printf ("importing to uri %s\n", uri);
352 		mail_importer_import_mbox_sync (
353 			m->session, filefull, uri, m->cancellable);
354 		g_free (uri);
355 
356 		/* This little gem re-uses the stat buffer and filefull
357 		 * to automagically scan mozilla-format folders. */
358 		if (!m->elmfmt) {
359 			gchar *tmp = g_strdup_printf ("%s.sbd", filefull);
360 
361 			g_free (filefull);
362 			filefull = tmp;
363 			if (g_stat (filefull, &st) == -1) {
364 				g_free (filefull);
365 				continue;
366 			}
367 		}
368 
369 		if (S_ISDIR (st.st_mode)) {
370 			foldersub = folderparent ?
371 				g_strdup_printf (
372 					"%s/%s", folderparent, folder) :
373 				g_strdup (folder);
374 			import_folders_rec (m, filefull, foldersub);
375 			g_free (foldersub);
376 		}
377 
378 		g_free (filefull);
379 	}
380 	g_dir_close (dir);
381 
382 	camel_operation_pop_message (m->cancellable);
383 }
384 
385 /**
386  * mail_importer_import_folders_sync:
387  * @filepath:
388  * @:
389  * @flags:
390  * @cancel:
391  *
392  * import from a base path @filepath into the root local folder tree,
393  * scanning all sub-folders.
394  *
395  * if @flags is MAIL_IMPORTER_MOZFMT, then subfolders are assumed to
396  * be in mozilla/evolutoin 1.5 format, appending '.sbd' to the
397  * directory name. Otherwise they are in elm/mutt/pine format, using
398  * standard unix directories.
399  **/
400 void
401 mail_importer_import_folders_sync (EMailSession *session,
402                                    const gchar *filepath,
403                                    MailImporterSpecial special_folders[],
404                                    gint flags,
405                                    GCancellable *cancellable)
406 {
407 	struct _import_folders_data m;
408 
409 	m.special_folders = special_folders;
410 	m.elmfmt = (flags & MAIL_IMPORTER_MOZFMT) == 0;
411 	m.session = g_object_ref (session);
412 	m.cancellable = cancellable;
413 
414 	import_folders_rec (&m, filepath, NULL);
415 }