diff options
-rw-r--r-- | filter/ChangeLog | 12 | ||||
-rw-r--r-- | filter/filter-message-search.c | 144 | ||||
-rw-r--r-- | filter/filtertypes.xml | 115 | ||||
-rw-r--r-- | filter/libfilter-i18n.h | 8 |
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"); |