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