Index: ChangeLog
authorMichael Zucci <zucchi@src.gnome.org>
Wed, 17 Jan 2001 01:07:02 +0000 (01:07 +0000)
committerMichael Zucci <zucchi@src.gnome.org>
Wed, 17 Jan 2001 01:07:02 +0000 (01:07 +0000)
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/camel/ChangeLog,v
retrieving revision 1.684
diff -r1.684 ChangeLog
0a1,34
> 2001-01-17  Not Zed  <NotZed@Ximian.com>
>
>  * camel-folder.c (free_summary): Call
>  camel_folder_summary_array_free() to do the work.
>  (get_summary): Use camel_folder_summary_array() to get the array
>  atomically.  These fixes allow folder/test8 to work again, and fix
>  a sort of race where the summary size can change while we were
>  making a copy of it.
>
>  * camel-folder-summary.c (camel_folder_summary_array): Get the
>  summary array atomically, so it can't contain empty records.
>  (camel_folder_summary_array_free): And free it.
>
>  * tests/lib/camel-test.c (die): If we are verbose & in threads,
>  then goto sleep so we can debug.
>
>  * tests/folder/test8.c (worker): Add a missing pull() for
>  comnparing content.
>
>  * camel-filter-search.c: Fix the symbol table, so match-all is an
>  immediate function, as it should be.
>
>  * tests/folder/test9.c (main): New test, tests some filtering
>  things.
>
>  * tests/message/test3.c (main): Dont use a boundary string with
>  spaces in it.  Folding can corrupt it.  Maybe the folding isn't
>  working entirely right, but anyway.
>
>  * camel-session.c: Debug out the debug.
>
>  * camel-filter-driver.c (camel_filter_driver_filter_folder): Plug
>  a messageinfo leak.
>
1a36,94
>
>  * camel-filter-search.c (header_exists): Changed to support
>  multiple args (or'd together).
>  (header_contains): Cleaned up to match the search code.  Why did
>  fejj change it? I'll never know.
>  (header_matches):
>  (header_starts_with):
>  (header_ends_with): Big cleanup of fejj's "i'm the cut & paste
>  king" code.  Also properly handle or'ing of additional args to
>  match what the folder-search code should do.
>  (check_match): New function which does the annoying matching
>  stuff (for header matches).
>  (check_header): Similarly, handles or'ing of the matches together.
>  (header_contains):
>  (header_matches):
>  (header_starts_with):
>  (header_ends_with): Call check_header to do the actual work.
>  (header_soundex): And here too.
>  (match_all): Yeah like match-all isn't passed expression results,
>  its passed expression terms.  Fix this so match-all works like it
>  should, by executing the contained expression.
>  (message_body_contains): Copied directly from
>  camel-folder-search.c, a more robust/faster/simpler body search
>  code.
>  (mime_part_matches): Removed entirely.
>  (handle_multipart): Removed entirely.
>  (build_match_regex): Copied from camel-folder-search.  Builds a
>  set of simple strings into a regex pattern that matches any of
>  them (for faster & simpler matching).  Expanded to accept regex
>  patterns itself, so it can merge them together.
>  (body_contains): Use build match/match message to match using a
>  built regex.
>  (body_regex): Likewise, this time we tell it we're building a
>  regex though.
>  (header_full_regex): Use build_match_regex to take the drudgery
>  out of it, and expand it to handle multiple regex's at once.
>  (get_full_header): slightly cleaner (well i dunno, the sprintf
>  stuff just got to me).
>  (header_regex): Cleaned up to use build_match_Regex too, and to
>  properly check types.
>  (filter_message_search): Just allocate 'fms' on the stack.
>
>  * camel-filter-driver.c (camel_filter_driver_finalise):
>  (camel_filter_driver_init):
>  (camel_filter_driver_class_init):
>  (camel_filter_driver_get_type): Changed from gtk object to camel
>  object.
>  (camel_filter_driver_add_rule): New function to add a rule to be
>  processed in sexp form.
>  (camel_filter_driver_init): Init the rules list.
>  (camel_filter_driver_finalise): Clear the rules/rules list.
>  (camel_filter_driver_filter_message): Scan rules list directly
>  rather than creating on the fly.
>
>  * Makefile.am (libcamelinclude_HEADERS): Added camel-filter-driver.h
>  (libcamel_la_SOURCES): Added camel-filter-driver.c, code taken
>  from filter-driver, which can drive, uh, filters based on sexp's.
>  (libcamelinclude_HEADERS):
>  (libcamel_la_SOURCES): Added camel-filter-search.[ch]

17 files changed:
camel/ChangeLog
camel/Makefile.am
camel/camel-filter-driver.c [new file with mode: 0644]
camel/camel-filter-driver.h [new file with mode: 0644]
camel/camel-filter-search.c [new file with mode: 0644]
camel/camel-filter-search.h [new file with mode: 0644]
camel/camel-folder-search.c
camel/camel-folder-summary.c
camel/camel-folder-summary.h
camel/camel-folder.c
camel/camel-session.c
camel/tests/folder/Makefile.am
camel/tests/folder/README
camel/tests/folder/test8.c
camel/tests/folder/test9.c [new file with mode: 0644]
camel/tests/lib/camel-test.c
camel/tests/message/test3.c

index 23531a9..7d28001 100644 (file)
@@ -1,3 +1,37 @@
+2001-01-17  Not Zed  <NotZed@Ximian.com>
+
+       * camel-folder.c (free_summary): Call
+       camel_folder_summary_array_free() to do the work.
+       (get_summary): Use camel_folder_summary_array() to get the array
+       atomically.  These fixes allow folder/test8 to work again, and fix
+       a sort of race where the summary size can change while we were
+       making a copy of it.
+
+       * camel-folder-summary.c (camel_folder_summary_array): Get the
+       summary array atomically, so it can't contain empty records.
+       (camel_folder_summary_array_free): And free it.
+
+       * tests/lib/camel-test.c (die): If we are verbose & in threads,
+       then goto sleep so we can debug.
+
+       * tests/folder/test8.c (worker): Add a missing pull() for
+       comnparing content.
+
+       * camel-filter-search.c: Fix the symbol table, so match-all is an
+       immediate function, as it should be.
+
+       * tests/folder/test9.c (main): New test, tests some filtering
+       things.
+
+       * tests/message/test3.c (main): Dont use a boundary string with
+       spaces in it.  Folding can corrupt it.  Maybe the folding isn't
+       working entirely right, but anyway.
+
+       * camel-session.c: Debug out the debug.
+
+       * camel-filter-driver.c (camel_filter_driver_filter_folder): Plug
+       a messageinfo leak.
+
 2001-01-16  Dan Winship  <danw@ximian.com>
 
        Delayed loading of IMAP message parts.
 
 2001-01-16  Not Zed  <NotZed@Ximian.com>
 
+       * camel-filter-search.c (header_exists): Changed to support
+       multiple args (or'd together).
+       (header_contains): Cleaned up to match the search code.  Why did
+       fejj change it? I'll never know.
+       (header_matches): 
+       (header_starts_with): 
+       (header_ends_with): Big cleanup of fejj's "i'm the cut & paste
+       king" code.  Also properly handle or'ing of additional args to
+       match what the folder-search code should do.
+       (check_match): New function which does the annoying matching
+       stuff (for header matches).
+       (check_header): Similarly, handles or'ing of the matches together.
+       (header_contains):
+       (header_matches): 
+       (header_starts_with): 
+       (header_ends_with): Call check_header to do the actual work.
+       (header_soundex): And here too.
+       (match_all): Yeah like match-all isn't passed expression results,
+       its passed expression terms.  Fix this so match-all works like it
+       should, by executing the contained expression.
+       (message_body_contains): Copied directly from
+       camel-folder-search.c, a more robust/faster/simpler body search
+       code.
+       (mime_part_matches): Removed entirely.
+       (handle_multipart): Removed entirely.
+       (build_match_regex): Copied from camel-folder-search.  Builds a
+       set of simple strings into a regex pattern that matches any of
+       them (for faster & simpler matching).  Expanded to accept regex
+       patterns itself, so it can merge them together.
+       (body_contains): Use build match/match message to match using a
+       built regex.
+       (body_regex): Likewise, this time we tell it we're building a
+       regex though.
+       (header_full_regex): Use build_match_regex to take the drudgery
+       out of it, and expand it to handle multiple regex's at once.
+       (get_full_header): slightly cleaner (well i dunno, the sprintf
+       stuff just got to me).
+       (header_regex): Cleaned up to use build_match_Regex too, and to
+       properly check types.
+       (filter_message_search): Just allocate 'fms' on the stack.
+
+       * camel-filter-driver.c (camel_filter_driver_finalise): 
+       (camel_filter_driver_init): 
+       (camel_filter_driver_class_init): 
+       (camel_filter_driver_get_type): Changed from gtk object to camel
+       object.
+       (camel_filter_driver_add_rule): New function to add a rule to be
+       processed in sexp form.
+       (camel_filter_driver_init): Init the rules list.
+       (camel_filter_driver_finalise): Clear the rules/rules list.
+       (camel_filter_driver_filter_message): Scan rules list directly
+       rather than creating on the fly.
+
+       * Makefile.am (libcamelinclude_HEADERS): Added camel-filter-driver.h
+       (libcamel_la_SOURCES): Added camel-filter-driver.c, code taken
+       from filter-driver, which can drive, uh, filters based on sexp's.
+       (libcamelinclude_HEADERS): 
+       (libcamel_la_SOURCES): Added camel-filter-search.[ch]
+
        * camel-folder-summary.c (camel_folder_summary_decode_string):
        Chganged len back to be unsigned.  And do a simple range check on
        the string value to try and detect corrupted summary files.
index 24483b1..8564db6 100644 (file)
@@ -20,6 +20,8 @@ libcamel_la_SOURCES =                                 \
        camel-address.c                         \
        camel-data-wrapper.c                    \
        camel-exception.c                       \
+       camel-filter-driver.c                   \
+       camel-filter-search.c                   \
        camel-folder-search.c                   \
        camel-folder-summary.c                  \
        camel-folder-thread.c                   \
@@ -73,6 +75,8 @@ libcamelinclude_HEADERS =                     \
        camel-data-wrapper.h                    \
        camel-exception-list.def                \
        camel-exception.h                       \
+       camel-filter-driver.h                   \
+       camel-filter-search.h                   \
        camel-folder-search.h                   \
        camel-folder-summary.h                  \
        camel-folder-thread.h                   \
diff --git a/camel/camel-filter-driver.c b/camel/camel-filter-driver.c
new file mode 100644 (file)
index 0000000..42aa442
--- /dev/null
@@ -0,0 +1,774 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2000 Helix Code Inc.
+ *  Copyright (C) 2001 Ximian Inc.
+ *
+ *  Authors: Michael Zucchi <notzed@helixcode.com>
+ *           Jeffrey Stedfast <fejj@helixcode.com>
+ *
+ *  This program is free software; you can redistribute it and/or 
+ *  modify it under the terms of the GNU General Public License as 
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  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
+ */
+
+#include "camel-filter-driver.h"
+#include "camel-filter-search.h"
+
+#include "camel-exception.h"
+#include "camel-service.h"
+#include "camel-mime-message.h"
+
+#include <glib.h>
+
+#include <time.h>
+
+#include "e-util/e-sexp.h"
+#include "e-util/e-memory.h"
+#include "e-util/e-msgport.h"  /* for edlist */
+
+#define d(x)
+
+/* type of status for a log report */
+enum filter_log_t {
+       FILTER_LOG_NONE,
+       FILTER_LOG_START,       /* start of new log entry */
+       FILTER_LOG_ACTION,      /* an action performed */
+       FILTER_LOG_END,         /* end of log */
+};
+
+/* list of rule nodes */
+struct _filter_rule {
+       struct _filter_rule *next;
+       struct _filter_rule *prev;
+
+       char *match;
+       char *action;
+       char *name;
+};
+
+struct _CamelFilterDriverPrivate {
+       GHashTable *globals;       /* global variables */
+
+       CamelFolder *defaultfolder;     /* defualt folder */
+       
+       FDStatusFunc *statusfunc;       /* status callback */
+       void *statusdata;               /* status callback data */
+       
+       /* for callback */
+       FilterGetFolderFunc get_folder;
+       void *data;
+       
+       /* run-time data */
+       GHashTable *folders;       /* folders that message has been copied to */
+       GHashTable *forwards;      /* addresses that have been forwarded the message */
+       
+       gboolean terminated;       /* message processing was terminated */
+       gboolean deleted;          /* message was marked for deletion */
+       gboolean copied;           /* message was copied to some folder or another */
+       
+       CamelMimeMessage *message; /* input message */
+       CamelMessageInfo *info;    /* message summary info */
+       
+       FILE *logfile;             /* log file */
+       
+       EDList rules;           /* list of _filter_rule structs */
+
+       CamelException *ex;
+       
+       /* evaluator */
+       ESExp *eval;
+};
+
+#define _PRIVATE(o) (((CamelFilterDriver *)(o))->priv)
+
+static void camel_filter_driver_class_init (CamelFilterDriverClass *klass);
+static void camel_filter_driver_init       (CamelFilterDriver *obj);
+static void camel_filter_driver_finalise   (CamelObject *obj);
+
+static void camel_filter_driver_log (CamelFilterDriver *driver, enum filter_log_t status, const char *desc, ...);
+
+static CamelFolder *open_folder (CamelFilterDriver *d, const char *folder_url);
+static int close_folders (CamelFilterDriver *d);
+
+static ESExpResult *do_delete (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *mark_forward (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_copy (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_move (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_stop (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_colour (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_score (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+static ESExpResult *do_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *);
+
+/* these are our filter actions - each must have a callback */
+static struct {
+       char *name;
+       ESExpFunc *func;
+       int type;               /* set to 1 if a function can perform shortcut evaluation, or
+                                  doesn't execute everything, 0 otherwise */
+} symbols[] = {
+       { "delete",          (ESExpFunc *) do_delete,    0 },
+       { "forward-to",      (ESExpFunc *) mark_forward, 0 },
+       { "copy-to",         (ESExpFunc *) do_copy,      0 },
+       { "move-to",         (ESExpFunc *) do_move,      0 },
+       { "stop",            (ESExpFunc *) do_stop,      0 },
+       { "set-colour",      (ESExpFunc *) do_colour,    0 },
+       { "set-score",       (ESExpFunc *) do_score,     0 },
+       { "set-system-flag", (ESExpFunc *) do_flag,      0 }
+};
+
+static CamelObjectClass *camel_filter_driver_parent;
+
+guint
+camel_filter_driver_get_type (void)
+{
+       static CamelType type = CAMEL_INVALID_TYPE;
+
+       if (type == CAMEL_INVALID_TYPE) {
+               type = camel_type_register(CAMEL_OBJECT_TYPE, "CamelFilterDriver",
+                                          sizeof(CamelFilterDriver),
+                                          sizeof(CamelFilterDriverClass),
+                                          (CamelObjectClassInitFunc)camel_filter_driver_class_init,
+                                          NULL,
+                                          (CamelObjectInitFunc)camel_filter_driver_init,
+                                          (CamelObjectFinalizeFunc)camel_filter_driver_finalise);
+       }
+       
+       return type;
+}
+
+static void
+camel_filter_driver_class_init (CamelFilterDriverClass *klass)
+{
+       /*CamelObjectClass *object_class = (CamelObjectClass *) klass;*/
+
+       camel_filter_driver_parent = camel_type_get_global_classfuncs(camel_object_get_type());
+}
+
+static void
+camel_filter_driver_init (CamelFilterDriver *obj)
+{
+       struct _CamelFilterDriverPrivate *p;
+       int i;
+       
+       p = _PRIVATE (obj) = g_malloc0 (sizeof (*p));
+
+       e_dlist_init(&p->rules);
+
+       p->eval = e_sexp_new ();
+       /* Load in builtin symbols */
+       for (i = 0; i < sizeof (symbols) / sizeof (symbols[0]); i++) {
+               if (symbols[i].type == 1) {
+                       e_sexp_add_ifunction (p->eval, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, obj);
+               } else {
+                       e_sexp_add_function (p->eval, 0, symbols[i].name, symbols[i].func, obj);
+               }
+       }
+       
+       p->globals = g_hash_table_new (g_str_hash, g_str_equal);
+       
+       p->folders = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+static void
+free_hash_strings (void *key, void *value, void *data)
+{
+       g_free (key);
+       g_free (value);
+}
+
+static void
+camel_filter_driver_finalise (CamelObject *obj)
+{
+       CamelFilterDriver *driver = (CamelFilterDriver *) obj;
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);        
+       struct _filter_rule *node;
+
+       /* close all folders that were opened for appending */
+       close_folders (driver);
+       g_hash_table_destroy (p->folders);
+       
+       g_hash_table_foreach (p->globals, free_hash_strings, driver);
+       g_hash_table_destroy (p->globals);
+
+       e_sexp_unref(p->eval);
+       
+       if (p->defaultfolder)
+               camel_object_unref (CAMEL_OBJECT (p->defaultfolder));
+
+       while ((node = (struct _filter_rule *)e_dlist_remhead(&p->rules))) {
+               g_free(node->match);
+               g_free(node->action);
+               g_free(node->name);
+               g_free(node);
+       }
+       
+       g_free (p);
+}
+
+/**
+ * camel_filter_driver_new:
+ * @system: path to system rules
+ * @user: path to user rules
+ * @get_folder: function to call to fetch folders
+ *
+ * Create a new CamelFilterDriver object.
+ * 
+ * Return value: A new CamelFilterDriver widget.
+ **/
+CamelFilterDriver *
+camel_filter_driver_new (FilterGetFolderFunc get_folder, void *data)
+{
+       CamelFilterDriver *new;
+       struct _CamelFilterDriverPrivate *p;
+       
+       new = CAMEL_FILTER_DRIVER (camel_object_new(camel_filter_driver_get_type ()));
+       p = _PRIVATE (new);
+       
+       p->get_folder = get_folder;
+       p->data = data;
+       
+       return new;
+}
+
+void
+camel_filter_driver_set_logfile (CamelFilterDriver *d, FILE *logfile)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (d);
+       
+       p->logfile = logfile;
+}
+
+void
+camel_filter_driver_set_status_func (CamelFilterDriver *d, FDStatusFunc *func, void *data)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (d);
+       
+       p->statusfunc = func;
+       p->statusdata = data;
+}
+
+void
+camel_filter_driver_set_default_folder (CamelFilterDriver *d, CamelFolder *def)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (d);
+       
+       if (p->defaultfolder)
+               camel_object_unref (CAMEL_OBJECT (p->defaultfolder));
+       
+       p->defaultfolder = def;
+       
+       if (p->defaultfolder)
+               camel_object_ref (CAMEL_OBJECT (p->defaultfolder));
+}
+
+void
+camel_filter_driver_add_rule(CamelFilterDriver *d, const char *name, const char *match, const char *action)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (d);
+       struct _filter_rule *node;
+
+       node = g_malloc(sizeof(*node));
+       node->match = g_strdup(match);
+       node->action = g_strdup(action);
+       node->name = g_strdup(name);
+       e_dlist_addtail(&p->rules, (EDListNode *)node);
+}
+
+static void
+report_status (CamelFilterDriver *driver, enum filter_status_t status, const char *desc, ...)
+{
+       /* call user-defined status report function */
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       va_list ap;
+       char *str;
+       
+       if (p->statusfunc) {
+               va_start (ap, desc);
+               str = g_strdup_vprintf (desc, ap);
+               p->statusfunc (driver, status, str, p->statusdata);
+               g_free (str);
+       }
+}
+
+
+#if 0
+void
+camel_filter_driver_set_global (CamelFilterDriver *d, const char *name, const char *value)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (d);
+       char *oldkey, *oldvalue;
+       
+       if (g_hash_table_lookup_extended (p->globals, name, (void *)&oldkey, (void *)&oldvalue)) {
+               g_free (oldvalue);
+               g_hash_table_insert (p->globals, oldkey, g_strdup (value));
+       } else {
+               g_hash_table_insert (p->globals, g_strdup (name), g_strdup (value));
+       }
+}
+#endif
+
+static ESExpResult *
+do_delete (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       d(fprintf (stderr, "doing delete\n"));
+       p->deleted = TRUE;
+       camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Delete");
+       
+       return NULL;
+}
+
+static ESExpResult *
+mark_forward (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       /*struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);*/
+       
+       d(fprintf (stderr, "marking message for forwarding\n"));
+       /* FIXME: do stuff here */
+       camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Forward");
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_copy (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       int i;
+       
+       d(fprintf (stderr, "copying message...\n"));
+       
+       for (i = 0; i < argc; i++) {
+               if (argv[i]->type == ESEXP_RES_STRING) {
+                       /* open folders we intent to copy to */
+                       char *folder = argv[i]->value.string;
+                       char *service_url;
+                       CamelFolder *outbox;
+                       
+                       outbox = open_folder (driver, folder);
+                       if (!outbox)
+                               continue;
+                       
+                       p->copied = TRUE;
+                       camel_folder_append_message (outbox, p->message, p->info, p->ex);
+                       
+                       service_url = camel_service_get_url (CAMEL_SERVICE (camel_folder_get_parent_store (outbox)));
+                       camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Copy to folder %s", service_url);
+                       g_free (service_url);
+               }
+       }
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_move (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       int i;
+       
+       d(fprintf (stderr, "moving message...\n"));
+       
+       for (i = 0; i < argc; i++) {
+               if (argv[i]->type == ESEXP_RES_STRING) {
+                       /* open folders we intent to move to */
+                       char *folder = argv[i]->value.string;
+                       char *service_url;
+                       CamelFolder *outbox;
+                       
+                       outbox = open_folder (driver, folder);
+                       if (!outbox)
+                               continue;
+                       
+                       p->copied = TRUE;
+                       p->deleted = TRUE;  /* a 'move' is a copy & delete */
+                       
+                       camel_folder_append_message (outbox, p->message, p->info, p->ex);
+                       
+                       service_url = camel_service_get_url (CAMEL_SERVICE (camel_folder_get_parent_store (outbox)));
+                       camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Move to folder %s", service_url);
+                       g_free (service_url);
+               }
+       }
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_stop (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Stopped processing");
+       d(fprintf (stderr, "terminating message processing\n"));
+       p->terminated = TRUE;
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_colour (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       d(fprintf (stderr, "setting colour tag\n"));
+       if (argc > 0 && argv[0]->type == ESEXP_RES_STRING) {
+               camel_tag_set (&p->info->user_tags, "colour", argv[0]->value.string);
+               camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Set colour to %s", argv[0]->value.string);
+       }
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_score (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       d(fprintf (stderr, "setting score tag\n"));
+       if (argc > 0 && argv[0]->type == ESEXP_RES_INT) {
+               char *value;
+               
+               value = g_strdup_printf ("%d", argv[0]->value.number);
+               camel_tag_set (&p->info->user_tags, "score", value);
+               camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Set score to %d", argv[0]->value.number);
+               g_free (value);
+       }
+       
+       return NULL;
+}
+
+static ESExpResult *
+do_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       d(fprintf (stderr, "setting flag\n"));
+       if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
+               p->info->flags |= camel_system_flag (argv[0]->value.string) | CAMEL_MESSAGE_FOLDER_FLAGGED;
+               camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Set %s flag", argv[0]->value.string);
+       }
+       
+       return NULL;
+}
+
+static CamelFolder *
+open_folder (CamelFilterDriver *driver, const char *folder_url)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       CamelFolder *camelfolder;
+       
+       /* we have a lookup table of currently open folders */
+       camelfolder = g_hash_table_lookup (p->folders, folder_url);
+       if (camelfolder)
+               return camelfolder;
+       
+       camelfolder = p->get_folder (driver, folder_url, p->data);
+       
+       if (camelfolder) {
+               g_hash_table_insert (p->folders, g_strdup (folder_url), camelfolder);
+               camel_folder_freeze (camelfolder);
+       }
+       
+       return camelfolder;
+}
+
+static void
+close_folder (void *key, void *value, void *data)
+{      
+       CamelFolder *folder = value;
+       CamelFilterDriver *driver = data;
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       g_free (key);
+       camel_folder_sync (folder, FALSE, p->ex);
+       camel_folder_thaw (folder);
+       camel_object_unref (CAMEL_OBJECT (folder));
+}
+
+/* flush/close all folders */
+static int
+close_folders (CamelFilterDriver *driver)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       g_hash_table_foreach (p->folders, close_folder, driver);
+       g_hash_table_destroy (p->folders);
+       p->folders = g_hash_table_new (g_str_hash, g_str_equal);
+       
+       /* FIXME: status from driver */
+       return 0;
+}
+
+#if 0
+static void
+free_key (gpointer key, gpointer value, gpointer user_data)
+{
+       g_free (key);
+}
+#endif
+
+
+static void
+camel_filter_driver_log (CamelFilterDriver *driver, enum filter_log_t status, const char *desc, ...)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       
+       if (p->logfile) {
+               char *str = NULL;
+               
+               if (desc) {
+                       va_list ap;
+                       
+                       va_start (ap, desc);
+                       str = g_strdup_vprintf (desc, ap);
+               }
+               
+               switch (status) {
+               case FILTER_LOG_START: {
+                       /* write log header */
+                       const char *subject = NULL;
+                       char *fromstr;
+                       const CamelInternetAddress *from;
+                       char date[50];
+                       time_t t;
+
+                       /* FIXME: does this need locking?  Probably */
+
+                       from = camel_mime_message_get_from (p->message);
+                       fromstr = camel_address_format((CamelAddress *)from);
+                       subject = camel_mime_message_get_subject (p->message);
+                       
+                       time (&t);
+                       strftime (date, 49, "%a, %d %b %Y %H:%M:%S", localtime (&t));
+                       fprintf (p->logfile, "Applied filter \"%s\" to message from %s - \"%s\" at %s\n",
+                                str, fromstr ? fromstr : "unknown", subject ? subject : "", date);
+                       g_free(fromstr);
+                       break;
+               }
+               case FILTER_LOG_ACTION:
+                       fprintf (p->logfile, "Action: %s\n", str);
+                       break;
+               case FILTER_LOG_END:
+                       fprintf (p->logfile, "\n");
+                       break;
+               default:
+                       /* nothing else is loggable */
+                       break;
+               }
+               
+               g_free (str);
+       }
+}
+
+
+/* will filter only an mbox - is more efficient as it doesn't need to open the folder through camel directly */
+void
+camel_filter_driver_filter_mbox (CamelFilterDriver *driver, const char *mbox, const char *source, CamelException *ex)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       CamelMimeParser *mp = NULL;
+       char *source_url = NULL;
+       int fd = -1;
+       int i = 0;
+       struct stat st;
+       
+       fd = open (mbox, O_RDONLY);
+       if (fd == -1) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, "Unable to open spool folder");
+               goto fail;
+       }
+       /* to get the filesize */
+       fstat (fd, &st);
+       
+       mp = camel_mime_parser_new ();
+       camel_mime_parser_scan_from (mp, TRUE);
+       if (camel_mime_parser_init_with_fd (mp, fd) == -1) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, "Unable to process spool folder");
+               goto fail;
+       }
+       fd = -1;
+       
+       source_url = g_strdup_printf ("file://%s", mbox);
+       
+       while (camel_mime_parser_step (mp, 0, 0) == HSCAN_FROM) {
+               CamelMimeMessage *msg;
+               int pc = 0;
+               
+               if (st.st_size > 0)
+                       pc = (int)(100.0 * ((double)camel_mime_parser_tell (mp) / (double)st.st_size));
+               
+               report_status (driver, FILTER_STATUS_START, "Getting message %d (%d%% of file)", i, pc);
+               
+               msg = camel_mime_message_new ();
+               if (camel_mime_part_construct_from_parser (CAMEL_MIME_PART (msg), mp) == -1) {
+                       report_status (driver, FILTER_STATUS_END, "Failed message %d", i);
+                       camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, "Cannot open message");
+                       camel_object_unref (CAMEL_OBJECT (msg));
+                       goto fail;
+               }
+               
+               camel_filter_driver_filter_message (driver, msg, NULL, source_url, source, ex);
+               camel_object_unref (CAMEL_OBJECT (msg));
+               if (camel_exception_is_set (ex)) {
+                       report_status (driver, FILTER_STATUS_END, "Failed message %d", i);
+                       goto fail;
+               }
+               
+               report_status (driver, FILTER_STATUS_END, "Finished message %d", i);
+               i++;
+               
+               /* skip over the FROM_END state */
+               camel_mime_parser_step (mp, 0, 0);
+       }
+       
+       if (p->defaultfolder)
+               camel_folder_sync (p->defaultfolder, FALSE, ex);
+       
+fail:
+       g_free (source_url);
+       if (fd != -1)
+               close (fd);
+       if (mp)
+               camel_object_unref (CAMEL_OBJECT (mp));
+}
+
+/* will filter a folder */
+void
+camel_filter_driver_filter_folder (CamelFilterDriver *driver, CamelFolder *folder, const char *source,
+                                  GPtrArray *uids, gboolean remove, CamelException *ex)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       int i;
+       int freeuids = FALSE;
+       CamelMimeMessage *message;
+       CamelMessageInfo *info;
+       char *source_url, *service_url;
+       
+       service_url = camel_service_get_url (CAMEL_SERVICE (camel_folder_get_parent_store (folder)));
+       source_url = g_strdup_printf ("%s%s", service_url, camel_folder_get_full_name (folder));
+       g_free (service_url);
+       
+       if (uids == NULL) {
+               uids = camel_folder_get_uids (folder);
+               freeuids = TRUE;
+       }
+       
+       for (i = 0; i < uids->len; i++) {
+               report_status (driver, FILTER_STATUS_START, "Getting message %d of %d", i+1, uids->len);
+               
+               message = camel_folder_get_message (folder, uids->pdata[i], ex);
+               if (camel_exception_is_set (ex)) {
+                       report_status (driver, FILTER_STATUS_END, "Failed at message %d of %d", i+1, uids->len);
+                       break;
+               }
+               
+               if (camel_folder_has_summary_capability (folder))
+                       info = camel_folder_get_message_info (folder, uids->pdata[i]);
+               else
+                       info = NULL;
+               
+               camel_filter_driver_filter_message (driver, message, info, source_url, source, ex);
+
+               if (camel_folder_has_summary_capability (folder))
+                       camel_folder_free_message_info (folder, info);
+
+               if (camel_exception_is_set (ex)) {
+                       report_status (driver, FILTER_STATUS_END, "Failed at message %d of %d", i+1, uids->len);
+                       break;
+               }
+               
+               if (remove)
+                       camel_folder_set_message_flags (folder, uids->pdata[i],
+                                                       CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
+               
+               camel_object_unref (CAMEL_OBJECT (message));
+       }
+       
+       if (freeuids)
+               camel_folder_free_uids (folder, uids);
+       
+       if (p->defaultfolder)
+               camel_folder_sync (p->defaultfolder, FALSE, ex);
+       
+       g_free (source_url);
+}
+
+void
+camel_filter_driver_filter_message (CamelFilterDriver *driver, CamelMimeMessage *message, CamelMessageInfo *info,
+                                   const char *source_url, const char *source, CamelException *ex)
+{
+       struct _CamelFilterDriverPrivate *p = _PRIVATE (driver);
+       ESExpResult *r;
+       struct _filter_rule *node;
+       gboolean freeinfo = FALSE;
+       gboolean filtered = FALSE;
+       
+       if (info == NULL) {
+               struct _header_raw *h = CAMEL_MIME_PART (message)->headers;
+               
+               info = camel_message_info_new_from_header (h);
+               freeinfo = TRUE;
+       } else {
+               if (info->flags & CAMEL_MESSAGE_DELETED)
+                       return;
+       }
+       
+       p->ex = ex;
+       p->terminated = FALSE;
+       p->deleted = FALSE;
+       p->copied = FALSE;
+       p->message = message;
+       p->info = info;
+
+       node = (struct _filter_rule *)p->rules.head;
+       while (node->next) {
+               d(fprintf (stderr, "applying rule %s\n action %s\n", node->match, node->action));
+               
+               if (camel_filter_search_match(p->message, p->info, source_url, node->match, p->ex)) {
+                       filtered = TRUE;
+                       camel_filter_driver_log (driver, FILTER_LOG_START, node->name);
+#ifndef NO_WARNINGS
+#warning "Must check expression parsed and executed properly?"
+#endif                 
+                       /* perform necessary filtering actions */
+                       e_sexp_input_text (p->eval, node->action, strlen (node->action));
+                       e_sexp_parse (p->eval);
+                       r = e_sexp_eval (p->eval);
+                       e_sexp_result_free (r);
+                       if (p->terminated)
+                               break;
+               }
+               node = node->next;
+       }
+       
+       /* Logic: if !Moved and there exists a default folder... */
+       if (!(p->copied && p->deleted) && p->defaultfolder) {
+               /* copy it to the default inbox */
+               filtered = TRUE;
+               camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Copy to default folder");
+               camel_folder_append_message (p->defaultfolder, p->message, p->info, p->ex);
+       }
+       
+       /* *Now* we can set the DELETED flag... */
+       if (p->deleted)
+               info->flags = info->flags | CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_FOLDER_FLAGGED;
+       
+       if (freeinfo)
+               camel_message_info_free (info);
+       
+       if (filtered)
+               camel_filter_driver_log (driver, FILTER_LOG_END, NULL);
+}
diff --git a/camel/camel-filter-driver.h b/camel/camel-filter-driver.h
new file mode 100644 (file)
index 0000000..0d3df60
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (C) 2000 Helix Code Inc.
+ *
+ *  Authors: Michael Zucchi <notzed@helixcode.com>
+ *           Jeffrey Stedfast <fejj@helixcode.com>
+ *
+ *  This program is free software; you can redistribute it and/or 
+ *  modify it under the terms of the GNU General Public License as 
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  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
+ */
+
+#ifndef _CAMEL_FILTER_DRIVER_H
+#define _CAMEL_FILTER_DRIVER_H
+
+#include <camel/camel-object.h>
+#include <camel/camel-session.h>
+#include <camel/camel-folder.h>
+
+#define CAMEL_FILTER_DRIVER_TYPE   (camel_filter_driver_get_type())
+#define CAMEL_FILTER_DRIVER(obj)         CAMEL_CHECK_CAST (obj, camel_filter_driver_get_type (), CamelFilterDriver)
+#define CAMEL_FILTER_DRIVER_CLASS(klass) CAMEL__CHECK_CLASS_CAST (klass, camel_filter_driver_get_type (), CamelFilterDriverClass)
+#define CAMEL_IS_FILTER_DRIVER(obj)      CAMEL_CHECK_TYPE (obj, camel_filter_driver_get_type ())
+
+typedef struct _CamelFilterDriver      CamelFilterDriver;
+typedef struct _CamelFilterDriverClass CamelFilterDriverClass;
+
+struct _CamelFilterDriver {
+       CamelObject parent;
+
+       struct _CamelFilterDriverPrivate *priv;
+};
+
+struct _CamelFilterDriverClass {
+       CamelObjectClass parent_class;
+};
+
+/* FIXME: this maybe should change... */
+/* type of status for a status report */
+enum filter_status_t {
+       FILTER_STATUS_NONE,
+       FILTER_STATUS_START,    /* start of new message processed */
+       FILTER_STATUS_ACTION,   /* an action performed */
+       FILTER_STATUS_PROGRESS, /* (an) extra update(s), if its taking longer to process */
+       FILTER_STATUS_END,      /* end of message */
+};
+
+typedef CamelFolder * (*FilterGetFolderFunc) (CamelFilterDriver *, const char *uri, void *data);
+/* report status */
+typedef void (FDStatusFunc)(CamelFilterDriver *driver, enum filter_status_t status, const char *desc, void *data);
+
+guint         camel_filter_driver_get_type (void);
+CamelFilterDriver  *camel_filter_driver_new     (FilterGetFolderFunc fetcher, void *data);
+
+/* modifiers */
+void    camel_filter_driver_set_logfile         (CamelFilterDriver *d, FILE *logfile);
+void   camel_filter_driver_set_status_func     (CamelFilterDriver *d, FDStatusFunc *func, void *data);
+void   camel_filter_driver_set_default_folder  (CamelFilterDriver *d, CamelFolder *def);
+void   camel_filter_driver_add_rule            (CamelFilterDriver *d, const char *name, const char *match, const char *action);
+
+/*void camel_filter_driver_set_global(CamelFilterDriver *, const char *name, const char *value);*/
+
+void    camel_filter_driver_filter_message      (CamelFilterDriver *driver, CamelMimeMessage *message, CamelMessageInfo *info,
+                                                const char *source_url, const char *source, CamelException *ex);
+void    camel_filter_driver_filter_mbox         (CamelFilterDriver *driver, const char *mbox, const char *source, CamelException *ex);
+void    camel_filter_driver_filter_folder       (CamelFilterDriver *driver, CamelFolder *folder, const char *source,
+                                                GPtrArray *uids, gboolean remove, CamelException *ex);
+
+#if 0
+/* generate the search query/action string for a filter option */
+void camel_filter_driver_expand_option (CamelFilterDriver *d, GString *s, GString *action, struct filter_option *op);
+
+/* get info about rules (options) */
+int camel_filter_driver_rule_count (CamelFilterDriver *d);
+struct filter_option *camel_filter_driver_rule_get (CamelFilterDriver *d, int n);
+#endif
+
+#endif /* ! _CAMEL_FILTER_DRIVER_H */
diff --git a/camel/camel-filter-search.c b/camel/camel-filter-search.c
new file mode 100644 (file)
index 0000000..5663bf6
--- /dev/null
@@ -0,0 +1,630 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *          Michael Zucchi <NotZed@Ximian.com>
+ *
+ *  Copyright 2000 Helix Code, Inc. (www.helixcode.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 the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* (from glibc headers:
+   POSIX says that <sys/types.h> must be included (by the caller) before <regex.h>.  */
+#include <sys/types.h>
+#include <regex.h>
+#include <string.h>
+#include <ctype.h>
+
+#warning "Fixme: remove gal/widgets/e-unicode dependency"
+#include <gal/widgets/e-unicode.h>
+
+#include "e-util/e-sexp.h"
+
+#include "camel-mime-message.h"
+#include "camel-filter-search.h"
+#include "camel-exception.h"
+#include "camel-multipart.h"
+#include "camel-stream-mem.h"
+
+#define d(x)
+
+typedef struct {
+       CamelMimeMessage *message;
+       CamelMessageInfo *info;
+       const char *source;
+       CamelException *ex;
+} FilterMessageSearch;
+
+/* ESExp callbacks */
+static ESExpResult *header_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_matches (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_starts_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_ends_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_exists (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_soundex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *header_full_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *match_all (struct _ESExp *f, int argc, struct _ESExpTerm **argv, FilterMessageSearch *fms);
+static ESExpResult *body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *body_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *user_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *user_tag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *system_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *get_sent_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *get_received_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *get_current_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *get_score (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+static ESExpResult *get_source (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
+
+/* builtin functions */
+static struct {
+       char *name;
+       ESExpFunc *func;
+       int type;               /* set to 1 if a function can perform shortcut evaluation, or
+                                  doesn't execute everything, 0 otherwise */
+} symbols[] = {
+       { "match-all",          (ESExpFunc *) match_all,          1 },
+       { "body-contains",      (ESExpFunc *) body_contains,      0 },
+       { "body-regex",         (ESExpFunc *) body_regex,         0 },
+       { "header-contains",    (ESExpFunc *) header_contains,    0 },
+       { "header-matches",     (ESExpFunc *) header_matches,     0 },
+       { "header-starts-with", (ESExpFunc *) header_starts_with, 0 },
+       { "header-ends-with",   (ESExpFunc *) header_ends_with,   0 },
+       { "header-exists",      (ESExpFunc *) header_exists,      0 },
+       { "header-soundex",     (ESExpFunc *) header_soundex,     0 },
+       { "header-regex",       (ESExpFunc *) header_regex,       0 },
+       { "header-full-regex",  (ESExpFunc *) header_full_regex,  0 },
+       { "user-tag",           (ESExpFunc *) user_tag,           0 },
+       { "user-flag",          (ESExpFunc *) user_flag,          0 },
+       { "system-flag",        (ESExpFunc *) system_flag,        0 },
+       { "get-sent-date",      (ESExpFunc *) get_sent_date,      0 },
+       { "get-received-date",  (ESExpFunc *) get_received_date,  0 },
+       { "get-current-date",   (ESExpFunc *) get_current_date,   0 },
+       { "get-score",          (ESExpFunc *) get_score,          0 },
+       { "get-source",         (ESExpFunc *) get_source,         0 },
+};
+
+/* builds the regex into pattern */
+/* taken from camel-folder-search, with added isregex & exception parameter */
+/* Basically, we build a new regex, either based on subset regex's, or substrings,
+   that can be executed once over the whoel body, to match anything suitable.
+   This is more efficient than multiple searches, and probably most (naive) strstr
+   implementations, over long content.
+
+   A small issue is that case-insenstivity wont work entirely correct for utf8 strings. */
+static int
+build_match_regex(regex_t *pattern, int isregex, int argc, struct _ESExpResult **argv, CamelException *ex)
+{
+       GString *match = g_string_new("");
+       int c, i, count=0, err;
+       char *word;
+
+       /* build a regex pattern we can use to match the words, we OR them together */
+       if (argc>1)
+               g_string_append_c(match, '(');
+       for (i=0;i<argc;i++) {
+               if (argv[i]->type == ESEXP_RES_STRING) {
+                       if (count > 0)
+                               g_string_append_c(match, '|');
+                       /* escape any special chars (not sure if this list is complete) */
+                       word = argv[i]->value.string;
+                       if (isregex) {
+                               g_string_append(match, word);
+                       } else {
+                               while ((c = *word++)) {
+                                       if (strchr("*\\.()[]^$+", c) != NULL) {
+                                               g_string_append_c(match, '\\');
+                                       }
+                                       g_string_append_c(match, c);
+                               }
+                       }
+                       count++;
+               } else {
+                       g_warning("Invalid type passed to body-contains match function");
+               }
+       }
+       if (argc>1)
+               g_string_append_c(match, ')');
+       err = regcomp(pattern, match->str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
+       if (err != 0) {
+               /* regerror gets called twice to get the full error string 
+                  length to do proper posix error reporting */
+               int len = regerror(err, pattern, 0, 0);
+               char *buffer = g_malloc0(len + 1);
+
+               regerror(err, pattern, buffer, len);
+               camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+                                    _("Regular expression compilation failed: %s: %s"),
+                                    match->str, buffer);
+
+               regfree(pattern);
+       }
+       d(printf("Built regex: '%s'\n", match->str));
+       g_string_free(match, TRUE);
+       return err;
+}
+
+static unsigned char soundex_table[256] = {
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0, 49, 50, 51,  0, 49, 50,  0,  0, 50, 50, 52, 53, 53,  0,
+        49, 50, 54, 50, 51,  0, 49,  0, 50,  0, 50,  0,  0,  0,  0,  0,
+         0,  0, 49, 50, 51,  0, 49, 50,  0,  0, 50, 50, 52, 53, 53,  0,
+        49, 50, 54, 50, 51,  0, 49,  0, 50,  0, 50,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+};
+
+static void
+soundexify (const gchar *sound, gchar code[5])
+{
+       guchar *c, last = '\0';
+       gint n;
+       
+       for (c = (guchar *) sound; *c && !isalpha (*c); c++);
+       code[0] = toupper (*c);
+       memset (code + 1, '0', 3);
+       for (n = 1; *c && n < 5; c++) {
+               guchar ch = soundex_table[*c];
+               
+               if (ch && ch != last) {
+                       code[n++] = ch;
+                       last = ch;
+               }
+       }
+       code[4] = '\0';
+}
+
+static gint
+soundexcmp (const gchar *sound1, const gchar *sound2)
+{
+       gchar code1[5], code2[5];
+       
+       soundexify (sound1, code1);
+       soundexify (sound2, code2);
+       
+       return strcmp (code1, code2);
+}
+
+static gboolean check_match(const char *value, const char *match, int how)
+{
+       const char *p;
+
+       while (*value && isspace(*value))
+               value++;
+
+       if (strlen(value) < strlen(match))
+               return FALSE;
+
+       /* from dan the man, if we have mixed case, perform a case-sensitive match,
+          otherwise not */
+       p = match;
+       while (*p) {
+               if (isupper(*p)) {
+                       switch(how) {
+                       case 0: /* is */
+                               return strcmp(value, match) == 0;
+                       case 1: /* contains */
+                               return strstr(value, match) != NULL;
+                       case 2: /* starts with */
+                               return strncmp(value, match, strlen(match)) == 0;
+                       case 3: /* ends with */
+                               return strcmp(value+strlen(value)-strlen(match), match) == 0;
+                       case 4: /* soundex */
+                               return soundexcmp(value, match) == 0;
+                       }
+                       return FALSE;
+               }
+               p++;
+       }
+       switch(how) {
+       case 0: /* is */
+               return strcasecmp(value, match) == 0;
+       case 1: /* contains */
+               return e_utf8_strstrcase(value, match) != NULL;
+       case 2: /* starts with */
+               return strncasecmp(value, match, strlen(match)) == 0;
+       case 3: /* ends with */
+               return strcasecmp(value+strlen(value)-strlen(match), match) == 0;
+       case 4: /* soundex */
+               return soundexcmp(value, match) == 0;
+       }
+
+       return FALSE;
+}
+
+static ESExpResult *
+check_header(struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms, int how)
+{
+       gboolean matched = FALSE;
+       ESExpResult *r;
+       int i;
+
+       if (argc > 1 && argv[0]->type == ESEXP_RES_STRING) {
+               const char *header = camel_medium_get_header (CAMEL_MEDIUM (fms->message), argv[0]->value.string);
+
+               if (header) {
+                       for (i=1;i<argc && !matched;i++) {
+                               if (argv[i]->type == ESEXP_RES_STRING
+                                   && check_match(header, argv[i]->value.string, how)) {
+                                       matched = TRUE;
+                                       break;
+                               }
+                       }
+               }
+       }
+       
+       r = e_sexp_result_new (ESEXP_RES_BOOL);
+       r->value.bool = matched;
+       
+       return r;
+}
+
+static ESExpResult *
+header_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       return check_header(f, argc, argv, fms, 1);
+}
+
+
+static ESExpResult *
+header_matches (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       return check_header(f, argc, argv, fms, 0);
+}
+
+static ESExpResult *
+header_starts_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       return check_header(f, argc, argv, fms, 2);
+}
+
+static ESExpResult *
+header_ends_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       return check_header(f, argc, argv, fms, 3);
+}
+
+static ESExpResult *
+header_soundex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       return check_header(f, argc, argv, fms, 4);
+}
+
+static ESExpResult *
+header_exists (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       gboolean matched = FALSE;
+       ESExpResult *r;
+       int i;
+
+       for (i=0;i<argc && !matched;i++) {
+               if (argv[i]->type == ESEXP_RES_STRING)
+                       matched = camel_medium_get_header (CAMEL_MEDIUM (fms->message), argv[i]->value.string) != NULL;
+       }
+       
+       r = e_sexp_result_new (ESEXP_RES_BOOL);
+       r->value.bool = matched;
+       
+       return r;
+}
+
+static ESExpResult *
+header_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r = e_sexp_result_new (ESEXP_RES_BOOL);
+       regex_t pattern;
+       const char *contents;
+
+       if (argc>1
+           && argv[0]->type == ESEXP_RES_STRING
+           && (contents = camel_medium_get_header (CAMEL_MEDIUM (fms->message), argv[0]->value.string))
+           && build_match_regex(&pattern, TRUE, argc-1, argv+1, fms->ex) == 0) {
+               r->value.bool = regexec(&pattern, contents, 0, NULL, 0) == 0;
+               regfree(&pattern);
+       } else
+               r->value.bool = FALSE;
+       
+       return r;
+}
+
+static gchar *
+get_full_header (CamelMimeMessage *message)
+{
+       CamelMimePart *mp = CAMEL_MIME_PART (message);
+       GString *str = g_string_new ("");
+       char   *ret;
+       struct _header_raw *h;
+       
+       for (h = mp->headers; h; h = h->next) {
+               if (h->value != NULL) {
+                       g_string_append(str, h->name);
+                       if (isspace(h->value[0]))
+                               g_string_append(str, ":");
+                       else
+                               g_string_append(str, ": ");
+                       g_string_append(str, h->value);
+               }
+       }
+       
+       ret = str->str;
+       g_string_free (str, FALSE);
+       
+       return ret;
+}
+
+static ESExpResult *
+header_full_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r = e_sexp_result_new (ESEXP_RES_BOOL);
+       regex_t pattern;
+       char *contents;
+
+       if (build_match_regex(&pattern, TRUE, argc, argv, fms->ex) == 0) {
+               contents = get_full_header (fms->message);
+               r->value.bool = regexec(&pattern, contents, 0, NULL, 0) == 0;
+               g_free(contents);
+               regfree(&pattern);
+       } else
+               r->value.bool = FALSE;
+       
+       return r;
+}
+
+static ESExpResult *
+match_all (struct _ESExp *f, int argc, struct _ESExpTerm **argv, FilterMessageSearch *fms)
+{
+       /* match-all: when dealing with single messages is a no-op */
+       ESExpResult *r;
+       
+       if (argc > 0)
+               return e_sexp_term_eval(f, argv[0]);
+
+       r = e_sexp_result_new (ESEXP_RES_BOOL);
+       r->value.bool = FALSE;
+
+       return r;
+}
+
+/* performs a 'slow' content-based match */
+/* taken directly from camel-folder-search.c */
+static gboolean
+message_body_contains(CamelDataWrapper *object, regex_t *pattern)
+{
+       CamelDataWrapper *containee;
+       int truth = FALSE;
+       int parts, i;
+
+       containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));
+
+       if (containee == NULL)
+               return FALSE;
+
+       /* TODO: I find it odd that get_part and get_content_object do not
+          add a reference, probably need fixing for multithreading */
+
+       /* using the object types is more accurate than using the mime/types */
+       if (CAMEL_IS_MULTIPART(containee)) {
+               parts = camel_multipart_get_number(CAMEL_MULTIPART(containee));
+               for (i=0;i<parts && truth==FALSE;i++) {
+                       CamelDataWrapper *part = (CamelDataWrapper *)camel_multipart_get_part(CAMEL_MULTIPART(containee), i);
+                       if (part) {
+                               truth = message_body_contains(part, pattern);
+                       }
+               }
+       } else if (CAMEL_IS_MIME_MESSAGE(containee)) {
+               /* for messages we only look at its contents */
+               truth = message_body_contains((CamelDataWrapper *)containee, pattern);
+       } else if (header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "text", "*")) {
+               /* for all other text parts, we look inside, otherwise we dont care */
+               CamelStreamMem *mem = (CamelStreamMem *)camel_stream_mem_new();
+
+               camel_data_wrapper_write_to_stream(containee, (CamelStream *)mem);
+               camel_stream_write((CamelStream *)mem, "", 1);
+               truth = regexec(pattern, mem->buffer->data, 0, NULL, 0) == 0;
+               camel_object_unref((CamelObject *)mem);
+       }
+       return truth;
+}
+
+static ESExpResult *
+body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r = e_sexp_result_new (ESEXP_RES_BOOL);
+       regex_t pattern;
+
+       if (build_match_regex(&pattern, FALSE, argc, argv, fms->ex) == 0) {
+               r->value.bool = message_body_contains((CamelDataWrapper *)fms->message, &pattern);
+               regfree(&pattern);
+       } else
+               r->value.bool = FALSE;
+       
+       return r;
+}
+
+static ESExpResult *
+body_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r = e_sexp_result_new (ESEXP_RES_BOOL);
+       regex_t pattern;
+
+       if (build_match_regex(&pattern, TRUE, argc, argv, fms->ex) == 0) {
+               r->value.bool = message_body_contains((CamelDataWrapper *)fms->message, &pattern);
+               regfree(&pattern);
+       } else
+               r->value.bool = FALSE;
+       
+       return r;
+}
+
+static ESExpResult *
+user_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       gboolean truth = FALSE;
+       int i;
+       
+       /* performs an OR of all words */
+       for (i = 0; i < argc && !truth; i++) {
+               if (argv[i]->type == ESEXP_RES_STRING
+                   && camel_flag_get (&fms->info->user_flags, argv[i]->value.string)) {
+                       truth = TRUE;
+                       break;
+               }
+       }
+       
+       r = e_sexp_result_new (ESEXP_RES_BOOL);
+       r->value.bool = truth;
+       
+       return r;
+}
+
+static ESExpResult *
+system_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       gboolean truth = FALSE;
+       
+       if (argc == 1)
+               truth = camel_system_flag_get (fms->info->flags, argv[0]->value.string);
+       
+       r = e_sexp_result_new (ESEXP_RES_BOOL);
+       r->value.bool = truth;
+       
+       return r;
+}
+
+static ESExpResult *
+user_tag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       const char *tag;
+       
+       tag = camel_tag_get (&fms->info->user_tags, argv[0]->value.string);
+       
+       r = e_sexp_result_new (ESEXP_RES_STRING);
+       r->value.string = g_strdup (tag ? tag : "");
+       
+       return r;
+}
+
+static ESExpResult *
+get_sent_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       
+       r = e_sexp_result_new(ESEXP_RES_INT);
+       r->value.number = camel_mime_message_get_date(fms->message, NULL);
+       
+       return r;
+}
+
+static ESExpResult *
+get_received_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       
+       r = e_sexp_result_new(ESEXP_RES_INT);
+       r->value.number = camel_mime_message_get_date_received(fms->message, NULL);
+       
+       return r;
+}
+
+static ESExpResult *
+get_current_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       
+       r = e_sexp_result_new (ESEXP_RES_INT);
+       r->value.number = time (NULL);
+       
+       return r;
+}
+
+static ESExpResult *
+get_score (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       const char *tag;
+       
+       tag = camel_tag_get (&fms->info->user_tags, "score");
+       
+       r = e_sexp_result_new (ESEXP_RES_INT);
+       if (tag)
+               r->value.number = atoi (tag);
+       else
+               r->value.number = 0;
+       
+       return r;
+}
+
+static ESExpResult *
+get_source (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+       ESExpResult *r;
+       
+       r = e_sexp_result_new (ESEXP_RES_STRING);
+       r->value.string = g_strdup (fms->source);
+       
+       return r;
+}
+
+gboolean camel_filter_search_match(CamelMimeMessage *message, CamelMessageInfo *info,
+                                  const char *source, const char *expression, CamelException *ex)
+{
+       FilterMessageSearch fms;
+       ESExp *sexp;
+       ESExpResult *result;
+       gboolean retval;
+       int i;
+       
+       fms.message = message;
+       fms.info = info;
+       fms.source = source;
+       fms.ex = ex;
+       
+       sexp = e_sexp_new ();
+       
+       for (i = 0; i < sizeof (symbols) / sizeof (symbols[0]); i++) {
+               if (symbols[i].type == 1)
+                       e_sexp_add_ifunction (sexp, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, &fms);
+               else
+                       e_sexp_add_function (sexp, 0, symbols[i].name, symbols[i].func, &fms);
+       }
+       
+       e_sexp_input_text (sexp, expression, strlen (expression));
+       e_sexp_parse (sexp);
+       result = e_sexp_eval (sexp);
+       
+       if (result->type == ESEXP_RES_BOOL)
+               retval = result->value.bool;
+       else
+               retval = FALSE;
+       
+       e_sexp_unref(sexp);
+       e_sexp_result_free (result);
+       
+       return retval;
+}
diff --git a/camel/camel-filter-search.h b/camel/camel-filter-search.h
new file mode 100644 (file)
index 0000000..0dba92e
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *          Michael Zucchi <NotZed@Ximian.com>
+ *
+ *  Copyright 2000 Helix Code, Inc. (www.helixcode.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 the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CAMEL_FILTER_SEARCH_H
+#define CAMEL_FILTER_SEARCH_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <glib.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-folder-summary.h>
+
+gboolean camel_filter_search_match(CamelMimeMessage *message, CamelMessageInfo *info,
+                                  const char *source, const char *expression, CamelException *ex); 
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! CAMEL_FILTER_SEARCH_H */
index bc9e13c..c1023b5 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/types.h>
 #include <regex.h>
 
+#warning "Fixme: remove gal/widgets/e-unicode dependency"
 #include <gal/widgets/e-unicode.h>
 #include "camel-folder-search.h"
 #include "string-utils.h"
@@ -800,6 +801,7 @@ g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup)
 }
 
 /* performs a 'slow' content-based match */
