aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--filter/ChangeLog12
-rw-r--r--filter/filter-message-search.c144
-rw-r--r--filter/filtertypes.xml115
-rw-r--r--filter/libfilter-i18n.h8
4 files changed, 264 insertions, 15 deletions
diff --git a/filter/ChangeLog b/filter/ChangeLog
index 8d2f6cdcf8..f91063269a 100644
--- a/filter/ChangeLog
+++ b/filter/ChangeLog
@@ -1,5 +1,17 @@
2000-10-27 Jeffrey Stedfast <fejj@helixcode.com>
+ * filtertypes.xml: Added header-starts-with, header-ends-with, and
+ header-exists menu items.
+
+ * filter-message-search.c (header_starts_with): New callback to
+ match the beginnings of headers.
+ (header_ends_with): New callback to match the ends of headers.
+ (header_exists): New callback to determine if a header exists
+ which is useful when filtering out all those pesky bug-buddy
+ emails!
+
+2000-10-27 Jeffrey Stedfast <fejj@helixcode.com>
+
* filtertypes.xml: Add header-matches expressions ("is" / "is not").
* filter-message-search.c (header_matches): New callback to match
diff --git a/filter/filter-message-search.c b/filter/filter-message-search.c
index c8a85df71d..81955671d1 100644
--- a/filter/filter-message-search.c
+++ b/filter/filter-message-search.c
@@ -36,6 +36,9 @@ typedef struct {
/* 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_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);
@@ -55,19 +58,22 @@ static struct {
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, 0 },
- { "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-regex", (ESExpFunc *) header_regex, 0 },
- { "user-tag", (ESExpFunc *) user_tag, 0 },
- { "user-flag", (ESExpFunc *) user_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 },
+ { "match-all", (ESExpFunc *) match_all, 0 },
+ { "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-regex", (ESExpFunc *) header_regex, 0 },
+ { "user-tag", (ESExpFunc *) user_tag, 0 },
+ { "user-flag", (ESExpFunc *) user_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 },
};
static ESExpResult *
@@ -140,6 +146,118 @@ header_matches (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMe
}
static ESExpResult *
+header_starts_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+ gboolean matched = FALSE;
+ ESExpResult *r;
+
+ if (argc == 2) {
+ char *header = (argv[0])->value.string;
+ char *match = (argv[1])->value.string;
+ const char *contents;
+
+ contents = camel_medium_get_header (CAMEL_MEDIUM (fms->message), header);
+
+ if (contents) {
+ /* danw says to use search-engine style matching...
+ * This means that if the search match string is
+ * lowercase then compare case-insensitive else
+ * compare case-sensitive. */
+ gboolean is_lowercase = TRUE;
+ char *c;
+
+ for (c = match; *c; c++) {
+ if (isalpha (*c) && isupper (*c)) {
+ is_lowercase = FALSE;
+ break;
+ }
+ }
+
+ if (is_lowercase) {
+ if (g_strncasecmp (contents, match, strlen (match)))
+ matched = TRUE;
+ } else {
+ if (strncmp (contents, match, strlen (match)))
+ matched = TRUE;
+ }
+ }
+ }
+
+ r = e_sexp_result_new (ESEXP_RES_BOOL);
+ r->value.bool = matched;
+
+ return r;
+}
+
+static ESExpResult *
+header_ends_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+ gboolean matched = FALSE;
+ ESExpResult *r;
+
+ if (argc == 2) {
+ char *header = (argv[0])->value.string;
+ char *match = (argv[1])->value.string;
+ const char *contents;
+
+ contents = camel_medium_get_header (CAMEL_MEDIUM (fms->message), header);
+
+ if (contents && strlen (contents) >= strlen (match)) {
+ /* danw says to use search-engine style matching...
+ * This means that if the search match string is
+ * lowercase then compare case-insensitive else
+ * compare case-sensitive. */
+ gboolean is_lowercase = TRUE;
+ char *c, *end;
+
+ for (c = match; *c; c++) {
+ if (isalpha (*c) && isupper (*c)) {
+ is_lowercase = FALSE;
+ break;
+ }
+ }
+
+ end = (char *) contents + strlen (contents) - strlen (match);
+
+ if (is_lowercase) {
+ if (g_strcasecmp (end, match))
+ matched = TRUE;
+ } else {
+ if (strcmp (end, match))
+ matched = TRUE;
+ }
+ }
+ }
+
+ r = e_sexp_result_new (ESEXP_RES_BOOL);
+ r->value.bool = matched;
+
+ return r;
+}
+
+static ESExpResult *
+header_exists (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
+{
+ gboolean matched = FALSE;
+ ESExpResult *r;
+
+ if (argc == 1) {
+ char *header = (argv[0])->value.string;
+ const char *contents;
+
+ contents = camel_medium_get_header (CAMEL_MEDIUM (fms->message), header);
+
+ if (contents)
+ matched = TRUE;
+ }
+
+ 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)
{
gboolean matched = FALSE;
diff --git a/filter/filtertypes.xml b/filter/filtertypes.xml
index f1aa389ab1..38723dbf98 100644
--- a/filter/filtertypes.xml
+++ b/filter/filtertypes.xml
@@ -28,6 +28,31 @@
(match-all (not (header-matches "From" ${sender})))
</code>
</option>
+
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "From" ${sender})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "From" ${sender})))
+ </code>
+ </option>
<option value="matches regex">
<title>matches regex</title>
<code>
@@ -77,6 +102,36 @@
(header-matches "Cc" ${recipient}))))
</code>
</option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (or (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (or
+ (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (or (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (or
+ (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient}))))
+ </code>
+ </option>
<option value="matches regex">
<title>matches regex</title>
<code>
@@ -123,6 +178,30 @@
(match-all (not (header-matches "Subject" ${subject}))
</code>
</option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "Subject" ${subject}))
+ </code>
+ </option>
<option value="matches regex">
<title>matches regex</title>
<code>
@@ -167,6 +246,42 @@
(match-all (not (header-matches ${header-field} ${word}))
</code>
</option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="exists">
+ <title>exists</title>
+ <code>
+ (match-all (header-exists ${header-field}))
+ </code>
+ </option>
+ <option value="not exists">
+ <title>does not exist</title>
+ <code>
+ (match-all (not (header-exists ${header-field}))
+ </code>
+ </option>
<option value="matches regex">
<title>matches regex</title>
<code>
diff --git a/filter/libfilter-i18n.h b/filter/libfilter-i18n.h
index 9451d25ee5..b85253bd3b 100644
--- a/filter/libfilter-i18n.h
+++ b/filter/libfilter-i18n.h
@@ -22,15 +22,19 @@ char *s = N_("after");
char *s = N_("before");
char *s = N_("contains");
char *s = N_("does not contain");
+char *s = N_("does not end with");
+char *s = N_("does not exist");
char *s = N_("does not match regex");
-char *s = N_("does not match");
+char *s = N_("does not start with");
+char *s = N_("ends with");
+char *s = N_("exists");
char *s = N_("is greater than");
char *s = N_("is less than");
char *s = N_("is not");
char *s = N_("is");
char *s = N_("matches regex");
-char *s = N_("matches");
char *s = N_("on or after");
char *s = N_("on or before");
+char *s = N_("starts with");
char *s = N_("was after");
char *s = N_("was before");