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