+/* there is also an identical copy of this in camel-filter-search.c */
 static gboolean
 message_body_contains(CamelDataWrapper *object, regex_t *pattern)
 {
index c948d68..52c9a4a 100644 (file)
@@ -347,6 +347,55 @@ camel_folder_summary_index(CamelFolderSummary *s, int i)
 }
 
 /**
+ * camel_folder_summary_index:
+ * @s: 
+ * @i: 
+ * 
+ * Obtain a copy of the summary array.  This is done atomically,
+ * so cannot contain empty entries.
+ *
+ * It must be freed using camel_folder_summary_array_free().
+ **/
+GPtrArray *
+camel_folder_summary_array(CamelFolderSummary *s)
+{
+       CamelMessageInfo *info;
+       GPtrArray *res = g_ptr_array_new();
+
+       CAMEL_SUMMARY_LOCK(s, ref_lock);
+       CAMEL_SUMMARY_LOCK(s, summary_lock);
+
+       g_ptr_array_set_size(res, s->messages->len);
+       for (i=0;i<s->messages->len;i++) {
+               info = res->pdata[i] = g_ptr_array_index(s->messages, i);
+               info->refcount++;
+       }
+
+       CAMEL_SUMMARY_UNLOCK(s, summary_lock);
+       CAMEL_SUMMARY_UNLOCK(s, ref_lock);
+
+       return res;
+}
+
+/**
+ * camel_folder_summary_array_free:
+ * @s: 
+ * @array: 
+ * 
+ * Free the folder summary array.
+ **/
+void
+camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array)
+{
+       int i;
+
+       for (i=0;i<array->len;i++)
+               camel_folder_summary_info_free(s, array->pdata[i]);
+
+       g_ptr_array_free(array, TRUE);
+}
+
+/**
  * camel_folder_summary_uid:
  * @s: 
  * @uid: 
index 4d07951..860b9cb 100644 (file)
@@ -247,6 +247,8 @@ void camel_folder_summary_clear(CamelFolderSummary *s);
 int camel_folder_summary_count(CamelFolderSummary *);
 CamelMessageInfo *camel_folder_summary_index(CamelFolderSummary *, int);
 CamelMessageInfo *camel_folder_summary_uid(CamelFolderSummary *, const char *uid);
+GPtrArray *camel_folder_summary_array(CamelFolderSummary *s);
+void camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array);
 
 /* summary formatting utils */
 char *camel_folder_summary_format_address(struct _header_raw *h, const char *name);
