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 }