source: filter.c @ 32eed98

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