source: filter.c @ 15b34fd

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