evolution-3.6.4/mail/importers/pine-importer.c

No issues found

  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  *
 16  * Authors:
 17  *		Iain Holmes  <iain@ximian.com>
 18  *	Michael Zucchi <notzed@ximian.com>
 19  *
 20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 21  *
 22  */
 23 
 24 #ifdef HAVE_CONFIG_H
 25 #include <config.h>
 26 #endif
 27 
 28 #include <stdio.h>
 29 #include <errno.h>
 30 #include <sys/types.h>
 31 #include <sys/stat.h>
 32 #include <unistd.h>
 33 #include <dirent.h>
 34 #include <string.h>
 35 
 36 #include <gtk/gtk.h>
 37 #include <glib/gi18n.h>
 38 
 39 #include <libebook/libebook.h>
 40 
 41 #include "mail-importer.h"
 42 
 43 #include "libemail-utils/mail-mt.h"
 44 #include "mail/e-mail-backend.h"
 45 #include "e-util/e-import.h"
 46 #include "shell/e-shell.h"
 47 
 48 #define d(x)
 49 
 50 struct _pine_import_msg {
 51 	MailMsg base;
 52 
 53 	EImport *import;
 54 	EImportTarget *target;
 55 
 56 	GMutex *status_lock;
 57 	gchar *status_what;
 58 	gint status_pc;
 59 	gint status_timeout_id;
 60 	GCancellable *cancellable;
 61 };
 62 
 63 static gboolean
 64 pine_supported (EImport *ei,
 65                 EImportTarget *target,
 66                 EImportImporter *im)
 67 {
 68 	gchar *maildir, *addrfile;
 69 	gboolean md_exists, addr_exists;
 70 
 71 	if (target->type != E_IMPORT_TARGET_HOME)
 72 		return FALSE;
 73 
 74 	maildir = g_build_filename (g_get_home_dir (), "mail", NULL);
 75 	md_exists = g_file_test (maildir, G_FILE_TEST_IS_DIR);
 76 	g_free (maildir);
 77 
 78 	addrfile = g_build_filename (g_get_home_dir (), ".addressbook", NULL);
 79 	addr_exists = g_file_test (addrfile, G_FILE_TEST_IS_REGULAR);
 80 	g_free (addrfile);
 81 
 82 	return md_exists || addr_exists;
 83 }
 84 
 85 /*
 86  * See: http://www.washington.edu/pine/tech-notes/low-level.html
 87  * 
 88  * addressbook line is:
 89  *      <nickname>TAB<fullname>TAB<address>TAB<fcc>TAB<comments>
 90  * lists, address is:
 91  *      "(" <address>, <address>, <address>, ... ")"
 92  * 
 93  * <address> is rfc822 address, or alias address.
 94  * if rfc822 address includes a phrase, then that overrides <fullname>
 95  * 
 96  * FIXME: we dont handle aliases in lists.
 97  */
 98 
 99 static void
