1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* pst-importer.c
3 *
4 * Author: Chris Halls <chris.halls@credativ.co.uk>
5 * Bharath Acharya <abharath@novell.com>
6 *
7 * Copyright (C) 2006 Chris Halls
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include "evolution-config.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #include <glib/gi18n-lib.h>
33 #include <glib/gstdio.h>
34 #include <glib/gprintf.h>
35
36 #include <gtk/gtk.h>
37 #include <libecal/libecal.h>
38 #include <libebook/libebook.h>
39
40 #include <shell/e-shell.h>
41 #include <shell/e-shell-window.h>
42 #include <shell/e-shell-view.h>
43 #include <shell/e-shell-sidebar.h>
44
45 #include <mail/e-mail-backend.h>
46 #include <mail/em-folder-selection-button.h>
47 #include <mail/em-utils.h>
48
49 #include <libpst/libpst.h>
50 #include <libpst/timeconv.h>
51
52 #ifdef WIN32
53 #ifdef gmtime_r
54 #undef gmtime_r
55 #endif
56 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
57 #endif
58
59 typedef struct _PstImporter PstImporter;
60
61 gint pst_init (pst_file *pst, gchar *filename);
62 gchar *get_pst_rootname (pst_file *pst, gchar *filename);
63 static void pst_error_msg (const gchar *fmt, ...);
64 static void pst_import_folders (PstImporter *m, pst_desc_tree *topitem);
65 static void pst_process_item (PstImporter *m, pst_desc_tree *d_ptr, gchar **previouss_folder);
66 static void pst_process_folder (PstImporter *m, pst_item *item);
67 static void pst_process_email (PstImporter *m, pst_item *item);
68 static void pst_process_contact (PstImporter *m, pst_item *item);
69 static void pst_process_appointment (PstImporter *m, pst_item *item);
70 static void pst_process_task (PstImporter *m, pst_item *item);
71 static void pst_process_journal (PstImporter *m, pst_item *item);
72
73 static void pst_import_file (PstImporter *m);
74 gchar *foldername_to_utf8 (const gchar *pstname);
75 gchar *string_to_utf8 (const gchar *string);
76 void contact_set_date (EContact *contact, EContactField id, FILETIME *date);
77 static void fill_calcomponent (PstImporter *m, pst_item *item, ECalComponent *ec, const gchar *type);
78 ICalTime *get_ical_date (FILETIME *date, gboolean is_date);
79 gchar *rfc2445_datetime_format (FILETIME *ft);
80
81 gboolean org_credativ_evolution_readpst_supported (EPlugin *epl, EImportTarget *target);
82 GtkWidget *org_credativ_evolution_readpst_getwidget (EImport *ei, EImportTarget *target, EImportImporter *im);
83 void org_credativ_evolution_readpst_import (EImport *ei, EImportTarget *target, EImportImporter *im);
84 void org_credativ_evolution_readpst_cancel (EImport *ei, EImportTarget *target, EImportImporter *im);
85 gint e_plugin_lib_enable (EPlugin *ep, gint enable);
86
87 /* em-folder-selection-button.h is private, even though other internal evo plugins use it!
88 * so declare the functions here
89 * TODO: sort out whether this should really be private
90 */
91
92 static guchar pst_signature[] = { '!', 'B', 'D', 'N' };
93
94 struct _PstImporter {
95 MailMsg base;
96
97 EImport *import;
98 EImportTarget *target;
99
100 gint waiting_open;
101 GMutex status_lock;
102 gchar *status_what;
103 gint status_pc;
104 gint status_timeout_id;
105 GCancellable *cancellable;
106
107 pst_file pst;
108
109 CamelFolder *folder;
110 gchar *folder_name;
111 gchar *folder_uri;
112 gint folder_count;
113 gint current_item;
114
115 EBookClient *addressbook;
116 ECalClient *calendar;
117 ECalClient *tasks;
118 ECalClient *journal;
119
120 /* progress indicator */
121 gint position;
122 gint total;
123 };
124
125 gboolean
org_credativ_evolution_readpst_supported(EPlugin * epl,EImportTarget * target)126 org_credativ_evolution_readpst_supported (EPlugin *epl,
127 EImportTarget *target)
128 {
129 gchar signature[sizeof (pst_signature)];
130 gboolean ret = FALSE;
131 gint fd, n;
132 EImportTargetURI *s;
133 gchar *filename;
134
135 if (target->type != E_IMPORT_TARGET_URI) {
136 return FALSE;
137 }
138
139 s = (EImportTargetURI *) target;
140
141 if (s->uri_src == NULL) {
142 return TRUE;
143 }
144
145 if (strncmp (s->uri_src, "file:///", strlen ("file:///")) != 0) {
146 return FALSE;
147 }
148
149 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
150 fd = g_open (filename, O_RDONLY, 0);
151 g_free (filename);
152
153 if (fd != -1) {
154 n = read (fd, signature, sizeof (pst_signature));
155 ret = n == sizeof (pst_signature) && memcmp (signature, pst_signature, sizeof (pst_signature)) == 0;
156 close (fd);
157 }
158
159 return ret;
160 }
161
162 static void
checkbox_mail_toggle_cb(GtkToggleButton * tb,EImportTarget * target)163 checkbox_mail_toggle_cb (GtkToggleButton *tb,
164 EImportTarget *target)
165 {
166 g_datalist_set_data (&target->data, "pst-do-mail", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
167 }
168
169 static void
checkbox_addr_toggle_cb(GtkToggleButton * tb,EImportTarget * target)170 checkbox_addr_toggle_cb (GtkToggleButton *tb,
171 EImportTarget *target)
172 {
173 g_datalist_set_data (&target->data, "pst-do-addr", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
174 }
175
176 static void
checkbox_appt_toggle_cb(GtkToggleButton * tb,EImportTarget * target)177 checkbox_appt_toggle_cb (GtkToggleButton *tb,
178 EImportTarget *target)
179 {
180 g_datalist_set_data (&target->data, "pst-do-appt", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
181 }
182
183 static void
checkbox_task_toggle_cb(GtkToggleButton * tb,EImportTarget * target)184 checkbox_task_toggle_cb (GtkToggleButton *tb,
185 EImportTarget *target)
186 {
187 g_datalist_set_data (&target->data, "pst-do-task", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
188 }
189
190 static void
checkbox_journal_toggle_cb(GtkToggleButton * tb,EImportTarget * target)191 checkbox_journal_toggle_cb (GtkToggleButton *tb,
192 EImportTarget *target)
193 {
194 g_datalist_set_data (&target->data, "pst-do-journal", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
195 }
196
197 static void
folder_selected(EMFolderSelectionButton * button,EImportTargetURI * target)198 folder_selected (EMFolderSelectionButton *button,
199 EImportTargetURI *target)
200 {
201 g_free (target->uri_dest);
202 target->uri_dest = g_strdup (em_folder_selection_button_get_folder_uri (button));
203 }
204
205 /**
206 * Suggest a folder to import data into
207 */
208 static gchar *
get_suggested_foldername(EImportTargetURI * target)209 get_suggested_foldername (EImportTargetURI *target)
210 {
211 EShell *shell;
212 EShellBackend *shell_backend;
213 EMailBackend *backend;
214 EMailSession *session;
215 GtkWindow *window;
216 const gchar *inbox;
217 gchar *delim, *filename;
218 gchar *rootname = NULL;
219 GString *foldername;
220 pst_file pst;
221
222 /* XXX Dig up the EMailSession from the default EShell.
223 * Since the EImport framework doesn't allow for user
224 * data, I don't see how else to get to it. */
225 shell = e_shell_get_default ();
226 shell_backend = e_shell_get_backend_by_name (shell, "mail");
227
228 backend = E_MAIL_BACKEND (shell_backend);
229 session = e_mail_backend_get_session (backend);
230
231 foldername = NULL;
232
233 /* preselect the folder selected in a mail view */
234 window = e_shell_get_active_window (shell);
235 if (E_IS_SHELL_WINDOW (window)) {
236 EShellWindow *shell_window;
237 const gchar *view;
238
239 shell_window = E_SHELL_WINDOW (window);
240 view = e_shell_window_get_active_view (shell_window);
241
242 if (view && g_str_equal (view, "mail")) {
243 EShellView *shell_view;
244 EShellSidebar *shell_sidebar;
245 EMFolderTree *folder_tree = NULL;
246 gchar *selected_uri;
247
248 shell_view = e_shell_window_get_shell_view (
249 shell_window, view);
250
251 shell_sidebar =
252 e_shell_view_get_shell_sidebar (shell_view);
253
254 g_object_get (
255 shell_sidebar, "folder-tree",
256 &folder_tree, NULL);
257
258 selected_uri = em_folder_tree_get_selected_uri (folder_tree);
259
260 g_object_unref (folder_tree);
261
262 if (selected_uri && *selected_uri)
263 foldername = g_string_new (selected_uri);
264
265 g_free (selected_uri);
266 }
267 }
268
269 if (!foldername) {
270 /* Suggest a folder that is in the same mail storage as the users' inbox,
271 * with a name derived from the .PST file */
272 inbox =
273 e_mail_session_get_local_folder_uri (
274 session, E_MAIL_LOCAL_FOLDER_INBOX);
275
276 delim = g_strrstr (inbox, "#");
277 if (delim != NULL) {
278 foldername = g_string_new_len (inbox, delim - inbox);
279 } else {
280 foldername = g_string_new (inbox);
281 }
282 }
283
284 g_string_append_c (foldername, '/');
285
286 filename = g_filename_from_uri (target->uri_src, NULL, NULL);
287
288 if (pst_init (&pst, filename) == 0) {
289 rootname = get_pst_rootname (&pst, filename);
290 }
291
292 g_free (filename);
293
294 if (rootname != NULL) {
295 gchar *utf8name;
296 utf8name = foldername_to_utf8 (rootname);
297 g_string_append (foldername, utf8name);
298 g_free (utf8name);
299 g_free (rootname);
300 } else {
301 g_string_append (foldername, "outlook_data");
302 }
303
304 /* FIXME Leaking a CamelFolder reference here. */
305 /* FIXME Not passing a GCancellable or GError here. */
306 if (e_mail_session_uri_to_folder_sync (
307 session, foldername->str, 0, NULL, NULL) != NULL) {
308 CamelFolder *folder;
309
310 /* Folder exists - add a number */
311 gint i, len;
312 len = foldername->len;
313
314 for (i = 1; i < 10000; i++) {
315 g_string_truncate (foldername, len);
316 g_string_append_printf (foldername, "_%d", i);
317 /* FIXME Not passing a GCancellable or GError here. */
318 if ((folder = e_mail_session_uri_to_folder_sync (
319 session, foldername->str, 0, NULL, NULL)) == NULL) {
320 /* Folder does not exist */
321 break;
322 }
323 }
324
325 if (folder != NULL) {
326 pst_error_msg ("Error searching for an unused folder name. uri=%s", foldername->str);
327 }
328 }
329
330 return g_string_free (foldername, FALSE);
331
332 }
333
334 static void
widget_sanitizer_cb(GtkToggleButton * button,GtkWidget * source_combo)335 widget_sanitizer_cb (GtkToggleButton *button,
336 GtkWidget *source_combo)
337 {
338 g_return_if_fail (button != NULL);
339 g_return_if_fail (source_combo != NULL);
340
341 gtk_widget_set_sensitive (source_combo, gtk_toggle_button_get_active (button));
342 }
343
344 static const gchar *
get_source_combo_key(const gchar * extension_name)345 get_source_combo_key (const gchar *extension_name)
346 {
347 if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_ADDRESS_BOOK) == 0)
348 return "pst-contacts-source-combo";
349
350 if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0)
351 return "pst-events-source-combo";
352
353 if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0)
354 return "pst-tasks-source-combo";
355
356 if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0)
357 return "pst-memos-source-combo";
358
359 return NULL;
360 }
361
362 static void
add_source_list_with_check(GtkWidget * frame,const gchar * caption,EClientCache * client_cache,const gchar * extension_name,GCallback toggle_callback,EImportTarget * target,gboolean active)363 add_source_list_with_check (GtkWidget *frame,
364 const gchar *caption,
365 EClientCache *client_cache,
366 const gchar *extension_name,
367 GCallback toggle_callback,
368 EImportTarget *target,
369 gboolean active)
370 {
371 ESourceRegistry *registry;
372 ESource *source = NULL;
373 GtkWidget *check, *hbox;
374 GtkWidget *combo = NULL;
375
376 g_return_if_fail (frame != NULL);
377 g_return_if_fail (caption != NULL);
378 g_return_if_fail (toggle_callback != NULL);
379
380 registry = e_client_cache_ref_registry (client_cache);
381 source = e_source_registry_ref_default_for_extension_name (
382 registry, extension_name);
383 g_object_unref (registry);
384 g_return_if_fail (source != NULL);
385
386 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
387
388 check = gtk_check_button_new_with_mnemonic (caption);
389 gtk_toggle_button_set_active ((GtkToggleButton *) check, active);
390 g_signal_connect (check, "toggled", toggle_callback, target);
391 gtk_box_pack_start ((GtkBox *) hbox, check, FALSE, FALSE, 0);
392
393 combo = e_client_combo_box_new (client_cache, extension_name);
394 e_source_combo_box_set_active (E_SOURCE_COMBO_BOX (combo), source);
395
396 gtk_box_pack_end ((GtkBox *) hbox, combo, FALSE, FALSE, 0);
397
398 g_signal_connect (
399 check, "toggled",
400 G_CALLBACK (widget_sanitizer_cb), combo);
401 widget_sanitizer_cb (GTK_TOGGLE_BUTTON (check), combo);
402
403 gtk_box_pack_start ((GtkBox *) frame, hbox, FALSE, FALSE, 0);
404
405 if (combo) {
406 const gchar *key = get_source_combo_key (extension_name);
407
408 g_return_if_fail (key != NULL);
409
410 g_datalist_set_data (&target->data, key, combo);
411 }
412
413 g_object_unref (source);
414 }
415
416 static void
pst_import_check_items(EImportTarget * target)417 pst_import_check_items (EImportTarget *target)
418 {
419 gboolean has_mail = FALSE, has_addr = FALSE, has_appt = FALSE, has_task = FALSE, has_journal = FALSE;
420 gchar *filename;
421 pst_file pst;
422 pst_item *item = NULL, *subitem;
423 pst_desc_tree *d_ptr, *topitem;
424
425 filename = g_filename_from_uri (((EImportTargetURI *) target)->uri_src, NULL, NULL);
426
427 if (pst_init (&pst, filename) < 0) {
428 goto end;
429 }
430
431 if ((item = pst_parse_item (&pst, pst.d_head, NULL)) == NULL) {
432 goto end;
433 }
434
435 if ((topitem = pst_getTopOfFolders (&pst, item)) == NULL) {
436 goto end;
437 }
438
439 d_ptr = topitem->child;
440
441 /* Walk through folder tree */
442 while (d_ptr != NULL && (!has_mail || !has_addr || !has_appt || !has_task || !has_journal)) {
443 subitem = pst_parse_item (&pst, d_ptr, NULL);
444
445 if (subitem != NULL &&
446 subitem->message_store == NULL &&
447 subitem->folder == NULL) {
448 switch (subitem->type) {
449 case PST_TYPE_CONTACT:
450 if (subitem->contact)
451 has_addr = TRUE;
452 break;
453 case PST_TYPE_APPOINTMENT:
454 if (subitem->appointment)
455 has_appt = TRUE;
456 break;
457 case PST_TYPE_TASK:
458 if (subitem->appointment)
459 has_task = TRUE;
460 break;
461 case PST_TYPE_JOURNAL:
462 if (subitem->appointment)
463 has_journal = TRUE;
464 break;
465 case PST_TYPE_NOTE:
466 case PST_TYPE_SCHEDULE:
467 case PST_TYPE_REPORT:
468 if (subitem->email)
469 has_mail = TRUE;
470 break;
471 }
472 }
473
474 pst_freeItem (subitem);
475
476 if (d_ptr->child != NULL) {
477 d_ptr = d_ptr->child;
478 } else if (d_ptr->next != NULL) {
479 d_ptr = d_ptr->next;
480 } else {
481 while (d_ptr != topitem && d_ptr->next == NULL) {
482 d_ptr = d_ptr->parent;
483 }
484
485 if (d_ptr == topitem)
486 break;
487
488 d_ptr = d_ptr->next;
489 }
490 }
491
492 pst_freeItem (item);
493
494 end:
495 g_free (filename);
496 g_datalist_set_data (&target->data, "pst-do-mail", GINT_TO_POINTER (has_mail));
497 g_datalist_set_data (&target->data, "pst-do-addr", GINT_TO_POINTER (has_addr));
498 g_datalist_set_data (&target->data, "pst-do-appt", GINT_TO_POINTER (has_appt));
499 g_datalist_set_data (&target->data, "pst-do-task", GINT_TO_POINTER (has_task));
500 g_datalist_set_data (&target->data, "pst-do-journal", GINT_TO_POINTER (has_journal));
501 }
502
503 GtkWidget *
org_credativ_evolution_readpst_getwidget(EImport * ei,EImportTarget * target,EImportImporter * im)504 org_credativ_evolution_readpst_getwidget (EImport *ei,
505 EImportTarget *target,
506 EImportImporter *im)
507 {
508 EShell *shell;
509 EClientCache *client_cache;
510 EShellBackend *shell_backend;
511 EMailBackend *backend;
512 EMailSession *session;
513 GtkWidget *hbox, *framebox, *w, *check;
514 gchar *foldername;
515
516 pst_import_check_items (target);
517
518 framebox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
519
520 /* Mail */
521 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
522 check = gtk_check_button_new_with_mnemonic (_("_Mail"));
523 gtk_toggle_button_set_active ((GtkToggleButton *) check, GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-mail")));
524 g_signal_connect (
525 check, "toggled",
526 G_CALLBACK (checkbox_mail_toggle_cb), target);
527 gtk_box_pack_start ((GtkBox *) hbox, check, FALSE, FALSE, 0);
528
529 shell = e_shell_get_default ();
530 client_cache = e_shell_get_client_cache (shell);
531 shell_backend = e_shell_get_backend_by_name (shell, "mail");
532
533 backend = E_MAIL_BACKEND (shell_backend);
534 session = e_mail_backend_get_session (backend);
535
536 w = em_folder_selection_button_new (
537 session, _("Select folder"),
538 _("Select folder to import into"));
539 foldername = get_suggested_foldername ((EImportTargetURI *) target);
540 ((EImportTargetURI *) target)->uri_dest = g_strdup (foldername);
541 em_folder_selection_button_set_folder_uri ((EMFolderSelectionButton *) w, foldername);
542 g_signal_connect (
543 w, "selected",
544 G_CALLBACK (folder_selected), target);
545 gtk_box_pack_end ((GtkBox *) hbox, w, FALSE, FALSE, 0);
546 g_signal_connect (
547 check, "toggled",
548 G_CALLBACK (widget_sanitizer_cb), w);
549 widget_sanitizer_cb (GTK_TOGGLE_BUTTON (check), w);
550
551 w = gtk_label_new (_("Destination folder:"));
552 gtk_box_pack_end ((GtkBox *) hbox, w, FALSE, TRUE, 6);
553 g_signal_connect (
554 check, "toggled",
555 G_CALLBACK (widget_sanitizer_cb), w);
556 widget_sanitizer_cb (GTK_TOGGLE_BUTTON (check), w);
557
558 gtk_box_pack_start ((GtkBox *) framebox, hbox, FALSE, FALSE, 0);
559
560 add_source_list_with_check (
561 framebox, _("_Address Book"),
562 client_cache, E_SOURCE_EXTENSION_ADDRESS_BOOK,
563 G_CALLBACK (checkbox_addr_toggle_cb), target,
564 GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-addr")));
565 add_source_list_with_check (
566 framebox, _("A_ppointments"),
567 client_cache, E_SOURCE_EXTENSION_CALENDAR,
568 G_CALLBACK (checkbox_appt_toggle_cb), target,
569 GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-appt")));
570 add_source_list_with_check (
571 framebox, _("_Tasks"),
572 client_cache, E_SOURCE_EXTENSION_TASK_LIST,
573 G_CALLBACK (checkbox_task_toggle_cb), target,
574 GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-task")));
575 add_source_list_with_check (
576 framebox, _("_Journal entries"),
577 client_cache, E_SOURCE_EXTENSION_MEMO_LIST,
578 G_CALLBACK (checkbox_journal_toggle_cb), target,
579 GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-journal")));
580
581 gtk_widget_show_all (framebox);
582
583 g_free (foldername);
584
585 return framebox;
586 }
587
588 static void
pst_get_client_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)589 pst_get_client_cb (GObject *source_object,
590 GAsyncResult *result,
591 gpointer user_data)
592 {
593 PstImporter *m = user_data;
594 EClient *client;
595 GError *error = NULL;
596
597 g_return_if_fail (result != NULL);
598 g_return_if_fail (m != NULL);
599 g_return_if_fail (m->waiting_open > 0);
600
601 client = e_client_combo_box_get_client_finish (
602 E_CLIENT_COMBO_BOX (source_object), result, &error);
603
604 /* Sanity check. */
605 g_return_if_fail (
606 ((client != NULL) && (error == NULL)) ||
607 ((client == NULL) && (error != NULL)));
608
609 if (error != NULL) {
610 g_warning ("%s: %s", G_STRFUNC, error->message);
611 g_error_free (error);
612 }
613
614 if (E_IS_BOOK_CLIENT (client))
615 m->addressbook = E_BOOK_CLIENT (client);
616
617 if (E_IS_CAL_CLIENT (client)) {
618 ECalClient *cal_client = E_CAL_CLIENT (client);
619
620 switch (e_cal_client_get_source_type (cal_client)) {
621 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
622 m->calendar = cal_client;
623 break;
624 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
625 m->tasks = cal_client;
626 break;
627 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
628 m->journal = cal_client;
629 break;
630 default:
631 g_warn_if_reached ();
632 break;
633 }
634 }
635
636 m->waiting_open--;
637 if (!m->waiting_open)
638 mail_msg_unordered_push (m);
639 }
640
641 static void
open_client(PstImporter * m,const gchar * extension_name)642 open_client (PstImporter *m,
643 const gchar *extension_name)
644 {
645 ESourceComboBox *combo_box;
646 ESource *source;
647 const gchar *key;
648
649 key = get_source_combo_key (extension_name);
650 combo_box = g_datalist_get_data (&m->target->data, key);
651 g_return_if_fail (combo_box != NULL);
652
653 source = e_source_combo_box_ref_active (combo_box);
654 g_return_if_fail (source != NULL);
655
656 m->waiting_open++;
657
658 e_client_combo_box_get_client (
659 E_CLIENT_COMBO_BOX (combo_box),
660 source, m->cancellable,
661 pst_get_client_cb, m);
662
663 g_object_unref (source);
664 }
665
666 static void
pst_prepare_run(PstImporter * m)667 pst_prepare_run (PstImporter *m)
668 {
669 if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-addr"))) {
670 open_client (m, E_SOURCE_EXTENSION_ADDRESS_BOOK);
671 }
672
673 if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-appt"))) {
674 open_client (m, E_SOURCE_EXTENSION_CALENDAR);
675 }
676
677 if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-task"))) {
678 open_client (m, E_SOURCE_EXTENSION_TASK_LIST);
679 }
680
681 if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-journal"))) {
682 open_client (m, E_SOURCE_EXTENSION_MEMO_LIST);
683 }
684
685 if (!m->waiting_open)
686 mail_msg_unordered_push (m);
687 }
688
689 static gchar *
pst_import_describe(PstImporter * m,gint complete)690 pst_import_describe (PstImporter *m,
691 gint complete)
692 {
693 return g_strdup (_("Importing Outlook data"));
694 }
695
696 static void
pst_import_import(PstImporter * m,GCancellable * cancellable,GError ** error)697 pst_import_import (PstImporter *m,
698 GCancellable *cancellable,
699 GError **error)
700 {
701 pst_import_file (m);
702 }
703
704 static void
count_items(PstImporter * m,pst_desc_tree * topitem)705 count_items (PstImporter *m,
706 pst_desc_tree *topitem)
707 {
708 pst_desc_tree *d_ptr;
709
710 m->position = 3;
711 m->total = 5;
712 d_ptr = topitem->child;
713
714 /* Walk through folder tree */
715 while (d_ptr != NULL) {
716 m->total++;
717
718 if (d_ptr->child != NULL) {
719 d_ptr = d_ptr->child;
720 } else if (d_ptr->next != NULL) {
721 d_ptr = d_ptr->next;
722 } else {
723 while (d_ptr != topitem && d_ptr->next == NULL) {
724 d_ptr = d_ptr->parent;
725 }
726
727 if (d_ptr == topitem)
728 break;
729
730 d_ptr = d_ptr->next;
731 }
732 }
733 }
734
735 static void
pst_import_file(PstImporter * m)736 pst_import_file (PstImporter *m)
737 {
738 EShell *shell;
739 EShellBackend *shell_backend;
740 EMailSession *session;
741 gint ret;
742 gchar *filename;
743 pst_item *item = NULL;
744 pst_desc_tree *d_ptr;
745
746 /* XXX Dig up the EMailSession from the default EShell.
747 * Since the EImport framework doesn't allow for user
748 * data, I don't see how else to get to it. */
749 shell = e_shell_get_default ();
750 shell_backend = e_shell_get_backend_by_name (shell, "mail");
751 session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
752
753 filename = g_filename_from_uri (((EImportTargetURI *) m->target)->uri_src, NULL, NULL);
754 m->folder_uri = g_strdup (((EImportTargetURI *) m->target)->uri_dest); /* Destination folder, was set in our widget */
755
756 camel_operation_push_message (m->cancellable, _("Importing “%s”"), filename);
757
758 if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-mail"))) {
759 e_mail_session_uri_to_folder_sync (
760 session, m->folder_uri, CAMEL_STORE_FOLDER_CREATE,
761 m->cancellable, &m->base.error);
762 }
763
764 ret = pst_init (&m->pst, filename);
765
766 if (ret < 0) {
767 g_free (filename);
768 camel_operation_pop_message (m->cancellable);
769 return;
770 }
771
772 g_free (filename);
773
774 camel_operation_progress (m->cancellable, 1);
775
776 if ((item = pst_parse_item (&m->pst, m->pst.d_head, NULL)) == NULL) {
777 pst_error_msg ("Could not get root record");
778 return;
779 }
780
781 camel_operation_progress (m->cancellable, 2);
782
783 if ((d_ptr = pst_getTopOfFolders (&m->pst, item)) == NULL) {
784 pst_error_msg ("Top of folders record not found. Cannot continue");
785 return;
786 }
787
788 camel_operation_progress (m->cancellable, 3);
789 count_items (m, d_ptr);
790 pst_import_folders (m, d_ptr);
791
792 camel_operation_progress (m->cancellable, 100);
793
794 camel_operation_pop_message (m->cancellable);
795
796 pst_freeItem (item);
797
798 }
799
800 static void
pst_import_folders(PstImporter * m,pst_desc_tree * topitem)801 pst_import_folders (PstImporter *m,
802 pst_desc_tree *topitem)
803 {
804 GHashTable *node_to_folderuri; /* pointers of hierarchy nodes, to them associated folder uris */
805 pst_desc_tree *d_ptr = NULL;
806
807 node_to_folderuri = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
808
809 if (topitem) {
810 d_ptr = topitem->child;
811 g_hash_table_insert (node_to_folderuri, topitem, g_strdup (m->folder_uri));
812 }
813
814 /* Walk through folder tree */
815 while (d_ptr != NULL && (g_cancellable_is_cancelled (m->cancellable) == FALSE)) {
816 gchar *previous_folder = NULL;
817
818 m->position++;
819 camel_operation_progress (m->cancellable, 100 * m->position / m->total);
820
821 pst_process_item (m, d_ptr, &previous_folder);
822
823 if (d_ptr->child != NULL) {
824 g_clear_object (&m->folder);
825
826 g_return_if_fail (m->folder_uri != NULL);
827 g_hash_table_insert (node_to_folderuri, d_ptr, g_strdup (m->folder_uri));
828
829 d_ptr = d_ptr->child;
830 } else if (d_ptr->next != NULL) {
831 /* for cases where there is an empty folder node, with no subnodes */
832 if (previous_folder) {
833 g_free (m->folder_uri);
834 m->folder_uri = previous_folder;
835 previous_folder = NULL;
836 }
837
838 d_ptr = d_ptr->next;
839 } else {
840 while (d_ptr && d_ptr != topitem && d_ptr->next == NULL) {
841 g_clear_object (&m->folder);
842
843 g_free (m->folder_uri);
844 m->folder_uri = NULL;
845
846 d_ptr = d_ptr->parent;
847
848 if (d_ptr && d_ptr != topitem) {
849 m->folder_uri = g_strdup (g_hash_table_lookup (node_to_folderuri, d_ptr->parent));
850 g_return_if_fail (m->folder_uri != NULL);
851 }
852 }
853
854 if (d_ptr == topitem) {
855 g_free (previous_folder);
856 break;
857 }
858
859 d_ptr = d_ptr ? d_ptr->next : NULL;
860 }
861
862 g_free (previous_folder);
863 }
864
865 g_hash_table_destroy (node_to_folderuri);
866 }
867
868 static void
pst_process_item(PstImporter * m,pst_desc_tree * d_ptr,gchar ** previous_folder)869 pst_process_item (PstImporter *m,
870 pst_desc_tree *d_ptr,
871 gchar **previous_folder)
872 {
873 pst_item *item = NULL;
874
875 if (d_ptr->desc == NULL)
876 return;
877
878 item = pst_parse_item (&m->pst, d_ptr, NULL);
879
880 if (item == NULL)
881 return;
882
883 if (item->message_store != NULL) {
884 pst_error_msg ("A second message_store has been found - ignored");
885 pst_freeItem (item);
886 return;
887 }
888
889 if (item->folder != NULL) {
890 if (previous_folder)
891 *previous_folder = g_strdup (m->folder_uri);
892 pst_process_folder (m, item);
893 } else {
894 switch (item->type) {
895 case PST_TYPE_CONTACT:
896 if (item->contact && m->addressbook && GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-addr")))
897 pst_process_contact (m, item);
898 break;
899 case PST_TYPE_APPOINTMENT:
900 if (item->appointment && m->calendar && GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-appt")))
901 pst_process_appointment (m, item);
902 break;
903 case PST_TYPE_TASK:
904 if (item->appointment && m->tasks && GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-task")))
905 pst_process_task (m, item);
906 break;
907 case PST_TYPE_JOURNAL:
908 if (item->appointment && m->journal && GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-journal")))
909 pst_process_journal (m, item);
910 break;
911 case PST_TYPE_NOTE:
912 case PST_TYPE_SCHEDULE:
913 case PST_TYPE_REPORT:
914 if (item->email && GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pst-do-mail")))
915 pst_process_email (m, item);
916 break;
917 }
918
919 m->current_item++;
920 }
921
922 pst_freeItem (item);
923 }
924
925 /**
926 * string_to_utf8:
927 * @string: String from PST file
928 *
929 * Convert string to utf8. Currently we just use the locale, but maybe
930 * there is encoding information hidden somewhere in the PST file?
931 *
932 * Returns: utf8 representation (caller should free), or NULL for error.
933 */
934 gchar *
string_to_utf8(const gchar * string)935 string_to_utf8 (const gchar *string)
936 {
937 gchar *utf8;
938
939 if (g_utf8_validate (string, -1, NULL))
940 return g_strdup (string);
941
942 utf8 = g_locale_to_utf8 (string, -1, NULL, NULL, NULL);
943
944 return utf8;
945 }
946
947 /**
948 * foldername_to_utf8:
949 * @foldername: from PST file
950 *
951 * Convert foldername to utf8 and escape characters if needed
952 *
953 * Returns: converted folder name, or NULL for error. Caller should free
954 */
955 gchar *
foldername_to_utf8(const gchar * pstname)956 foldername_to_utf8 (const gchar *pstname)
957 {
958 gchar *utf8name, *folder_name;
959
960 utf8name = string_to_utf8 (pstname);
961
962 if (utf8name == NULL) {
963 folder_name = camel_url_encode (pstname, NULL);
964 g_warning ("foldername_to_utf8: Cannot convert to utf8! foldername=%s", folder_name);
965 } else {
966 /* Encode using the current locale */
967 folder_name = camel_url_encode (utf8name, NULL);
968 g_free (utf8name);
969 }
970
971 g_strdelimit (folder_name, "/", '_');
972 g_strescape (folder_name, NULL);
973
974 return folder_name;
975 }
976
977 static void
pst_process_folder(PstImporter * m,pst_item * item)978 pst_process_folder (PstImporter *m,
979 pst_item *item)
980 {
981 gchar *uri;
982 g_free (m->folder_name);
983
984 if (item->file_as.str != NULL) {
985 m->folder_name = foldername_to_utf8 (item->file_as.str);
986 } else {
987 g_critical ("Folder: No name! item->file_as=%s", item->file_as.str);
988 m->folder_name = g_strdup ("unknown_name");
989 }
990
991 uri = g_strjoin ("/", m->folder_uri, m->folder_name, NULL);
992 g_free (m->folder_uri);
993 m->folder_uri = uri;
994
995 g_clear_object (&m->folder);
996
997 m->folder_count = item->folder->item_count;
998 m->current_item = 0;
999 }
1000
1001 /**
1002 * pst_create_folder:
1003 * @m: PstImporter set to current folder
1004 *
1005 * Create current folder in mail hierarchy. Parent folders will also be
1006 * created.
1007 */
1008 static void
pst_create_folder(PstImporter * m)1009 pst_create_folder (PstImporter *m)
1010 {
1011 EShell *shell;
1012 EShellBackend *shell_backend;
1013 EMailSession *session;
1014 const gchar *parent;
1015 gchar *dest, *dest_end, *pos;
1016 gint dest_len;
1017
1018 /* XXX Dig up the EMailSession from the default EShell.
1019 * Since the EImport framework doesn't allow for user
1020 * data, I don't see how else to get to it. */
1021 shell = e_shell_get_default ();
1022 shell_backend = e_shell_get_backend_by_name (shell, "mail");
1023 session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
1024
1025 parent = ((EImportTargetURI *) m->target)->uri_dest;
1026 dest = g_strdup (m->folder_uri);
1027
1028 g_return_if_fail (g_str_has_prefix (dest, parent));
1029
1030 g_clear_object (&m->folder);
1031
1032 dest_len = strlen (dest);
1033 dest_end = dest + dest_len;
1034
1035 pos = dest + strlen (parent);
1036
1037 while (pos != NULL && pos < dest_end) {
1038 pos = g_strstr_len (pos + 1, dest_end - pos, "/");
1039 if (pos != NULL) {
1040 CamelFolder *folder;
1041
1042 *pos = '\0';
1043
1044 folder = e_mail_session_uri_to_folder_sync (
1045 session, dest, CAMEL_STORE_FOLDER_CREATE,
1046 m->cancellable, &m->base.error);
1047 if (folder)
1048 g_object_unref (folder);
1049 else
1050 break;
1051 *pos = '/';
1052 }
1053 }
1054
1055 g_free (dest);
1056
1057 if (!m->base.error)
1058 m->folder = e_mail_session_uri_to_folder_sync (
1059 session, m->folder_uri, CAMEL_STORE_FOLDER_CREATE,
1060 m->cancellable, &m->base.error);
1061 }
1062
1063 /**
1064 * attachment_to_part:
1065 * @m: a #PstImporter
1066 * @attach: attachment to convert
1067 *
1068 * Create a #CamelMimePart from given PST attachment
1069 *
1070 * Returns: #CamelMimePart containing data and mime type
1071 */
1072 static CamelMimePart *
attachment_to_part(PstImporter * m,pst_item_attach * attach)1073 attachment_to_part (PstImporter *m,
1074 pst_item_attach *attach)
1075 {
1076 CamelMimePart *part;
1077 const gchar *mimetype;
1078
1079 part = camel_mime_part_new ();
1080
1081 if (attach->filename2.str || attach->filename1.str) {
1082 camel_mime_part_set_filename (part, (attach->filename2.str ? attach->filename2.str : attach->filename1.str));
1083 camel_mime_part_set_disposition (part, "attachment");
1084 camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
1085 } else {
1086 camel_mime_part_set_disposition (part, "inline");
1087 }
1088
1089 if (attach->mimetype.str != NULL) {
1090 mimetype = attach->mimetype.str;
1091 } else {
1092 mimetype = "application/octet-stream";
1093 }
1094
1095 if (attach->data.data != NULL) {
1096 camel_mime_part_set_content (part, attach->data.data, attach->data.size, mimetype);
1097 } else {
1098 pst_binary attach_rc;
1099 attach_rc = pst_attach_to_mem (&m->pst, attach);
1100
1101 camel_mime_part_set_content (part, (gchar *) attach_rc.data, attach_rc.size, mimetype);
1102 free (attach_rc.data);
1103 }
1104
1105 return part;
1106 }
1107
1108 static void
dequote_string(gchar * str)1109 dequote_string (gchar *str)
1110 {
1111 if (str[0] == '\'' || str[0] == '\"') {
1112 gint len = strlen (str);
1113
1114 if (len > 1 && (str[len - 1] == '\'' || str[len - 1] == '\"')) {
1115 str[0] = ' ';
1116 str[len - 1] = ' ';
1117 g_strstrip (str);
1118 }
1119 }
1120 }
1121
1122 static gboolean
lookup_address(pst_item * item,const gchar * str,gboolean is_unique,CamelAddress * addr)1123 lookup_address (pst_item *item,
1124 const gchar *str,
1125 gboolean is_unique,
1126 CamelAddress *addr)
1127 {
1128 gboolean res = FALSE;
1129 gchar *address;
1130
1131 if (!item || !str || !*str || !addr)
1132 return FALSE;
1133
1134 address = g_strdup (str);
1135 dequote_string (address);
1136
1137 if (item->contact && item->file_as.str &&
1138 (is_unique || g_str_equal (item->file_as.str, str)) &&
1139 item->contact->address1.str &&
1140 item->contact->address1_transport.str &&
1141 g_ascii_strcasecmp (item->contact->address1_transport.str, "SMTP") == 0 &&
1142 !g_str_equal (address, item->contact->address1.str)) {
1143 gchar *tmp = address;
1144
1145 address = g_strconcat ("\"", address, "\" <", item->contact->address1.str, ">", NULL);
1146
1147 g_free (tmp);
1148 }
1149
1150 res = camel_address_decode (addr, address) > 0;
1151
1152 g_free (address);
1153
1154 return res;
1155 }
1156
1157 static const gchar *
strip_smtp(const gchar * str)1158 strip_smtp (const gchar *str)
1159 {
1160 if (str && g_ascii_strncasecmp (str, "SMTP:", 5) == 0)
1161 return str + 5;
1162
1163 return str;
1164 }
1165
1166 static void
pst_process_email(PstImporter * m,pst_item * item)1167 pst_process_email (PstImporter *m,
1168 pst_item *item)
1169 {
1170 CamelMimeMessage *msg;
1171 CamelInternetAddress *addr;
1172 CamelMultipart *mp;
1173 CamelMimePart *part;
1174 CamelMessageInfo *info;
1175 pst_item_attach *attach;
1176 gboolean has_attachments;
1177 gchar *comp_str = NULL;
1178 gboolean success;
1179
1180 if (m->folder == NULL) {
1181 pst_create_folder (m);
1182 if (!m->folder)
1183 return;
1184 }
1185
1186 /* stops on the first valid attachment */
1187 for (attach = item->attach; attach; attach = attach->next) {
1188 if (attach->data.data || attach->i_id)
1189 break;
1190 }
1191
1192 has_attachments = attach != NULL;
1193
1194 if (item->type == PST_TYPE_SCHEDULE && item->appointment) {
1195 ECalComponent *comp;
1196 ICalComponent *vcal;
1197 ICalPropertyMethod method;
1198
1199 comp = e_cal_component_new ();
1200 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1201 fill_calcomponent (m, item, comp, "meeting-request");
1202
1203 vcal = e_cal_util_new_top_level ();
1204
1205 method = I_CAL_METHOD_PUBLISH;
1206 if (item->ascii_type) {
1207 if (g_str_has_prefix (item->ascii_type, "IPM.Schedule.Meeting.Request"))
1208 method = I_CAL_METHOD_REQUEST;
1209 else if (g_str_has_prefix (item->ascii_type, "IPM.Schedule.Meeting.Canceled"))
1210 method = I_CAL_METHOD_CANCEL;
1211 else if (g_str_has_prefix (item->ascii_type, "IPM.Schedule.Meeting.Resp."))
1212 method = I_CAL_METHOD_REPLY;
1213 }
1214
1215 i_cal_component_set_method (vcal, method);
1216
1217 i_cal_component_take_component (vcal, i_cal_component_clone (e_cal_component_get_icalcomponent (comp)));
1218
1219 comp_str = i_cal_component_as_ical_string (vcal);
1220
1221 g_object_unref (vcal);
1222 g_object_unref (comp);
1223
1224 if (comp_str && !*comp_str) {
1225 g_free (comp_str);
1226 comp_str = NULL;
1227 }
1228 }
1229
1230 camel_folder_freeze (m->folder);
1231
1232 msg = camel_mime_message_new ();
1233
1234 if (item->subject.str != NULL) {
1235 gchar *subj;
1236
1237 subj = string_to_utf8 (item->subject.str);
1238 if (subj == NULL) {
1239 g_warning ("Could not convert email subject to utf8: %s", item->subject.str);
1240 camel_mime_message_set_subject (msg, "(lost subject)");
1241 } else {
1242 camel_mime_message_set_subject (msg, subj);
1243 g_free (subj);
1244 }
1245 }
1246
1247 addr = camel_internet_address_new ();
1248
1249 if (item->email->outlook_sender_name.str != NULL && item->email->outlook_sender.str != NULL) {
1250 camel_internet_address_add (addr, item->email->outlook_sender_name.str, strip_smtp (item->email->outlook_sender.str));
1251 } else if (item->email->outlook_sender_name.str != NULL) {
1252 camel_address_decode (CAMEL_ADDRESS (addr), strip_smtp (item->email->outlook_sender_name.str));
1253 } else if (item->email->outlook_sender.str != NULL) {
1254 camel_address_decode (CAMEL_ADDRESS (addr), strip_smtp (item->email->outlook_sender.str));
1255 } else {
1256 /* Evo prints a warning if no from is set, so supply an empty address */
1257 camel_internet_address_add (addr, "", "");
1258 }
1259
1260 camel_mime_message_set_from (msg, addr);
1261 g_object_unref (addr);
1262
1263 if (item->email->sent_date != NULL) {
1264 camel_mime_message_set_date (msg, pst_fileTimeToUnixTime (item->email->sent_date), 0);
1265 }
1266
1267 if (item->email->messageid.str != NULL) {
1268 camel_mime_message_set_message_id (msg, item->email->messageid.str);
1269 }
1270
1271 if (item->email->header.str != NULL) {
1272 /* Use mime parser to read headers */
1273 CamelStream *stream;
1274 /*g_debug (" Email headers length=%zd", strlen (item->email->header));*/
1275 /*g_message (" Email headers... %s...", item->email->header);*/
1276
1277 stream = camel_stream_mem_new_with_buffer (item->email->header.str, strlen (item->email->header.str));
1278 if (!camel_data_wrapper_construct_from_stream_sync ((CamelDataWrapper *) msg, stream, NULL, NULL))
1279 g_warning ("Error reading headers, skipped");
1280
1281 } else {
1282
1283 if (item->email->sentto_address.str != NULL) {
1284 addr = camel_internet_address_new ();
1285
1286 if (lookup_address (item, item->email->sentto_address.str, item->email->cc_address.str == NULL, CAMEL_ADDRESS (addr)))
1287 camel_mime_message_set_recipients (msg, "To", addr);
1288
1289 g_object_unref (addr);
1290 }
1291
1292 if (item->email->cc_address.str != NULL) {
1293 addr = camel_internet_address_new ();
1294
1295 if (lookup_address (item, item->email->cc_address.str, item->email->sentto_address.str == NULL, CAMEL_ADDRESS (addr)))
1296 camel_mime_message_set_recipients (msg, "CC", addr);
1297
1298 g_object_unref (addr);
1299 }
1300 }
1301
1302 mp = camel_multipart_new ();
1303
1304 if (has_attachments) {
1305
1306 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (mp), "multipart/mixed");
1307
1308 } else if (item->email->htmlbody.str && item->body.str) {
1309
1310 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (mp), "multipart/alternative");
1311
1312 } else if (item->email->htmlbody.str) {
1313
1314 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (mp), "text/html");
1315
1316 }
1317
1318 camel_multipart_set_boundary (mp, NULL);
1319
1320 if (item->body.str != NULL) {
1321 /* Read internet headers */
1322
1323 /*g_debug (" Email body length=%zd", strlen (item->email->body));
1324 g_message (" Email body %100s...", item->email->body);*/
1325
1326 part = camel_mime_part_new ();
1327 camel_mime_part_set_content (part, item->body.str, strlen (item->body.str), "text/plain");
1328 camel_multipart_add_part (mp, part);
1329 g_object_unref (part);
1330 }
1331
1332 if (item->email->htmlbody.str != NULL) {
1333 /*g_debug (" HTML body length=%zd", strlen (item->email->htmlbody));*/
1334 part = camel_mime_part_new ();
1335 camel_mime_part_set_content (part, item->email->htmlbody.str, strlen (item->email->htmlbody.str), "text/html");
1336 camel_multipart_add_part (mp, part);
1337 g_object_unref (part);
1338 }
1339
1340 if (comp_str) {
1341 part = camel_mime_part_new ();
1342 camel_mime_part_set_content (part, comp_str, strlen (comp_str), "text/calendar");
1343 camel_multipart_add_part (mp, part);
1344 g_object_unref (part);
1345 }
1346
1347 for (attach = item->attach; attach; attach = attach->next) {
1348 if (attach->data.data || attach->i_id) {
1349 part = attachment_to_part (m, attach);
1350 camel_multipart_add_part (mp, part);
1351 g_object_unref (part);
1352 }
1353 }
1354
1355 /*camel_mime_message_dump (msg, TRUE);*/
1356
1357 if (item->email->htmlbody.str || item->attach) {
1358 camel_medium_set_content (CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER (mp));
1359 } else if (item->body.str) {
1360 camel_mime_part_set_content (CAMEL_MIME_PART (msg), item->body.str, strlen (item->body.str), "text/plain");
1361 } else {
1362 g_warning (
1363 "Email without body. Subject:%s",
1364 (item->subject.str ? item->subject.str : "(empty)"));
1365 camel_mime_part_set_content (CAMEL_MIME_PART (msg), "\n", 1, "text/plain");
1366 }
1367
1368 info = camel_message_info_new (NULL);
1369
1370 /* Read message flags (see comments in libpst.c */
1371 if (item->flags & 0x01)
1372 camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
1373
1374 if (item->email->importance == 2)
1375 camel_message_info_set_flags (info, CAMEL_MESSAGE_FLAGGED, ~0);
1376
1377 if (item->flags & 0x08)
1378 camel_message_info_set_flags (info, CAMEL_MESSAGE_DRAFT, ~0);
1379
1380 /* FIXME Not passing a GCancellable or GError here. */
1381 success = camel_folder_append_message_sync (
1382 m->folder, msg, info, NULL, NULL, NULL);
1383 g_clear_object (&info);
1384 g_object_unref (msg);
1385
1386 /* FIXME Not passing a GCancellable or GError here. */
1387 camel_folder_synchronize_sync (m->folder, FALSE, NULL, NULL);
1388 camel_folder_thaw (m->folder);
1389
1390 g_free (comp_str);
1391
1392 if (!success) {
1393 g_debug ("%s: Exception!", G_STRFUNC);
1394 return;
1395 }
1396
1397 }
1398
1399 static void
contact_set_string(EContact * contact,EContactField id,gchar * string)1400 contact_set_string (EContact *contact,
1401 EContactField id,
1402 gchar *string)
1403 {
1404 if (string != NULL) {
1405 e_contact_set (contact, id, string);
1406 }
1407 }
1408
1409 static void
unknown_field(EContact * contact,GString * notes,const gchar * name,gchar * string)1410 unknown_field (EContact *contact,
1411 GString *notes,
1412 const gchar *name,
1413 gchar *string)
1414 {
1415 /* Field could not be mapped directly so add to notes field */
1416 if (string != NULL) {
1417 g_string_append_printf (notes, "%s: %s\n", name, string);
1418 }
1419 }
1420
1421 static void
contact_set_address(EContact * contact,EContactField id,gchar * address,gchar * city,gchar * country,gchar * po_box,gchar * postal_code,gchar * state,gchar * street)1422 contact_set_address (EContact *contact,
1423 EContactField id,
1424 gchar *address,
1425 gchar *city,
1426 gchar *country,
1427 gchar *po_box,
1428 gchar *postal_code,
1429 gchar *state,
1430 gchar *street)
1431 {
1432 EContactAddress *eaddress;
1433
1434 if (address || city || country || po_box || postal_code || state || street) {
1435 eaddress = e_contact_address_new ();
1436 if (po_box) {
1437 eaddress->po = g_strdup (po_box);
1438 }
1439 /* eaddress->ext = */
1440
1441 if (street) {
1442 eaddress->street = g_strdup (street);
1443 }
1444
1445 if (city) {
1446 eaddress->locality = g_strdup (city);
1447 }
1448
1449 if (state) {
1450 eaddress->region = g_strdup (state);
1451 }
1452
1453 if (postal_code) {
1454 eaddress->code = g_strdup (postal_code);
1455 }
1456
1457 if (country) {
1458 eaddress->country = g_strdup (country);
1459 }
1460
1461 e_contact_set (contact, id, eaddress);
1462 e_contact_address_free (eaddress);
1463 }
1464 }
1465
1466 void
contact_set_date(EContact * contact,EContactField id,FILETIME * date)1467 contact_set_date (EContact *contact,
1468 EContactField id,
1469 FILETIME *date)
1470 {
1471 if (date && (date->dwLowDateTime || date->dwHighDateTime)) {
1472 time_t t1;
1473 struct tm tm;
1474 EContactDate *bday;
1475 bday = e_contact_date_new ();
1476
1477 t1 = pst_fileTimeToUnixTime (date);
1478 gmtime_r (&t1, &tm);
1479
1480 bday->year = tm.tm_year + 1900;
1481 bday->month = tm.tm_mon + 1;
1482 bday->day = tm.tm_mday;
1483
1484 e_contact_set (contact, id, bday);
1485 }
1486 }
1487
1488 static void
pst_process_contact(PstImporter * m,pst_item * item)1489 pst_process_contact (PstImporter *m,
1490 pst_item *item)
1491 {
1492 pst_item_contact *c;
1493 EContact *ec;
1494 GString *notes;
1495 GError *error = NULL;
1496
1497 c = item->contact;
1498 notes = g_string_sized_new (2048);
1499
1500 ec = e_contact_new ();
1501 /* pst's fullname field only contains first, middle, surname */
1502 if (c->display_name_prefix.str || c->suffix.str) {
1503 GString *name = g_string_sized_new (128);
1504
1505 if (c->display_name_prefix.str) {
1506 g_string_assign (name, c->display_name_prefix.str);
1507 }
1508
1509 if (c->first_name.str) {
1510 g_string_append_printf (name, "%s%s", (name->len ? " " : ""), c->first_name.str);
1511 }
1512
1513 if (c->middle_name.str) {
1514 g_string_append_printf (name, "%s%s", (name->len ? " " : ""), c->middle_name.str);
1515 }
1516
1517 if (c->surname.str) {
1518 g_string_append_printf (name, "%s%s", (name->len ? " " : ""), c->surname.str);
1519 }
1520
1521 if (c->surname.str) {
1522 g_string_append_printf (name, "%s%s", (name->len ? " " : ""), c->surname.str);
1523 }
1524
1525 contact_set_string (ec, E_CONTACT_FULL_NAME, name->str);
1526 g_string_free (name, TRUE);
1527
1528 } else {
1529 contact_set_string (ec, E_CONTACT_FULL_NAME, c->fullname.str);
1530 }
1531
1532 /* unknown_field (ec, notes, "initials", c->initials); */
1533
1534 contact_set_string (ec, E_CONTACT_NICKNAME, c->nickname.str);
1535
1536 contact_set_string (ec, E_CONTACT_ORG, c->company_name.str);
1537 contact_set_string (ec, E_CONTACT_ORG_UNIT, c->department.str);
1538 contact_set_string (ec, E_CONTACT_TITLE, c->job_title.str);
1539
1540 contact_set_address (
1541 ec,E_CONTACT_ADDRESS_WORK,
1542 c->business_address.str, c->business_city.str, c->business_country.str,
1543 c->business_po_box.str, c->business_postal_code.str, c->business_state.str, c->business_street.str);
1544
1545 contact_set_address (
1546 ec,E_CONTACT_ADDRESS_HOME,
1547 c->home_address.str, c->home_city.str, c->home_country.str,
1548 c->home_po_box.str, c->home_postal_code.str, c->home_state.str, c->home_street.str);
1549
1550 contact_set_address (
1551 ec,E_CONTACT_ADDRESS_OTHER,
1552 c->other_address.str, c->other_city.str, c->other_country.str,
1553 c->other_po_box.str, c->other_postal_code.str, c->other_state.str, c->other_street.str);
1554
1555 contact_set_string (ec, E_CONTACT_PHONE_ASSISTANT, c->assistant_phone.str);
1556 contact_set_string (ec, E_CONTACT_PHONE_BUSINESS_FAX, c->business_fax.str);
1557 contact_set_string (ec, E_CONTACT_PHONE_BUSINESS, c->business_phone.str);
1558 contact_set_string (ec, E_CONTACT_PHONE_BUSINESS_2, c->business_phone2.str);
1559 contact_set_string (ec, E_CONTACT_PHONE_CALLBACK, c->callback_phone.str);
1560 contact_set_string (ec, E_CONTACT_PHONE_CAR, c->car_phone.str);
1561 contact_set_string (ec, E_CONTACT_PHONE_COMPANY, c->company_main_phone.str);
1562 contact_set_string (ec, E_CONTACT_PHONE_HOME_FAX, c->home_fax.str);
1563 contact_set_string (ec, E_CONTACT_PHONE_HOME, c->home_phone.str);
1564 contact_set_string (ec, E_CONTACT_PHONE_HOME_2, c->home_phone2.str);
1565 contact_set_string (ec, E_CONTACT_PHONE_ISDN, c->isdn_phone.str);
1566 contact_set_string (ec, E_CONTACT_PHONE_MOBILE, c->mobile_phone.str);
1567 contact_set_string (ec, E_CONTACT_PHONE_OTHER_FAX, c->primary_fax.str); /* ? */
1568 contact_set_string (ec, E_CONTACT_PHONE_PAGER, c->pager_phone.str);
1569 contact_set_string (ec, E_CONTACT_PHONE_PRIMARY, c->primary_phone.str);
1570 contact_set_string (ec, E_CONTACT_PHONE_RADIO, c->radio_phone.str);
1571 contact_set_string (ec, E_CONTACT_PHONE_TTYTDD, c->ttytdd_phone.str);
1572 contact_set_string (ec, E_CONTACT_PHONE_TELEX, c->telex.str);
1573 unknown_field (ec, notes, "account_name", c->account_name.str);
1574 contact_set_date (ec, E_CONTACT_ANNIVERSARY, c->wedding_anniversary);
1575 contact_set_string (ec, E_CONTACT_ASSISTANT, c->assistant_name.str);
1576 unknown_field (ec, notes, "billing_information", c->billing_information.str);
1577 contact_set_date (ec, E_CONTACT_BIRTH_DATE, c->birthday);
1578 /* contact_set_string (ec, E_CONTACT_CATEGORIES, c->??); */
1579
1580 contact_set_string (ec, E_CONTACT_EMAIL_1 , c->address1.str);
1581 contact_set_string (ec, E_CONTACT_EMAIL_2 , c->address2.str);
1582 contact_set_string (ec, E_CONTACT_EMAIL_3 , c->address3.str);
1583
1584 /*unknown_field (ec, notes, "address1_desc" , c->address1_desc);
1585 unknown_field (ec, notes, "address1_transport" , c->address1_transport);
1586 unknown_field (ec, notes, "address2_desc" , c->address2_desc);
1587 unknown_field (ec, notes, "address2_transport" , c->address2_transport);
1588 unknown_field (ec, notes, "address3_desc" , c->address3_desc);
1589 unknown_field (ec, notes, "address3_transport" , c->address3_transport);*/
1590
1591 /*unknown_field (ec, notes, "def_postal_address", c->def_postal_address);*/
1592
1593 /* unknown_field (ec, ??, c->gender); */
1594 unknown_field (ec, notes, "gov_id", c->gov_id.str);
1595 unknown_field (ec, notes, "customer_id", c->customer_id.str);
1596 unknown_field (ec, notes, "hobbies", c->hobbies.str);
1597 unknown_field (ec, notes, "followup", c->followup.str);
1598
1599 contact_set_string (ec, E_CONTACT_FREEBUSY_URL , c->free_busy_address.str);
1600
1601 unknown_field (ec, notes, "keyword", c->keyword.str);
1602 unknown_field (ec, notes, "language", c->language.str);
1603 unknown_field (ec, notes, "location", c->location.str);
1604 contact_set_string (ec, E_CONTACT_OFFICE, c->office_loc.str);
1605 unknown_field (ec, notes, "computer_name", c->computer_name.str);
1606 unknown_field (ec, notes, "ftp_site", c->ftp_site.str);
1607
1608 contact_set_string (ec, E_CONTACT_MANAGER , c->manager_name.str);
1609 unknown_field (ec, notes, "mileage", c->mileage.str);
1610 unknown_field (ec, notes, "org_id", c->org_id.str);
1611 contact_set_string (ec, E_CONTACT_ROLE, c->profession.str);
1612
1613 contact_set_string (ec, E_CONTACT_SPOUSE , c->spouse_name.str);
1614
1615 if (c->personal_homepage.str) {
1616 contact_set_string (ec, E_CONTACT_HOMEPAGE_URL , c->personal_homepage.str);
1617 if (c->business_homepage.str) {
1618 unknown_field (ec, notes, "business_homepage", c->business_homepage.str);
1619 }
1620 } else if (c->business_homepage.str) {
1621 contact_set_string (ec, E_CONTACT_HOMEPAGE_URL , c->business_homepage.str);
1622 }
1623
1624 if (item->comment.str) {
1625 g_string_append_printf (notes, "%s\n", item->comment.str);
1626 }
1627
1628 if (item->email && item->body.str) {
1629 g_string_append_printf (notes, "%s\n", item->body.str);
1630 }
1631
1632 contact_set_string (ec, E_CONTACT_NOTE, notes->str);
1633 g_string_free (notes, TRUE);
1634
1635 e_book_client_add_contact_sync (
1636 m->addressbook, ec, E_BOOK_OPERATION_FLAG_NONE, NULL, NULL, &error);
1637
1638 g_object_unref (ec);
1639
1640 if (error != NULL) {
1641 g_warning (
1642 "%s: Failed to add contact: %s",
1643 G_STRFUNC, error->message);
1644 g_error_free (error);
1645 }
1646 }
1647
1648 /**
1649 * get_ical_date:
1650 * @date: time value from libpst
1651 * @is_date: treat as date only (all day event)?
1652 *
1653 * Convert pst time to ICalTime
1654 *
1655 * Returns: converted date
1656 */
1657 ICalTime *
get_ical_date(FILETIME * date,gboolean is_date)1658 get_ical_date (FILETIME *date,
1659 gboolean is_date)
1660 {
1661 if (date && (date->dwLowDateTime || date->dwHighDateTime)) {
1662 time_t t;
1663
1664 t = pst_fileTimeToUnixTime (date);
1665 return i_cal_time_new_from_timet_with_zone (t, is_date, NULL);
1666 } else {
1667 return NULL;
1668 }
1669 }
1670
1671 static void
set_cal_attachments(ECalClient * cal,ECalComponent * ec,PstImporter * m,pst_item_attach * attach)1672 set_cal_attachments (ECalClient *cal,
1673 ECalComponent *ec,
1674 PstImporter *m,
1675 pst_item_attach *attach)
1676 {
1677 GSList *list = NULL;
1678 const gchar *uid;
1679 gchar *store_dir;
1680
1681 if (attach == NULL) {
1682 return;
1683 }
1684
1685 uid = e_cal_component_get_uid (ec);
1686 store_dir = g_filename_from_uri (e_cal_client_get_local_attachment_store (cal), NULL, NULL);
1687
1688 while (attach != NULL) {
1689 const gchar * orig_filename;
1690 gchar *filename, *tmp, *path, *dirname, *uri;
1691 CamelMimePart *part;
1692 CamelDataWrapper *content;
1693 CamelStream *stream;
1694 struct stat st;
1695
1696 part = attachment_to_part (m, attach);
1697
1698 orig_filename = camel_mime_part_get_filename (part);
1699
1700 if (orig_filename == NULL) {
1701 g_warning ("Ignoring unnamed attachment");
1702 attach = attach->next;
1703 continue; /* Ignore unnamed attachments */
1704 }
1705
1706 tmp = camel_file_util_safe_filename (orig_filename);
1707 filename = g_strdup_printf ("%s-%s", uid, tmp);
1708 path = g_build_filename (store_dir, filename, NULL);
1709
1710 g_free (tmp);
1711 g_free (filename);
1712
1713 dirname = g_path_get_dirname (path);
1714 if (g_mkdir_with_parents (dirname, 0777) == -1) {
1715 g_warning ("Could not create directory %s: %s", dirname, g_strerror (errno));
1716 g_free (dirname);
1717 attach = attach->next;
1718 continue;
1719 }
1720 g_free (dirname);
1721
1722 if (g_access (path, F_OK) == 0) {
1723 if (g_access (path, W_OK) != 0) {
1724 g_warning ("Could not write file %s - file exists", path);
1725 attach = attach->next;
1726 continue;
1727 }
1728 }
1729
1730 if (g_stat (path, &st) != -1 && !S_ISREG (st.st_mode)) {
1731 g_warning ("Could not write file %s - not a file", path);
1732 attach = attach->next;
1733 continue;
1734 }
1735
1736 if (!(stream = camel_stream_fs_new_with_name (path, O_WRONLY | O_CREAT | O_TRUNC, 0666, NULL))) {
1737 g_warning ("Could not create stream for file %s - %s", path, g_strerror (errno));
1738 attach = attach->next;
1739 continue;
1740 }
1741
1742 content = camel_medium_get_content (CAMEL_MEDIUM (part));
1743
1744 if (camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL) == -1
1745 || camel_stream_flush (stream, NULL, NULL) == -1)
1746 {
1747 g_warning ("Could not write attachment to %s: %s", path, g_strerror (errno));
1748 g_object_unref (stream);
1749 attach = attach->next;
1750 continue;
1751 }
1752
1753 g_object_unref (stream);
1754
1755 uri = g_filename_to_uri (path, NULL, NULL);
1756 list = g_slist_append (list, i_cal_attach_new_from_url (uri));
1757 g_free (uri);
1758
1759 g_object_unref (part);
1760 g_free (path);
1761
1762 attach = attach->next;
1763
1764 }
1765
1766 g_free (store_dir);
1767
1768 e_cal_component_set_attachments (ec, list);
1769 g_slist_free_full (list, g_object_unref);
1770 }
1771
1772 static void
fill_calcomponent(PstImporter * m,pst_item * item,ECalComponent * ec,const gchar * type)1773 fill_calcomponent (PstImporter *m,
1774 pst_item *item,
1775 ECalComponent *ec,
1776 const gchar *type)
1777 {
1778 pst_item_appointment *a;
1779 pst_item_email *e;
1780 ECalComponentText *text;
1781
1782 a = item->appointment;
1783 e = item->email;
1784
1785 g_return_if_fail (item->appointment != NULL);
1786
1787 if (item->create_date) {
1788 ICalTime *tt;
1789 tt = get_ical_date (item->create_date, FALSE);
1790 e_cal_component_set_created (ec, tt);
1791 g_clear_object (&tt);
1792 }
1793 if (item->modify_date) {
1794 ICalTime *tt;
1795 tt = get_ical_date (item->modify_date, FALSE);
1796 e_cal_component_set_last_modified (ec, tt);
1797 g_clear_object (&tt);
1798 }
1799
1800 if (e) {
1801 if (item->subject.str || e->processed_subject.str) {
1802 text = NULL;
1803 if (item->subject.str) {
1804 text = e_cal_component_text_new (item->subject.str, NULL);
1805 } else if (e->processed_subject.str) {
1806 text = e_cal_component_text_new (e->processed_subject.str, NULL);
1807 }
1808
1809 e_cal_component_set_summary (ec, text);
1810 e_cal_component_text_free (text);
1811 }
1812 if (item->body.str) {
1813 GSList l;
1814 text = e_cal_component_text_new (item->body.str, NULL);
1815 l.data = text;
1816 l.next = NULL;
1817 e_cal_component_set_descriptions (ec, &l);
1818 e_cal_component_text_free (text);
1819 }
1820 } else {
1821 g_warning ("%s without subject / body!", type);
1822 }
1823
1824 if (a->location.str) {
1825 e_cal_component_set_location (ec, a->location.str);
1826 }
1827
1828 if (a->start) {
1829 ECalComponentDateTime *dtstart;
1830
1831 dtstart = e_cal_component_datetime_new_take (
1832 get_ical_date (a->start, a->all_day),
1833 g_strdup (a->timezonestring.str));
1834 e_cal_component_set_dtstart (ec, dtstart);
1835 e_cal_component_datetime_free (dtstart);
1836 }
1837
1838 if (a->end) {
1839 ECalComponentDateTime *dtend;
1840
1841 dtend = e_cal_component_datetime_new_take (
1842 get_ical_date (a->end, a->all_day),
1843 g_strdup (a->timezonestring.str));
1844 e_cal_component_set_dtend (ec, dtend);
1845 e_cal_component_datetime_free (dtend);
1846 }
1847
1848 switch (a->showas) {
1849 case PST_FREEBUSY_TENTATIVE:
1850 e_cal_component_set_status (ec, I_CAL_STATUS_TENTATIVE);
1851 break;
1852 case PST_FREEBUSY_FREE:
1853 /* mark as transparent and as confirmed */
1854 e_cal_component_set_transparency (ec, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
1855 e_cal_component_set_status (ec, I_CAL_STATUS_CONFIRMED);
1856 break;
1857 case PST_FREEBUSY_BUSY:
1858 case PST_FREEBUSY_OUT_OF_OFFICE:
1859 e_cal_component_set_status (ec, I_CAL_STATUS_CONFIRMED);
1860 break;
1861 }
1862 switch (a->label) {
1863 case PST_APP_LABEL_NONE:
1864 break;
1865 case PST_APP_LABEL_IMPORTANT:
1866 e_cal_component_set_categories (ec, "Important"); break;
1867 case PST_APP_LABEL_BUSINESS:
1868 e_cal_component_set_categories (ec, "Business"); break;
1869 case PST_APP_LABEL_PERSONAL:
1870 e_cal_component_set_categories (ec, "Personal"); break;
1871 case PST_APP_LABEL_VACATION:
1872 e_cal_component_set_categories (ec, "Vacation"); break;
1873 case PST_APP_LABEL_MUST_ATTEND:
1874 e_cal_component_set_categories (ec, "Must-attend"); break;
1875 case PST_APP_LABEL_TRAVEL_REQ:
1876 e_cal_component_set_categories (ec, "Travel-required"); break;
1877 case PST_APP_LABEL_NEEDS_PREP:
1878 e_cal_component_set_categories (ec, "Needs-preparation"); break;
1879 case PST_APP_LABEL_BIRTHDAY:
1880 e_cal_component_set_categories (ec, "Birthday"); break;
1881 case PST_APP_LABEL_ANNIVERSARY:
1882 e_cal_component_set_categories (ec, "Anniversary"); break;
1883 case PST_APP_LABEL_PHONE_CALL:
1884 e_cal_component_set_categories (ec, "Phone-call"); break;
1885 }
1886
1887 if (a->alarm || a->alarm_minutes) {
1888 ECalComponentAlarm *alarm;
1889
1890 alarm = e_cal_component_alarm_new ();
1891
1892 if (a->alarm_minutes) {
1893 ECalComponentAlarmTrigger *trigger = NULL;
1894 ICalDuration *duration;
1895
1896 duration = i_cal_duration_new_from_int (- (a->alarm_minutes) * 60);
1897 trigger = e_cal_component_alarm_trigger_new_relative (E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START, duration);
1898 e_cal_component_alarm_take_trigger (alarm, trigger);
1899 g_object_unref (duration);
1900 }
1901
1902 if (a->alarm) {
1903 if (a->alarm_filename.str) {
1904 e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_AUDIO);
1905 } else {
1906 e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
1907 }
1908 }
1909
1910 e_cal_component_add_alarm (ec, alarm);
1911 e_cal_component_alarm_free (alarm);
1912
1913 }
1914
1915 if (a->recurrence_description.str != PST_APP_RECUR_NONE) {
1916 ICalRecurrence *recr;
1917 GSList recur_list;
1918
1919 recr = i_cal_recurrence_new ();
1920
1921 i_cal_recurrence_set_interval (recr, 1); /* Interval not implemented in libpst */
1922 if (a->recurrence_end) {
1923 ICalTime *tt;
1924
1925 tt = get_ical_date (a->recurrence_end, FALSE);
1926 if (tt) {
1927 i_cal_recurrence_set_until (recr, tt);
1928 g_object_unref (tt);
1929 }
1930 }
1931
1932 switch (a->recurrence_type) {
1933 case PST_APP_RECUR_DAILY:
1934 i_cal_recurrence_set_freq (recr, I_CAL_DAILY_RECURRENCE);
1935 break;
1936 case PST_APP_RECUR_WEEKLY:
1937 i_cal_recurrence_set_freq (recr, I_CAL_WEEKLY_RECURRENCE);
1938 break;
1939 case PST_APP_RECUR_MONTHLY:
1940 i_cal_recurrence_set_freq (recr, I_CAL_MONTHLY_RECURRENCE);
1941 break;
1942 case PST_APP_RECUR_YEARLY:
1943 i_cal_recurrence_set_freq (recr, I_CAL_YEARLY_RECURRENCE);
1944 break;
1945 default:
1946 i_cal_recurrence_set_freq (recr, I_CAL_NO_RECURRENCE);
1947 break;
1948 }
1949
1950 recur_list.data = recr;
1951 recur_list.next = NULL;
1952 e_cal_component_set_rrules (ec, &recur_list);
1953 g_object_unref (recr);
1954 }
1955
1956 if (item->type == PST_TYPE_SCHEDULE && item->email && item->ascii_type) {
1957 const gchar *organizer, *organizer_addr, *attendee, *attendee_addr;
1958
1959 if (g_str_has_prefix (item->ascii_type, "IPM.Schedule.Meeting.Resp.")) {
1960 organizer = item->email->outlook_recipient_name.str;
1961 organizer_addr = item->email->outlook_recipient.str;
1962 attendee = item->email->outlook_sender_name.str;
1963 attendee_addr = item->email->outlook_sender.str;
1964 } else {
1965 organizer = item->email->outlook_sender_name.str;
1966 organizer_addr = item->email->outlook_sender.str;
1967 attendee = item->email->outlook_recipient_name.str;
1968 attendee_addr = item->email->outlook_recipient.str;
1969 }
1970
1971 if (organizer || organizer_addr) {
1972 ECalComponentOrganizer *org;
1973
1974 org = e_cal_component_organizer_new ();
1975 e_cal_component_organizer_set_value (org, organizer_addr);
1976 e_cal_component_organizer_set_cn (org, organizer);
1977
1978 e_cal_component_set_organizer (ec, org);
1979 e_cal_component_organizer_free (org);
1980 }
1981
1982 if (attendee || attendee_addr) {
1983 ECalComponentAttendee *att;
1984 GSList *attendees;
1985
1986 att = e_cal_component_attendee_new ();
1987 e_cal_component_attendee_set_value (att, attendee_addr);
1988 e_cal_component_attendee_set_cn (att, attendee);
1989 e_cal_component_attendee_set_cutype (att, I_CAL_CUTYPE_INDIVIDUAL);
1990 e_cal_component_attendee_set_partstat (att, I_CAL_PARTSTAT_NEEDSACTION);
1991 e_cal_component_attendee_set_role (att, I_CAL_ROLE_REQPARTICIPANT);
1992 e_cal_component_attendee_set_rsvp (att, TRUE);
1993
1994 attendees = g_slist_append (NULL, att);
1995 e_cal_component_set_attendees (ec, attendees);
1996 g_slist_free_full (attendees, e_cal_component_attendee_free);
1997 }
1998 }
1999
2000 e_cal_component_commit_sequence (ec);
2001 }
2002
2003 static void
pst_process_component(PstImporter * m,pst_item * item,const gchar * comp_type,ECalComponentVType vtype,ECalClient * cal)2004 pst_process_component (PstImporter *m,
2005 pst_item *item,
2006 const gchar *comp_type,
2007 ECalComponentVType vtype,
2008 ECalClient *cal)
2009 {
2010 ECalComponent *ec;
2011 GError *error = NULL;
2012
2013 g_return_if_fail (item->appointment != NULL);
2014
2015 ec = e_cal_component_new ();
2016 e_cal_component_set_new_vtype (ec, vtype);
2017
2018 fill_calcomponent (m, item, ec, comp_type);
2019 set_cal_attachments (cal, ec, m, item->attach);
2020
2021 e_cal_client_create_object_sync (
2022 cal, e_cal_component_get_icalcomponent (ec),
2023 E_CAL_OPERATION_FLAG_NONE, NULL, NULL, &error);
2024
2025 if (error != NULL) {
2026 g_warning (
2027 "Creation of %s failed: %s",
2028 comp_type, error->message);
2029 g_error_free (error);
2030 }
2031
2032 g_object_unref (ec);
2033 }
2034
2035 static void
pst_process_appointment(PstImporter * m,pst_item * item)2036 pst_process_appointment (PstImporter *m,
2037 pst_item *item)
2038 {
2039 pst_process_component (m, item, "appointment", E_CAL_COMPONENT_EVENT, m->calendar);
2040 }
2041
2042 static void
pst_process_task(PstImporter * m,pst_item * item)2043 pst_process_task (PstImporter *m,
2044 pst_item *item)
2045 {
2046 pst_process_component (m, item, "task", E_CAL_COMPONENT_TODO, m->tasks);
2047 }
2048
2049 static void
pst_process_journal(PstImporter * m,pst_item * item)2050 pst_process_journal (PstImporter *m,
2051 pst_item *item)
2052 {
2053 pst_process_component (m, item, "journal", E_CAL_COMPONENT_JOURNAL, m->journal);
2054 }
2055
2056 /* Print an error message - maybe later bring up an error dialog? */
2057 static void
pst_error_msg(const gchar * fmt,...)2058 pst_error_msg (const gchar *fmt,
2059 ...)
2060 {
2061 va_list ap;
2062
2063 va_start (ap, fmt);
2064 g_critical (fmt, ap);
2065 va_end (ap);
2066 }
2067
2068 static void
pst_import_imported(PstImporter * m)2069 pst_import_imported (PstImporter *m)
2070 {
2071 e_import_complete (m->target->import, (EImportTarget *) m->target, m->base.error);
2072 }
2073
2074 static void
pst_import_free(PstImporter * m)2075 pst_import_free (PstImporter *m)
2076 {
2077 /* pst_close (&m->pst); */
2078 if (m->addressbook)
2079 g_object_unref (m->addressbook);
2080 if (m->calendar)
2081 g_object_unref (m->calendar);
2082 if (m->tasks)
2083 g_object_unref (m->tasks);
2084 if (m->journal)
2085 g_object_unref (m->journal);
2086
2087 g_object_unref (m->cancellable);
2088
2089 g_free (m->status_what);
2090 g_mutex_clear (&m->status_lock);
2091
2092 g_source_remove (m->status_timeout_id);
2093 m->status_timeout_id = 0;
2094
2095 g_free (m->folder_name);
2096 g_free (m->folder_uri);
2097
2098 g_object_unref (m->import);
2099 }
2100
2101 static MailMsgInfo pst_import_info = {
2102 sizeof (PstImporter),
2103 (MailMsgDescFunc) pst_import_describe,
2104 (MailMsgExecFunc) pst_import_import,
2105 (MailMsgDoneFunc) pst_import_imported,
2106 (MailMsgFreeFunc) pst_import_free,
2107 };
2108
2109 static gboolean
pst_status_timeout(gpointer data)2110 pst_status_timeout (gpointer data)
2111 {
2112 PstImporter *importer = data;
2113 gint pc;
2114 gchar *what;
2115
2116 if (importer->status_what) {
2117 g_mutex_lock (&importer->status_lock);
2118 what = importer->status_what;
2119 importer->status_what = NULL;
2120 pc = importer->status_pc;
2121 g_mutex_unlock (&importer->status_lock);
2122
2123 e_import_status (importer->target->import, (EImportTarget *) importer->target, what, pc);
2124 }
2125
2126 return TRUE;
2127 }
2128
2129 static void
pst_status(CamelOperation * op,const gchar * what,gint pc,gpointer data)2130 pst_status (CamelOperation *op,
2131 const gchar *what,
2132 gint pc,
2133 gpointer data)
2134 {
2135 PstImporter *importer = data;
2136
2137 g_mutex_lock (&importer->status_lock);
2138 g_free (importer->status_what);
2139 importer->status_what = g_strdup (what);
2140 importer->status_pc = pc;
2141 g_mutex_unlock (&importer->status_lock);
2142 }
2143
2144 static void
pst_import(EImport * ei,EImportTarget * target)2145 pst_import (EImport *ei,
2146 EImportTarget *target)
2147 {
2148 PstImporter *m;
2149
2150 m = mail_msg_new (&pst_import_info);
2151 g_datalist_set_data (&target->data, "pst-msg", m);
2152 m->import = ei;
2153 g_object_ref (m->import);
2154 m->target = target;
2155
2156 m->folder_name = NULL;
2157 m->folder_uri = NULL;
2158
2159 m->addressbook = NULL;
2160 m->calendar = NULL;
2161 m->tasks = NULL;
2162 m->journal = NULL;
2163 m->waiting_open = 0;
2164
2165 m->status_timeout_id =
2166 e_named_timeout_add (100, pst_status_timeout, m);
2167 g_mutex_init (&m->status_lock);
2168 m->cancellable = camel_operation_new ();
2169
2170 g_signal_connect (
2171 m->cancellable, "status",
2172 G_CALLBACK (pst_status), m);
2173
2174 pst_prepare_run (m);
2175 }
2176
2177 /* Start the main import operation */
2178 void
org_credativ_evolution_readpst_import(EImport * ei,EImportTarget * target,EImportImporter * im)2179 org_credativ_evolution_readpst_import (EImport *ei,
2180 EImportTarget *target,
2181 EImportImporter *im)
2182 {
2183 if (GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-mail"))
2184 || GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-addr"))
2185 || GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-appt"))
2186 || GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-task"))
2187 || GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pst-do-journal"))) {
2188 pst_import (ei, target);
2189 } else {
2190 e_import_complete (target->import, target, NULL);
2191 }
2192 }
2193
2194 void
org_credativ_evolution_readpst_cancel(EImport * ei,EImportTarget * target,EImportImporter * im)2195 org_credativ_evolution_readpst_cancel (EImport *ei,
2196 EImportTarget *target,
2197 EImportImporter *im)
2198 {
2199 PstImporter *m = g_datalist_get_data (&target->data, "pst-msg");
2200
2201 if (m) {
2202 g_cancellable_cancel (m->cancellable);
2203 }
2204 }
2205
2206 gint
e_plugin_lib_enable(EPlugin * ep,gint enable)2207 e_plugin_lib_enable (EPlugin *ep,
2208 gint enable)
2209 {
2210 return 0;
2211 }
2212
2213 /**
2214 * pst_init:
2215 * @pst: pst_file structure to be used by libpst
2216 * @filename: path to file
2217 *
2218 * Open PST file and determine root folder name
2219 *
2220 * Returns: 0 for sucess, -1 for failure
2221 */
2222 gint
pst_init(pst_file * pst,gchar * filename)2223 pst_init (pst_file *pst,
2224 gchar *filename)
2225 {
2226
2227 #if 0
2228 gchar *d_log = "readpst.log";
2229 /* initialize log file */
2230 DEBUG_INIT (d_log);
2231 DEBUG_REGISTER_CLOSE ();
2232 #endif
2233
2234 if (pst_open (pst, filename, NULL) < 0) {
2235 pst_error_msg ("Error opening PST file %s", filename);
2236 return -1;
2237 }
2238
2239 if (pst_load_index (pst) < 0) {
2240 pst_error_msg ("Error loading indexes");
2241 return -1;
2242 }
2243
2244 if (pst_load_extended_attributes (pst) < 0) {
2245 pst_error_msg ("Error loading file items");
2246 return -1;
2247 }
2248
2249 return 0;
2250 }
2251
2252 /**
2253 * get_pst_rootname:
2254 * @pst: pst_file structure to be used by libpst
2255 * @filename: if non %NULL, fallback to this name if folder name is not
2256 * available
2257 *
2258 * Open determine root folder name of PST file
2259 *
2260 * Returns: pointer to name of root folder (should be freed by caller),
2261 * or %NULL if error
2262 */
2263 gchar *
get_pst_rootname(pst_file * pst,gchar * filename)2264 get_pst_rootname (pst_file *pst,
2265 gchar *filename)
2266 {
2267 pst_item *item = NULL;
2268 gchar *rootname = NULL;
2269
2270 if ((item = pst_parse_item (pst, pst->d_head, NULL)) == NULL) {
2271 pst_error_msg ("Could not get root record");
2272 return NULL;
2273 }
2274
2275 if (item->message_store == NULL) {
2276 pst_error_msg ("Could not get root message store");
2277 pst_freeItem (item);
2278 return NULL;
2279 }
2280
2281 /* default the file_as to the same as the main filename if it doesn't exist */
2282 if (item->file_as.str == NULL) {
2283 if (filename == NULL) {
2284 pst_freeItem (item);
2285 return NULL;
2286 }
2287 rootname = g_path_get_basename (filename);
2288 } else {
2289 rootname = g_strdup (item->file_as.str);
2290 }
2291
2292 pst_freeItem (item);
2293
2294 return rootname;
2295 }
2296