source: filter.c @ cb769bb

barnowl_perlaimdebianrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since cb769bb was cb769bb, checked in by Nelson Elhage <nelhage@mit.edu>, 14 years ago
r15874@phanatique: nelhage | 2006-12-24 18:25:33 -0500 Don't quit if we can't contact the hostmaster. r15884@phanatique: nelhage | 2006-12-24 18:56:03 -0500 Respect the displayoutgoing variable r15885@phanatique: nelhage | 2006-12-24 20:10:44 -0500 You can now write filters based off arbitrary message attributes r15887@phanatique: nelhage | 2006-12-24 22:59:39 -0500 r15886@phanatique (orig r476): nelhage | 2006-12-24 22:59:10 -0500 r24493@heretique: nelhage | 2006-12-24 22:59:02 -0500 Moving zephyr initialization later, so that zephyr works again r15891@phanatique: nelhage | 2006-12-25 14:40:08 -0500 * perl messages hashes use `private', not `isprivate' * get rid of a perl warning if login fails r15900@phanatique: nelhage | 2006-12-25 21:04:15 -0500 Merging in filter regression tests from my local branch. r15926@phanatique: nelhage | 2006-12-26 00:57:07 -0500 r15901@phanatique: nelhage | 2006-12-25 21:08:47 -0500 Base framework for the filter rewrite system. Only understands regexes and true/false so far. r15927@phanatique: nelhage | 2006-12-26 00:57:08 -0500 r15902@phanatique: nelhage | 2006-12-25 23:03:33 -0500 support for negation and parentheses r15928@phanatique: nelhage | 2006-12-26 00:57:08 -0500 r15924@phanatique: nelhage | 2006-12-26 00:16:30 -0500 Now passing all tests except for recursion detection r15929@phanatique: nelhage | 2006-12-26 00:57:08 -0500 r15925@phanatique: nelhage | 2006-12-26 00:52:09 -0500 Checking for filter recursion loops
  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[7d4fbcd]1#include <string.h>
