1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* dbx-importer.c
3  *
4  * Author: David Woodhouse <dwmw2@infradead.org>
5  *
6  * Copyright © 2010 Intel Corporation
7  *
8  * Evolution parts largely lifted from pst-import.c:
9  *   Author: Chris Halls <chris.halls@credativ.co.uk>
10  *	    Bharath Acharya <abharath@novell.com>
11  *   Copyright © 2006 Chris Halls
12  *
13  * Some DBX bits from libdbx:
14  *   Author: David Smith <Dave.S@Earthcorp.Com>
15  *    Copyright © 2001 David Smith
16  *
17  * This program is free software; you can redistribute it and/or modify it
18  * under the terms of the GNU Lesser General Public License as published by
19  * the Free Software Foundation.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 
31 #include "evolution-config.h"
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <errno.h>
39 
40 #include <glib/gi18n-lib.h>
41 #include <glib/gstdio.h>
42 #include <glib/gprintf.h>
43 
44 #include <gtk/gtk.h>
45 #include <libecal/libecal.h>
46 #include <libebook/libebook.h>
47 
48 #include <shell/e-shell.h>
49 #include <shell/e-shell-window.h>
50 #include <shell/e-shell-view.h>
51 
52 #include <mail/e-mail-backend.h>
53 #include <mail/em-folder-selection-button.h>
54 #include <mail/em-utils.h>
55 
56 #define d(x)
57 
58 #ifdef WIN32
59 #ifdef gmtime_r
60 #undef gmtime_r
61 #endif
62 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
63 #endif
64 
65 gboolean	org_gnome_evolution_readdbx_supported
66 						(EPlugin *epl,
67 						 EImportTarget *target);
68 GtkWidget *	org_gnome_evolution_readdbx_getwidget
69 						(EImport *ei,
70 						 EImportTarget *target,
71 						 EImportImporter *im);
72 void		org_gnome_evolution_readdbx_import
73 						(EImport *ei,
74 						 EImportTarget *target,
75 						 EImportImporter *im);
76 void		org_gnome_evolution_readdbx_cancel
77 						(EImport *ei,
78 						 EImportTarget *target,
79 						 EImportImporter *im);
80 gint		e_plugin_lib_enable		(EPlugin *ep,
81 						 gint enable);
82 
83 /* em-folder-selection-button.h is private, even though other internal
84  * evo plugins use it!
85  * so declare the functions here
86  * TODO: sort out whether this should really be private
87 */
88 
89 typedef struct {
90 	MailMsg base;
91 
92 	EImport *import;
93 	EImportTarget *target;
94 
95 	GMutex status_lock;
96 	gchar *status_what;
97 	gint status_pc;
98 	gint status_timeout_id;
99 	GCancellable *cancellable;
100 
101 	guint32 *indices;
102 	guint32 index_count;
103 
104 	gchar *uri;
105 	gint dbx_fd;
106 
107 	CamelOperation *cancel;
108 	CamelFolder *folder;
109 	gchar *parent_uri;
110 	gchar *folder_name;
111 	gchar *folder_uri;
112 	gint folder_count;
113 	gint current_item;
114 } DbxImporter;
115 
116 static guchar oe56_mbox_sig[16] = {
117 	0xcf, 0xad, 0x12, 0xfe, 0xc5, 0xfd, 0x74, 0x6f,
118 	0x66, 0xe3, 0xd1, 0x11, 0x9a, 0x4e, 0x00, 0xc0
119 };
120 static guchar oe56_flist_sig[16] = {
121 	0xcf, 0xad, 0x12, 0xfe, 0xc6, 0xfd, 0x74, 0x6f,
122 	0x66, 0xe3, 0xd1, 0x11, 0x9a, 0x4e, 0x00, 0xc0
123 };
124 static guchar oe4_mbox_sig[8] = {
125 	0x4a, 0x4d, 0x46, 0x36, 0x03, 0x00, 0x01, 0x00
126 };
127 
128 gboolean
org_gnome_evolution_readdbx_supported(EPlugin * epl,EImportTarget * target)129 org_gnome_evolution_readdbx_supported (EPlugin *epl,
130                                        EImportTarget *target)
131 {
132 	gchar signature[16];
133 	gboolean ret = FALSE;
134 	gint fd, n;
135 	EImportTargetURI *s;
136 	gchar *filename;
137 
138 	if (target->type != E_IMPORT_TARGET_URI) {
139 		return FALSE;
140 	}
141 
142 	s = (EImportTargetURI *) target;
143 
144 	if (s->uri_src == NULL) {
145 		return TRUE;
146 	}
147 
148 	if (strncmp (s->uri_src, "file:///", strlen ("file:///")) != 0) {
149 		return FALSE;
150 	}
151 
152 	filename = g_filename_from_uri (s->uri_src, NULL, NULL);
153 	fd = g_open (filename, O_RDONLY, 0);
154 	g_free (filename);
155 
156 	if (fd != -1) {
157 		n = read (fd, signature, sizeof (signature));
158 		if (n == sizeof (signature)) {
159 			if (!memcmp (signature, oe56_mbox_sig, sizeof (oe56_mbox_sig))) {
160 				ret = TRUE;
161 			} else if (!memcmp (signature, oe56_flist_sig, sizeof (oe56_flist_sig))) {
162 				d (printf ("Found DBX folder list file\n"));
163 			} else if (!memcmp (signature, oe4_mbox_sig, sizeof (oe4_mbox_sig))) {
164 				d (printf ("Found OE4 DBX file\n"));
165 			}
166 		}
167 		close (fd);
168 	}
169 
170 	return ret;
171 }
172 
173 static void
folder_selected(EMFolderSelectionButton * button,EImportTargetURI * target)174 folder_selected (EMFolderSelectionButton *button,
175                  EImportTargetURI *target)
176 {
177 	g_free (target->uri_dest);
178 	target->uri_dest = g_strdup (em_folder_selection_button_get_folder_uri (button));
179 }
180 
181 GtkWidget *
org_gnome_evolution_readdbx_getwidget(EImport * ei,EImportTarget * target,EImportImporter * im)182 org_gnome_evolution_readdbx_getwidget (EImport *ei,
183                                        EImportTarget *target,
184                                        EImportImporter *im)
185 {
186 	EShell *shell;
187 	EShellBackend *shell_backend;
188 	EMailBackend *backend;
189 	EMailSession *session;
190 	GtkWidget *hbox, *w;
191 	GtkLabel *label;
192 	gchar *select_uri = NULL;
193 
194 #if 1
195 	GtkWindow *window;
196 	/* preselect the folder selected in a mail view */
197 	window = e_shell_get_active_window (e_shell_get_default ());
198 	if (E_IS_SHELL_WINDOW (window)) {
199 		EShellWindow *shell_window;
200 		const gchar *view;
201 
202 		shell_window = E_SHELL_WINDOW (window);
203 		view = e_shell_window_get_active_view (shell_window);
204 
205 		if (view && g_str_equal (view, "mail")) {
206 			EShellView *shell_view;
207 			EMFolderTree *folder_tree = NULL;
208 			EShellSidebar *shell_sidebar;
209 
210 			shell_view = e_shell_window_get_shell_view (
211 				shell_window, view);
212 
213 			shell_sidebar = e_shell_view_get_shell_sidebar (
214 				shell_view);
215 
216 			g_object_get (
217 				shell_sidebar, "folder-tree",
218 				&folder_tree, NULL);
219 
220 			select_uri = em_folder_tree_get_selected_uri (
221 				folder_tree);
222 		}
223 	}
224 #endif
225 
226 	shell = e_shell_get_default ();
227 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
228 
229 	backend = E_MAIL_BACKEND (shell_backend);
230 	session = e_mail_backend_get_session (backend);
231 
232 	if (!select_uri) {
233 		const gchar *local_inbox_uri;
234 		local_inbox_uri =
235 			e_mail_session_get_local_folder_uri (
236 			session, E_MAIL_LOCAL_FOLDER_INBOX);
237 		select_uri = g_strdup (local_inbox_uri);
238 	}
239 
240 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
241 
242 	w = gtk_label_new_with_mnemonic (_("_Destination folder:"));
243 	gtk_box_pack_start ((GtkBox *) hbox, w, FALSE, TRUE, 6);
244 
245 	label = GTK_LABEL (w);
246 
247 	w = em_folder_selection_button_new (
248 		session, _("Select folder"),
249 		_("Select folder to import into"));
250 
251 	gtk_label_set_mnemonic_widget (label, w);
252 	em_folder_selection_button_set_folder_uri (
253 		EM_FOLDER_SELECTION_BUTTON (w), select_uri);
254 	folder_selected (
255 		EM_FOLDER_SELECTION_BUTTON (w), (EImportTargetURI *) target);
256 	g_signal_connect (
257 		w, "selected",
258 		G_CALLBACK (folder_selected), target);
259 	gtk_box_pack_start ((GtkBox *) hbox, w, FALSE, TRUE, 6);
260 
261 	w = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
262 	gtk_box_pack_start ((GtkBox *) w, hbox, FALSE, FALSE, 0);
263 	gtk_widget_show_all (w);
264 
265 	g_free (select_uri);
266 
267 	return w;
268 }
269 
270 static gchar *
dbx_import_describe(DbxImporter * m,gint complete)271 dbx_import_describe (DbxImporter *m,
272                      gint complete)
273 {
274 	return g_strdup (_("Importing Outlook Express data"));
275 }
276 
277 /* Types taken from libdbx and fixed */
278 struct _dbx_tableindexstruct {
279 	guint32 self;
280 	guint32 unknown1;
281 	guint32 anotherTablePtr;
282 	guint32 parent;
283 	gchar unknown2;
284 	gchar ptrCount;
285 	gchar reserve3;
286 	gchar reserve4;
287 	guint32 indexCount;
288 };
289 
290 struct _dbx_indexstruct {
291 	guint32 indexptr;
292 	guint32 anotherTablePtr;
293 	guint32 indexCount;
294 };
295 
296 #define INDEX_POINTER 0xE4
297 #define ITEM_COUNT 0xC4
298 
299 struct _dbx_email_headerstruct {
300 	guint32 self;
301 	guint32 size;
302 	gushort u1;
303 	guchar count;
304 	guchar u2;
305 };
306 
307 struct _dbx_block_hdrstruct {
308 	guint32 self;
309 	guint32 nextaddressoffset;
310 	gushort blocksize;
311 	guchar intcount;
312 	guchar unknown1;
313 	guint32 nextaddress;
314 };
315 
dbx_pread(gint fd,gpointer buf,guint32 count,guint32 offset)316 static gint dbx_pread (gint fd, gpointer buf, guint32 count, guint32 offset)
317 {
318 	if (lseek (fd, offset, SEEK_SET) != offset)
319 		return -1;
320 	return read (fd, buf, count);
321 }
322 
dbx_load_index_table(DbxImporter * m,guint32 pos,guint32 * index_ofs)323 static gboolean dbx_load_index_table (DbxImporter *m, guint32 pos, guint32 *index_ofs)
324 {
325 	struct _dbx_tableindexstruct tindex;
326 	struct _dbx_indexstruct index;
327 	gint i;
328 
329 	d (printf ("Loading index table at 0x%x\n", pos));
330 
331 	if (dbx_pread (m->dbx_fd, &tindex, sizeof (tindex), pos) != sizeof (tindex)) {
332 		g_set_error (
333 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
334 			"Failed to read table index from DBX file");
335 		return FALSE;
336 	}
337 	tindex.anotherTablePtr = GUINT32_FROM_LE (tindex.anotherTablePtr);
338 	tindex.self = GUINT32_FROM_LE (tindex.self);
339 	tindex.indexCount = GUINT32_FROM_LE (tindex.indexCount);
340 
341 	if (tindex.self != pos) {
342 		g_set_error (
343 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
344 			"Corrupt DBX file: Index table at 0x%x does not "
345 			"point to itself", pos);
346 		return FALSE;
347 	}
348 
349 	d (
350 		printf ("Index at %x: indexCount %x, anotherTablePtr %x\n",
351 		pos, tindex.indexCount, tindex.anotherTablePtr));
352 
353 	if (tindex.indexCount > 0) {
354 		if (!dbx_load_index_table (m, tindex.anotherTablePtr, index_ofs))
355 			return FALSE;
356 	}
357 
358 	d (printf ("Index at %x has ptrCount %d\n", pos, tindex.ptrCount));
359 
360 	pos += sizeof (tindex);
361 
362 	for (i = 0; i < tindex.ptrCount; i++) {
363 		if (dbx_pread (m->dbx_fd, &index, sizeof (index), pos) != sizeof (index)) {
364 			g_set_error (
365 				&m->base.error,
366 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
367 				"Failed to read index entry from DBX file");
368 			return FALSE;
369 		}
370 		index.indexptr = GUINT32_FROM_LE (index.indexptr);
371 		index.anotherTablePtr = GUINT32_FROM_LE (index.anotherTablePtr);
372 		index.indexCount = GUINT32_FROM_LE (index.indexCount);
373 
374 		if (*index_ofs == m->index_count) {
375 			g_set_error (
376 				&m->base.error,
377 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
378 				"Corrupt DBX file: Seems to contain more "
379 				"than %d entries claimed in its header",
380 				m->index_count);
381 			return FALSE;
382 		}
383 		m->indices[(*index_ofs)++] = index.indexptr;
384 		if (index.indexCount > 0) {
385 			if (!dbx_load_index_table (m, index.anotherTablePtr, index_ofs))
386 				return FALSE;
387 		}
388 		pos += sizeof (index);
389 	}
390 	return TRUE;
391 }
dbx_load_indices(DbxImporter * m)392 static gboolean dbx_load_indices (DbxImporter *m)
393 {
394 	guint indexptr, itemcount;
395 	guint32 index_ofs = 0;
396 
397 	if (dbx_pread (m->dbx_fd, &indexptr, 4, INDEX_POINTER) != 4) {
398 		g_set_error (
399 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
400 			"Failed to read first index pointer from DBX file");
401 		return FALSE;
402 	}
403 
404 	if (dbx_pread (m->dbx_fd, &itemcount, 4, ITEM_COUNT) != 4) {
405 		g_set_error (
406 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
407 			"Failed to read item count from DBX file");
408 		return FALSE;
409 	}
410 
411 	indexptr = GUINT32_FROM_LE (indexptr);
412 	m->index_count = itemcount = GUINT32_FROM_LE (itemcount);
413 	m->indices = g_malloc (itemcount * 4);
414 
415 	d (printf ("indexptr %x, itemcount %d\n", indexptr, itemcount));
416 
417 	if (indexptr && !dbx_load_index_table (m, indexptr, &index_ofs))
418 		return FALSE;
419 
420 	d (printf ("Loaded %d of %d indices\n", index_ofs, m->index_count));
421 
422 	if (index_ofs < m->index_count) {
423 		g_set_error (
424 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
425 			"Corrupt DBX file: Seems to contain fewer than %d "
426 			"entries claimed in its header", m->index_count);
427 		return FALSE;
428 	}
429 	return TRUE;
430 }
431 
432 static gboolean
dbx_read_mail_body(DbxImporter * m,guint32 offset,gint bodyfd)433 dbx_read_mail_body (DbxImporter *m,
434                     guint32 offset,
435                     gint bodyfd)
436 {
437 	/* FIXME: We really ought to set up CamelStream that we can feed to the
438 	 * MIME parser, rather than using a temporary file */
439 
440 	struct _dbx_block_hdrstruct hdr;
441 	guint32 buflen = 0x200;
442 	guchar *buffer = g_malloc (buflen);
443 
444 	if (ftruncate (bodyfd, 0) == -1)
445 		g_warning ("%s: Failed to truncate file: %s", G_STRFUNC, g_strerror (errno));
446 	lseek (bodyfd, 0, SEEK_SET);
447 
448 	while (offset) {
449 		d (printf ("Reading mail data chunk from %x\n", offset));
450 
451 		if (dbx_pread (m->dbx_fd, &hdr, sizeof (hdr), offset) != sizeof (hdr)) {
452 			g_set_error (
453 				&m->base.error,
454 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
455 				"Failed to read mail data block from "
456 				"DBX file at offset %x", offset);
457 			g_free (buffer);
458 			return FALSE;
459 		}
460 		hdr.self = GUINT32_FROM_LE (hdr.self);
461 		hdr.blocksize = GUINT16_FROM_LE (hdr.blocksize);
462 		hdr.nextaddress = GUINT32_FROM_LE (hdr.nextaddress);
463 
464 		if (hdr.self != offset) {
465 			g_set_error (
466 				&m->base.error,
467 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
468 				"Corrupt DBX file: Mail data block at "
469 				"0x%x does not point to itself", offset);
470 			g_free (buffer);
471 			return FALSE;
472 		}
473 
474 		if (hdr.blocksize > buflen) {
475 			g_free (buffer);
476 			buflen = hdr.blocksize;
477 			buffer = g_malloc (buflen);
478 		}
479 		if (dbx_pread (m->dbx_fd, buffer, hdr.blocksize,
480 			offset + sizeof (hdr)) != hdr.blocksize) {
481 			g_set_error (
482 				&m->base.error,
483 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
484 				"Failed to read mail data from DBX file "
485 				"at offset %lx",
486 				(long)(offset + sizeof (hdr)));
487 			g_free (buffer);
488 			return FALSE;
489 		}
490 		if (write (bodyfd, buffer, hdr.blocksize) != hdr.blocksize) {
491 			g_set_error (
492 				&m->base.error,
493 				CAMEL_ERROR, CAMEL_ERROR_GENERIC,
494 				"Failed to write mail data to temporary file");
495 			g_free (buffer);
496 			return FALSE;
497 		}
498 		offset = hdr.nextaddress;
499 	}
500 
501 	g_free (buffer);
502 
503 	return TRUE;
504 }
505 
506 static gboolean
dbx_read_email(DbxImporter * m,guint32 offset,gint bodyfd,gint * flags)507 dbx_read_email (DbxImporter *m,
508                 guint32 offset,
509                 gint bodyfd,
510                 gint *flags)
511 {
512 	struct _dbx_email_headerstruct hdr;
513 	guchar *buffer;
514 	guint32 dataptr = 0;
515 	gint i;
516 
517 	if (dbx_pread (m->dbx_fd, &hdr, sizeof (hdr), offset) != sizeof (hdr)) {
518 		g_set_error (
519 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
520 			"Failed to read mail header from DBX file at offset %x",
521 			offset);
522 		return FALSE;
523 	}
524 	hdr.self = GUINT32_FROM_LE (hdr.self);
525 	hdr.size = GUINT32_FROM_LE (hdr.size);
526 
527 	if (hdr.self != offset) {
528 		g_set_error (
529 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
530 			"Corrupt DBX file: Mail header at 0x%x does not "
531 			"point to itself", offset);
532 		return FALSE;
533 	}
534 	buffer = g_malloc (hdr.size);
535 	offset += sizeof (hdr);
536 	if (dbx_pread (m->dbx_fd, buffer, hdr.size, offset) != hdr.size) {
537 		g_set_error (
538 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
539 			"Failed to read mail data block from DBX file "
540 			"at offset %x", offset);
541 		g_free (buffer);
542 		return FALSE;
543 	}
544 
545 	for (i = 0; i < hdr.count; i++) {
546 		guchar type = buffer[i *4];
547 		gint val;
548 
549 		val = buffer[i *4 + 1] +
550 			(buffer[i *4 + 2] << 8) +
551 			(buffer[i *4 + 3] << 16);
552 
553 		switch (type) {
554 		case 0x01:
555 			*flags = buffer[hdr.count*4 + val];
556 			d (printf ("Got type 0x01 flags %02x\n", *flags));
557 			break;
558 		case 0x81:
559 			*flags = val;
560 			d (printf ("Got type 0x81 flags %02x\n", *flags));
561 			break;
562 		case 0x04:
563 			dataptr = GUINT32_FROM_LE (*(guint32 *)(buffer + hdr.count *4 + val));
564 			d (printf ("Got type 0x04 data pointer %x\n", dataptr));
565 			break;
566 		case 0x84:
567 			dataptr = val;
568 			d (printf ("Got type 0x84 data pointer %x\n", dataptr));
569 			break;
570 		default:
571 			/* We don't care about anything else */
572 			d (printf ("Ignoring type %02x datum\n", type));
573 			break;
574 		}
575 	}
576 	g_free (buffer);
577 
578 	if (!dataptr)
579 		return FALSE;
580 
581 	return dbx_read_mail_body (m, dataptr, bodyfd);
582 }
583 
584 static void
dbx_import_file(DbxImporter * m)585 dbx_import_file (DbxImporter *m)
586 {
587 	EShell *shell;
588 	EShellBackend *shell_backend;
589 	EMailSession *session;
590 	GCancellable *cancellable;
591 	gchar *filename;
592 	CamelFolder *folder;
593 	gint tmpfile;
594 	gint i;
595 	gint missing = 0;
596 	m->status_what = NULL;
597 	filename = g_filename_from_uri (
598 		((EImportTargetURI *) m->target)->uri_src, NULL, NULL);
599 
600 	/* Destination folder, was set in our widget */
601 	m->parent_uri = g_strdup (((EImportTargetURI *) m->target)->uri_dest);
602 
603 	cancellable = m->base.cancellable;
604 
605 	/* XXX Dig up the EMailSession from the default EShell.
606 	 *     Since the EImport framework doesn't allow for user
607 	 *     data, I don't see how else to get to it. */
608 	shell = e_shell_get_default ();
609 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
610 	session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
611 
612 	camel_operation_push_message (NULL, _("Importing “%s”"), filename);
613 	folder = e_mail_session_uri_to_folder_sync (
614 		session, m->parent_uri, CAMEL_STORE_FOLDER_CREATE,
615 		cancellable, &m->base.error);
616 	if (!folder)
617 		return;
618 	d (printf ("importing to %s\n", camel_folder_get_full_name (folder)));
619 
620 	camel_folder_freeze (folder);
621 
622 	filename = g_filename_from_uri (
623 		((EImportTargetURI *) m->target)->uri_src, NULL, NULL);
624 	m->dbx_fd = g_open (filename, O_RDONLY, 0);
625 	g_free (filename);
626 
627 	if (m->dbx_fd == -1) {
628 		g_set_error (
629 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
630 			"Failed to open import file");
631 		goto out;
632 	}
633 
634 	if (!dbx_load_indices (m))
635 		goto out;
636 
637 	tmpfile = e_mkstemp ("dbx-import-XXXXXX");
638 	if (tmpfile == -1) {
639 		g_set_error (
640 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
641 			"Failed to create temporary file for import");
642 		goto out;
643 	}
644 
645 	for (i = 0; i < m->index_count; i++) {
646 		CamelMessageInfo *info;
647 		CamelMimeMessage *msg;
648 		CamelMimeParser *mp;
649 		gint dbx_flags = 0;
650 		gint flags = 0;
651 		gboolean success;
652 
653 		camel_operation_progress (NULL, 100 * i / m->index_count);
654 		camel_operation_progress (cancellable, 100 * i / m->index_count);
655 
656 		if (!dbx_read_email (m, m->indices[i], tmpfile, &dbx_flags)) {
657 			d (
658 				printf ("Cannot read email index %d at %x\n",
659 				i, m->indices[i]));
660 			if (m->base.error != NULL)
661 				goto out;
662 			missing++;
663 			continue;
664 		}
665 		if (dbx_flags & 0x40)
666 			flags |= CAMEL_MESSAGE_DELETED;
667 		if (dbx_flags & 0x80)
668 			flags |= CAMEL_MESSAGE_SEEN;
669 		if (dbx_flags & 0x80000)
670 			flags |= CAMEL_MESSAGE_ANSWERED;
671 
672 		mp = camel_mime_parser_new ();
673 
674 		lseek (tmpfile, 0, SEEK_SET);
675 		camel_mime_parser_init_with_fd (mp, tmpfile);
676 
677 		msg = camel_mime_message_new ();
678 		if (!camel_mime_part_construct_from_parser_sync (
679 			(CamelMimePart *) msg, mp, NULL, NULL)) {
680 			/* set exception? */
681 			g_object_unref (msg);
682 			g_object_unref (mp);
683 			break;
684 		}
685 
686 		info = camel_message_info_new (NULL);
687 		camel_message_info_set_flags (info, flags, ~0);
688 		success = camel_folder_append_message_sync (
689 			folder, msg, info, NULL,
690 			cancellable, &m->base.error);
691 		g_clear_object (&info);
692 		g_object_unref (msg);
693 
694 		if (!success) {
695 			g_object_unref (mp);
696 			break;
697 		}
698 	}
699  out:
700 	if (m->dbx_fd != -1)
701 		close (m->dbx_fd);
702 	g_free (m->indices);
703 	/* FIXME Not passing GCancellable or GError here. */
704 	camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
705 	camel_folder_thaw (folder);
706 	g_object_unref (folder);
707 	if (missing && m->base.error == NULL) {
708 		g_set_error (
709 			&m->base.error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
710 			"%d messages imported correctly; %d message "
711 			"bodies were not present in the DBX file",
712 			m->index_count - missing, missing);
713 	}
714 	camel_operation_pop_message (NULL);
715 }
716 
717 static void
dbx_import_import(DbxImporter * m,GCancellable * cancellable,GError ** error)718 dbx_import_import (DbxImporter *m,
719                    GCancellable *cancellable,
720                    GError **error)
721 {
722 	dbx_import_file (m);
723 }
724 
725 static void
dbx_import_imported(DbxImporter * m)726 dbx_import_imported (DbxImporter *m)
727 {
728 	e_import_complete (m->target->import, (EImportTarget *) m->target, m->base.error);
729 }
730 
731 static void
dbx_import_free(DbxImporter * m)732 dbx_import_free (DbxImporter *m)
733 {
734 	g_free (m->status_what);
735 	g_mutex_clear (&m->status_lock);
736 
737 	g_source_remove (m->status_timeout_id);
738 	m->status_timeout_id = 0;
739 
740 	g_free (m->folder_name);
741 	g_free (m->folder_uri);
742 	g_free (m->parent_uri);
743 
744 	g_object_unref (m->import);
745 }
746 
747 static MailMsgInfo dbx_import_info = {
748 	sizeof (DbxImporter),
749 	(MailMsgDescFunc) dbx_import_describe,
750 	(MailMsgExecFunc) dbx_import_import,
751 	(MailMsgDoneFunc) dbx_import_imported,
752 	(MailMsgFreeFunc) dbx_import_free,
753 };
754 
755 static gboolean
dbx_status_timeout(gpointer data)756 dbx_status_timeout (gpointer data)
757 {
758 	DbxImporter *importer = data;
759 	gint pc;
760 	gchar *what;
761 
762 	if (importer->status_what) {
763 		g_mutex_lock (&importer->status_lock);
764 		what = importer->status_what;
765 		importer->status_what = NULL;
766 		pc = importer->status_pc;
767 		g_mutex_unlock (&importer->status_lock);
768 
769 		e_import_status (
770 			importer->target->import,
771 			(EImportTarget *) importer->target, what, pc);
772 	}
773 
774 	return TRUE;
775 }
776 
777 static void
dbx_status(CamelOperation * op,const gchar * what,gint pc,gpointer data)778 dbx_status (CamelOperation *op,
779             const gchar *what,
780             gint pc,
781             gpointer data)
782 {
783 	DbxImporter *importer = data;
784 
785 	g_mutex_lock (&importer->status_lock);
786 	g_free (importer->status_what);
787 	importer->status_what = g_strdup (what);
788 	importer->status_pc = pc;
789 	g_mutex_unlock (&importer->status_lock);
790 }
791 
792 /* Start the main import operation */
793 void
org_gnome_evolution_readdbx_import(EImport * ei,EImportTarget * target,EImportImporter * im)794 org_gnome_evolution_readdbx_import (EImport *ei,
795                                     EImportTarget *target,
796                                     EImportImporter *im)
797 {
798 	DbxImporter *m;
799 
800 	m = mail_msg_new (&dbx_import_info);
801 	g_datalist_set_data (&target->data, "dbx-msg", m);
802 	m->import = ei;
803 	g_object_ref (m->import);
804 	m->target = target;
805 
806 	m->parent_uri = NULL;
807 	m->folder_name = NULL;
808 	m->folder_uri = NULL;
809 
810 	m->status_timeout_id =
811 		e_named_timeout_add (100, dbx_status_timeout, m);
812 	g_mutex_init (&m->status_lock);
813 	m->cancellable = camel_operation_new ();
814 
815 	g_signal_connect (
816 		m->cancellable, "status",
817 		G_CALLBACK (dbx_status), m);
818 
819 	mail_msg_unordered_push (m);
820 }
821 
822 void
org_gnome_evolution_readdbx_cancel(EImport * ei,EImportTarget * target,EImportImporter * im)823 org_gnome_evolution_readdbx_cancel (EImport *ei,
824                                     EImportTarget *target,
825                                     EImportImporter *im)
826 {
827 	DbxImporter *m = g_datalist_get_data (&target->data, "dbx-msg");
828 
829 	if (m) {
830 		g_cancellable_cancel (m->cancellable);
831 	}
832 }
833 
834 gint
e_plugin_lib_enable(EPlugin * ep,gint enable)835 e_plugin_lib_enable (EPlugin *ep,
836                      gint enable)
837 {
838 	return 0;
839 }
840