index 682a490..17d6509 100644 (file)
@@ -927,14 +927,9 @@ get_summary(CamelFolder *folder)
        GPtrArray *res = g_ptr_array_new();
        int i, count;
 
-       g_return_val_if_fail(folder->summary != NULL, res);
+       g_assert(folder->summary != NULL);
 
-       count = camel_folder_summary_count(folder->summary);
-       g_ptr_array_set_size(res, count);
-       for (i=0;i<count;i++)
-               res->pdata[i] = camel_folder_summary_index(folder->summary, i);
-
-       return res;
+       return camel_folder_summary_array(folder->summary);
 }
 
 /**
@@ -964,12 +959,9 @@ free_summary(CamelFolder *folder, GPtrArray *summary)
 {
        int i;
 
-       g_return_if_fail(folder->summary != NULL);
+       g_assert(folder->summary != NULL);
 
-       for (i=0;i<summary->len;i++)
-               camel_folder_summary_info_free(folder->summary, summary->pdata[i]);
-
-       g_ptr_array_free(summary, TRUE);
+       camel_folder_summary_array_free(folder->summary, summary);
 }
 
 /**
@@ -1095,6 +1087,8 @@ copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest, CamelE
  * This copies a message from one folder to another. If the @source and
  * @dest folders have the same parent_store, this may be more efficient
  * than a camel_folder_append_message().
+ *
+ * This function is still depcreated, it is not the same as move_message_to.
  **/
 void
 camel_folder_copy_message_to (CamelFolder *source, const char *uid,
index 1c3f108..d32ec54 100644 (file)
@@ -43,6 +43,8 @@
 
 #include "camel-private.h"
 
+#define d(x)
+
 static CamelObjectClass *parent_class;
 
 static void
@@ -641,7 +643,7 @@ void camel_cancel_cancel(CamelCancel *cc)
                        CAMEL_ACTIVE_UNLOCK();
                }
        } else if ((cc->flags & CAMEL_CANCEL_CANCELLED) == 0) {
-               printf("cancelling thread %d\n", cc->id);
+               d(printf("cancelling thread %d\n", cc->id));
 
                CAMEL_CANCEL_LOCK(cc);
                msg = g_malloc0(sizeof(*msg));
@@ -671,7 +673,7 @@ void camel_cancel_register(CamelCancel *cc)
        cc->id = id;
        g_hash_table_insert(cancel_active, (void *)id, cc);
 
-       printf("registering thread %d for cancellation\n", id);
+       d(printf("registering thread %d for cancellation\n", id));
 
        CAMEL_ACTIVE_UNLOCK();
 
@@ -698,8 +700,7 @@ void camel_cancel_unregister(CamelCancel *cc)
 
        CAMEL_ACTIVE_UNLOCK();
 
-       if (cc)
-               printf("unregistering thread %d for cancellation\n", cc->id);
+       d({if (cc) printf("unregistering thread %d for cancellation\n", cc->id)});
 
        if (cc)
                camel_cancel_unref(cc);
@@ -710,7 +711,7 @@ gboolean camel_cancel_check(CamelCancel *cc)
 {
        CamelCancelMsg *msg;
 
-       printf("checking for cancel in thread %d\n", pthread_self());
+       d(printf("checking for cancel in thread %d\n", pthread_self()));
 
        if (cc == NULL) {
                if (cancel_active) {
@@ -723,18 +724,18 @@ gboolean camel_cancel_check(CamelCancel *cc)
        }
 
        if (cc->blocked > 0) {
-               printf("ahah!  cancellation is blocked\n");
+               d(printf("ahah!  cancellation is blocked\n"));
                return FALSE;
        }
 
        if (cc->flags & CAMEL_CANCEL_CANCELLED) {
-               printf("previously cancelled\n");
+               d(printf("previously cancelled\n"));
                return TRUE;
        }
 
        msg = (CamelCancelMsg *)e_msgport_get(cc->cancel_port);
        if (msg) {
-               printf("Got cancellation message\n");
+               d(printf("Got cancellation message\n"));
                CAMEL_CANCEL_LOCK(cc);
                cc->flags |= CAMEL_CANCEL_CANCELLED;
                CAMEL_CANCEL_UNLOCK(cc);
index 4401d31..f6dd852 100644 (file)
@@ -16,12 +16,14 @@ check_PROGRAMS =    \
        test1   test4   test5   \
        test2   test6   test7   \
        test3                   \
-       test8
+       test8                   \
+       test9
 
 TESTS = test1 test4 test5 \
        test2 test6 test7 \
        test3             \
-       test8
+       test8             \
+       test9
 
 
 
index e02308d..4fed421 100644 (file)
@@ -8,4 +8,4 @@ test6   basic folder operations, IMAP
 test7  basic folder operations, NNTP
 
 test8  multithreaded folder torture test, local
-
+test9  filtering
index 30552cf..5665f96 100644 (file)
@@ -107,6 +107,7 @@ worker(void *d)
                content = g_strdup_printf("Test message %d contents\n\n", id+i);
                test_message_compare_content(camel_medium_get_content_object((CamelMedium *)msg), content, strlen(content));
                test_free(content);
+               pull();
 
                push("deleting message, cleanup");
                j=(100.0*rand()/(RAND_MAX+1.0));
diff --git a/camel/tests/folder/test9.c b/camel/tests/folder/test9.c
new file mode 100644 (file)
index 0000000..68c761c
--- /dev/null
@@ -0,0 +1,176 @@
+/* folder/index testing */
+
+#include "camel-test.h"
+#include "messages.h"
+#include "folders.h"
+
+#include "camel/camel-exception.h"
+#include "camel/camel-service.h"
+#include "camel/camel-session.h"
+#include "camel/camel-store.h"
+
+#include "camel/camel-folder.h"
+#include "camel/camel-folder-summary.h"
+#include "camel/camel-mime-message.h"
+#include "camel/camel-filter-driver.h"
+#include "camel/camel-stream-fs.h"
+
+#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0]))
+
+
+/* god, who designed this horrid interface */
+static char *auth_callback(CamelAuthCallbackMode mode,
+                          char *data, gboolean secret,
+                          CamelService *service, char *item,
+                          CamelException *ex)
+{
+       return NULL;
+}
+
+struct {
+       char *name;
+       CamelFolder *folder;
+} mailboxes[] = {
+       { "INBOX", NULL },
+       { "folder1", NULL },
+       { "folder2", NULL },
+       { "folder3", NULL },
+       { "folder4", NULL },
+};
+
+struct {
+       char *name, *match, *action;
+} rules[] = {
+       { "empty1", "(match-all (header-contains \"Frobnitz\"))", "(copy-to \"folder1\")" },
+       { "empty2", "(header-contains \"Frobnitz\")", "(copy-to \"folder2\")" },
+       { "count11", "(and (header-contains \"subject\" \"Test1\") (header-contains \"subject\" \"subject\"))", "(move-to \"folder3\")" },
+       { "empty3", "(and (header-contains \"subject\" \"Test1\") (header-contains \"subject\" \"subject\"))", "(move-to \"folder4\")" },
+       { "count1", "(body-contains \"data50\")", "(copy-to \"folder1\")" },
+       { "stop", "(body-contains \"data2\")", "(stop)" },
+       { "notreached1", "(body-contains \"data2\")", "(move-to \"folder2\")" },
+       { "count1", "(body-contains \"data3\")", "(move-to \"folder2\")" },
+};
+
+
+static CamelFolder *get_folder(CamelFilterDriver *d, const char *uri, void *data)
+{
+       int i;
+
+       for (i=0;i<ARRAY_LEN(mailboxes);i++)
+               if (!strcmp(mailboxes[i].name, uri)) {
+                       camel_object_ref((CamelObject *)mailboxes[i].folder);
+                       return mailboxes[i].folder;
+               }
+       return NULL;
+}
+
+int main(int argc, char **argv)
+{
+       CamelSession *session;
+       CamelStore *store;
+       CamelException *ex;
+       CamelFolder *folder;
+       CamelMimeMessage *msg;
+       int i, j;
+       CamelStream *mbox;
+       CamelFilterDriver *driver;
+
+       gtk_init(&argc, &argv);
+
+       camel_test_init(argc, argv);
+
+       ex = camel_exception_new();
+
+       /* clear out any camel-test data */
+       system("/bin/rm -rf /tmp/camel-test");
+
+       camel_test_start("Simple filtering of mbox");
+
+       session = camel_session_new("/tmp/camel-test", auth_callback, NULL, NULL);
+
+       /* todo: cross-check everything with folder_info checks as well */
+       /* todo: work out how to do imap/pop/nntp tests */
+
+       push("getting store");
+       store = camel_session_get_store(session, "mbox:///tmp/camel-test/mbox", ex);
+       check_msg(!camel_exception_is_set(ex), "getting store: %s", camel_exception_get_description(ex));
+       check(store != NULL);
+       pull();
+
+       push("Creating output folders");
+       for (i=0;i<ARRAY_LEN(mailboxes);i++) {
+               push("creating %s", mailboxes[i].name);
+               mailboxes[i].folder = folder = camel_store_get_folder(store, mailboxes[i].name, CAMEL_STORE_FOLDER_CREATE, ex);
+               check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex));
+               check(folder != NULL);
+               
+               /* we need an empty folder for this to work */
+               test_folder_counts(folder, 0, 0);
+               pull();
+       }
+       pull();
+
+       /* append a bunch of messages with specific content */
+       push("creating 100 test message mbox");
+       mbox = camel_stream_fs_new_with_name("/tmp/camel-test/inbox", O_WRONLY|O_CREAT|O_EXCL, 0600);
+       for (j=0;j<100;j++) {
+               char *content, *subject;
+                       
+               push("creating test message");
+               msg = test_message_create_simple();
+               content = g_strdup_printf("data%d content\n", j);
+               test_message_set_content_simple((CamelMimePart *)msg, 0, "text/plain",
+                                               content, strlen(content));
+               test_free(content);
+               subject = g_strdup_printf("Test%d message%d subject", j, 100-j);
+               camel_mime_message_set_subject(msg, subject);
+               
+               camel_mime_message_set_date(msg, j*60*24, 0);
+               pull();
+               
+               camel_stream_printf(mbox, "From \n");
+               check(camel_data_wrapper_write_to_stream((CamelDataWrapper *)msg, mbox) != -1);
+#if 0          
+               push("appending simple message %d", j);
+               camel_folder_append_message(folder, msg, NULL, ex);
+               check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex));
+               pull();
+#endif                         
+               test_free(subject);
+               
+               check_unref(msg, 1);
+       }
+       check(camel_stream_close(mbox) != -1);
+       check_unref(mbox, 1);
+       pull();
+
+       push("Building filters");
+       driver = camel_filter_driver_new(get_folder, NULL);
+       for (i=0;i<ARRAY_LEN(rules);i++) {
+               camel_filter_driver_add_rule(driver, rules[i].name, rules[i].match, rules[i].action);
+       }
+       pull();
+
+       push("Executing filters");
+       camel_filter_driver_set_default_folder(driver, mailboxes[0].folder);
+       camel_filter_driver_filter_mbox(driver, "/tmp/camel-test/inbox", "", ex);
+       check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex));
+
+       /* now need to check the folder counts/etc */
+
+       check_unref(driver, 1);
+       pull();
+
+       for (i=0;i<ARRAY_LEN(mailboxes);i++) {
+               check_unref(mailboxes[i].folder, 1);
+       }
+
+       check_unref(store, 1);
+
+       check_unref(session, 1);
+       camel_exception_free(ex);
+
+       camel_test_end();
+
+       return 0;
+}
index e6a7c29..a61f949 100644 (file)
@@ -7,6 +7,7 @@
 
 #ifdef ENABLE_THREADS
 #include <pthread.h>
