source: filter.c @ bf73bdd

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since bf73bdd was bf73bdd, checked in by James M. Kretchmar <kretch@mit.edu>, 17 years ago
Fixed memory bug on receiving pings
  • Property mode set to 100644
File size: 14.9 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  int i, j, error;
22  owl_filterelement *fe;
23  char *regexstr;
24  owl_list list;
25   
26  f->name=owl_strdup(name);
27  f->polarity=0;
28  f->color=OWL_COLOR_DEFAULT;
29  f->cachedmsgid=-1;
30  owl_list_create(&(f->fes));
31 
32  /* first take arguments that have to come first */
33  /* set the color */
34  if (argc>=2 && !strcmp(argv[0], "-c")) {
35    if (owl_util_string_to_color(argv[1])==-1) {
36      owl_function_error("The color '%s' is not available, using default.", argv[1]);
37    } else {
38      f->color=owl_util_string_to_color(argv[1]);
39    }
40    argc-=2;
41    argv+=2;
42  }
43 
44  /* then deal with the expression */
45  for (i=0; i<argc; i++) {
46    error=0;
47    fe=owl_malloc(sizeof(owl_filterelement));
48   
49    /* all the 0 argument possibilities */
50    if (!strcmp(argv[i], "(")) {
51      owl_filterelement_create_openbrace(fe);
52    } else if (!strcmp(argv[i], ")")) {
53      owl_filterelement_create_closebrace(fe);
54    } else if (!strcasecmp(argv[i], "and")) {
55      owl_filterelement_create_and(fe);
56    } else if (!strcasecmp(argv[i], "or")) {
57      owl_filterelement_create_or(fe);
58    } else if (!strcasecmp(argv[i], "not")) {
59      owl_filterelement_create_not(fe);
60    } else if (!strcasecmp(argv[i], "true")) {
61      owl_filterelement_create_true(fe);
62    } else if (!strcasecmp(argv[i], "false")) {
63      owl_filterelement_create_false(fe);
64     
65    } else if (i==argc-1) { /* we need more than one arg at this point */
66      error=1;
67    } else {
68      if (!strcasecmp(argv[i], "class") ||
69          !strcasecmp(argv[i], "instance") ||
70          !strcasecmp(argv[i], "sender") ||
71          !strcasecmp(argv[i], "recipient") ||
72          !strcasecmp(argv[i], "body") ||
73          !strcasecmp(argv[i], "opcode") ||
74          !strcasecmp(argv[i], "realm") ||
75          !strcasecmp(argv[i], "type") ||
76          !strcasecmp(argv[i], "direction") ||
77          !strcasecmp(argv[i], "hostname") ||
78          !strcasecmp(argv[i], "login")) {
79        regexstr=owl_text_substitute(argv[i+1], "%me%", owl_zephyr_get_sender());
80        owl_filterelement_create_re(fe, argv[i], regexstr);
81        owl_free(regexstr);
82        i++;
83      } else if (!strcasecmp(argv[i], "filter")) {
84        owl_filterelement_create_filter(fe, argv[i+1]);
85        i++;
86      } else {
87        error=1;
88      }
89    }
90
91    if (!error) {
92      owl_list_append_element(&(f->fes), fe);
93    } else {
94      owl_free(fe);
95      owl_filter_free(f);
96      return(-1);
97    }
98  }
99 
100  /* Are we trying to use the filter we're creating?  Bad. */
101  owl_list_create(&list);
102  _owl_filter_get_subfilter_names(f, &list);
103  j=owl_list_get_size(&list);
104  for (i=0; i<j; i++) {
105    if (!strcasecmp(name, owl_list_get_element(&list, i))) {
106      owl_function_error("Filter loop.");
107      owl_filter_free(f);
108      owl_list_free_all(&list, owl_free);
109      return(-1);
110    }
111  }
112  owl_list_free_all(&list, owl_free);
113
114  /* Now check for more subtle recursion. */
115  if (owl_filter_is_toodeep(f)) {
116    owl_function_error("Filter loop or exceeds recursion depth");
117    owl_filter_free(f);
118    return(-1);
119  }
120 
121  return(0);
122}
123
124char *owl_filter_get_name(owl_filter *f)
125{
126  return(f->name);
127}
128
129void owl_filter_set_polarity_match(owl_filter *f)
130{
131  f->polarity=0;
132}
133
134void owl_filter_set_polarity_unmatch(owl_filter *f)
135{
136  f->polarity=1;
137}
138
139void owl_filter_set_color(owl_filter *f, int color)
140{
141  f->color=color;
142}
143
144int owl_filter_get_color(owl_filter *f)
145{
146  return(f->color);
147}
148
149void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid)
150{
151  f->cachedmsgid=cachedmsgid;
152}
153
154int owl_filter_get_cachedmsgid(owl_filter *f)
155{
156  return(f->cachedmsgid);
157}
158
159int owl_filter_message_match(owl_filter *f, owl_message *m)
160{
161  int i, j, tmp;
162  owl_list work_fes, *fes;
163  owl_filterelement *fe;
164  char *field, *match;
165
166  /* create the working list of expression elements */
167  fes=&(f->fes);
168  owl_list_create(&work_fes);
169  j=owl_list_get_size(fes);
170  for (i=0; i<j; i++) {
171    owl_list_append_element(&work_fes, owl_list_get_element(fes, i));
172  }
173
174  /* first go thru and evaluate all RE elements to true or false */
175  match="";
176  for (i=0; i<j; i++) {
177    fe=owl_list_get_element(&work_fes, i);
178    if (!owl_filterelement_is_re(fe)) continue;
179    field=owl_filterelement_get_field(fe);
180    if (!strcasecmp(field, "class")) {
181      match=owl_message_get_class(m);
182    } else if (!strcasecmp(field, "instance")) {
183      match=owl_message_get_instance(m);
184    } else if (!strcasecmp(field, "sender")) {
185      match=owl_message_get_sender(m);
186    } else if (!strcasecmp(field, "recipient")) {
187      match=owl_message_get_recipient(m);
188    } else if (!strcasecmp(field, "body")) {
189      match=owl_message_get_body(m);
190    } else if (!strcasecmp(field, "opcode")) {
191      match=owl_message_get_opcode(m);
192    } else if (!strcasecmp(field, "realm")) {
193      match=owl_message_get_realm(m);
194    } else if (!strcasecmp(field, "type")) {
195      match=owl_message_get_type(m);
196    } else if (!strcasecmp(field, "hostname")) {
197      match=owl_message_get_hostname(m);
198    } else if (!strcasecmp(field, "direction")) {
199      if (owl_message_is_direction_out(m)) {
200        match="out";
201      } else if (owl_message_is_direction_in(m)) {
202        match="in";
203      } else if (owl_message_is_direction_none(m)) {
204        match="none";
205      } else {
206        match="";
207      }
208    } else if (!strcasecmp(field, "login")) {
209      if (owl_message_is_login(m)) {
210        match="login";
211      } else if (owl_message_is_logout(m)) {
212        match="logout";
213      } else {
214        match="none";
215      }
216    }
217
218    tmp=owl_regex_compare(owl_filterelement_get_re(fe), match);
219    if (!tmp) {
220      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
221    } else {
222      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
223    }
224  }
225
226  /* now subfilters */
227  for (i=0; i<j; i++) {
228    owl_filter *subfilter;
229                           
230    fe=owl_list_get_element(&work_fes, i);
231    if (!owl_filterelement_is_filter(fe)) continue;
232
233    subfilter=owl_global_get_filter(&g, owl_filterelement_get_filtername(fe));
234    if (!subfilter) {
235      /* the filter does not exist, maybe because it was deleted.
236       * Default to not matching
237       */
238      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
239    } else if (owl_filter_message_match(subfilter, m)) {
240      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
241    } else {
242      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
243    }
244  }
245
246  /* call the recrsive helper */
247  i=_owl_filter_message_match_recurse(f, m, &work_fes, 0, owl_list_get_size(&(f->fes))-1);
248
249  /* now there will be only one TRUE / FALSE, find it among the NULL's */
250  tmp=0;
251  for (i=0; i<j; i++) {
252    fe=owl_list_get_element(&work_fes, i);
253    if (owl_filterelement_is_null(fe)) continue;
254    if (owl_filterelement_is_true(fe)) {
255      tmp=1;
256      break;
257    }
258    if (owl_filterelement_is_false(fe)) {
259      tmp=0;
260      break;
261    }
262  } 
263
264  /* reverse the answer if negative polarity is in use */
265  if (f->polarity) tmp=!tmp;
266
267  owl_list_free_simple(&work_fes);
268  return(tmp);
269}
270
271int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end)
272{
273  int a=0, b=0, i, x, y, z, score, ret, type;
274  owl_filterelement *fe, *tmpfe=NULL;
275
276  /* Deal with parens first. */
277  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
278    /* Find first open paren and matching close paren, store in x, y */
279    score=x=y=0;
280    for (i=start; i<=end; i++) {
281      fe=owl_list_get_element(fes, i);
282      if (owl_filterelement_is_openbrace(fe)) {
283        if (score==0) x=i;
284        score++;
285      } else if (owl_filterelement_is_closebrace(fe)) {
286        score--;
287        if (score<0) {
288          /* unblanaced parens */
289          return(-1);
290        } else if (score==0) {
291          y=i; /* this is the matching close paren */
292          break;
293        }
294      }
295    }
296    if (score>0) {
297      /* unblanaced parens */
298      return(-1);
299    }
300
301    /* Simply the parens by removing them and evaluating what was in between */
302    if (y>0) {
303      /* null out the parens */
304      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
305      owl_list_replace_element(fes, y, owl_global_get_filterelement_null(&g));
306
307      /* evaluate expression in between */
308      ret=_owl_filter_message_match_recurse(f, m, fes, x+1, y-1);
309      if (ret<0) return(-1);
310
311      /* there may be more, so we continue */
312      continue;
313    } else {
314      /* otherwise we're done with this part */
315      break;
316    }
317  }
318  if (i==OWL_FILTER_MAX_DEPTH) {
319    /* hit the saftey limit, consider it invalid */
320    return(-1);
321  }
322
323  /* Find AND / OR / NOT.
324   *   For binary expressions (AND/OR):
325   *     "type" is 1
326   *     "x" will index first val, "y" the operator and "z" the second val
327   *   For unary expressions (NOT):
328   *     "type" is 2
329   *     "x" will index the operator, "y" the value
330   *   "score" tallys how many expression elements have been found so far
331   */
332  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
333    type=score=x=y=z=0;
334    for (i=start; i<=end; i++) {
335      fe=owl_list_get_element(fes, i);
336      if (owl_filterelement_is_null(fe)) continue;
337      if (score==0) {
338        if (owl_filterelement_is_value(fe)) {
339          x=i;
340          score=1;
341          type=1;
342        } else if (owl_filterelement_is_not(fe)) {
343          x=i;
344          score=1;
345          type=2;
346        }
347      } else if (score==1) {
348        if (type==1) {
349          if (owl_filterelement_is_and(fe) || owl_filterelement_is_or(fe)) {
350            score=2;
351            y=i;
352          } else {
353            /* it's not a valid binary expression */
354            x=y=z=score=0;
355          }
356        } else if (type==2) {
357          if (owl_filterelement_is_value(fe)) {
358            /* valid unary expression, we're done */
359            y=i;
360            break;
361          }
362        }
363      } else if (score==2) {
364        if (owl_filterelement_is_value(fe)) {
365          /* valid binary expression, we're done */
366          z=i;
367          break;
368        } else {
369          x=y=z=score=0;
370        }
371      }
372    }
373
374    /* simplify AND / OR */
375    if ((type==1) && (z>0)) {
376      fe=owl_list_get_element(fes, x);
377      if (owl_filterelement_is_true(fe)) {
378        a=1;
379      } else if (owl_filterelement_is_false(fe)) {
380        a=0;
381      }
382
383      fe=owl_list_get_element(fes, z);
384      if (owl_filterelement_is_true(fe)) {
385        b=1;
386      } else if (owl_filterelement_is_false(fe)) {
387        b=0;
388      }
389
390      fe=owl_list_get_element(fes, y);
391      if (owl_filterelement_is_and(fe)) {
392        if (a && b) {
393          tmpfe=owl_global_get_filterelement_true(&g);
394        } else {
395          tmpfe=owl_global_get_filterelement_false(&g);
396        }
397      } else if (owl_filterelement_is_or(fe)) {
398        if (a || b) {
399          tmpfe=owl_global_get_filterelement_true(&g);
400        } else {
401          tmpfe=owl_global_get_filterelement_false(&g);
402        }
403      }
404      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
405      owl_list_replace_element(fes, y, tmpfe);
406      owl_list_replace_element(fes, z, owl_global_get_filterelement_null(&g));
407    } else if ((type==2) && (y>0)) {
408      /* simplify NOT */
409      fe=owl_list_get_element(fes, y);
410      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
411      if (owl_filterelement_is_false(fe)) {
412        owl_list_replace_element(fes, y, owl_global_get_filterelement_true(&g));
413      } else {
414        owl_list_replace_element(fes, y, owl_global_get_filterelement_false(&g));
415      }
416    } else {
417      break;
418    }
419  }
420  return(0);
421
422}
423
424void owl_filter_print(owl_filter *f, char *out)
425{
426  int i, j;
427  owl_filterelement *fe;
428  char *tmp;
429
430  strcpy(out, owl_filter_get_name(f));
431  strcat(out, ": ");
432
433  if (f->color!=OWL_COLOR_DEFAULT) {
434    strcat(out, "-c ");
435    strcat(out, owl_util_color_to_string(f->color));
436    strcat(out, " ");
437  }
438
439  j=owl_list_get_size(&(f->fes));
440  for (i=0; i<j; i++) {
441    fe=owl_list_get_element(&(f->fes), i);
442    tmp=owl_filterelement_to_string(fe);
443    strcat(out, tmp);
444    owl_free(tmp);
445  }
446  strcat(out, "\n");
447}
448
449/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */
450int owl_filter_equiv(owl_filter *a, owl_filter *b)
451{
452  char buff[LINE], buff2[LINE];
453
454  owl_filter_print(a, buff);
455  owl_filter_print(b, buff2);
456
457  if (!strcmp(buff, buff2)) return(1);
458  return(0);
459}
460
461/* Private
462 * 'list' should already be allocated and initialized
463 * This function places into list the string names of all filters
464 * used in the filter expression for 'f'.
465 * Caller must do a full free on 'list', including elements.
466 */
467void _owl_filter_get_subfilter_names(owl_filter *f, owl_list *list)
468{
469  int i, j;
470  owl_filterelement *fe;
471
472  j=owl_list_get_size(&(f->fes));
473  for (i=0; i<j; i++) {
474    fe=owl_list_get_element(&(f->fes), i);
475    if (owl_filterelement_is_filter(fe)) {
476      owl_list_append_element(list, owl_strdup(owl_filterelement_get_filtername(fe)));
477    }
478  }
479}
480
481int owl_filter_is_toodeep(owl_filter *f)
482{
483  owl_list seen, tocheck, tmp;
484  int i, j, x, y;
485  owl_filter *subfilter;
486
487  owl_list_create(&seen);
488  owl_list_create(&tocheck);
489  owl_list_create(&tmp);
490
491  /* seed 'tocheck' with the first set of filters */
492  _owl_filter_get_subfilter_names(f, &tmp);
493  j=owl_list_get_size(&tmp);
494  for (i=0; i<j; i++) {
495    owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, i));
496  }
497  owl_list_free_all(&tmp, owl_free);
498  owl_list_create(&tmp);
499
500  /* add this list to the 'seen' list */
501  owl_list_append_element(&seen, owl_strdup(owl_filter_get_name(f)));
502
503  for (i=0; i<OWL_FILTER_MAXRECURSE; i++) {
504    /* if anything in 'tocheck' is in 'seen' we have a loop */
505    if (owl_util_common_strings_in_lists(&tocheck, &seen)) {
506      owl_list_free_all(&tmp, owl_free);
507      owl_list_free_all(&tocheck, owl_free);
508      owl_list_free_all(&seen, owl_free);
509      return(1);
510    }
511
512    /* if there's nothing to check, we're done */
513    y=owl_list_get_size(&tocheck);
514    if (y==0) {
515      owl_list_free_all(&tmp, owl_free);
516      owl_list_free_all(&tocheck, owl_free);
517      owl_list_free_all(&seen, owl_free);
518      return(0);
519    }
520
521    /* add everything in 'tocheck' to the 'seen' list */
522    /* y=owl_list_get_size(&tocheck); */
523    for (x=0; x<y; x++) {
524      owl_list_append_element(&seen, owl_strdup(owl_list_get_element(&tocheck, x)));
525    }
526
527    /* make a new list into 'tmp' with the children of 'tocheck' */
528    /* y=owl_list_get_size(&tocheck); */
529    for (x=0; x<y; x++) {
530      subfilter=owl_global_get_filter(&g, owl_list_get_element(&tocheck, x));
531      if (!subfilter) return(0);
532      _owl_filter_get_subfilter_names(subfilter, &tmp);
533    }
534
535    /* clean out 'tocheck' */
536    owl_list_free_all(&tocheck, owl_free);
537    owl_list_create(&tocheck);
538
539    /* put 'tmp' into 'tocheck' */
540    y=owl_list_get_size(&tmp);
541    for (x=0; x<y; x++) {
542      owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, x));
543    }
544
545    /* clean out 'tmp' */
546    owl_list_free_all(&tmp, owl_free);
547    owl_list_create(&tmp);
548  }
549
550  owl_list_free_all(&tmp, owl_free);
551  owl_list_free_all(&tocheck, owl_free);
552  owl_list_free_all(&seen, owl_free);
553
554  return(1);
555}
556
557void owl_filter_free(owl_filter *f)
558{
559  void (*func)();
560
561  func=&owl_filterelement_free;
562 
563  if (f->name) owl_free(f->name);
564  owl_list_free_all(&(f->fes), func);
565}
Note: See TracBrowser for help on using the repository browser.