diff options
-rw-r--r-- | importers/ChangeLog | 8 | ||||
-rw-r--r-- | importers/Makefile.am | 4 | ||||
-rw-r--r-- | importers/netscape-importer.c | 1244 |
3 files changed, 1220 insertions, 36 deletions
diff --git a/importers/ChangeLog b/importers/ChangeLog index 3eaf9893a7..1f1ab1be48 100644 --- a/importers/ChangeLog +++ b/importers/ChangeLog @@ -1,3 +1,11 @@ +2002-04-29 Iain Holmes <iain@ximian.com> + + * Makefile.am: Link the netscape importer against camel, filters and + shell. + + * netscape-importer.c: Apply patch from Christian Kreibich to import + netscape filters. + 2002-04-26 Jeffrey Stedfast <fejj@ximian.com> * Makefile.am: Don't link to libibex anymore, it has been diff --git a/importers/Makefile.am b/importers/Makefile.am index e8a525f6f5..8ba389df41 100644 --- a/importers/Makefile.am +++ b/importers/Makefile.am @@ -5,6 +5,7 @@ bin_PROGRAMS = evolution-netscape-importer \ INCLUDES = \ -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_DATADIR=\""$(datadir)"\" \ -DG_LOG_DOMAIN=\"Evolution-Importer\" \ -I$(top_srcdir) \ -I$(top_builddir)/shell \ @@ -30,7 +31,10 @@ evolution_netscape_importer_SOURCES = \ evolution_netscape_importer_LDADD = \ $(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) evolution_elm_importer_SOURCES = \ diff --git a/importers/netscape-importer.c b/importers/netscape-importer.c index d53795adfa..397cfa4322 100644 --- a/importers/netscape-importer.c +++ b/importers/netscape-importer.c @@ -3,6 +3,7 @@ * * Authors: * Iain Holmes <iain@ximian.com> + * Christian Kreibich <cK@whoop.org> (email filter import) * * Copyright 2001 Ximian, Inc. (www.ximian.com) * @@ -50,11 +51,24 @@ #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-score.h> +#include <shell/evolution-shell-client.h> + #include "Mail.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_Netscape_Intelligent_Importer_Factory" #define MBOX_IMPORTER_IID "OAFIID:GNOME_Evolution_Mail_Mbox_Importer" #define MAIL_CONFIG_IID "OAFIID:GNOME_Evolution_MailConfig" @@ -68,6 +82,8 @@ static GHashTable *user_prefs = NULL; #define d(x) #endif +#define MAXLEN 4096 + typedef struct { GList *dir_list; @@ -82,9 +98,9 @@ typedef struct { /* GtkWidget *addrs; gboolean do_addrs; - GtkWidget *filters; - gboolean do_filters; */ + GtkWidget *filters; + gboolean do_filters; GtkWidget *settings; gboolean do_settings; @@ -94,12 +110,1110 @@ typedef struct { GtkWidget *dialog; GtkWidget *label; GtkWidget *progressbar; -} NetscapeImporter; +} 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 = FROM; + } 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]; + + 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: + ((FilterScore *)el)->score = -2; + break; + case LOW: + ((FilterScore *)el)->score = -1; + break; + case NORMAL: + ((FilterScore *)el)->score = 0; + break; + case HIGH: + ((FilterScore *)el)->score = 1; + break; + case HIGHEST: + ((FilterScore *)el)->score = 2; + break; + default: + gtk_object_unref (GTK_OBJECT(ff)); + return NULL; + } + + 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, FilterScore *el) +{ + switch (cond->prop_val_id) { + case LOWEST: + el->score = -2; + break; + case LOW: + el->score = -1; + break; + case NORMAL: + el->score = 0; + break; + case HIGH: + el->score = 1; + break; + case HIGHEST: + el->score = 2; + break; + default: + return FALSE; + } + + 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); + gtk_object_unref (GTK_OBJECT(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); + gtk_object_unref (GTK_OBJECT(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); + gtk_object_unref (GTK_OBJECT(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); + gtk_object_unref (GTK_OBJECT(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); + gtk_object_unref (GTK_OBJECT(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, (FilterScore*)el)) { + filter_rule_remove_part (fr, fp); + gtk_object_unref (GTK_OBJECT(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, (FilterScore*)el)) { + filter_rule_remove_part (fr, fp); + gtk_object_unref (GTK_OBJECT(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, (FilterScore*)el)) { + filter_rule_remove_part (fr, fp); + gtk_object_unref (GTK_OBJECT(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, (FilterScore*)el)) { + filter_rule_remove_part (fr, fp); + gtk_object_unref (GTK_OBJECT(fp)); + continue; + } + break; + default: + filter_rule_remove_part (fr, fp); + gtk_object_unref (GTK_OBJECT(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); + gtk_object_unref (GTK_OBJECT(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) { + gtk_object_unref (GTK_OBJECT(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: + ((FilterScore *)el)->score = -2; + action_added = TRUE; + break; + case LOW: + ((FilterScore *)el)->score = -1; + action_added = TRUE; + break; + case NORMAL: + ((FilterScore *)el)->score = 0; + action_added = TRUE; + break; + case HIGH: + ((FilterScore *)el)->score = 1; + action_added = TRUE; + break; + case HIGHEST: + ((FilterScore *)el)->score = 2; + action_added = TRUE; + break; + default: + gtk_object_unref (GTK_OBJECT(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: + } + + if (!action_added) { + gtk_object_unref (GTK_OBJECT(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/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); + gtk_object_unref (GTK_OBJECT (fc)); + +} + + + + +/* Email folder & accounts stuff ----------------------------------------------- */ -static void import_next (NetscapeImporter *importer); static GtkWidget * -create_importer_gui (NetscapeImporter *importer) +create_importer_gui (NsImporter *importer) { GtkWidget *dialog; @@ -118,19 +1232,22 @@ create_importer_gui (NetscapeImporter *importer) } static void -netscape_store_settings (NetscapeImporter *importer) +netscape_store_settings (NsImporter *importer) { bonobo_config_set_boolean (importer->db, "/Importer/Netscape/mail", importer->do_mail, NULL); bonobo_config_set_boolean (importer->db, "/Importer/Netscape/settings", importer->do_settings, NULL); + bonobo_config_set_boolean (importer->db, "/Importer/Netscape/filters", + importer->do_filters, NULL); } static void -netscape_restore_settings (NetscapeImporter *importer) +netscape_restore_settings (NsImporter *importer) { importer->do_mail = FALSE; importer->do_settings = FALSE; + importer->do_filters = FALSE; } static const char * @@ -259,7 +1376,7 @@ netscape_init_prefs (void) { FILE *prefs_handle; char *nsprefs; - char line[4096]; + char line[MAXLEN]; user_prefs = g_hash_table_new (g_str_hash, g_str_equal); @@ -275,7 +1392,7 @@ netscape_init_prefs (void) } /* Find the user mail dir */ - while (fgets (line, 4096, prefs_handle)) { + while (fgets (line, MAXLEN, prefs_handle)) { char *key, *value; if (*line == 0) { @@ -338,7 +1455,7 @@ get_user_fullname (void) } static void -netscape_import_accounts (NetscapeImporter *importer) +netscape_import_accounts (NsImporter *importer) { char *username; const char *nstr; @@ -587,9 +1704,6 @@ static gboolean netscape_can_import (EvolutionIntelligentImporter *ii, void *closure) { - NetscapeImporter *importer = closure; - gboolean mail, settings; - if (user_prefs == NULL) { netscape_init_prefs (); } @@ -610,7 +1724,7 @@ netscape_can_import (EvolutionIntelligentImporter *ii, static gboolean importer_timeout_fn (gpointer data) { - NetscapeImporter *importer = (NetscapeImporter *) data; + NsImporter *importer = (NsImporter *) data; CORBA_Object objref; CORBA_Environment ev; @@ -628,7 +1742,7 @@ importer_cb (EvolutionImporterListener *listener, gboolean more_items, void *data) { - NetscapeImporter *importer = (NetscapeImporter *) data; + NsImporter *importer = (NsImporter *) data; CORBA_Object objref; CORBA_Environment ev; @@ -670,7 +1784,7 @@ importer_cb (EvolutionImporterListener *listener, } static gboolean -netscape_import_file (NetscapeImporter *importer, +netscape_import_file (NsImporter *importer, const char *path, const char *folderpath) { @@ -680,7 +1794,7 @@ netscape_import_file (NetscapeImporter *importer, char *str; /* Do import */ - d(g_warning ("Importing %s as %s\n", path, folderpath)); + d(g_warning ("Importing %s as %s", path, folderpath)); CORBA_exception_init (&ev); @@ -718,14 +1832,14 @@ netscape_import_file (NetscapeImporter *importer, } typedef struct { - NetscapeImporter *importer; + NsImporter *importer; char *parent; char *path; char *foldername; } NetscapeCreateDirectoryData; static void -import_next (NetscapeImporter *importer) +import_next (NsImporter *importer) { NetscapeCreateDirectoryData *data; @@ -746,12 +1860,28 @@ import_next (NetscapeImporter *importer) } } +/* 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 * -maybe_replace_name (const char *original_name) +fix_netscape_folder_names (const char *original_name) { - if (strcmp (original_name, "Trash") == 0) { - return g_strdup ("Netscape-Trash"); /* Trash is an invalid name */ - } else if (strcmp (original_name, "Unsent Messages") == 0) { + 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"); } @@ -761,7 +1891,7 @@ maybe_replace_name (const char *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 (NetscapeImporter *importer, +scan_dir (NsImporter *importer, const char *orig_parent, const char *dirname) { @@ -797,7 +1927,7 @@ scan_dir (NetscapeImporter *importer, } if (*orig_parent == '/') { - foldername = maybe_replace_name (current->d_name); + foldername = fix_netscape_folder_names (current->d_name); } else { foldername = g_strdup (current->d_name); } @@ -846,12 +1976,13 @@ scan_dir (NetscapeImporter *importer, } } + static void netscape_create_structure (EvolutionIntelligentImporter *ii, void *closure) { CORBA_Environment ev; - NetscapeImporter *importer = closure; + NsImporter *importer = closure; g_return_if_fail (nsmail_dir != NULL); @@ -862,6 +1993,8 @@ netscape_create_structure (EvolutionIntelligentImporter *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); @@ -878,6 +2011,18 @@ netscape_create_structure (EvolutionIntelligentImporter *ii, } if (importer->do_mail == TRUE) { + + /* Import the mail filters if needed ... */ + if (importer->do_filters == TRUE) { + bonobo_config_set_boolean (importer->db, + "/Importer/Netscape/filters-imported", TRUE, NULL); + + gtk_label_set_text (GTK_LABEL (importer->label), + _("Scanning mail filters")); + + netscape_import_filters (importer); + } + bonobo_config_set_boolean (importer->db, "/Importer/Netscape/mail-imported", TRUE, NULL); /* Scan the nsmail folder and find out what folders @@ -896,8 +2041,8 @@ netscape_create_structure (EvolutionIntelligentImporter *ii, while (gtk_events_pending ()) { gtk_main_iteration (); } - import_next (importer); - } + import_next (importer); + } CORBA_exception_init (&ev); Bonobo_ConfigDatabase_sync (importer->db, &ev); @@ -913,7 +2058,7 @@ netscape_create_structure (EvolutionIntelligentImporter *ii, static void netscape_destroy_cb (GtkObject *object, - NetscapeImporter *importer) + NsImporter *importer) { CORBA_Environment ev; @@ -940,13 +2085,33 @@ netscape_destroy_cb (GtkObject *object, /* Fun with aggregation */ static void checkbox_toggle_cb (GtkToggleButton *tb, - gboolean *do_item) + NsImporter *importer) { - *do_item = gtk_toggle_button_get_active (tb); + /* 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 (NetscapeImporter *importer) +create_checkboxes_control (NsImporter *importer) { GtkWidget *hbox; BonoboControl *control; @@ -956,15 +2121,22 @@ create_checkboxes_control (NetscapeImporter *importer) 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); importer->settings = gtk_check_button_new_with_label (_("Settings")); gtk_signal_connect (GTK_OBJECT (importer->settings), "toggled", GTK_SIGNAL_FUNC (checkbox_toggle_cb), - &importer->do_settings); + importer); + + importer->filters = gtk_check_button_new_with_label (_("Mail Filters")); + gtk_widget_set_sensitive(GTK_WIDGET(importer->filters), FALSE); + gtk_signal_connect (GTK_OBJECT (importer->filters), "toggled", + GTK_SIGNAL_FUNC (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); @@ -977,12 +2149,12 @@ factory_fn (BonoboGenericFactory *_factory, { EvolutionIntelligentImporter *importer; BonoboControl *control; - NetscapeImporter *netscape; + 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 (NetscapeImporter, 1); + netscape = g_new0 (NsImporter, 1); CORBA_exception_init (&ev); |