+#include <unistd.h>
 #endif
 
 #ifdef ENABLE_THREADS
@@ -74,6 +75,13 @@ static void die(int sig)
                indie = 1;
                printf("\n\nReceived fatal signal %d\n", sig);
                g_hash_table_foreach(info_table, (GHFunc)dump_action, 0);
+
+#ifdef ENABLE_THREADS
+               if (camel_test_verbose > 2) {
+                       printf("Attach debugger to pid %d to debug\n", getpid());
+                       sleep(1000);
+               }
+#endif
        }
 
        _exit(1);
index 78ac7b3..0a9683d 100644 (file)
@@ -30,8 +30,8 @@ int main(int argc, char **argv)
        mp = camel_multipart_new();
 
        /* Hrm, this should be able to set its own boundary, no? */
-       camel_multipart_set_boundary(mp, "_=,.XYZ Kangaroo Meat is ! ABADF00D");
-       check(strcmp(camel_multipart_get_boundary(mp), "_=,.XYZ Kangaroo Meat is ! ABADF00D") == 0);
+       camel_multipart_set_boundary(mp, "_=,.XYZ_Kangaroo_Meat_is_!_ABADF00D");
+       check(strcmp(camel_multipart_get_boundary(mp), "_=,.XYZ_Kangaroo_Meat_is_!_ABADF00D") == 0);
 
        camel_medium_set_content_object((CamelMedium *)msg, (CamelDataWrapper *)mp);
        check(camel_multipart_get_number(mp) == 0);
