source: filter.c @ 40458b9

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