100 import_contact (EBookClient *book_client,
101                 gchar *line)
102 {
103 	gchar **strings, *addr, **addrs;
104 	gint i;
105 	GList *list;
106 	/*EContactName *name;*/
107 	EContact *card;
108 	gsize len;
109 	GError *error = NULL;
110 
111 	card = e_contact_new ();
112 	strings = g_strsplit (line, "\t", 5);
113 	if (strings[0] && strings[1] && strings[2]) {
114 		gchar *new_uid = NULL;
115 
116 		e_contact_set (card, E_CONTACT_NICKNAME, strings[0]);
117 		e_contact_set (card, E_CONTACT_FULL_NAME, strings[1]);
118 
119 		addr = strings[2];
120 		len = strlen (addr);
121 		if (addr[0] == '(' && addr[len - 1] == ')') {
122 			addr[0] = 0;
123 			addr[len - 1] = 0;
124 			addrs = g_strsplit (addr + 1, ",", 0);
125 			list = NULL;
126 			/* XXX So ... this api is just insane ... we set
127 			 *     plain strings as the contact email if it
128 			 *     is a normal contact, but need to do this
129 			 *     XML crap for mailing lists. */
130 			for (i = 0; addrs[i]; i++) {
131 				EDestination *d;
132 				EVCardAttribute *attr;
133 
134 				d = e_destination_new ();
135 				e_destination_set_email (d, addrs[i]);
136 
137 				attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
138 				e_destination_export_to_vcard_attribute (d, attr);
139 				list = g_list_append (list, attr);
140 				g_object_unref (d);
141 			}
142 			e_contact_set_attributes (card, E_CONTACT_EMAIL, list);
143 			g_list_foreach (list, (GFunc) e_vcard_attribute_free, NULL);
144 			g_list_free (list);
145 			g_strfreev (addrs);
146 			e_contact_set (card, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
147 		} else {
148 			e_contact_set (card, E_CONTACT_EMAIL_1, strings[2]);
149 		}
150 
151 		/*name = e_contact_name_from_string(strings[1]);*/
152 
153 		if (strings[3] && strings[4])
154 			e_contact_set (card, E_CONTACT_NOTE, strings[4]);
155 
156 		e_book_client_add_contact_sync (
157 			book_client, card, &new_uid, NULL, &error);
158 
159 		if (error != NULL) {
160 			g_warning (
161 				"%s: Failed to add contact: %s",
162 				G_STRFUNC, error->message);
163 			g_error_free (error);
164 		} else {
165 			g_free (new_uid);
166 		}
167 
168 		g_object_unref (card);
169 	}
170 	g_strfreev (strings);
171 }
172 
173 static void
174 import_contacts (void)
175 {
176 	EShell *shell;
177 	ESourceRegistry *registry;
178 	EBookClient *book_client = NULL;
179 	GList *list;
180 	gchar *name;
181 	GString *line;
182 	FILE *fp;
183 	gsize offset;
184 	const gchar *extension_name;
185 	GError *error = NULL;
186 
187 	printf ("importing pine addressbook\n");
188 
189 	shell = e_shell_get_default ();
190 	registry = e_shell_get_registry (shell);
191 	extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
192 
193 	name = g_build_filename (g_get_home_dir (), ".addressbook", NULL);
194 	fp = fopen (name, "r");
195 	g_free (name);
196 	if (fp == NULL)
197 		return;
198 
199 	list = e_source_registry_list_sources (registry, extension_name);
200 
201 	if (list != NULL) {
202 		ESource *source;
203 
204 		source = E_SOURCE (list->data);
205 		book_client = e_book_client_new (source, &error);
206 	}
207 
208 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
209 
210 	if (book_client != NULL)
211 		e_client_open_sync (E_CLIENT (book_client), TRUE, NULL, &error);
212 
213 	if (error != NULL) {
214 		g_warning (
215 			"%s: Failed to open book client: %s",
216 			G_STRFUNC, error ? error->message : "Unknown error");
217 		g_clear_error (&error);
218 		fclose (fp);
219 		return;
220 	}
221 
222 	line = g_string_new ("");
223 	g_string_set_size (line, 256);
224 	offset = 0;
225 	while (fgets (line->str + offset, 256, fp)) {
226 		gsize len;
227 
228 		len = strlen (line->str + offset) + offset;
229 		if (line->str[len - 1] == '\n')
230 			g_string_truncate (line, len - 1);
231 		else if (!feof (fp)) {
232 			offset = len;
233 			g_string_set_size (line, len + 256);
234 			continue;
235 		} else {
236 			g_string_truncate (line, len);
237 		}
238 
239 		import_contact (book_client, line->str);
240 		offset = 0;
241 	}
242 
243 	g_string_free (line, TRUE);
244 	fclose (fp);
245 	g_object_unref (book_client);
246 }
247 
248 static gchar *
249 pine_import_desc (struct _pine_import_msg *m)
250 {
251 	return g_strdup (_("Importing Pine data"));
252 }
253 
254 static MailImporterSpecial pine_special_folders[] = {
255 	{ "sent-mail", "Sent" }, /* pine */
256 	{ "saved-messages", "Drafts" },	/* pine */
257 	{ NULL },
258 };
259 
260 static void
261 pine_import_exec (struct _pine_import_msg *m,
262                   GCancellable *cancellable,
263                   GError **error)
264 {
265 	EShell *shell;
266 	EShellBackend *shell_backend;
267 	EMailSession *session;
268 
269 	/* XXX Dig up the EMailSession from the default EShell.
270 	 *     Since the EImport framework doesn't allow for user
271 	 *     data, I don't see how else to get to it. */
272 	shell = e_shell_get_default ();
273 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
274 	session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend));
275 
276 	if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pine-do-addr")))
277 		import_contacts ();
278 
279 	if (GPOINTER_TO_INT (g_datalist_get_data (&m->target->data, "pine-do-mail"))) {
280 		gchar *path;
281 
282 		path = g_build_filename (g_get_home_dir (), "mail", NULL);
283 		mail_importer_import_folders_sync (
284 			session, path, pine_special_folders, 0, m->cancellable);
285 		g_free (path);
286 	}
287 }
288 
289 static void
290 pine_import_done (struct _pine_import_msg *m)
291 {
292 	e_import_complete (m->import, (EImportTarget *) m->target);
293 }
294 
295 static void
296 pine_import_free (struct _pine_import_msg *m)
297 {
298 	g_object_unref (m->cancellable);
299 
300 	g_free (m->status_what);
301 	g_mutex_free (m->status_lock);
302 
303 	g_source_remove (m->status_timeout_id);
304 	m->status_timeout_id = 0;
305 
306 	g_object_unref (m->import);
307 }
308 
309 static void
310 pine_status (CamelOperation *op,
311              const gchar *what,
312              gint pc,
313              gpointer data)
314 {
315 	struct _pine_import_msg *importer = data;
316 
317 	g_mutex_lock (importer->status_lock);
318 	g_free (importer->status_what);
319 	importer->status_what = g_strdup (what);
320 	importer->status_pc = pc;
321 	g_mutex_unlock (importer->status_lock);
322 }
323 
324 static gboolean
325 pine_status_timeout (struct _pine_import_msg *importer)
326 {
327 	gint pc;
328 	gchar *what;
329 
330 	if (importer->status_what) {
331 		g_mutex_lock (importer->status_lock);
332 		what = importer->status_what;
333 		importer->status_what = NULL;
334 		pc = importer->status_pc;
335 		g_mutex_unlock (importer->status_lock);
336 
337 		e_import_status (
338 			importer->import, (EImportTarget *)
339 			importer->target, what, pc);
340 	}
341 
342 	return TRUE;
343 }
344 
345 static MailMsgInfo pine_import_info = {
346 	sizeof (struct _pine_import_msg),
347 	(MailMsgDescFunc) pine_import_desc,
348 	(MailMsgExecFunc) pine_import_exec,
349 	(MailMsgDoneFunc) pine_import_done,
350 	(MailMsgFreeFunc) pine_import_free
351 };
352 
353 static gint
354 mail_importer_pine_import (EImport *ei,
355                            EImportTarget *target)
356 {
357 	struct _pine_import_msg *m;
358 	gint id;
359 
360 	m = mail_msg_new (&pine_import_info);
361 	g_datalist_set_data (&target->data, "pine-msg", m);
362 	m->import = ei;
363 	g_object_ref (m->import);
364 	m->target = target;
365 	m->status_timeout_id = g_timeout_add (
366 		100, (GSourceFunc) pine_status_timeout, m);
367 	m->status_lock = g_mutex_new ();
368 	m->cancellable = camel_operation_new ();
369 
370 	g_signal_connect (
371 		m->cancellable, "status",
372 		G_CALLBACK (pine_status), m);
373 
374 	id = m->base.seq;
375 
376 	mail_msg_fast_ordered_push (m);
377 
378 	return id;
379 }
380 
381 static void
382 checkbox_mail_toggle_cb (GtkToggleButton *tb,
383                          EImportTarget *target)
384 {
385 	gboolean active;
386 
387 	active = gtk_toggle_button_get_active (tb);
388 
389 	g_datalist_set_data (
390 		&target->data, "pine-do-mail",
391 		GINT_TO_POINTER (active));
392 }
393 
394 static void
395 checkbox_addr_toggle_cb (GtkToggleButton *tb,
396                          EImportTarget *target)
397 {
398 	gboolean active;
399 
400 	active = gtk_toggle_button_get_active (tb);
401 
402 	g_datalist_set_data (
403 		&target->data, "pine-do-addr",
404 		GINT_TO_POINTER (active));
405 }
406 
407 static GtkWidget *
408 pine_getwidget (EImport *ei,
409                 EImportTarget *target,
410                 EImportImporter *im)
411 {
412 	GtkWidget *box, *w;
413 
414 	g_datalist_set_data (
415 		&target->data, "pine-do-mail",
416 		GINT_TO_POINTER (TRUE));
417 	g_datalist_set_data (
418 		&target->data, "pine-do-addr",
419 		GINT_TO_POINTER (TRUE));
420 
421 	box = gtk_vbox_new (FALSE, 2);
422 
423 	w = gtk_check_button_new_with_label (_("Mail"));
424 	gtk_toggle_button_set_active ((GtkToggleButton *) w, TRUE);
425 	g_signal_connect (
426 		w, "toggled",
427 		G_CALLBACK (checkbox_mail_toggle_cb), target);
428 	gtk_box_pack_start ((GtkBox *) box, w, FALSE, FALSE, 0);
429 
430 	w = gtk_check_button_new_with_label (_("Address Book"));
431 	gtk_toggle_button_set_active ((GtkToggleButton *) w, TRUE);
432 	g_signal_connect (
433 		w, "toggled",
434 		G_CALLBACK (checkbox_addr_toggle_cb), target);
435 	gtk_box_pack_start ((GtkBox *) box, w, FALSE, FALSE, 0);
436 
437 	gtk_widget_show_all (box);
438 
439 	return box;
440 }
441 
442 static void
443 pine_import (EImport *ei,
444              EImportTarget *target,
445              EImportImporter *im)
446 {
447 	if (GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pine-do-mail"))
448 	    || GPOINTER_TO_INT (g_datalist_get_data (&target->data, "pine-do-addr")))
449 		mail_importer_pine_import (ei, target);
450 	else
451 		e_import_complete (ei, target);
452 }
453 
454 static void
455 pine_cancel (EImport *ei,
456              EImportTarget *target,
457              EImportImporter *im)
458 {
459 	struct _pine_import_msg *m = g_datalist_get_data (&target->data, "pine-msg");
460 
461 	if (m)
462 		g_cancellable_cancel (m->cancellable);
463 }
464 
465 static EImportImporter pine_importer = {
466 	E_IMPORT_TARGET_HOME,
467 	0,
468 	pine_supported,
469 	pine_getwidget,
470 	pine_import,
471 	pine_cancel,
472 	NULL, /* get_preview */
473 };
474 
475 EImportImporter *
476 pine_importer_peek (void)
477 {
478 	pine_importer.name = _("Evolution Pine importer");
479 	pine_importer.description = _("Import mail from Pine.");
480 
481 	return &pine_importer;
482 }