2#include "owl.h"
3
[1aee7d9]4static const char fileIdent[] = "$Id$";
5
[40458b9]6#define OWL_FILTER_MAXRECURSE 20
7
[e187445]8int owl_filter_init_fromstring(owl_filter *f, char *name, char *string)
9{
[7d4fbcd]10  char **argv;
11  int argc, out;
12
13  argv=owl_parseline(string, &argc);
14  out=owl_filter_init(f, name, argc, argv);
15  /* owl_parsefree(argv, argc); */
16  return(out);
17}
18
[e187445]19int owl_filter_init(owl_filter *f, char *name, int argc, char **argv)
20{
[7d4fbcd]21  f->name=owl_strdup(name);
22  f->polarity=0;
23  f->color=OWL_COLOR_DEFAULT;
[59cf91c]24  f->cachedmsgid=-1;
[cb769bb]25
[7d4fbcd]26  /* first take arguments that have to come first */
27  /* set the color */
28  if (argc>=2 && !strcmp(argv[0], "-c")) {
[12c35df]29    if (owl_util_string_to_color(argv[1])==-1) {
30      owl_function_error("The color '%s' is not available, using default.", argv[1]);
31    } else {
32      f->color=owl_util_string_to_color(argv[1]);
33    }
[7d4fbcd]34    argc-=2;
35    argv+=2;
36  }
37
[cb769bb]38  if(!(f->root = owl_filter_parse_expression(argc, argv, NULL)))
39    return(-1);
[7d4fbcd]40
[cb769bb]41  /* Now check for recursion. */
[40458b9]42  if (owl_filter_is_toodeep(f)) {
43    owl_function_error("Filter loop or exceeds recursion depth");
44    owl_filter_free(f);
45    return(-1);
[7d4fbcd]46  }
[cb769bb]47
[7d4fbcd]48  return(0);
49}
50
[cb769bb]51
52/* A primitive expression is one without any toplevel ``and'' or ``or''s*/
53
54static owl_filterelement * owl_filter_parse_primitive_expression(int argc, char **argv, int *next)
55{
56  if(!argc) return NULL;
57
58  owl_filterelement * fe = owl_malloc(sizeof(owl_filterelement));
59  owl_filterelement *op;
60
61  owl_filterelement_create(fe);
62  int i = 0, skip;
63
64  if(!strcasecmp(argv[i], "(")) {
65    i++;
66    op = owl_filter_parse_expression(argc-i, argv+i, &skip);
67    if(!op) goto err;
68    i += skip;
69    if(strcasecmp(argv[i++], ")")) goto err;
70    owl_filterelement_create_group(fe, op);
71  } else if(!strcasecmp(argv[i], "not")) {
72    i++;
73    op = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
74    if(!op) goto err;
75    i += skip;
76    owl_filterelement_create_not(fe, op);
77  } else if(!strcasecmp(argv[i], "true")) {
78    i++;
79    owl_filterelement_create_true(fe);
80  } else if(!strcasecmp(argv[i], "false")) {
81    i++;
82    owl_filterelement_create_false(fe);
83  } else {
84    if(argc == 1) goto err;
85    if(!strcasecmp(*argv, "filter")) {
86      owl_filterelement_create_filter(fe, *(argv+1));
87    } else if(!strcasecmp(*argv, "perl")) {
88      owl_filterelement_create_perl(fe, *(argv+1));
89    } else {
90      owl_filterelement_create_re(fe, *argv, *(argv+1));
91    }
92    i += 2;
93  }
94
95  if(next) {
96    *next = i;
97  } else if(i != argc) {
98    goto err;
99  }
100  return fe;
101err:
102  owl_filterelement_free(fe);
103  owl_free(fe);
104  return NULL;
105}
106
107owl_filterelement * owl_filter_parse_expression(int argc, char **argv, int *next)
108{
109  int i = 0, skip;
110  owl_filterelement * op1 = NULL, * op2 = NULL;
111
112  op1 = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
113  i += skip;
114  if(!op1) goto err;
115
116  while(i < argc) {
117    if(strcasecmp(argv[i], "and") &&
118       strcasecmp(argv[i], "or")) break;
119    op2 = owl_filter_parse_primitive_expression(argc-i-1, argv+i+1, &skip);
120    if(!op2) goto err;
121    owl_filterelement * tmp = owl_malloc(sizeof(owl_filterelement));
122    if(!strcasecmp(argv[i], "and")) {
123      owl_filterelement_create_and(tmp, op1, op2);
124    } else {
125      owl_filterelement_create_or(tmp, op1, op2);
126    }
127    op1 = tmp;
128    op2 = NULL;
129    i += skip+1;
130  }
131
132  if(next) {
133    *next = i;
134  } else if(i != argc) {
135    goto err;
136  }
137  return op1;
138err:
139  if(op1) {
140    owl_filterelement_free(op1);
141    owl_free(op1);
142  }
143  return NULL;
144}
145
[e187445]146char *owl_filter_get_name(owl_filter *f)
147{
[7d4fbcd]148  return(f->name);
149}
150
[e187445]151void owl_filter_set_polarity_match(owl_filter *f)
152{
[7d4fbcd]153  f->polarity=0;
154}
155
[e187445]156void owl_filter_set_polarity_unmatch(owl_filter *f)
157{
[7d4fbcd]158  f->polarity=1;
159}
160
[e187445]161void owl_filter_set_color(owl_filter *f, int color)
162{
[7d4fbcd]163  f->color=color;
164}
165
[e187445]166int owl_filter_get_color(owl_filter *f)
167{
[7d4fbcd]168  return(f->color);
169}
170
[e187445]171void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid)
172{
[59cf91c]173  f->cachedmsgid=cachedmsgid;
174}
175
[e187445]176int owl_filter_get_cachedmsgid(owl_filter *f)
177{
[59cf91c]178  return(f->cachedmsgid);
179}
180
[15b34fd]181/* return 1 if the message matches the given filter, otherwise
182 * return 0.
183 */
[e187445]184int owl_filter_message_match(owl_filter *f, owl_message *m)
185{
[cb769bb]186  if(!f->root) return 0;
187  int ret = owl_filterelement_match(f->root, m);
188  if(f->polarity) ret = !ret;
189  return ret;
[7d4fbcd]190}
191
192
[e187445]193void owl_filter_print(owl_filter *f, char *out)
194{
[cb769bb]195  strcpy(out, "");
196  if(!f->root) return;
197  owl_filterelement_print(f->root, out);
[7d4fbcd]198  strcat(out, "\n");
199}
200
[40458b9]201/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */
[e187445]202int owl_filter_equiv(owl_filter *a, owl_filter *b)
203{
[7d4fbcd]204  char buff[LINE], buff2[LINE];
205
206  owl_filter_print(a, buff);
207  owl_filter_print(b, buff2);
208
209  if (!strcmp(buff, buff2)) return(1);
210  return(0);
211}
212
[cb769bb]213
214int owl_filter_is_toodeep(owl_filter *f)
[40458b9]215{
[cb769bb]216  return owl_filterelement_is_toodeep(f, f->root);
[40458b9]217}
218
[cb769bb]219void owl_filter_free(owl_filter *f)
[40458b9]220{
[cb769bb]221  if(f->root) {
222    owl_filterelement_free(f->root);
223    owl_free(f->root);
[40458b9]224  }
[cb769bb]225  if (f->name) owl_free(f->name);
226}
[40458b9]227
[cb769bb]228/**************************************************************************/
229/************************* REGRESSION TESTS *******************************/
230/**************************************************************************/
[40458b9]231
[cb769bb]232#ifdef OWL_INCLUDE_REG_TESTS
[40458b9]233
[cb769bb]234int owl_filter_test_string(char * filt, owl_message *m, int shouldmatch) /* noproto */ {
235  owl_filter f;
236  int ok;
237  int failed = 0;
238  if(owl_filter_init_fromstring(&f, "test-filter", filt)) {
239    printf("\tFAIL: parse %s\n", filt);
240    failed = 1;
241    goto out;
242  }
243  ok = owl_filter_message_match(&f, m);
244  if((shouldmatch && !ok) || (!shouldmatch && ok)) {
245    printf("\tFAIL: match %s (got %d, expected %d)\n", filt, ok, shouldmatch);
246    failed = 1;
247  }
248 out:
249  owl_filter_free(&f);
250  if(!failed) {
251    printf("\tok  : %s %s\n", shouldmatch ? "matches" : "doesn't match", filt);
252  }
253  return failed;
254}
[40458b9]255
256
[cb769bb]257#include "test.h"
[40458b9]258
259
[cb769bb]260int owl_filter_regtest(void) {
261  owl_list_create(&(g.filterlist));
262  int numfailed=0;
263  owl_message m;
264  owl_message_init(&m);
265  owl_message_set_type_zephyr(&m);
266  owl_message_set_direction_in(&m);
267  owl_message_set_class(&m, "owl");
268  owl_message_set_instance(&m, "tester");
269  owl_message_set_sender(&m, "owl-user");
270  owl_message_set_recipient(&m, "joe");
271  owl_message_set_attribute(&m, "foo", "bar");
[40458b9]272
[cb769bb]273#define TEST_FILTER(f, e) numfailed += owl_filter_test_string(f, &m, e)
[40458b9]274
[7d4fbcd]275
[cb769bb]276  TEST_FILTER("true", 1);
277  TEST_FILTER("false", 0);
278  TEST_FILTER("( true )", 1);
279  TEST_FILTER("not false", 1);
280  TEST_FILTER("( true ) or ( false )", 1);
281  TEST_FILTER("true and false", 0);
282  TEST_FILTER("( true or true ) or ( ( false ) )", 1);
283
284  TEST_FILTER("class owl", 1);
285  TEST_FILTER("class ^owl$", 1);
286  TEST_FILTER("instance test", 1);
287  TEST_FILTER("instance ^test$", 0);
288  TEST_FILTER("instance ^tester$", 1);
289
290  TEST_FILTER("foo bar", 1);
291  TEST_FILTER("class owl and instance tester", 1);
292  TEST_FILTER("type ^zephyr$ and direction ^in$ and ( class ^owl$ or instance ^owl$ )", 1);
293
294  // Order of operations and precedence
295  TEST_FILTER("not true or false", 0);
296  TEST_FILTER("true or true and false", 0);
297  TEST_FILTER("true and true and false or true", 1);
298  TEST_FILTER("false and false or true", 1);
299  TEST_FILTER("true and false or false", 0);
300
301  owl_filter f1, f2, f3, f4;
302
303  owl_filter_init_fromstring(&f1, "f1", "class owl");
304  owl_global_add_filter(&g, &f1);
305  TEST_FILTER("filter f1", 1);
306
307  // Test recursion prevention
308  FAIL_UNLESS("self reference", owl_filter_init_fromstring(&f2, "test", "filter test"));
309
310  // mutual recursion
311  owl_filter_init_fromstring(&f3, "f3", "filter f4");
312  owl_global_add_filter(&g, &f3);
313  FAIL_UNLESS("mutual recursion",   owl_filter_init_fromstring(&f4, "f4", "filter f3"));
314
315  return 0;
[7d4fbcd]316}
[cb769bb]317
318
319#endif /* OWL_INCLUDE_REG_TESTS */
Note: See TracBrowser for help on using the repository browser.