diff options
-rw-r--r-- | mail/ChangeLog | 84 | ||||
-rw-r--r-- | mail/importers/Makefile.am | 70 | ||||
-rw-r--r-- | mail/importers/elm-importer.c | 593 | ||||
-rw-r--r-- | mail/importers/evolution-mbox-importer.c | 45 | ||||
-rw-r--r-- | mail/importers/netscape-importer.c | 2185 | ||||
-rw-r--r-- | mail/importers/pine-importer.c | 699 |
6 files changed, 3661 insertions, 15 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog index 928fba418b..edf9336ab9 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,87 @@ +2003-02-05 Not Zed <NotZed@Ximian.com> + + * importers/netscape-importer.c: update from ../../importers/. + (main): Removed. + (mail_importer_module_init): Setup module init fn. + (factory_fn): api changes. + (is_dir_empty): deprecated changes, and clean up logic. + (importer_cb): pulse progress bar, use idle function for + processing next item. + (import_next): Remove link before recursing, also fix memleak, and + api changes. + (netscape_import_file): dont release importer. + (*): gconf'ify + + * importers/elm-importer.c (elm_factory_fn): Track the + evolution_intelligent_importer, so we can unref it when done. + (*): gconf'ify. + + * importers/pine-importer.c (parse_line): use gobject stuff rather + than gtkobject. + (import_addressfile): close down 'properly' when finished. + (importer_timeout_fn): Do most processing decisions here, either + from a timeout or idle function. This prevents us getting 1 stack + frame per message and per folder. Close down properly also. + (importer_cb): Add a timeout, ignore the callback, or add an idle + function to process the next item. + (pine_import_file): dont release the importer if we can't load it, + its released elsewhere, i think. + (import_addressfile): step the progress bar as we go. + (factory_fn): Track the evolution_intelligent_importer, so we can + unref when done. + (*): gconf'ify + +2003-01-31 Not Zed <NotZed@Ximian.com> + + * importers/elm-importer.c (elm_can_import): g_file_exists -> + lstat, and g_build_filename api changes. + + * importers/pine-importer.c: moved from + ../../importers/pine-importer.c + (factory_fn): oaf->bonobo_activation + (mail_importer_module_init): setup factory. + (main): Removed. + (*): REemove bonobo config stuff. + (factory_fn): destroy signal -> weak ref. + (pine_destroy_cb): Fix signature for weak ref notify. + (import_addressfile): use new glib filename stuff. + (import_addressbook): same. + (pine_can_import): and here. + (import_next): and here. + (scan_dir): and here + (pine_create_structure): And here. + (pine_can_import): g_file_exists -> lstat. + (importer_cb): If there are more items, use an idle handler to + drop back a few stack frames rather than recursing for each + message. + (import_next): unlink data from dir_list before recursing, and fix + leak. + +2003-01-30 Not Zed <NotZed@Ximian.com> + + * importers/elm-importer.c (elm_destroy_cb): Change for weak ref + setup. + (elm_factory_fn): destroy -> weak ref. + +2003-01-29 Not Zed <NotZed@Ximian.com> + + * importers/elm-importer.c (importer_cb): Pass processItem off to + an idle handler, so we dont blow our stacks. Also update to use + progress_bar_pulse(). + (import_item_idle): Get the next message here instead. + (import_next): Fix a glist leak. Unlink the file before we import + it too. And close the dialogue and clean up when we've run out of + folders to import. + +2003-01-17 Not Zed <NotZed@Ximian.com> + + * importers/elm-importer.c (elm_create_structure): use/free elmdir + rather than double-free maildir. + +2003-01-16 Not Zed <NotZed@Ximian.com> + + * importers/elm-importer.c: update from ../importers/elm-importer.c + 2003-02-03 Jeffrey Stedfast <fejj@ximian.com> * message-list.c (mail_regen_list): Get the thread_subject setting diff --git a/mail/importers/Makefile.am b/mail/importers/Makefile.am index 7300acb9f2..e9d35224f1 100644 --- a/mail/importers/Makefile.am +++ b/mail/importers/Makefile.am @@ -1,6 +1,9 @@ importersdir = $(privlibdir)/evolution-mail-importers -importers_LTLIBRARIES = liboutlook.la libmbox.la +importers_LTLIBRARIES = liboutlook.la libmbox.la \ + libevolution-elm-importer.la \ + libevolution-pine-importer.la \ + libevolution-netscape-importer.la INCLUDES = -I.. \ -I$(srcdir)/.. \ @@ -9,6 +12,9 @@ INCLUDES = -I.. \ -I$(top_builddir)/shell \ -I$(includedir) \ -DG_LOG_DOMAIN=\"evolution-mail-importer\" \ + -I$(top_srcdir)/addressbook/backend \ + -I$(top_builddir)/addressbook/backend \ + -DEVOLUTION_DATADIR=\""$(datadir)"\" \ $(IMPORTERS_CFLAGS) liboutlook_la_SOURCES = \ @@ -19,9 +25,69 @@ libmbox_la_SOURCES = evolution-mbox-importer.c \ mozilla-status-headers.h libmbox_la_LDFLAGS = -avoid-version -module +IDLS = \ + $(top_srcdir)/mail/Mailer.idl + +MAIL_GENERATED = \ + Mailer.h \ + Mailer-common.c \ + Mailer-skels.c \ + Mailer-stubs.c + +$(MAIL_GENERATED): $(IDLS) + $(ORBIT_IDL) -I $(srcdir) $(IDL_INCLUDES) $(top_srcdir)/mail/Mailer.idl + +libevolution_netscape_importer_la_SOURCES = \ + $(MAIL_GENERATED) \ + netscape-importer.c + +libevolution_netscape_importer_la_LIBADD = \ + $(top_builddir)/shell/importer/libevolution-importer.la \ + $(top_builddir)/camel/libcamel.la \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/filter/libfilter.la \ + $(top_builddir)/shell/libeshell.la \ + $(IMPORTERS_LIBS) + +libevolution_elm_importer_la_SOURCES = \ + elm-importer.c + +libevolution_elm_importer_la_LIBADD = \ + $(top_builddir)/shell/importer/libevolution-importer.la \ + $(top_builddir)/e-util/libeutil.la \ + $(IMPORTERS_LIBS) + +libevolution_pine_importer_la_SOURCES = \ + pine-importer.c + +libevolution_pine_importer_la_LIBADD = \ + $(top_builddir)/shell/importer/libevolution-importer.la \ + $(top_builddir)/addressbook/backend/ebook/libebook.la \ + $(top_builddir)/camel/libcamel.la \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/e-util/ename/libename.la \ + $(top_builddir)/libversit/libversit.a \ + $(IMPORTERS_LIBS) + +# evolution_gnomecard_importer_SOURCES = \ +# evolution-gnomecard-importer.c + +# evolution_gnomecard_importer_LDADD = \ +# $(top_builddir)/shell/importer/libevolution-importer.la \ +# $(top_builddir)/addressbook/backend/ebook/libebook.la \ +# $(top_builddir)/camel/libcamel.la \ +# $(top_builddir)/e-util/libeutil.la \ +# $(top_builddir)/e-util/ename/libename.la \ +# $(top_builddir)/libversit/libversit.a \ +# $(IMPORTERS_LIBS) + + serverdir = $(libdir)/bonobo/servers server_in_files = GNOME_Evolution_Mail_Mbox_Importer.server.in.in \ - GNOME_Evolution_Mail_Outlook_Importer.server.in.in + GNOME_Evolution_Mail_Outlook_Importer.server.in.in \ + GNOME_Evolution_Mail_Netscape_Intelligent_Importer.server.in.in \ + GNOME_Evolution_Mail_Elm_Intelligent_Importer.server.in.in \ + GNOME_Evolution_Mail_Pine_Intelligent_Importer.server.in.in server_DATA = $(server_in_files:.server.in.in=.server) %.server.in: %.server.in.in sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ diff --git a/mail/importers/elm-importer.c b/mail/importers/elm-importer.c new file mode 100644 index 0000000000..439a6a69b9 --- /dev/null +++ b/mail/importers/elm-importer.c @@ -0,0 +1,593 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* elm-importer.c + * + * Authors: + * Iain Holmes <iain@ximian.com> + * + * Copyright 2001 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include <glib.h> +#include <gnome.h> + +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> + +#include <bonobo/bonobo-object.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-context.h> +#include <bonobo/bonobo-generic-factory.h> +#include <bonobo/bonobo-main.h> +#include <bonobo/bonobo-exception.h> +#include <bonobo/bonobo-moniker-util.h> + +#include <bonobo-activation/bonobo-activation.h> + +#include <importer/evolution-intelligent-importer.h> +#include <importer/evolution-importer-client.h> +#include <importer/GNOME_Evolution_Importer.h> + +#define ELM_INTELLIGENT_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Elm_Intelligent_Importer_Factory" +#define MBOX_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Mbox_Importer" +#define KEY "elm-mail-imported" + +/*#define SUPER_IMPORTER_DEBUG*/ +#ifdef SUPER_IMPORTER_DEBUG +#define d(x) x +#else +#define d(x) +#endif + +typedef struct { + EvolutionIntelligentImporter *ii; + + GList *dir_list; + + int num; + int progress_count; + int import_id; + + GNOME_Evolution_Importer importer; + EvolutionImporterListener *listener; + + GtkWidget *mail; + gboolean do_mail; + + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *progressbar; +} ElmImporter; + +typedef struct { + char *parent; + char *foldername; + char *path; +} ElmFolder; + +static GHashTable *elm_prefs = NULL; + +static void import_next (ElmImporter *importer); + +static GtkWidget * +create_importer_gui (ElmImporter *importer) +{ + GtkWidget *dialog; + + dialog = gnome_message_box_new (_("Evolution is importing your old Elm mail"), GNOME_MESSAGE_BOX_INFO, NULL); + gtk_window_set_title (GTK_WINDOW (dialog), _("Importing...")); + + importer->label = gtk_label_new (_("Please wait")); + importer->progressbar = gtk_progress_bar_new (); + gtk_progress_set_activity_mode (GTK_PROGRESS (importer->progressbar), TRUE); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), importer->label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), importer->progressbar, FALSE, FALSE, 0); + + return dialog; +} + +static void +elm_store_settings (ElmImporter *importer) +{ + GConfClient *gconf; + + gconf = gconf_client_get_default (); + gconf_client_set_bool (gconf, "/apps/evolution/importer/elm/mail", importer->do_mail, NULL); +} + +static void +elm_restore_settings (ElmImporter *importer) +{ + GConfClient *gconf = gconf_client_get_default (); + + importer->do_mail = gconf_client_get_bool (gconf, "/apps/evolution/importer/elm/mail", NULL); +} + +static gboolean +import_item_idle(void *data) +{ + ElmImporter *importer = data; + CORBA_Environment ev; + + importer->import_id = 0; + + CORBA_exception_init (&ev); + GNOME_Evolution_Importer_processItem (importer->importer, + bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)), + &ev); + if (ev._major != CORBA_NO_EXCEPTION) + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + + CORBA_exception_free (&ev); + + return FALSE; +} + +static void +importer_cb (EvolutionImporterListener *listener, + EvolutionImporterResult result, + gboolean more_items, + void *data) +{ + ElmImporter *importer = (ElmImporter *) data; + CORBA_Object objref; + CORBA_Environment ev; + + if (more_items) { + g_assert(importer->import_id == 0); + importer->progress_count++; + if ((importer->progress_count & 0xf) == 0) + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(importer->progressbar)); + importer->import_id = g_idle_add(import_item_idle, importer); + } else { + import_next (importer); + } +} + +static gboolean +elm_import_file (ElmImporter *importer, + const char *path, + const char *folderpath) +{ + CORBA_boolean result; + CORBA_Environment ev; + CORBA_Object objref; + char *str; + + CORBA_exception_init (&ev); + + str = g_strdup_printf (_("Importing %s as %s"), path, folderpath); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + result = GNOME_Evolution_Importer_loadFile (importer->importer, path, + folderpath, &ev); + if (ev._major != CORBA_NO_EXCEPTION || result == FALSE) { + g_warning ("Exception here: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return FALSE; + } + + importer->listener = evolution_importer_listener_new (importer_cb, + importer); + objref = bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)); + GNOME_Evolution_Importer_processItem (importer->importer, objref, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return FALSE; + } + CORBA_exception_free (&ev); + + return TRUE; +} + +static void +parse_elm_rc (const char *elmrc) +{ + static gboolean parsed = FALSE; + char line[4096]; + FILE *handle; + gboolean exists; + + if (parsed == TRUE) + return; + + elm_prefs = g_hash_table_new (g_str_hash, g_str_equal); + + exists = g_file_exists (elmrc); + if (exists == FALSE) { + parsed = TRUE; + return; + } + + handle = fopen (elmrc, "r"); + if (handle == NULL) { + parsed = TRUE; + return; + } + + while (fgets (line, 4096, handle) != NULL) { + char *linestart, *end; + char *key, *value; + if (*line == '#' && + (line[1] != '#' && line[2] != '#')) { + continue; + } else if (*line == '\n') { + continue; + } else if (*line == '#' && line[1] == '#' && line[2] == '#') { + linestart = line + 4; + } else { + linestart = line; + } + + end = strstr (linestart, " = "); + if (end == NULL) { + g_warning ("Broken line"); + continue; + } + + *end = 0; + key = g_strdup (linestart); + + linestart = end + 3; + end = strchr (linestart, '\n'); + if (end == NULL) { + g_warning ("Broken line"); + g_free (key); + continue; + } + + *end = 0; + value = g_strdup (linestart); + + g_hash_table_insert (elm_prefs, key, value); + } + + parsed = TRUE; + fclose (handle); +} + +static char * +elm_get_rc_value (const char *value) +{ + if (elm_prefs == NULL) + return NULL; + + return g_hash_table_lookup (elm_prefs, value); +} + +static gboolean +elm_can_import (EvolutionIntelligentImporter *ii, + void *closure) +{ + ElmImporter *importer = closure; + char *elmdir, *maildir, *aliasfile; + char *elmrc; + gboolean exists, mailexists, aliasexists; + gboolean mail, alias; + struct stat st; + GConfClient *gconf = gconf_client_get_default(); + + mail = gconf_client_get_bool(gconf, "/apps/evolution/importer/elm/mail-imported", NULL); + if (mail) + return FALSE; + + importer->do_mail = !mail; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (importer->mail), + importer->do_mail); + + elmdir = gnome_util_prepend_user_home (".elm"); + exists = lstat(elmdir, &st) == 0 && S_ISDIR(st.st_mode); + + g_free (elmdir); + if (exists == FALSE) + return FALSE; + + elmrc = gnome_util_prepend_user_home (".elm/elmrc"); + parse_elm_rc (elmrc); + + maildir = elm_get_rc_value ("maildir"); + if (maildir == NULL) { + maildir = g_strdup ("Mail"); + } else { + maildir = g_strdup (maildir); + } + + if (!g_path_is_absolute (maildir)) { + elmdir = gnome_util_prepend_user_home (maildir); + } else { + elmdir = g_strdup (maildir); + } + + g_free (maildir); + + mailexists = lstat(elmdir, &st) == 0 && S_ISDIR(st.st_mode); + g_free (elmdir); + + aliasfile = gnome_util_prepend_user_home (".elm/aliases"); + aliasexists = lstat(aliasfile, &st) == 0 && S_ISREG(st.st_mode); + g_free (aliasfile); + + exists = (aliasexists || mailexists); + + return exists; +} + +static void +import_next (ElmImporter *importer) +{ + ElmFolder *data; + + if (importer->dir_list) { + char *folder; + GList *l; + + l = importer->dir_list; + data = l->data; + + folder = g_concat_dir_and_file (data->parent, data->foldername); + + importer->dir_list = l->next; + g_list_free_1(l); + + elm_import_file (importer, data->path, folder); + g_free (folder); + g_free (data->parent); + g_free (data->path); + g_free (data->foldername); + g_free (data); + } else { + bonobo_object_unref((BonoboObject *)importer->ii); + } +} + +static void +scan_dir (ElmImporter *importer, + const char *orig_parent, + const char *dirname) +{ + DIR *maildir; + struct stat buf; + struct dirent *current; + char *str; + + maildir = opendir (dirname); + if (maildir == NULL) { + g_warning ("Could not open %s\nopendir returned: %s", + dirname, g_strerror (errno)); + return; + } + + str = g_strdup_printf (_("Scanning %s"), dirname); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + current = readdir (maildir); + while (current) { + ElmFolder *pf; + char *fullname; + + /* Ignore . and .. */ + if (current->d_name[0] == '.') { + if (current->d_name[1] == '\0' || + (current->d_name[1] == '.' && current->d_name[2] == '\0')) { + current = readdir (maildir); + continue; + } + } + + fullname = g_concat_dir_and_file (dirname, current->d_name); + if (stat (fullname, &buf) == -1) { + g_warning ("Could not stat %s\nstat returned: %s", + fullname, g_strerror (errno)); + current = readdir (maildir); + g_free (fullname); + continue; + } + + if (S_ISREG (buf.st_mode)) { + pf = g_new (ElmFolder, 1); + pf->path = g_strdup (fullname); + pf->parent = g_strdup (orig_parent); + pf->foldername = g_strdup (current->d_name); + importer->dir_list = g_list_append (importer->dir_list, pf); + } else if (S_ISDIR (buf.st_mode)) { + char *subdir; + + pf = g_new (ElmFolder, 1); + pf->path = NULL; + pf->parent = g_strdup (orig_parent); + pf->foldername = g_strdup (current->d_name); + importer->dir_list = g_list_append (importer->dir_list, pf); + + subdir = g_concat_dir_and_file (orig_parent, current->d_name); + scan_dir (importer, subdir, fullname); + g_free (subdir); + } + + g_free (fullname); + current = readdir (maildir); + } +} + +static void +elm_create_structure (EvolutionIntelligentImporter *ii, + void *closure) +{ + CORBA_Environment ev; + ElmImporter *importer = closure; + char *maildir; + + /* Reference our object so when the shell release_unrefs us + we will still exist and not go byebye */ + bonobo_object_ref (BONOBO_OBJECT (ii)); + + elm_store_settings (importer); + + if (importer->do_mail == TRUE) { + char *elmdir; + GConfClient *gconf = gconf_client_get_default(); + + importer->dialog = create_importer_gui (importer); + gtk_widget_show_all (importer->dialog); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + gconf_client_set_bool(gconf, "/apps/evolution/importer/elm/mail-imported", TRUE, NULL); + + maildir = elm_get_rc_value ("maildir"); + if (maildir == NULL) { + maildir = g_strdup ("Mail"); + } else { + maildir = g_strdup (maildir); + } + + if (!g_path_is_absolute (maildir)) { + elmdir = gnome_util_prepend_user_home (maildir); + } else { + elmdir = g_strdup (maildir); + } + + g_free (maildir); + + scan_dir (importer, "/", elmdir); + g_free (elmdir); + + /* Import them */ + import_next (importer); + } + + bonobo_object_unref (BONOBO_OBJECT (ii)); +} + +static void +elm_destroy_cb (ElmImporter *importer, GtkObject *object) +{ + CORBA_Environment ev; + + elm_store_settings (importer); + + if (importer->dialog) + gtk_widget_destroy(importer->dialog); + + bonobo_object_release_unref (importer->importer, NULL); +} + +/* Fun initialisation stuff */ +/* Fun control stuff */ +static void +checkbox_toggle_cb (GtkToggleButton *tb, + gboolean *do_item) +{ + *do_item = gtk_toggle_button_get_active (tb); +} + +static BonoboControl * +create_checkboxes_control (ElmImporter *importer) +{ + GtkWidget *hbox; + BonoboControl *control; + + hbox = gtk_vbox_new (FALSE, 2); + + importer->mail = gtk_check_button_new_with_label (_("Mail")); + gtk_signal_connect (GTK_OBJECT (importer->mail), "toggled", + GTK_SIGNAL_FUNC (checkbox_toggle_cb), + &importer->do_mail); + + gtk_box_pack_start (GTK_BOX (hbox), importer->mail, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + control = bonobo_control_new (hbox); + return control; +} + +static BonoboObject * +elm_factory_fn (BonoboGenericFactory *_factory, + const char *id, + void *closure) +{ + EvolutionIntelligentImporter *importer; + BonoboControl *control; + ElmImporter *elm; + CORBA_Environment ev; + char *message = N_("Evolution has found Elm mail files\n" + "Would you like to import them into Evolution?"); + + elm = g_new0 (ElmImporter, 1); + + CORBA_exception_init (&ev); + + elm_restore_settings (elm); + + elm->importer = bonobo_activation_activate_from_id (MBOX_IMPORTER_IID, 0, NULL, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_free (elm); + g_warning ("Could not start MBox importer\n%s", + CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return NULL; + } + CORBA_exception_free (&ev); + + importer = evolution_intelligent_importer_new (elm_can_import, + elm_create_structure, + _("Elm"), + _(message), elm); + g_object_weak_ref(G_OBJECT (importer), (GWeakNotify)elm_destroy_cb, elm); + elm->ii = importer; + + control = create_checkboxes_control (elm); + bonobo_object_add_interface (BONOBO_OBJECT (importer), + BONOBO_OBJECT (control)); + return BONOBO_OBJECT (importer); +} + +void +mail_importer_module_init (void) +{ + static gboolean initialised = FALSE; + BonoboGenericFactory *factory; + + if (initialised == TRUE) + return; + + factory = bonobo_generic_factory_new (ELM_INTELLIGENT_IMPORTER_IID, + elm_factory_fn, NULL); + if (factory == NULL) + g_warning ("Could not initialise Elm Intelligent Mail Importer."); + initialised = TRUE; +} diff --git a/mail/importers/evolution-mbox-importer.c b/mail/importers/evolution-mbox-importer.c index 181f817898..4c948a595c 100644 --- a/mail/importers/evolution-mbox-importer.c +++ b/mail/importers/evolution-mbox-importer.c @@ -62,6 +62,8 @@ typedef struct { char *filename; int num; + GNOME_Evolution_Storage_Result create_result; + CamelMimeParser *mp; gboolean is_folder; } MboxImporter; @@ -118,9 +120,18 @@ process_item_fn (EvolutionImporter *eimporter, const char *mozilla_status; if (importer->folder == NULL) { - GNOME_Evolution_ImporterListener_notifyResult (listener, - GNOME_Evolution_ImporterListener_NOT_READY, - TRUE, ev); + /* if it failed, need to say it failed ... */ + /* the create_folder callback needs to store the create result */ + /* here we need to pass FALSE for more items */ + printf("not ready\n"); + if (mbi->create_result == GNOME_Evolution_Storage_OK) + GNOME_Evolution_ImporterListener_notifyResult (listener, + GNOME_Evolution_ImporterListener_NOT_READY, + TRUE, ev); + else + GNOME_Evolution_ImporterListener_notifyResult (listener, + GNOME_Evolution_ImporterListener_BAD_FILE, + FALSE, ev); return; } @@ -147,7 +158,6 @@ process_item_fn (EvolutionImporter *eimporter, } else { mozilla_status = camel_medium_get_header (CAMEL_MEDIUM (msg), "X-Mozilla-Status"); if (mozilla_status != NULL) { - g_print ("Got Mozilla status header: %s\n", mozilla_status); info = get_info_from_mozilla (mozilla_status, &deleted); } else { deleted = FALSE; @@ -183,6 +193,7 @@ process_item_fn (EvolutionImporter *eimporter, camel_mime_parser_step (mbi->mp, 0, 0); camel_exception_free (ex); + GNOME_Evolution_ImporterListener_notifyResult (listener, GNOME_Evolution_ImporterListener_OK, !done, ev); @@ -220,13 +231,14 @@ importer_destroy_cb (void *data, GObject *object) MboxImporter *mbi = data; MailImporter *importer = data; - if (importer->frozen) { - camel_folder_sync (importer->folder, FALSE, NULL); - camel_folder_thaw (importer->folder); - } + if (importer->folder) { + if (importer->frozen) { + camel_folder_sync (importer->folder, FALSE, NULL); + camel_folder_thaw (importer->folder); + } - if (importer->folder) camel_object_unref (importer->folder); + } g_free (mbi->filename); if (mbi->mp) @@ -251,6 +263,13 @@ folder_created_cb (BonoboListener *listener, } result = event_data->_value; + + printf("folder created cb, result = %d\n", result->result); + ((MboxImporter *)importer)->create_result = result->result; + + if (result->result != GNOME_Evolution_Storage_OK) + return; + fullpath = g_strconcat ("file://", result->path, NULL); ex = camel_exception_new (); @@ -260,6 +279,8 @@ folder_created_cb (BonoboListener *listener, camel_exception_free (ex); g_free (fullpath); + ((MboxImporter *)importer)->create_result = GNOME_Evolution_Storage_GENERIC_ERROR; + return; } @@ -337,11 +358,9 @@ load_file_fn (EvolutionImporter *eimporter, g_signal_connect((listener), "event-notify", G_CALLBACK (folder_created_cb), importer); - + mbi->create_result = GNOME_Evolution_Storage_OK; mail_importer_create_folder (parent, name, NULL, listener); - importer->folder = NULL; - g_print ("No folder yet\n"); - delayed = TRUE; + delayed = importer->folder == NULL; g_free (parent); } camel_exception_free (ex); diff --git a/mail/importers/netscape-importer.c b/mail/importers/netscape-importer.c new file mode 100644 index 0000000000..501d7681ae --- /dev/null +++ b/mail/importers/netscape-importer.c @@ -0,0 +1,2185 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* netscape-importer.c + * + * Authors: + * Iain Holmes <iain@ximian.com> + * Christian Kreibich <cK@whoop.org> (email filter import) + * + * Copyright 2001 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <ctype.h> +#include <unistd.h> +#include <dirent.h> + +#include <glib.h> +#include <gnome.h> + +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> + +#include <bonobo/bonobo-main.h> +#include <bonobo/bonobo-object.h> +#include <bonobo/bonobo-generic-factory.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-context.h> +#include <bonobo/bonobo-exception.h> +#include <bonobo/bonobo-moniker-util.h> + +#include <bonobo-activation/bonobo-activation.h> + +#include <importer/evolution-intelligent-importer.h> +#include <importer/GNOME_Evolution_Importer.h> +#include <importer/evolution-importer-client.h> + +#include <filter/filter-context.h> +#include <filter/filter-filter.h> +#include <filter/filter-rule.h> +#include <filter/filter-option.h> +#include <filter/filter-folder.h> +#include <filter/filter-int.h> +#include <shell/evolution-shell-client.h> + +#include "Mailer.h" + +static char *nsmail_dir = NULL; +static GHashTable *user_prefs = NULL; + +/* This is rather ugly -- libfilter needs this symbol: */ +EvolutionShellClient *global_shell_client = NULL; + +static char *filter_name = N_("Priority Filter \"%s\""); + +#define FACTORY_IID "OAFIID:GNOME_Evolution_Mail_Netscape_Intelligent_Importer_Factory" +#define MBOX_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Mbox_Importer" +#define MAIL_CONFIG_IID "OAFIID:GNOME_Evolution_MailConfig" + +#define KEY "netscape-mail-imported" + +/*#define SUPER_IMPORTER_DEBUG*/ +#ifdef SUPER_IMPORTER_DEBUG +#define d(x) x +#else +#define d(x) +#endif + +#define MAXLEN 4096 + +typedef struct { + EvolutionIntelligentImporter *ii; + + GList *dir_list; + + int progress_count; + int num; + guint import_id; + + GNOME_Evolution_Importer importer; + EvolutionImporterListener *listener; + + /* Checkboxes */ + GtkWidget *mail; + gboolean do_mail; +/* + GtkWidget *addrs; + gboolean do_addrs; +*/ + GtkWidget *filters; + gboolean do_filters; + GtkWidget *settings; + gboolean do_settings; + + /*Bonobo_ConfigDatabase db;*/ + + /* GUI */ + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *progressbar; +} NsImporter; + + +/* Email filter datastructures ---------------------------------------------- */ + + +typedef enum { + MOVE_TO_FOLDER, CHANGE_PRIORITY, DELETE, + MARK_READ, IGNORE_THREAD, WATCH_THREAD +} NsFilterActionType; + +static char* ns_filter_action_types[] = +{ + "Move to folder", "Change priority", "Delete", + "Mark read", "Ignore thread", "Watch thread" +}; + + +typedef enum { + HIGHEST, HIGH, NORMAL, LOW, LOWEST, FREE, NONE +} NsFilterActionValueType; + +static char *ns_filter_action_value_types[] = +{ + "Highest", "High", "Normal", "Low", "Lowest" +}; + + +typedef enum { + FROM, SUBJECT, TO, CC, TO_OR_CC, BODY, DATE, PRIORITY, + STATUS, AGE_IN_DAYS, X_MSG_HEADER +} NsFilterConditionType; + +static char *ns_filter_condition_types[] = +{ + "from", "subject", "to", "CC", "to or CC", "body", "date", + "priority", "age in days" +}; + + +typedef enum { + CONTAINS, CONTAINS_NOT, IS, IS_NOT, BEGINS_WITH, ENDS_WITH, + IS_BEFORE, IS_AFTER, IS_GREATER_THAN, IS_LESS_THAN, READ, + REPLIED, IS_HIGHER_THAN, IS_LOWER_THAN +} NsFilterConditionPropertyType; + +static char *ns_filter_condition_property_types[] = +{ + "contains", "doesn't contain", "is", "isn't", "begins with", + "ends with", "is before", "is after", "is greater than", + "is less than", "read", "replied", "is higher than", + "is lower than" +}; + + +typedef struct +{ + NsFilterConditionType type; + NsFilterConditionPropertyType prop; + NsFilterActionValueType prop_val_id; /* for dealing with priority levels */ + char *prop_val_str; +} NsFilterCondition; + +typedef struct { + char *name; + char *description; + + gboolean enabled; + + NsFilterActionType action; + NsFilterActionValueType action_val_id; + char *action_val_str; + + enum _filter_grouping_t grouping; + GList *conditions; /* List of NSFilterConditions */ +} NsFilter; + + +/* Prototypes ------------------------------------------------------------- */ +static void netscape_filter_cleanup (NsFilter *nsf); +static char *fix_netscape_folder_names (const char *original_name); +static void import_next (NsImporter *importer); + + + +/* Email filter stuff ----------------------------------------------------- */ + +static gboolean +netscape_filter_flatfile_get_entry (FILE *f, char *key, char *val) +{ + char line[MAXLEN]; + char *ptr = NULL; + char *ptr2 = NULL; + + if (fgets (line, MAXLEN, f)) { + + ptr = strchr(line, '='); + *ptr = '\0'; + + memcpy (key, line, strlen(line)+1); + + ptr += 2; /* Skip '=' and '"' */ + ptr2 = strrchr (ptr, '"'); + *ptr2 = '\0'; + + memcpy (val, ptr, strlen(ptr)+1); + + d(g_warning ("Parsing key/val '%s' '%s'", key, val)); + return TRUE; + + } + + *key = '\0'; *val = '\0'; + return FALSE; +} + +/* This function parses the filtering condition strings. + Netscape describes the conditions that determine when + to apply a filter through a string of the form + + " OR (type, property, value) OR (type, property, value) ... " + + or + " AND (type, property, value) AND (type, property, value) ... " + + where type can be "subject", "from", "to", "CC" etc, property + is "contains" etc, and value is the according pattern. +*/ +static void +netscape_filter_parse_conditions (NsFilter *nsf, FILE *f, char *condition) +{ + char *ptr = condition, *ptr2 = NULL; + char type[MAXLEN]; + char prop[MAXLEN]; + char val[MAXLEN]; + NsFilterCondition *cond; + + if ( (ptr = strstr (condition, "OR")) == NULL) { + nsf->grouping = FILTER_GROUP_ALL; + } else { + nsf->grouping = FILTER_GROUP_ANY; + } + + ptr = condition; + while ( (ptr = strchr (ptr, '(')) != NULL) { + + /* Move ptr to start of type */ + ptr++; + + /* Move ptr2 up to next comma: */ + if ( (ptr2 = strchr (ptr, ',')) == NULL) + continue; + + memcpy (type, ptr, ptr2-ptr); + type[ptr2-ptr] = '\0'; + + /* Move ptr to start of property */ + ptr = ptr2 + 1; + + /* Move ptr2 up to next comma: */ + if ( (ptr2 = strchr (ptr, ',')) == NULL) + continue; + + memcpy (prop, ptr, ptr2-ptr); + prop[ptr2-ptr] = '\0'; + + /* Move ptr to start of value */ + ptr = ptr2 + 1; + + /* Move ptr2 to end of value: */ + if ( (ptr2 = strchr (ptr, ')')) == NULL) + continue; + + memcpy (val, ptr, ptr2-ptr); + val[ptr2-ptr] = '\0'; + + cond = g_new0 (NsFilterCondition, 1); + + if (!strcmp (type, ns_filter_condition_types[FROM])) { + cond->type = FROM; + } else if (!strcmp (type, ns_filter_condition_types[SUBJECT])) { + cond->type = SUBJECT; + } else if (!strcmp (type, ns_filter_condition_types[TO])) { + cond->type = TO; + } else if (!strcmp (type, ns_filter_condition_types[CC])) { + cond->type = CC; + } else if (!strcmp (type, ns_filter_condition_types[TO_OR_CC])) { + cond->type = TO_OR_CC; + } else if (!strcmp (type, ns_filter_condition_types[BODY])) { + cond->type = BODY; + } else if (!strcmp (type, ns_filter_condition_types[DATE])) { + cond->type = DATE; + } else if (!strcmp (type, ns_filter_condition_types[PRIORITY])) { + cond->type = PRIORITY; + } else if (!strcmp (type, ns_filter_condition_types[STATUS])) { + cond->type = STATUS; + } else if (!strcmp (type, ns_filter_condition_types[AGE_IN_DAYS])) { + cond->type = AGE_IN_DAYS; + } else if (!strcmp (type, ns_filter_condition_types[X_MSG_HEADER])) { + cond->type = X_MSG_HEADER; + } else { + d(g_warning ("Unknown condition type '%s' encountered -- skipping.", type)); + g_free (cond); + continue; + } + + + if (!strcmp (prop, ns_filter_condition_property_types[CONTAINS])) { + cond->prop = CONTAINS; + } else if (!strcmp (prop, ns_filter_condition_property_types[CONTAINS_NOT])) { + cond->prop = CONTAINS_NOT; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS])) { + cond->prop = IS; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_NOT])) { + cond->prop = IS_NOT; + } else if (!strcmp (prop, ns_filter_condition_property_types[BEGINS_WITH])) { + cond->prop = BEGINS_WITH; + } else if (!strcmp (prop, ns_filter_condition_property_types[ENDS_WITH])) { + cond->prop = ENDS_WITH; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_BEFORE])) { + cond->prop = IS_BEFORE; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_AFTER])) { + cond->prop = IS_AFTER; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_GREATER_THAN])) { + cond->prop = IS_GREATER_THAN; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_LESS_THAN])) { + cond->prop = IS_LESS_THAN; + } else if (!strcmp (prop, ns_filter_condition_property_types[READ])) { + cond->prop = READ; + } else if (!strcmp (prop, ns_filter_condition_property_types[REPLIED])) { + cond->prop = REPLIED; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_HIGHER_THAN])) { + cond->prop = IS_HIGHER_THAN; + } else if (!strcmp (prop, ns_filter_condition_property_types[IS_LOWER_THAN])) { + cond->prop = IS_LOWER_THAN; + } else { + d(g_warning ("Unknown condition property '%s' encountered -- skipping.", prop)); + g_free (cond); + continue; + } + + cond->prop_val_id = FREE; + + if (!strcmp (val, ns_filter_action_value_types[LOWEST])) { + cond->prop_val_id = LOWEST; + } else if (!strcmp (val, ns_filter_action_value_types[LOW])) { + cond->prop_val_id = LOW; + } else if (!strcmp (val, ns_filter_action_value_types[NORMAL])) { + cond->prop_val_id = NORMAL; + } else if (!strcmp (val, ns_filter_action_value_types[HIGH])) { + cond->prop_val_id = HIGH; + } else if (!strcmp (val, ns_filter_action_value_types[HIGHEST])) { + cond->prop_val_id = HIGHEST; + } + + cond->prop_val_str = g_strdup (val); + nsf->conditions = g_list_append (nsf->conditions, cond); + } +} + + +static NsFilter * +netscape_filter_read_next (FILE *mailrule_handle) +{ + NsFilter *nsf; + char key[MAXLEN]; + char val[MAXLEN]; + + key[0] = '\0'; + + for ( ; ; ) { + + /* Skip stuff at the beginning, until beginning of next filter + is read: */ + + do { + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + return NULL; + + } while (strcmp(key, "name")); + + nsf = g_new0 (NsFilter, 1); + nsf->name = g_strdup (val); + + + /* Read value for "enabled" setting */ + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "enabled")) { + goto cleanup; + } + if (strcmp (val, "true")) + nsf->enabled = TRUE; + else + nsf->enabled = FALSE; + + + /* Read filter description */ + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "description")) { + goto cleanup; + } + nsf->description = g_strdup (val); + + + /* Skip one line -- it's a "type" entry and always seems to be "1"? */ + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "type")) { + goto cleanup; + } + + /* Read filter action and handle action value accordingly */ + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "action")) { + goto cleanup; + } + if (!strcmp (val, ns_filter_action_types[MOVE_TO_FOLDER])) { + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "actionValue")) { + goto cleanup; + } + nsf->action = MOVE_TO_FOLDER; + nsf->action_val_id = FREE; + nsf->action_val_str = g_strdup(val); + } + else if (!strcmp (val, ns_filter_action_types[CHANGE_PRIORITY])) { + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "actionValue")) { + goto cleanup; + } + + nsf->action = CHANGE_PRIORITY; + + if (!strcmp (val, ns_filter_action_value_types[LOWEST])) { + nsf->action_val_id = LOWEST; + } else if (!strcmp (val, ns_filter_action_value_types[LOW])) { + nsf->action_val_id = LOW; + } else if (!strcmp (val, ns_filter_action_value_types[NORMAL])) { + nsf->action_val_id = NORMAL; + } else if (!strcmp (val, ns_filter_action_value_types[HIGH])) { + nsf->action_val_id = HIGH; + } else if (!strcmp (val, ns_filter_action_value_types[HIGHEST])) { + nsf->action_val_id = HIGHEST; + } else { + d(g_warning ("Unknown Netscape filter action value '%s' for action '%s'", + val, ns_filter_action_types[CHANGE_PRIORITY])); + goto cleanup; + } + + nsf->action_val_str = NULL; + + } + else if (!strcmp (val, ns_filter_action_types[DELETE])) { + + nsf->action = DELETE; + nsf->action_val_id = NONE; + } + else if (!strcmp (val, ns_filter_action_types[MARK_READ])) { + + nsf->action = MARK_READ; + nsf->action_val_id = NONE; + } + else if (!strcmp (val, ns_filter_action_types[IGNORE_THREAD])) { + + nsf->action = IGNORE_THREAD; + nsf->action_val_id = NONE; + } + else if (!strcmp (val, ns_filter_action_types[WATCH_THREAD])) { + + nsf->action = WATCH_THREAD; + nsf->action_val_id = NONE; + } + else { + d(g_warning ("Unknown Netscape filter action '%s'", val)); + goto cleanup; + } + + + /* Read conditions, the fun part ... */ + + if (!netscape_filter_flatfile_get_entry (mailrule_handle, key, val)) + goto cleanup; + if (strcmp (key, "condition")) { + goto cleanup; + } + netscape_filter_parse_conditions (nsf, mailrule_handle, val); + + return nsf; + + cleanup: + netscape_filter_cleanup (nsf); + } + + return NULL; +} + + +static void +netscape_filter_cleanup (NsFilter *nsf) +{ + GList *l; + + g_free (nsf->name); + g_free (nsf->description); + g_free (nsf->action_val_str); + + for (l = nsf->conditions; l; l = l->next) { + + NsFilterCondition *cond = (NsFilterCondition *)l->data; + + g_free (cond->prop_val_str); + g_free (cond); + } + + g_list_free (nsf->conditions); + g_free (nsf); +} + + +static gboolean +netscape_filter_set_opt_for_cond (NsFilterCondition *cond, FilterOption* op) +{ + switch (cond->prop) { + case CONTAINS: + filter_option_set_current (op, "contains"); + break; + case CONTAINS_NOT: + filter_option_set_current (op, "does not contain"); + break; + case IS: + filter_option_set_current (op, "is"); + break; + case IS_NOT: + filter_option_set_current (op, "is not"); + break; + case BEGINS_WITH: + filter_option_set_current (op, "starts with"); + break; + case ENDS_WITH: + filter_option_set_current (op, "ends with"); + break; + default: + return FALSE; + } + + return TRUE; +} + + +/* Translates a string of the form + folder1.sbd/folder2.sbd/.../folderN.sbd/folder + + into one that looks like this: + + folder1/folder2/.../folderN/folder +*/ +static char* +netscape_filter_strip_sbd (char *ns_folder) +{ + char *folder_copy; + char s[MAXLEN]; + char *ptr, *ptr2; + char *fixed_folder; + + folder_copy = g_strdup (ns_folder); + ptr = folder_copy; + s[0] = '\0'; + + while (ptr) { + if ( (ptr2 = strstr (ptr, ".sbd")) == NULL) + break; + + *ptr2 = '\0'; + strcat (s, ptr); + + ptr = ptr2 + 4; /* skip ".sbd" */ + } + + fixed_folder = fix_netscape_folder_names (ptr); + strcat (s, fixed_folder); + g_free (folder_copy); + g_free (fixed_folder); + + d(g_warning ("Stripped '%s' to '%s'", ns_folder, s)); + + return g_strdup (s); +} + + +static char * +netscape_filter_map_folder_to_uri (char *folder) +{ + char *folder_copy; + char s[MAXLEN]; + char *ptr, *ptr2; + + folder_copy = g_strdup (folder); + ptr = folder_copy; + + g_snprintf (s, MAXLEN, "file://%s/evolution/local/", g_get_home_dir ()); + + while (ptr) { + if ( (ptr2 = strchr (ptr, '/')) == NULL) + break; + + *ptr2 = '\0'; + strcat (s, ptr); + strcat (s, "/subfolders/"); + + ptr = ptr2 + 1; + } + + strcat (s, ptr); + g_free (folder_copy); + + d(g_warning ("Mapped '%s' to '%s'", folder, s)); + + return g_strdup (s); +} + + +static void +netscape_filter_change_priority_warning (void) +{ + GtkWidget *dialog; + static gboolean already_shown = FALSE; + + if (!already_shown) { + already_shown = TRUE; + dialog = gnome_ok_dialog (_("Some of your Netscape email filters are based on\n" + "email priorities, which are not used in Evolution.\n" + "Instead, Evolution provides scores in the range of\n" + "-3 to 3 that can be assigned to emails and filtered\n" + "accordingly.\n" + "\n" + "As a workaround, a set of filters called \"Priority Filter\"\n" + "was added that converts Netscape's email priorities into\n" + "Evolution's scores, and the affected filters use scores instead\n" + "of priorities. Check the imported filters to make sure\n" + "everything still works as intended.")); + gnome_dialog_run_and_close (GNOME_DIALOG (dialog)); + } +} + + +static void +netscape_filter_threads_action_not_supported (void) +{ + GtkWidget *dialog; + static gboolean already_shown = FALSE; + + if (!already_shown) { + already_shown = TRUE; + dialog = gnome_ok_dialog (_("Some of your Netscape email filters use\n" + "the \"Ignore Thread\" or \"Watch Thread\"\n" + "feature, which is not supported in Evolution.\n" + "These filters will be dropped.")); + gnome_dialog_run_and_close (GNOME_DIALOG (dialog)); + } +} + + +static void +netscape_filter_body_is_not_supported (void) +{ + GtkWidget *dialog; + static gboolean already_shown = FALSE; + + if (!already_shown) { + already_shown = TRUE; + dialog = gnome_ok_dialog (_("Some of your Netscape email filters test the\n" + "body of emails for (in)equality to a given string,\n" + "which is not supported in Evolution. Those filters\n" + "were modified to test whether that string is or is not\n" + "contained in the message body.")); + gnome_dialog_run_and_close (GNOME_DIALOG (dialog)); + } +} + + +static FilterRule* +netscape_create_priority_converter (FilterContext *fc, NsFilterActionValueType priority) +{ + FilterFilter *ff; + FilterPart *fp; + FilterRule *fr; + FilterElement *el; + char s[MAXLEN]; + int v; + + ff = filter_filter_new (); + fr = FILTER_RULE(ff); + + g_snprintf (s, MAXLEN, filter_name, ns_filter_action_value_types[priority]); + filter_rule_set_name (fr, s); + filter_rule_set_source (fr, FILTER_SOURCE_INCOMING); + + fp = rule_context_create_part (RULE_CONTEXT(fc), "header"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "header-field"); + filter_input_set_value ((FilterInput*)el, "X-Priority"); + el = filter_part_find_element (fp, "header-type"); + filter_option_set_current ((FilterOption*)el, "contains"); + el = filter_part_find_element (fp, "word"); + filter_input_set_value ((FilterInput*)el, + ns_filter_action_value_types[priority]); + + fp = filter_context_create_action (fc, "score"); + el = filter_part_find_element (fp, "score"); + + switch (priority) { + case LOWEST: + v = -2; + break; + case LOW: + v = -1; + break; + case NORMAL: + v = 0; + break; + case HIGH: + v = 1; + break; + case HIGHEST: + v = 2; + break; + default: + g_object_unref((ff)); + return NULL; + } + + filter_int_set_value((FilterInt *)el, v); + filter_filter_add_action (ff, fp); + + return FILTER_RULE(ff); +} + + +static void +netscape_add_priority_workaround_filters (FilterContext *fc) +{ + FilterRule *fr; + + fr = netscape_create_priority_converter (fc, LOWEST); + rule_context_add_rule (RULE_CONTEXT(fc), FILTER_RULE(fr)); + rule_context_rank_rule (RULE_CONTEXT(fc), FILTER_RULE(fr), 0); + + fr = netscape_create_priority_converter (fc, LOW); + rule_context_add_rule (RULE_CONTEXT(fc), FILTER_RULE(fr)); + rule_context_rank_rule (RULE_CONTEXT(fc), FILTER_RULE(fr), 1); + + fr = netscape_create_priority_converter (fc, HIGH); + rule_context_add_rule (RULE_CONTEXT(fc), FILTER_RULE(fr)); + rule_context_rank_rule (RULE_CONTEXT(fc), FILTER_RULE(fr), 2); + + fr = netscape_create_priority_converter (fc, HIGHEST); + rule_context_add_rule (RULE_CONTEXT(fc), FILTER_RULE(fr)); + rule_context_rank_rule (RULE_CONTEXT(fc), FILTER_RULE(fr), 3); +} + + +static gboolean +netscape_filter_score_set (NsFilterCondition *cond, FilterInt *el) +{ + int v; + + switch (cond->prop_val_id) { + case LOWEST: + v = -2; + break; + case LOW: + v = -1; + break; + case NORMAL: + v = 0; + break; + case HIGH: + v = 1; + break; + case HIGHEST: + v = 2; + break; + default: + return FALSE; + } + + filter_int_set_value(el, v); + + return TRUE; +} + + +static FilterFilter * +netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *priority_needed) +{ + RuleContext *rc = RULE_CONTEXT(fc); + FilterFilter *ff = NULL; + FilterPart *fp; + FilterRule *fr; + FilterElement *el; + GList *l; + gboolean part_added = FALSE, action_added = FALSE; + + + ff = filter_filter_new (); + fr = FILTER_RULE(ff); + + filter_rule_set_name (fr, nsf->name); + filter_rule_set_source (fr, FILTER_SOURCE_INCOMING); + fr->grouping = nsf->grouping; + + + /* build and add partset */ + + for (l = nsf->conditions; l; l = l->next) { + + NsFilterCondition *cond = (NsFilterCondition*) l->data; + + fp = NULL; + + switch (cond->type) { + case FROM: + fp = rule_context_create_part (rc, "sender"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "sender-type"); + + if (!netscape_filter_set_opt_for_cond (cond, (FilterOption*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + el = filter_part_find_element (fp, "sender"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + part_added = TRUE; + break; + + case SUBJECT: + fp = rule_context_create_part (rc, "subject"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "subject-type"); + + if (!netscape_filter_set_opt_for_cond (cond, (FilterOption*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + el = filter_part_find_element (fp, "subject"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + part_added = TRUE; + break; + case TO: + case CC: + case TO_OR_CC: + fp = rule_context_create_part (rc, "to"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "recipient-type"); + + if (!netscape_filter_set_opt_for_cond (cond, (FilterOption*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + el = filter_part_find_element (fp, "recipient"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + part_added = TRUE; + break; + case BODY: + fp = rule_context_create_part (rc, "body"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "body-type"); + + switch (cond->prop) { + case CONTAINS: + filter_option_set_current ((FilterOption*)el, "contains"); + break; + case CONTAINS_NOT: + filter_option_set_current ((FilterOption*)el, "not contains"); + break; + case IS: + netscape_filter_body_is_not_supported (); + filter_option_set_current ((FilterOption*)el, "contains"); + break; + case IS_NOT: + netscape_filter_body_is_not_supported (); + filter_option_set_current ((FilterOption*)el, "not contains"); + break; + default: + g_warning("Body rule dropped"); + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + el = filter_part_find_element (fp, "word"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + part_added = TRUE; + break; + case DATE: + fp = rule_context_create_part (rc, "sent-date"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "date-spec-type"); + + switch (cond->prop) { + case IS: + filter_option_set_current ((FilterOption*)el, "is"); + break; + case IS_NOT: + filter_option_set_current ((FilterOption*)el, "is-not"); + break; + case IS_BEFORE: + filter_option_set_current ((FilterOption*)el, "before"); + break; + case IS_AFTER: + filter_option_set_current ((FilterOption*)el, "after"); + break; + default: + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + el = filter_part_find_element (fp, "versus"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + part_added = TRUE; + break; + case PRIORITY: + switch (cond->prop) { + case IS: + *priority_needed = TRUE; + fp = rule_context_create_part (rc, "score"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "score-type"); + filter_option_set_current ((FilterOption*)el, "is"); + el = filter_part_find_element (fp, "versus"); + + if (!netscape_filter_score_set(cond, (FilterInt*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + break; + case IS_NOT: + *priority_needed = TRUE; + fp = rule_context_create_part (rc, "score"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "score-type"); + filter_option_set_current ((FilterOption*)el, "is-not"); + el = filter_part_find_element (fp, "versus"); + + if (!netscape_filter_score_set(cond, (FilterInt*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + break; + case IS_HIGHER_THAN: + *priority_needed = TRUE; + fp = rule_context_create_part (rc, "score"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "score-type"); + filter_option_set_current ((FilterOption*)el, "greater-than"); + el = filter_part_find_element (fp, "versus"); + + if (!netscape_filter_score_set(cond, (FilterInt*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + + break; + case IS_LOWER_THAN: + *priority_needed = TRUE; + fp = rule_context_create_part (rc, "score"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "score-type"); + filter_option_set_current ((FilterOption*)el, "less-than"); + el = filter_part_find_element (fp, "versus"); + + if (!netscape_filter_score_set(cond, (FilterInt*)el)) { + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + break; + default: + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + part_added = TRUE; + break; + + case STATUS: + fp = rule_context_create_part (rc, "status"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "match-type"); + + switch (cond->prop) { + case IS: + filter_option_set_current ((FilterOption*)el, "is"); + el = filter_part_find_element (fp, "flag"); + + if (!strcmp (cond->prop_val_str, + ns_filter_condition_property_types[READ])) { + filter_option_set_current ((FilterOption*)el, "Seen"); + } else if (!strcmp (cond->prop_val_str, + ns_filter_condition_property_types[REPLIED])) { + filter_option_set_current ((FilterOption*)el, "Answered"); + } + break; + case IS_NOT: + filter_option_set_current ((FilterOption*)el, "is not"); + el = filter_part_find_element (fp, "flag"); + + if (!strcmp (cond->prop_val_str, + ns_filter_condition_property_types[READ])) { + filter_option_set_current ((FilterOption*)el, "Seen"); + } else if (!strcmp (cond->prop_val_str, + ns_filter_condition_property_types[REPLIED])) { + filter_option_set_current ((FilterOption*)el, "Answered"); + } + default: + filter_rule_remove_part (fr, fp); + g_object_unref((fp)); + continue; + } + part_added = TRUE; + break; + case AGE_IN_DAYS: + /* I guess we can skip that -- Netscape crashes anyway + whenever you try to use that setting ... :) */ + break; + case X_MSG_HEADER: + fp = rule_context_create_part (rc, "header"); + filter_rule_add_part (fr, fp); + el = filter_part_find_element (fp, "header-field"); + filter_input_set_value ((FilterInput *)el, cond->prop_val_str); + el = filter_part_find_element (fp, "header-type"); + filter_option_set_current ((FilterOption*)el, "exists"); + part_added = TRUE; + break; + default: + continue; + } + } + + if (!part_added) { + g_object_unref((ff)); + return NULL; + } + + /* build and add actionset */ + + switch (nsf->action) { + case MOVE_TO_FOLDER: + { + char *evol_folder; + char *evol_folder_uri; + + fp = filter_context_create_action (fc, "move-to-folder"); + filter_filter_add_action (ff, fp); + el = filter_part_find_element (fp, "folder"); + + evol_folder = netscape_filter_strip_sbd (nsf->action_val_str); + evol_folder_uri = netscape_filter_map_folder_to_uri (evol_folder); + filter_folder_set_value ((FilterFolder *)el, evol_folder_uri); + g_free (evol_folder); + g_free (evol_folder_uri); + + action_added = TRUE; + } + break; + case CHANGE_PRIORITY: + fp = filter_context_create_action (fc, "score"); + el = filter_part_find_element (fp, "score"); + + switch (nsf->action_val_id) { + case LOWEST: + filter_int_set_value((FilterInt *)el, -2); + action_added = TRUE; + break; + case LOW: + filter_int_set_value((FilterInt *)el, -1); + action_added = TRUE; + break; + case NORMAL: + filter_int_set_value((FilterInt *)el, 0); + action_added = TRUE; + break; + case HIGH: + filter_int_set_value((FilterInt *)el, 1); + action_added = TRUE; + break; + case HIGHEST: + filter_int_set_value((FilterInt *)el, 2); + action_added = TRUE; + break; + default: + g_object_unref((fp)); + } + if (action_added) { + *priority_needed = TRUE; + filter_filter_add_action (ff, fp); + } + break; + case DELETE: + fp = filter_context_create_action (fc, "delete"); + filter_filter_add_action (ff, fp); + action_added = TRUE; + break; + case MARK_READ: + fp = filter_context_create_action (fc, "set-status"); + el = filter_part_find_element (fp, "flag"); + filter_option_set_current ((FilterOption *)el, "Seen"); + filter_filter_add_action (ff, fp); + action_added = TRUE; + break; + case IGNORE_THREAD: + case WATCH_THREAD: + netscape_filter_threads_action_not_supported (); + break; + default: + break; + } + + if (!action_added) { + g_object_unref((ff)); + return NULL; + } + + return ff; +} + + +static void +netscape_import_filters (NsImporter *importer) +{ + FilterContext *fc; + char *user, *system; + FILE *mailrule_handle; + char *ns_mailrule; + NsFilter *nsf; + FilterFilter *ff; + gboolean priority_needed = FALSE; + + ns_mailrule = gnome_util_prepend_user_home (".netscape/mailrule"); + mailrule_handle = fopen (ns_mailrule, "r"); + g_free (ns_mailrule); + + if (mailrule_handle == NULL) { + d(g_warning ("No .netscape/mailrule found.")); + user_prefs = NULL; + return; + } + + fc = filter_context_new (); + user = g_concat_dir_and_file (g_get_home_dir (), + "evolution/filters.xml"); + system = EVOLUTION_DATADIR "/evolution-" BASE_VERSION "/filtertypes.xml"; + + if (rule_context_load ((RuleContext *)fc, system, user) < 0) { + g_warning ("Could not load rule context."); + goto exit; + } + + while ( (nsf = netscape_filter_read_next (mailrule_handle)) != NULL) { + + if ( (ff = netscape_filter_to_evol_filter (fc, nsf, &priority_needed)) != NULL) + rule_context_add_rule (RULE_CONTEXT(fc), FILTER_RULE(ff)); + netscape_filter_cleanup (nsf); + } + + if (priority_needed) { + netscape_filter_change_priority_warning (); + netscape_add_priority_workaround_filters (fc); + } + + if (rule_context_save(RULE_CONTEXT(fc), user) < 0) { + g_warning ("Could not save user's rule context."); + } + + exit: + g_free(user); + g_object_unref((fc)); + +} + + + + +/* Email folder & accounts stuff ----------------------------------------------- */ + + +static GtkWidget * +create_importer_gui (NsImporter *importer) +{ + GtkWidget *dialog; + + dialog = gnome_message_box_new (_("Evolution is importing your old Netscape data"), GNOME_MESSAGE_BOX_INFO, NULL); + gtk_window_set_title (GTK_WINDOW (dialog), _("Importing...")); + + importer->label = gtk_label_new (_("Please wait")); + importer->progressbar = gtk_progress_bar_new (); + gtk_progress_set_activity_mode (GTK_PROGRESS (importer->progressbar), TRUE); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), + importer->label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), + importer->progressbar, FALSE, FALSE, 0); + + return dialog; +} + +static void +netscape_store_settings (NsImporter *importer) +{ + GConfClient *gconf = gconf_client_get_default(); + + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/mail", importer->do_mail, NULL); + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/settings", importer->do_settings, NULL); + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/filters", importer->do_filters, NULL); +} + +static void +netscape_restore_settings (NsImporter *importer) +{ + GConfClient *gconf = gconf_client_get_default(); + + importer->do_mail = gconf_client_get_bool(gconf, "/apps/evolution/importer/netscape/mail", NULL); + importer->do_settings = gconf_client_get_bool(gconf, "/apps/evolution/importer/netscape/settings", NULL); + importer->do_filters = gconf_client_get_bool(gconf, "/apps/evolution/importer/netscape/filters", NULL); +} + +static const char * +netscape_get_string (const char *strname) +{ + return g_hash_table_lookup (user_prefs, strname); +} + +static int +netscape_get_integer (const char *strname) +{ + char *intstr; + + intstr = g_hash_table_lookup (user_prefs, strname); + if (intstr == NULL) { + return 0; + } else { + return atoi (intstr); + } +} + +static gboolean +netscape_get_boolean (const char *strname) +{ + char *boolstr; + + boolstr = g_hash_table_lookup (user_prefs, strname); + + if (boolstr == NULL) { + return FALSE; + } else { + if (strcasecmp (boolstr, "false") == 0) { + return FALSE; + } else if (strcasecmp (boolstr, "true") == 0) { + return TRUE; + } + } + + return FALSE; +} + +static char * +netscape_get_key (const char *line) +{ + char *line_dup; + char *start, *end; + char *key; + + line_dup = g_strdup (line); + start = strchr (line_dup, '\"'); + if (start == NULL) + goto die; + start++; + if (*start == '\0') + goto die; + + end = strchr (start, '\"'); + if (end == NULL) + goto die; + *end = '\0'; + + key = g_strdup (start); + g_free (line_dup); + + d(g_warning ("Found key: %s", key)); + return key; + + die: + g_free (line_dup); + g_warning ("Broken line: %s", line); + return NULL; +} + +static char * +netscape_get_value (const char *line) +{ + char *line_dup; + char *start, *end; + char *value; + + line_dup = g_strdup (line); + start = strchr (line_dup, ','); + if (start == NULL) + goto die; + start++; + if (*start == '\0') + goto die; + + if (*start == ' ') + start++; + if (*start == '\0') + goto die; + + if (*start == '\"') + start++; + if (*start == '\0') + goto die; + + /* Start should now be the start of the value */ + end = strrchr (start, ')'); + if (end == NULL) + goto die; + *end = '\0'; + if (*(end - 1) == '\"') + *(end - 1) = '\0'; + + if (start == (end - 1)) { + g_free (line_dup); + return NULL; + } + + value = g_strdup (start); + g_free (line_dup); + + d(g_warning ("Found value: %s", value)); + return value; + + die: + g_free (line_dup); + g_warning ("Broken line: %s", line); + return NULL; +} + +static void +netscape_init_prefs (void) +{ + FILE *prefs_handle; + char *nsprefs; + char line[MAXLEN]; + + user_prefs = g_hash_table_new (g_str_hash, g_str_equal); + + nsprefs = gnome_util_prepend_user_home (".netscape/preferences.js"); + prefs_handle = fopen (nsprefs, "r"); + g_free (nsprefs); + + if (prefs_handle == NULL) { + d(g_warning ("No .netscape/preferences.js")); + g_hash_table_destroy (user_prefs); + user_prefs = NULL; + return; + } + + /* Find the user mail dir */ + while (fgets (line, MAXLEN, prefs_handle)) { + char *key, *value; + + if (*line == 0) { + continue; + } + + if (*line == '/' && line[1] == '/') { + continue; + } + + key = netscape_get_key (line); + value = netscape_get_value (line); + + if (key == NULL) + continue; + + g_hash_table_insert (user_prefs, key, value); + } + + return; +} + +static char * +get_user_fullname (void) +{ + char *uname, *gecos, *special; + struct passwd *pwd; + + uname = getenv ("USER"); + pwd = getpwnam (uname); + + if (strcmp (pwd->pw_gecos, "") == 0) { + return g_strdup (uname); + } + + special = strchr (pwd->pw_gecos, ','); + if (special == NULL) { + gecos = g_strdup (pwd->pw_gecos); + } else { + gecos = g_strndup (pwd->pw_gecos, special - pwd->pw_gecos); + } + + special = strchr (gecos, '&'); + if (special == NULL) { + return gecos; + } else { + char *capname, *expanded, *noamp; + + capname = g_strdup (uname); + capname[0] = toupper ((int) capname[0]); + noamp = g_strndup (gecos, special - gecos - 1); + expanded = g_strconcat (noamp, capname, NULL); + + g_free (noamp); + g_free (capname); + g_free (gecos); + + return expanded; + } +} + +static void +netscape_import_accounts (NsImporter *importer) +{ + char *username; + const char *nstr; + const char *imap; + GNOME_Evolution_MailConfig_Account account; + GNOME_Evolution_MailConfig_Service source, transport; + GNOME_Evolution_MailConfig_Identity id; + CORBA_Object objref; + CORBA_Environment ev; + + if (user_prefs == NULL) { + netscape_init_prefs (); + if (user_prefs == NULL) + return; + } + + CORBA_exception_init (&ev); + objref = bonobo_activation_activate_from_id (MAIL_CONFIG_IID, 0, NULL, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Error starting mail config"); + CORBA_exception_free (&ev); + return; + } + + if (objref == CORBA_OBJECT_NIL) { + g_warning ("Error activating mail config"); + return; + } + + /* Create identify structure */ + nstr = netscape_get_string ("mail.identity.username"); + if (nstr != NULL) { + username = g_strdup (nstr); + } else { + username = get_user_fullname (); + } + + id.name = CORBA_string_dup (username); + nstr = netscape_get_string ("mail.identity.useremail"); + id.address = CORBA_string_dup (nstr ? nstr : ""); + nstr = netscape_get_string ("mail.identity.organization"); + id.organization = CORBA_string_dup (nstr ? nstr : ""); + nstr = netscape_get_string ("mail.signature_file"); + /* FIXME rodo id.signature = CORBA_string_dup (nstr ? nstr : ""); + id.html_signature = CORBA_string_dup (""); + id.has_html_signature = FALSE; */ + + /* Create transport */ + nstr = netscape_get_string ("network.hosts.smtp_server"); + if (nstr != NULL) { + char *url; + const char *nstr2; + + nstr2 = netscape_get_string ("mail.smtp_name"); + if (nstr2) { + url = g_strconcat ("smtp://", nstr2, "@", nstr, NULL); + } else { + url = g_strconcat ("smtp://", nstr, NULL); + } + transport.url = CORBA_string_dup (url); + transport.keep_on_server = FALSE; + transport.auto_check = FALSE; + transport.auto_check_time = 10; + transport.save_passwd = FALSE; + transport.enabled = TRUE; + g_free (url); + } else { + transport.url = CORBA_string_dup (""); + transport.keep_on_server = FALSE; + transport.auto_check = FALSE; + transport.auto_check_time = 0; + transport.save_passwd = FALSE; + transport.enabled = FALSE; + } + + /* Create account */ + account.name = CORBA_string_dup (username); + account.id = id; + account.transport = transport; + + account.drafts_folder_uri = CORBA_string_dup (""); + account.sent_folder_uri = CORBA_string_dup (""); + + /* Create POP3 source */ + nstr = netscape_get_string ("network.hosts.pop_server"); + if (nstr != NULL && *nstr != 0) { + char *url; + gboolean bool; + const char *nstr2; + + nstr2 = netscape_get_string ("mail.pop_name"); + if (nstr2) { + url = g_strconcat ("pop://", nstr2, "@", nstr, NULL); + } else { + url = g_strconcat ("pop://", nstr, NULL); + } + source.url = CORBA_string_dup (url); + bool = netscape_get_boolean ("mail.leave_on_server"); + g_warning ("mail.leave_on_server: %s", bool ? "true" : "false"); + source.keep_on_server = netscape_get_boolean ("mail.leave_on_server"); + source.auto_check = TRUE; + source.auto_check_time = 10; + bool = netscape_get_boolean ("mail.remember_password"); + g_warning ("mail.remember_password: %s", bool ? "true" : "false"); + source.save_passwd = netscape_get_boolean ("mail.remember_password"); + source.enabled = TRUE; + g_free (url); + } else { + /* Are there IMAP accounts? */ + imap = netscape_get_string ("network.hosts.imap_servers"); + if (imap != NULL) { + char **servers; + int i; + + servers = g_strsplit (imap, ",", 1024); + for (i = 0; servers[i] != NULL; i++) { + GNOME_Evolution_MailConfig_Service imapsource; + char *serverstr, *name, *url; + const char *username; + + /* Create a server for each of these */ + serverstr = g_strdup_printf ("mail.imap.server.%s.", servers[i]); + name = g_strconcat (serverstr, "userName", NULL); + username = netscape_get_string (name); + g_free (name); + + if (username) + url = g_strconcat ("imap://", username, + "@", servers[i], NULL); + else + url = g_strconcat ("imap://", servers[i], NULL); + + imapsource.url = CORBA_string_dup (url); + + imapsource.keep_on_server = netscape_get_boolean ("mail.leave_on_server"); + + name = g_strconcat (serverstr, "check_new_mail", NULL); + imapsource.auto_check = netscape_get_boolean (name); + g_free (name); + + name = g_strconcat (serverstr, "check_time", NULL); + imapsource.auto_check_time = netscape_get_integer (name); + g_free (name); + + name = g_strconcat (serverstr, "remember_password", NULL); + imapsource.save_passwd = netscape_get_boolean (name); + g_free (name); + imapsource.enabled = TRUE; + + account.source = imapsource; + + GNOME_Evolution_MailConfig_addAccount (objref, &account, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Error setting account: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return; + } + + g_free (url); + g_free (serverstr); + } + + CORBA_exception_free (&ev); + g_strfreev (servers); + return; + } else { + char *url, *path; + + /* Using Movemail */ + path = getenv ("MAIL"); + url = g_strconcat ("mbox://", path, NULL); + source.url = CORBA_string_dup (url); + g_free (url); + + source.keep_on_server = netscape_get_boolean ("mail.leave_on_server"); + source.auto_check = TRUE; + source.auto_check_time = 10; + source.save_passwd = netscape_get_boolean ("mail.remember_password"); + source.enabled = FALSE; + } + } + account.source = source; + + GNOME_Evolution_MailConfig_addAccount (objref, &account, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Error setting account: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return; + } + + g_free (username); + CORBA_exception_free (&ev); +} + +static gboolean +is_dir_empty (const char *path) +{ + DIR *base; + struct stat buf; + struct dirent *contents; + + base = opendir (path); + if (base == NULL) { + return TRUE; /* Can't open dir */ + } + + while ((contents = readdir(base)) != NULL) { + char *fullpath; + + if (strcmp (contents->d_name, ".") == 0 || + strcmp (contents->d_name, "..") == 0) { + continue; + } + + fullpath = g_build_filename(path, contents->d_name, NULL); + if (lstat (fullpath, &buf) == -1) { + g_free(fullpath); + continue; + } + + if ((S_ISDIR (buf.st_mode) && !is_dir_empty (fullpath)) + || (S_ISREG(buf.st_mode) && buf.st_size != 0)) { + g_free (fullpath); + closedir (base); + return FALSE; + } + + g_free (fullpath); + } + + closedir (base); + return TRUE; +} + +static gboolean +netscape_can_import (EvolutionIntelligentImporter *ii, + void *closure) +{ + if (user_prefs == NULL) { + netscape_init_prefs (); + } + + if (user_prefs == NULL) { + d(g_warning ("No netscape dir")); + return FALSE; + } + + nsmail_dir = g_hash_table_lookup (user_prefs, "mail.directory"); + if (nsmail_dir == NULL) { + return FALSE; + } else { + return !is_dir_empty (nsmail_dir); + } +} + +static gboolean +importer_timeout_fn (gpointer data) +{ + NsImporter *importer = (NsImporter *) data; + CORBA_Object objref; + CORBA_Environment ev; + + importer->import_id = 0; + + CORBA_exception_init (&ev); + objref = bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)); + GNOME_Evolution_Importer_processItem (importer->importer, objref, &ev); + CORBA_exception_free (&ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + + return FALSE; +} + +static void +importer_cb (EvolutionImporterListener *listener, + EvolutionImporterResult result, + gboolean more_items, + void *data) +{ + NsImporter *importer = (NsImporter *) data; + CORBA_Object objref; + CORBA_Environment ev; + + if (result == EVOLUTION_IMPORTER_NOT_READY || + result == EVOLUTION_IMPORTER_BUSY) { + g_timeout_add (1000, importer_timeout_fn, data); + return; + } + + if (more_items) { + importer->progress_count++; + if ((importer->progress_count & 0xf) == 0) + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(importer->progressbar)); + importer->import_id = g_idle_add(importer_timeout_fn, importer); + return; + } + + if (importer->dir_list) + import_next (importer); + else + bonobo_object_unref((BonoboObject *)importer->ii); +} + +static gboolean +netscape_import_file (NsImporter *importer, + const char *path, + const char *folderpath) +{ + CORBA_boolean result; + CORBA_Environment ev; + CORBA_Object objref; + char *str; + + /* Do import */ + d(g_warning ("Importing %s as %s", path, folderpath)); + + CORBA_exception_init (&ev); + + str = g_strdup_printf (_("Importing %s as %s"), path, folderpath); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + result = GNOME_Evolution_Importer_loadFile (importer->importer, path, + folderpath, &ev); + if (ev._major != CORBA_NO_EXCEPTION || result == FALSE) { + g_warning ("Exception here: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return FALSE; + } + + importer->listener = evolution_importer_listener_new (importer_cb, + importer); + objref = bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)); + d(g_print ("%s:Processing...\n", __FUNCTION__)); + CORBA_exception_init (&ev); + GNOME_Evolution_Importer_processItem (importer->importer, + objref, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return FALSE; + } + CORBA_exception_free (&ev); + + return TRUE; +} + +typedef struct { + NsImporter *importer; + char *parent; + char *path; + char *foldername; +} NetscapeCreateDirectoryData; + +static void +import_next (NsImporter *importer) +{ + NetscapeCreateDirectoryData *data; + + if (importer->dir_list) { + char *folder; + GList *l; + + l = importer->dir_list; + data = l->data; + + folder = g_build_filename(data->parent, data->foldername, NULL); + + importer->dir_list = l->next; + g_list_free_1(l); + + netscape_import_file (importer, data->path, folder); + g_free (folder); + g_free (data->parent); + g_free (data->path); + g_free (data->foldername); + g_free (data); + } +} + +/* We don't allow any mail to be imported into a reservered Evolution folder name */ +static char *reserved_names[] = { + N_("Trash"), + N_("Calendar"), + N_("Contacts"), + N_("Tasks"), + NULL +}; + +static char * +fix_netscape_folder_names (const char *original_name) +{ + int i; + + for (i = 0; reserved_names[i] != NULL; i++) { + if (strcmp (original_name, _(reserved_names[i])) == 0) { + return g_strdup_printf ("Netscape-%s", + _(reserved_names[i])); + } + } + + if (strcmp (original_name, "Unsent Messages") == 0) { + return g_strdup ("Outbox"); + } + + return g_strdup (original_name); +} + +/* This function basically flattens the tree structure. + It makes a list of all the directories that are to be imported. */ +static void +scan_dir (NsImporter *importer, + const char *orig_parent, + const char *dirname) +{ + DIR *nsmail; + struct stat buf; + struct dirent *current; + char *str; + + nsmail = opendir (dirname); + if (nsmail == NULL) { + d(g_warning ("Could not open %s\nopendir returned: %s", + dirname, g_strerror (errno))); + return; + } + + str = g_strdup_printf (_("Scanning %s"), dirname); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + current = readdir (nsmail); + while (current) { + char *fullname, *foldername; + + /* Ignore things which start with . + which should be ., .., and the summaries. */ + if (current->d_name[0] =='.') { + current = readdir (nsmail); + continue; + } + + if (*orig_parent == '/') { + foldername = fix_netscape_folder_names (current->d_name); + } else { + foldername = g_strdup (current->d_name); + } + + fullname = g_concat_dir_and_file (dirname, current->d_name); + if (stat (fullname, &buf) == -1) { + d(g_warning ("Could not stat %s\nstat returned:%s", + fullname, g_strerror (errno))); + current = readdir (nsmail); + g_free (fullname); + continue; + } + + if (S_ISREG (buf.st_mode)) { + char *sbd, *parent; + NetscapeCreateDirectoryData *data; + + d(g_print ("File: %s\n", fullname)); + + data = g_new0 (NetscapeCreateDirectoryData, 1); + data->importer = importer; + data->parent = g_strdup (orig_parent); + data->path = g_strdup (fullname); + data->foldername = g_strdup (foldername); + + importer->dir_list = g_list_append (importer->dir_list, + data); + + + parent = g_concat_dir_and_file (orig_parent, + data->foldername); + + /* Check if a .sbd folder exists */ + sbd = g_strconcat (fullname, ".sbd", NULL); + if (g_file_exists (sbd)) { + scan_dir (importer, parent, sbd); + } + + g_free (parent); + g_free (sbd); + } + + g_free (fullname); + g_free (foldername); + current = readdir (nsmail); + } +} + + +static void +netscape_create_structure (EvolutionIntelligentImporter *ii, + void *closure) +{ + CORBA_Environment ev; + NsImporter *importer = closure; + GConfClient *gconf = gconf_client_get_default(); + + g_return_if_fail (nsmail_dir != NULL); + + /* Reference our object so when the shell release_unrefs us + we will still exist and not go byebye */ + bonobo_object_ref (BONOBO_OBJECT (ii)); + + netscape_store_settings (importer); + + /* Create a dialog if we're going to be active */ + /* Importing mail filters is not a criterion because it makes + little sense to import the filters but not the mail folders. */ + if (importer->do_settings == TRUE || + importer->do_mail == TRUE) { + importer->dialog = create_importer_gui (importer); + gtk_widget_show_all (importer->dialog); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + } + + if (importer->do_settings == TRUE) { + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/settings-imported", TRUE, NULL); + netscape_import_accounts (importer); + } + + if (importer->do_mail == TRUE) { + + /* Import the mail filters if needed ... */ + if (importer->do_filters == TRUE) { + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/filters-imported", TRUE, NULL); + gtk_label_set_text (GTK_LABEL (importer->label), + _("Scanning mail filters")); + + netscape_import_filters (importer); + } + + gconf_client_set_bool(gconf, "/apps/evolution/importer/netscape/mail-imported", TRUE, NULL); + + /* Scan the nsmail folder and find out what folders + need to be imported */ + + gtk_label_set_text (GTK_LABEL (importer->label), + _("Scanning directory")); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + scan_dir (importer, "/", nsmail_dir); + + /* Import them */ + gtk_label_set_text (GTK_LABEL (importer->label), + _("Starting import")); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + import_next (importer); + } + + if (importer->do_mail == FALSE) { + /* Destroy it here if we weren't importing mail + otherwise the mail importer destroys itself + once the mail in imported */ + bonobo_object_unref (BONOBO_OBJECT (ii)); + } + + bonobo_object_unref (BONOBO_OBJECT (ii)); +} + +static void +netscape_destroy_cb (NsImporter *importer, GObject *object) +{ + CORBA_Environment ev; + + netscape_store_settings (importer); + + if (importer->importer != CORBA_OBJECT_NIL) { + bonobo_object_release_unref (importer->importer, NULL); + } + + if (importer->dialog) + gtk_widget_destroy(importer->dialog); + + g_free(importer); +} + +/* Fun initialisation stuff */ + +/* Fun with aggregation */ +static void +checkbox_toggle_cb (GtkToggleButton *tb, + NsImporter *importer) +{ + /* Some extra logic here to make the filters choice + depending on the mail choice */ + if (GTK_WIDGET(tb) == importer->mail) { + importer->do_mail = gtk_toggle_button_get_active (tb); + + if (importer->do_mail == FALSE) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(importer->filters), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(importer->filters), FALSE); + importer->do_filters = FALSE; + } else { + gtk_widget_set_sensitive(GTK_WIDGET(importer->filters), TRUE); + } + + } else if (GTK_WIDGET(tb) == importer->settings) { + importer->do_settings = gtk_toggle_button_get_active (tb); + + } else if (GTK_WIDGET(tb) == importer->filters) { + importer->do_filters = gtk_toggle_button_get_active (tb); + + } + /* *do_item = gtk_toggle_button_get_active (tb); */ +} + +static BonoboControl * +create_checkboxes_control (NsImporter *importer) +{ + GtkWidget *hbox; + BonoboControl *control; + + hbox = gtk_hbox_new (FALSE, 2); + + importer->mail = gtk_check_button_new_with_label (_("Mail")); + g_signal_connect((importer->mail), "toggled", + G_CALLBACK (checkbox_toggle_cb), + importer); + + importer->settings = gtk_check_button_new_with_label (_("Settings")); + g_signal_connect((importer->settings), "toggled", + G_CALLBACK (checkbox_toggle_cb), + importer); + + importer->filters = gtk_check_button_new_with_label (_("Mail Filters")); + gtk_widget_set_sensitive(GTK_WIDGET(importer->filters), FALSE); + g_signal_connect((importer->filters), "toggled", + G_CALLBACK (checkbox_toggle_cb), + importer); + + gtk_box_pack_start (GTK_BOX (hbox), importer->mail, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), importer->settings, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), importer->filters, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + control = bonobo_control_new (hbox); + return control; +} + +static BonoboObject * +factory_fn (BonoboGenericFactory *_factory, + const char *iid, + void *closure) +{ + EvolutionIntelligentImporter *importer; + BonoboControl *control; + NsImporter *netscape; + CORBA_Environment ev; + char *message = N_("Evolution has found Netscape mail files.\n" + "Would you like them to be imported into Evolution?"); + + netscape = g_new0 (NsImporter, 1); + + CORBA_exception_init (&ev); + + netscape_restore_settings (netscape); + + netscape->importer = bonobo_activation_activate_from_id (MBOX_IMPORTER_IID, 0, NULL, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Could not start MBox importer\n%s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return NULL; + } + CORBA_exception_free (&ev); + + importer = evolution_intelligent_importer_new (netscape_can_import, + netscape_create_structure, + "Netscape", + _(message), netscape); + g_object_weak_ref(G_OBJECT (importer), (GWeakNotify)netscape_destroy_cb, netscape); + netscape->ii = importer; + + control = create_checkboxes_control (netscape); + bonobo_object_add_interface (BONOBO_OBJECT (importer), + BONOBO_OBJECT (control)); + return BONOBO_OBJECT (importer); +} + +void +mail_importer_module_init (void) +{ + BonoboGenericFactory *factory; + static int init = FALSE; + + if (init) + return; + + factory = bonobo_generic_factory_new (FACTORY_IID, factory_fn, NULL); + if (factory == NULL) + g_warning("Could not initialise Netscape intelligent mail importer"); + init = 1; +} diff --git a/mail/importers/pine-importer.c b/mail/importers/pine-importer.c new file mode 100644 index 0000000000..67b6e8747c --- /dev/null +++ b/mail/importers/pine-importer.c @@ -0,0 +1,699 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* pine-importer.c + * + * Authors: + * Iain Holmes <iain@ximian.com> + * + * Copyright 2001 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <ctype.h> +#include <string.h> + +#include <glib.h> + +#include <libgnomeui/gnome-messagebox.h> +#include <gtk/gtk.h> + +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> + +#include <bonobo/bonobo-object.h> +#include <bonobo/bonobo-generic-factory.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-context.h> +#include <bonobo/bonobo-main.h> +#include <bonobo/bonobo-exception.h> +#include <bonobo/bonobo-moniker-util.h> + +#include <importer/evolution-intelligent-importer.h> +#include <importer/evolution-importer-client.h> +#include <importer/GNOME_Evolution_Importer.h> + +#include <ebook/e-book.h> +#include <ebook/e-card-simple.h> + +#define PINE_INTELLIGENT_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Pine_Intelligent_Importer_Factory" +#define MBOX_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Mbox_Importer" +#define KEY "pine-mail-imported" + +/*#define SUPER_IMPORTER_DEBUG*/ +#ifdef SUPER_IMPORTER_DEBUG +#define d(x) x +#else +#define d(x) +#endif + +typedef struct { + EvolutionIntelligentImporter *ii; + + GList *dir_list; + + int progress_count; + + GNOME_Evolution_Importer importer; + EvolutionImporterListener *listener; + + GtkWidget *mail; + gboolean do_mail; + GtkWidget *address; + gboolean do_address; + + EBook *book; + + int timeout_id; + int more; + + /* GUI */ + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *progressbar; +} PineImporter; + +typedef struct { + char *parent; + char *foldername; + char *path; + gboolean folder; +} PineFolder; + +static void import_next (PineImporter *importer); + +static GtkWidget * +create_importer_gui (PineImporter *importer) +{ + GtkWidget *dialog; + + dialog = gnome_message_box_new (_("Evolution is importing your old Pine data"), GNOME_MESSAGE_BOX_INFO, NULL); + gtk_window_set_title (GTK_WINDOW (dialog), _("Importing...")); + + importer->label = gtk_label_new (_("Please wait")); + importer->progressbar = gtk_progress_bar_new (); + gtk_progress_set_activity_mode (GTK_PROGRESS (importer->progressbar), TRUE); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), + importer->label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), + importer->progressbar, FALSE, FALSE, 0); + return dialog; +} + +static void +pine_store_settings (PineImporter *importer) +{ + GConfClient *gconf = gconf_client_get_default (); + + gconf_client_set_bool (gconf, "/apps/evolution/importer/pine/mail", importer->do_mail, NULL); + gconf_client_set_bool (gconf, "/apps/evolution/importer/pine/address", importer->do_address, NULL); +} + +static void +pine_restore_settings (PineImporter *importer) +{ + GConfClient *gconf = gconf_client_get_default (); + + importer->do_mail = gconf_client_get_bool (gconf, "/apps/evolution/importer/pine/mail", NULL); + importer->do_address = gconf_client_get_bool (gconf, "/apps/evolution/importer/pine/address", NULL); +} + +/* A very basic address spliter. + Returns the first email address + denoted by <address> */ +static char * +parse_address (const char *address) +{ + char *addr_dup, *result, *start, *end; + + if (address == NULL) { + return NULL; + } + + addr_dup = g_strdup (address); + start = strchr (addr_dup, '<'); + if (start == NULL) { + /* Whole line is an address */ + return addr_dup; + } + + start += 1; + end = strchr (start, '>'); + if (end == NULL) { + result = g_strdup (start); + g_free (addr_dup); + + return result; + } + + *end = 0; + result = strdup (start); + g_free (addr_dup); + + return result; +} + +static void +add_card_cb (EBook *book, + EBookStatus status, + const char *id, + gpointer closure) +{ + g_object_unref (closure); +} + +static void +parse_line (EBook *book, + char *line) +{ + char **strings; + ECardName *name; + ECard *card; + EList *list; + + card = e_card_new (""); + strings = g_strsplit (line, "\t", 3); + if (strings[0] && strings[1] && strings[2]) { + name = e_card_name_from_string (strings[1]); + g_object_set (card, + "nickname", strings[0], + "full_name", strings[1], + "name", name, NULL); + g_object_get (card, + "email", &list, + NULL); + e_list_append (list, strings[2]); + g_strfreev (strings); + e_book_add_card (book, card, add_card_cb, card); + } +} + +static void +import_addressfile (EBook *book, + EBookStatus status, + gpointer user_data) +{ + char *addressbook; + FILE *handle; + char line[2 * 1024]; + int which = 0; + char *lastline = NULL; + PineImporter *importer = user_data; + + addressbook = g_build_filename(g_get_home_dir(), ".addressbook", NULL); + handle = fopen (addressbook, "r"); + g_free (addressbook); + + if (handle == NULL) { + g_warning ("Cannot open .addressbook"); + return; + } + + while (fgets (line + which * 1024, 1024, handle)) { + int length; + char *thisline = line + which * 1024; + + importer->progress_count++; + if ((importer->progress_count & 0xf) == 0) + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(importer->progressbar)); + + length = strlen (thisline); + if (thisline[length - 1] == '\n') { + line[--length] = 0; + } + + if (lastline && *thisline && isspace ((int) *thisline)) { + char *temp; + + while (*thisline && isspace ((int) *thisline)) { + thisline++; + } + temp = lastline; + lastline = g_strdup_printf ("%s%s", lastline, thisline); + g_free (temp); + continue; + } + + if (lastline) { + parse_line (book, lastline); + g_free (lastline); + } + + lastline = g_strdup (thisline); + } + + if (lastline) { + parse_line (book, lastline); + g_free (lastline); + } + + fclose (handle); +} + +static void +import_addressbook (PineImporter *importer) +{ + char *path, *uri; + + importer->book = e_book_new (); + if (importer->book == NULL) { + g_warning ("Could not create EBook."); + return; + } + + path = g_build_filename(g_get_home_dir (), + "evolution/local/Contacts/addressbook.db", NULL); + uri = g_strdup_printf ("file://%s", path); + g_free (path); + + if (!e_book_load_uri (importer->book, uri, import_addressfile, importer)) { + g_warning ("Error calling load_uri"); + } + g_free (uri); +} + +static gboolean +importer_timeout_fn (gpointer data) +{ + PineImporter *importer = (PineImporter *) data; + CORBA_Environment ev; + + importer->timeout_id = 0; + + if (importer->more) { + importer->progress_count++; + if ((importer->progress_count & 0xf) == 0) + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(importer->progressbar)); + + CORBA_exception_init (&ev); + GNOME_Evolution_Importer_processItem (importer->importer, bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)), &ev); + if (ev._major != CORBA_NO_EXCEPTION) + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + } else if (importer->dir_list) { + import_next (importer); + } else { + bonobo_object_unref (BONOBO_OBJECT (importer->ii)); + } + + return FALSE; +} + +static void +importer_cb (EvolutionImporterListener *listener, + EvolutionImporterResult result, + gboolean more_items, + void *data) +{ + PineImporter *importer = (PineImporter *) data; + CORBA_Object objref; + CORBA_Environment ev; + + if (result == EVOLUTION_IMPORTER_NOT_READY || + result == EVOLUTION_IMPORTER_BUSY) { + importer->more = more_items; + importer->timeout_id = gtk_timeout_add (1000, importer_timeout_fn, data); + return; + } + + if (importer->timeout_id) { + /* we ignore multiple calls, we can get them if for + example we're waiting for a folder to open, yet we + tried to open the next. Uh, this shouldn't happen + anyway, but ... */ + return; + } + + importer->more = more_items; + + importer->timeout_id = g_idle_add(importer_timeout_fn, importer); + return; +} + +static gboolean +pine_import_file (PineImporter *importer, + const char *path, + const char *folderpath, + gboolean folder) +{ + CORBA_boolean result; + CORBA_Environment ev; + CORBA_Object objref; + char *str; + + CORBA_exception_init (&ev); + + str = g_strdup_printf (_("Importing %s as %s"), path, folderpath); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + result = GNOME_Evolution_Importer_loadFile (importer->importer, path, + folderpath, &ev); + if (ev._major != CORBA_NO_EXCEPTION || result == FALSE) { + g_warning ("Exception here: %s\n%s, %s", CORBA_exception_id (&ev), path, folderpath); + CORBA_exception_free (&ev); + return FALSE; + } + + importer->listener = evolution_importer_listener_new (importer_cb, + importer); + objref = bonobo_object_corba_objref (BONOBO_OBJECT (importer->listener)); + GNOME_Evolution_Importer_processItem (importer->importer, objref, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Exception: %s", CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + } + CORBA_exception_free (&ev); + + return TRUE; +} + +static gboolean +pine_can_import (EvolutionIntelligentImporter *ii, + void *closure) +{ + PineImporter *importer = closure; + char *maildir, *addrfile; + gboolean mail; + gboolean md_exists = FALSE, addr_exists = FALSE; + struct stat st; + GConfClient *gconf = gconf_client_get_default(); + + maildir = g_build_filename(g_get_home_dir(), "mail", NULL); + md_exists = lstat(maildir, &st) == 0 && S_ISDIR(st.st_mode); + importer->do_mail = md_exists; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (importer->mail), + importer->do_mail); + + gtk_widget_set_sensitive (importer->mail, md_exists); + g_free (maildir); + + addrfile = g_build_filename(g_get_home_dir(), ".addressbook", NULL); + addr_exists = lstat(addrfile, &st) == 0 && S_ISREG(st.st_mode); + g_free (addrfile); + gtk_widget_set_sensitive (importer->address, addr_exists); + + return md_exists || addr_exists; +} + +static void +import_next (PineImporter *importer) +{ + PineFolder *data; + + if (importer->dir_list) { + char *folder; + GList *l; + + l = importer->dir_list; + data = l->data; + folder = g_build_filename(data->parent, data->foldername, NULL); + importer->dir_list = l->next; + g_list_free_1(l); + + pine_import_file (importer, data->path, folder, data->folder); + g_free (folder); + g_free (data->parent); + g_free (data->path); + g_free (data->foldername); + g_free (data); + } + +} + +/* Pine uses sent-mail and saved-mail whereas Evolution uses Sent and Drafts */ +static char * +maybe_replace_name (const char *original_name) +{ + if (strcmp (original_name, "sent-mail") == 0) { + return g_strdup ("Sent"); + } else if (strcmp (original_name, "saved-messages") == 0) { + return g_strdup ("Drafts"); + } + + return g_strdup (original_name); +} + +static void +scan_dir (PineImporter *importer, + const char *dirname, + const char *orig_parent) +{ + DIR *maildir; + struct stat buf; + struct dirent *current; + char *str; + + maildir = opendir (dirname); + if (maildir == NULL) { + g_warning ("Could not open %s\nopendir returned: %s", + dirname, g_strerror (errno)); + return; + } + + str = g_strdup_printf (_("Scanning %s"), dirname); + gtk_label_set_text (GTK_LABEL (importer->label), str); + g_free (str); + + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + current = readdir (maildir); + while (current) { + PineFolder *pf; + char *fullname, *foldername; + + /* Ignore . and .. */ + if (current->d_name[0] == '.') { + if (current->d_name[1] == '\0' || + (current->d_name[1] == '.' && current->d_name[2] == '\0')) { + current = readdir (maildir); + continue; + } + } + + if (*orig_parent == '/') { + foldername = maybe_replace_name (current->d_name); + } else { + foldername = g_strdup (current->d_name); + } + + fullname = g_build_filename(dirname, current->d_name, NULL); + if (stat (fullname, &buf) == -1) { + g_warning ("Could not stat %s\nstat returned: %s", + fullname, g_strerror (errno)); + current = readdir (maildir); + g_free (fullname); + continue; + } + + if (S_ISREG (buf.st_mode)) { + pf = g_new (PineFolder, 1); + pf->path = g_strdup (fullname); + pf->parent = g_strdup (orig_parent); + pf->foldername = g_strdup (foldername); + pf->folder = FALSE; + importer->dir_list = g_list_append (importer->dir_list, pf); + } else if (S_ISDIR (buf.st_mode)) { + char *subdir; + + pf = g_new (PineFolder, 1); + pf->path = g_strdup (fullname); + pf->parent = g_strdup (orig_parent); + pf->foldername = g_strdup (foldername); + pf->folder = TRUE; + importer->dir_list = g_list_append (importer->dir_list, pf); + + subdir = g_build_filename (orig_parent, foldername, NULL); + scan_dir (importer, fullname, subdir); + g_free (subdir); + } + + g_free (fullname); + g_free (foldername); + current = readdir (maildir); + } +} + +static void +pine_create_structure (EvolutionIntelligentImporter *ii, + void *closure) +{ + CORBA_Environment ev; + PineImporter *importer = closure; + char *maildir; + GConfClient *gconf = gconf_client_get_default (); + + bonobo_object_ref (BONOBO_OBJECT (ii)); + pine_store_settings (importer); + + /* Create a dialog */ + if (importer->do_address == TRUE || + importer->do_mail == TRUE) { + importer->dialog = create_importer_gui (importer); + gtk_widget_show_all (importer->dialog); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + } + + if (importer->do_address == TRUE) { + gconf_client_set_bool(gconf, "/apps/evolution/importer/pine/address-imported", TRUE, NULL); + import_addressbook (importer); + } + + if (importer->do_mail == TRUE) { + gconf_client_set_bool(gconf, "/apps/evolution/importer/pine/mail-imported", TRUE, NULL); + maildir = g_build_filename(g_get_home_dir(), "mail", NULL); + gtk_label_set_text (GTK_LABEL (importer->label), + _("Scanning directory")); + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + scan_dir (importer, maildir, "/"); + g_free (maildir); + + /* Import them */ + import_next (importer); + } + + if (importer->do_mail == FALSE && importer->do_address == FALSE) { + /* Destroy it here if we weren't importing mail + otherwise the mail importer destroys itself + once the mail is imported */ + bonobo_object_unref (BONOBO_OBJECT (ii)); + } + bonobo_object_unref (BONOBO_OBJECT (ii)); +} + +static void +pine_destroy_cb (PineImporter *importer, GtkObject *object) +{ + CORBA_Environment ev; + + pine_store_settings (importer); + + if (importer->dialog) + gtk_widget_destroy(importer->dialog); + + if (importer->importer != CORBA_OBJECT_NIL) { + bonobo_object_release_unref (importer->importer, NULL); + } +} + +/* Fun inity stuff */ + +/* Fun control stuff */ +static void +checkbox_toggle_cb (GtkToggleButton *tb, + gboolean *do_item) +{ + *do_item = gtk_toggle_button_get_active (tb); +} + +static BonoboControl * +create_checkboxes_control (PineImporter *importer) +{ + GtkWidget *hbox; + BonoboControl *control; + + hbox = gtk_hbox_new (FALSE, 2); + + importer->mail = gtk_check_button_new_with_label (_("Mail")); + gtk_signal_connect (GTK_OBJECT (importer->mail), "toggled", + GTK_SIGNAL_FUNC (checkbox_toggle_cb), + &importer->do_mail); + + importer->address = gtk_check_button_new_with_label (_("Addressbook")); + gtk_signal_connect (GTK_OBJECT (importer->address), "toggled", + GTK_SIGNAL_FUNC (checkbox_toggle_cb), + &importer->do_address); + + gtk_box_pack_start (GTK_BOX (hbox), importer->mail, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), importer->address, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + control = bonobo_control_new (hbox); + return control; +} + +static BonoboObject * +factory_fn (BonoboGenericFactory *_factory, + const char *iid, + void *closure) +{ + EvolutionIntelligentImporter *importer; + BonoboControl *control; + PineImporter *pine; + CORBA_Environment ev; + char *message = N_("Evolution has found Pine mail files.\n" + "Would you like to import them into Evolution?"); + + pine = g_new0 (PineImporter, 1); + + CORBA_exception_init (&ev); + pine_restore_settings (pine); + + pine->importer = bonobo_activation_activate_from_id (MBOX_IMPORTER_IID, 0, NULL, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Could not start MBox importer\n%s", + CORBA_exception_id (&ev)); + CORBA_exception_free (&ev); + return NULL; + } + CORBA_exception_free (&ev); + + importer = evolution_intelligent_importer_new (pine_can_import, + pine_create_structure, + _("Pine"), + _(message), pine); + g_object_weak_ref((GObject *)importer, (GWeakNotify)pine_destroy_cb, pine); + pine->ii = importer; + + control = create_checkboxes_control (pine); + bonobo_object_add_interface (BONOBO_OBJECT (importer), + BONOBO_OBJECT (control)); + return BONOBO_OBJECT (importer); +} + +void +mail_importer_module_init (void) +{ + BonoboGenericFactory *factory; + static int init = FALSE; + + if (init) + return; + + factory = bonobo_generic_factory_new (PINE_INTELLIGENT_IMPORTER_IID, + factory_fn, NULL); + if (factory == NULL) + g_warning ("Could not initialise Pine Intelligent Mail Importer."); + init = TRUE; +} |