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