@@ -110,7 +110,7 @@ int main(int argc, char **argv)
        check(CAMEL_IS_MULTIPART(mp2));
        check(camel_multipart_get_number(mp2) == 3);
 
-       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ Kangaroo Meat is ! ABADF00D") == 0);
+       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ_Kangaroo_Meat_is_!_ABADF00D") == 0);
        check(mp2->preface == NULL || strlen(mp2->preface) == 0);
 
        /* FIXME */
@@ -137,7 +137,7 @@ int main(int argc, char **argv)
        check(CAMEL_IS_MULTIPART(mp2));
        check(camel_multipart_get_number(mp2) == 3);
 
-       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ Kangaroo Meat is ! ABADF00D") == 0);
+       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ_Kangaroo_Meat_is_!_ABADF00D") == 0);
        check(mp2->preface == NULL || strlen(mp2->preface) == 0);
 
        /* FIXME */
@@ -174,7 +174,7 @@ int main(int argc, char **argv)
        check(CAMEL_IS_MULTIPART(mp2));
        check(camel_multipart_get_number(mp2) == 3);
 
-       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ Kangaroo Meat is ! ABADF00D") == 0);
+       check(strcmp(camel_multipart_get_boundary(mp2), "_=,.XYZ_Kangaroo_Meat_is_!_ABADF00D") == 0);
        check(strcmp(mp2->preface, "pre-text\nLines.") == 0);
        check(strcmp(mp2->postface, "post-text, no lines.\nOne line.\n") == 0);
        test_message_compare_content(camel_medium_get_content_object(CAMEL_MEDIUM(camel_multipart_get_part(mp2, 0))),