source: filter.c @ cb769bb

barnowl_perlaimdebianrelease-1.10release-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>, 17 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
Line 
1#include <string.h>
2#include "owl.h"
3
4static const char fileIdent[] = "$Id$";
5
6#define OWL_FILTER_MAXRECURSE 20
7
8int owl_filter_init_fromstring(owl_filter *f, char *name, char *string)
9{
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
19int owl_filter_init(owl_filter *f, char *name, int argc, char **argv)
20{
21  f->name=owl_strdup(name);
22  f->polarity=0;
23  f->color=OWL_COLOR_DEFAULT;
24  f->cachedmsgid=-1;
25
26  /* first take arguments that have to come first */
27  /* set the color */
28  if (argc>=2 && !strcmp(argv[0], "-c")) {
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    }
34    argc-=2;
35    argv+=2;
36  }
37
38  if(!(f->root = owl_filter_parse_expression(argc, argv, NULL)))
39    return(-1);
40
41  /* Now check for recursion. */
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);
46  }
47
48  return(0);
49}
50
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
146char *owl_filter_get_name(owl_filter *f)
147{
148  return(f->name);
149}
150
151void owl_filter_set_polarity_match(owl_filter *f)
152{
153  f->polarity=0;
154}
155
156void owl_filter_set_polarity_unmatch(owl_filter *f)
157{
158  f->polarity=1;
159}
160
161void owl_filter_set_color(owl_filter *f, int color)
162{
163  f->color=color;
164}
165
166int owl_filter_get_color(owl_filter *f)
167{
168  return(f->color);
169}
170
171void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid)
172{
173  f->cachedmsgid=cachedmsgid;
174}
175
176int owl_filter_get_cachedmsgid(owl_filter *f)
177{
178  return(f->cachedmsgid);
179}
180
181/* return 1 if the message matches the given filter, otherwise
182 * return 0.
183 */
184int owl_filter_message_match(owl_filter *f, owl_message *m)
185{
186  if(!f->root) return 0;
187  int ret = owl_filterelement_match(f->root, m);
188  if(f->polarity) ret = !ret;
189  return ret;
190}
191
192
193void owl_filter_print(owl_filter *f, char *out)
194{
195  strcpy(out, "");
196  if(!f->root) return;
197  owl_filterelement_print(f->root, out);
198  strcat(out, "\n");
199}
200
201/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */
202int owl_filter_equiv(owl_filter *a, owl_filter *b)
203{
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
213
214int owl_filter_is_toodeep(owl_filter *f)
215{
216  return owl_filterelement_is_toodeep(f, f->root);
217}
218
219void owl_filter_free(owl_filter *f)
220{
221  if(f->root) {
222    owl_filterelement_free(f->root);
223    owl_free(f->root);
224  }
225  if (f->name) owl_free(f->name);
226}
227
228/**************************************************************************/
229/************************* REGRESSION TESTS *******************************/
230/**************************************************************************/
231
232#ifdef OWL_INCLUDE_REG_TESTS
233
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}
255
256
257#include "test.h"
258
259
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");
272
273#define TEST_FILTER(f, e) numfailed += owl_filter_test_string(f, &m, e)
274
275
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;
316}
317
318
319#endif /* OWL_INCLUDE_REG_TESTS */
Note: See TracBrowser for help on using the repository browser.