source: filter.c @ 9cd7e6a

owl
Last change on this file since 9cd7e6a was dab82f29, checked in by James M. Kretchmar <kretch@mit.edu>, 15 years ago
Don't crash on mobile device AIM messages [BZ 20] Don't crash when sending to someone not on AIM buddy list [BZ 94] Fix overflow vulerability in zwrite.c and zcrypt.c Add stack protector and -D_FORTIFY_SOURCE=2 where possible Fix hang [barnowl 4c46dfdebc294ca24fef59924f01688b8ee07dee] Fix segfault on missing subs [barnowl 99dabeed7de6d19acb50f1e73aa0bfe5f9469c02] Fix zcrypt bufffer size bug [barnowl 559076cd86edc3a8317819814dd5877b8bc7c3cb] Fix double free [barnowl e97c4a306ae2c9d2147d83da29fee59918198e70] Memory leak fix [barnowl c0a90c2c7ab97b9465c9873757faf312faa0021e] Memory leak fix [barnowl 95caa16b2e9ba10770d87d0955198c14c2d0e16a] Memory leak fix [barnowl 1716fed8c2650e46892cf6571555eac937266c6e] Add getstyle command [barnowl 216c73421653e3ef0e957aa9a9804e208a77c80e] Binary search for msgid [barnowl 0c8ab5eadbb6ecc97a120c91b9a824b33538c764] File-handle leak [barnowl e78397d6ac5da0de31a4e269c0ba7f3d691857a3] Fix delay in jump from top to bottom [barnowl 801b7ac63b962640debbcfd422cb9a60da5fea31] Load subs in chunks [barnowl 93e883d60051b80bf6d35391f9d76fd7dfd198e3] Load subs in chunks [barnowl f6050ee9b0a171a5031f84409eb181062afacd18] Better zsig logging [barnowl d857b667a5a9b108b1a2a26b4a5513bef2b53f80] free() -> owlfree() [barnowl d524c838ac7c115628424b8ac171c3489622ea3a] Escape AIM users in smartfilters [barnowl af9b92e3e7ccb7de276a94b5c7e5861b24e71eff] Better regex escape chars [barnowl 80e54a7631f091be8c9762adb9746bad38104738] Deal with quotes in smart filters [barnowl 4099cf83702763fa8d1efc4f1582a605431bdb77] Deal with 0 length zephyr fields [barnowl 128171aaf7cefa91a4bb1eada93a19d8fd0c355c] Deal with 0 length zephyr fields [barnowl 50e29e35c64d64e223d378d350a7bc4f038d78f5] Deal with 0 length zephyr fields [barnowl 804ab8af8b6d00bcd7e2402df892db8fbd61a3ec] Leave curmsg on screen after resize [barnowl c0f9e3009bc03e80a44de64cd5f2b4033290236e] Rip out brower stuff [barnowl 8e5935d11c699a7ce5a3e6e9a47799564c696d6a] Rip out browser stuff [barnowl 4f15e8e9ceada0d4b2cc969ebf43b0a1fb3709ea] No passwords in command history [barnowl 6e400cc71aa59e041dce677aadf50dc1f25228e2] Format NOC mssages [barnowl a1bb1980e4bca23b8329cc0e7c0bd5027055ea0a] Expand ~ in loadsubs [barnowl 27d8d835dc6d58c08fae10e75aae306c49215143] Expand ~ in source [barnowl 10d67d57cb29221f63a43a30643c697fc7b38911] Only use resizeterm() if it's available Debian backports Change license to GPLv3
  • Property mode set to 100644
File size: 16.7 KB
Line 
1/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar
2 *
3 * This file is part of Owl.
4 *
5 * Owl is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Owl is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Owl.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 * ---------------------------------------------------------------
19 *
20 * As of Owl version 2.1.12 there are patches contributed by
21 * developers of the the branched BarnOwl project, Copyright (c)
22 * 2006-2008 The BarnOwl Developers. All rights reserved.
23 */
24
25#include <string.h>
26#include "owl.h"
27
28static const char fileIdent[] = "$Id$";
29
30#define OWL_FILTER_MAXRECURSE 20
31
32int owl_filter_init_fromstring(owl_filter *f, char *name, char *string)
33{
34  char **argv;
35  int argc, out;
36
37  argv=owl_parseline(string, &argc);
38  out=owl_filter_init(f, name, argc, argv);
39  owl_parsefree(argv, argc);
40  return(out);
41}
42
43int owl_filter_init(owl_filter *f, char *name, int argc, char **argv)
44{
45  int i, j, error;
46  owl_filterelement *fe;
47  char *regexstr;
48  owl_list list;
49   
50  f->name=owl_strdup(name);
51  f->polarity=0;
52  f->color=OWL_COLOR_DEFAULT;
53  f->cachedmsgid=-1;
54  owl_list_create(&(f->fes));
55 
56  /* first take arguments that have to come first */
57  /* set the color */
58  if (argc>=2 && !strcmp(argv[0], "-c")) {
59    if (owl_util_string_to_color(argv[1])==-1) {
60      owl_function_error("The color '%s' is not available, using default.", argv[1]);
61    } else {
62      f->color=owl_util_string_to_color(argv[1]);
63    }
64    argc-=2;
65    argv+=2;
66  }
67 
68  /* then deal with the expression */
69  for (i=0; i<argc; i++) {
70    error=0;
71    fe=owl_malloc(sizeof(owl_filterelement));
72   
73    /* all the 0 argument possibilities */
74    if (!strcmp(argv[i], "(")) {
75      owl_filterelement_create_openbrace(fe);
76    } else if (!strcmp(argv[i], ")")) {
77      owl_filterelement_create_closebrace(fe);
78    } else if (!strcasecmp(argv[i], "and")) {
79      owl_filterelement_create_and(fe);
80    } else if (!strcasecmp(argv[i], "or")) {
81      owl_filterelement_create_or(fe);
82    } else if (!strcasecmp(argv[i], "not")) {
83      owl_filterelement_create_not(fe);
84    } else if (!strcasecmp(argv[i], "true")) {
85      owl_filterelement_create_true(fe);
86    } else if (!strcasecmp(argv[i], "false")) {
87      owl_filterelement_create_false(fe);
88     
89    } else if (i==argc-1) { /* we need more than one arg at this point */
90      error=1;
91    } else {
92      if (!strcasecmp(argv[i], "class") ||
93          !strcasecmp(argv[i], "instance") ||
94          !strcasecmp(argv[i], "sender") ||
95          !strcasecmp(argv[i], "recipient") ||
96          !strcasecmp(argv[i], "body") ||
97          !strcasecmp(argv[i], "opcode") ||
98          !strcasecmp(argv[i], "realm") ||
99          !strcasecmp(argv[i], "type") ||
100          !strcasecmp(argv[i], "direction") ||
101          !strcasecmp(argv[i], "hostname") ||
102          !strcasecmp(argv[i], "login")) {
103        regexstr=owl_text_substitute(argv[i+1], "%me%", owl_zephyr_get_sender());
104        owl_filterelement_create_re(fe, argv[i], regexstr);
105        owl_free(regexstr);
106        i++;
107      } else if (!strcasecmp(argv[i], "filter")) {
108        owl_filterelement_create_filter(fe, argv[i+1]);
109        i++;
110      } else if (!strcasecmp(argv[i], "perl")) {
111        owl_filterelement_create_perl(fe, argv[i+1]);
112        i++;
113      } else {
114        error=1;
115      }
116    }
117
118    if (!error) {
119      owl_list_append_element(&(f->fes), fe);
120    } else {
121      owl_free(fe);
122      owl_filter_free(f);
123      return(-1);
124    }
125  }
126 
127  /* Are we trying to use the filter we're creating?  Bad. */
128  owl_list_create(&list);
129  _owl_filter_get_subfilter_names(f, &list);
130  j=owl_list_get_size(&list);
131  for (i=0; i<j; i++) {
132    if (!strcasecmp(name, owl_list_get_element(&list, i))) {
133      owl_function_error("Filter loop.");
134      owl_filter_free(f);
135      owl_list_free_all(&list, owl_free);
136      return(-1);
137    }
138  }
139  owl_list_free_all(&list, owl_free);
140
141  /* Now check for more subtle recursion. */
142  if (owl_filter_is_toodeep(f)) {
143    owl_function_error("Filter loop or exceeds recursion depth");
144    owl_filter_free(f);
145    return(-1);
146  }
147 
148  return(0);
149}
150
151char *owl_filter_get_name(owl_filter *f)
152{
153  return(f->name);
154}
155
156void owl_filter_set_polarity_match(owl_filter *f)
157{
158  f->polarity=0;
159}
160
161void owl_filter_set_polarity_unmatch(owl_filter *f)
162{
163  f->polarity=1;
164}
165
166void owl_filter_set_color(owl_filter *f, int color)
167{
168  f->color=color;
169}
170
171int owl_filter_get_color(owl_filter *f)
172{
173  return(f->color);
174}
175
176void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid)
177{
178  f->cachedmsgid=cachedmsgid;
179}
180
181int owl_filter_get_cachedmsgid(owl_filter *f)
182{
183  return(f->cachedmsgid);
184}
185
186/* return 1 if the message matches the given filter, otherwise
187 * return 0.
188 */
189int owl_filter_message_match(owl_filter *f, owl_message *m)
190{
191  int i, j, tmp;
192  owl_list work_fes, *fes;
193  owl_filterelement *fe;
194  char *field, *match;
195
196  /* create the working list of expression elements */
197  fes=&(f->fes);
198  owl_list_create(&work_fes);
199  j=owl_list_get_size(fes);
200  for (i=0; i<j; i++) {
201    owl_list_append_element(&work_fes, owl_list_get_element(fes, i));
202  }
203
204  /* first go thru and evaluate all RE elements to true or false */
205  match="";
206  for (i=0; i<j; i++) {
207    fe=owl_list_get_element(&work_fes, i);
208    if (!owl_filterelement_is_re(fe)) continue;
209    field=owl_filterelement_get_field(fe);
210    if (!strcasecmp(field, "class")) {
211      match=owl_message_get_class(m);
212    } else if (!strcasecmp(field, "instance")) {
213      match=owl_message_get_instance(m);
214    } else if (!strcasecmp(field, "sender")) {
215      match=owl_message_get_sender(m);
216    } else if (!strcasecmp(field, "recipient")) {
217      match=owl_message_get_recipient(m);
218    } else if (!strcasecmp(field, "body")) {
219      match=owl_message_get_body(m);
220    } else if (!strcasecmp(field, "opcode")) {
221      match=owl_message_get_opcode(m);
222    } else if (!strcasecmp(field, "realm")) {
223      match=owl_message_get_realm(m);
224    } else if (!strcasecmp(field, "type")) {
225      match=owl_message_get_type(m);
226    } else if (!strcasecmp(field, "hostname")) {
227      match=owl_message_get_hostname(m);
228    } else if (!strcasecmp(field, "direction")) {
229      if (owl_message_is_direction_out(m)) {
230        match="out";
231      } else if (owl_message_is_direction_in(m)) {
232        match="in";
233      } else if (owl_message_is_direction_none(m)) {
234        match="none";
235      } else {
236        match="";
237      }
238    } else if (!strcasecmp(field, "login")) {
239      if (owl_message_is_login(m)) {
240        match="login";
241      } else if (owl_message_is_logout(m)) {
242        match="logout";
243      } else {
244        match="none";
245      }
246    }
247
248    tmp=owl_regex_compare(owl_filterelement_get_re(fe), match);
249    if (!tmp) {
250      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
251    } else {
252      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
253    }
254  }
255
256  /* now subfilters and perl functions */
257  for (i=0; i<j; i++) {
258    owl_filter *subfilter;
259                           
260    fe=owl_list_get_element(&work_fes, i);
261
262    if (owl_filterelement_is_filter(fe)) {
263
264      subfilter=owl_global_get_filter(&g, owl_filterelement_get_filtername(fe));
265      if (!subfilter) {
266        /* the filter does not exist, maybe because it was deleted.
267         * Default to not matching
268         */
269        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
270      } else if (owl_filter_message_match(subfilter, m)) {
271        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
272      } else {
273        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
274      }
275
276    } else if (owl_filterelement_is_perl(fe)) {
277      char *subname, *perlrv;
278      int   tf=0;
279
280      subname = owl_filterelement_get_filtername(fe);
281      if (!owl_perlconfig_is_function(subname)) {
282        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
283        continue;
284      }
285      perlrv = owl_perlconfig_call_with_message(subname, m);
286      if (perlrv) {
287        if (0 == strcmp(perlrv, "1")) {
288          tf=1;
289        }
290        owl_free(perlrv);
291      } 
292      if (tf) {
293        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
294      } else {
295        owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
296      }
297    }
298  }
299
300  /* call the recrsive helper */
301  i=_owl_filter_message_match_recurse(f, m, &work_fes, 0, owl_list_get_size(&(f->fes))-1);
302
303  /* now there will be only one TRUE / FALSE, find it among the NULL's */
304  tmp=0;
305  for (i=0; i<j; i++) {
306    fe=owl_list_get_element(&work_fes, i);
307    if (owl_filterelement_is_null(fe)) continue;
308    if (owl_filterelement_is_true(fe)) {
309      tmp=1;
310      break;
311    }
312    if (owl_filterelement_is_false(fe)) {
313      tmp=0;
314      break;
315    }
316  } 
317
318  /* reverse the answer if negative polarity is in use */
319  if (f->polarity) tmp=!tmp;
320
321  owl_list_free_simple(&work_fes);
322  return(tmp);
323}
324
325int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end)
326{
327  int a=0, b=0, i, x, y, z, score, ret, type;
328  owl_filterelement *fe, *tmpfe=NULL;
329
330  /* Deal with parens first. */
331  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
332    /* Find first open paren and matching close paren, store in x, y */
333    score=x=y=0;
334    for (i=start; i<=end; i++) {
335      fe=owl_list_get_element(fes, i);
336      if (owl_filterelement_is_openbrace(fe)) {
337        if (score==0) x=i;
338        score++;
339      } else if (owl_filterelement_is_closebrace(fe)) {
340        score--;
341        if (score<0) {
342          /* unblanaced parens */
343          return(-1);
344        } else if (score==0) {
345          y=i; /* this is the matching close paren */
346          break;
347        }
348      }
349    }
350    if (score>0) {
351      /* unblanaced parens */
352      return(-1);
353    }
354
355    /* Simply the parens by removing them and evaluating what was in between */
356    if (y>0) {
357      /* null out the parens */
358      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
359      owl_list_replace_element(fes, y, owl_global_get_filterelement_null(&g));
360
361      /* evaluate expression in between */
362      ret=_owl_filter_message_match_recurse(f, m, fes, x+1, y-1);
363      if (ret<0) return(-1);
364
365      /* there may be more, so we continue */
366      continue;
367    } else {
368      /* otherwise we're done with this part */
369      break;
370    }
371  }
372  if (i==OWL_FILTER_MAX_DEPTH) {
373    /* hit the saftey limit, consider it invalid */
374    return(-1);
375  }
376
377  /* Find AND / OR / NOT.
378   *   For binary expressions (AND/OR):
379   *     "type" is 1
380   *     "x" will index first val, "y" the operator and "z" the second val
381   *   For unary expressions (NOT):
382   *     "type" is 2
383   *     "x" will index the operator, "y" the value
384   *   "score" tallys how many expression elements have been found so far
385   */
386  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
387    type=score=x=y=z=0;
388    for (i=start; i<=end; i++) {
389      fe=owl_list_get_element(fes, i);
390      if (owl_filterelement_is_null(fe)) continue;
391      if (score==0) {
392        if (owl_filterelement_is_value(fe)) {
393          x=i;
394          score=1;
395          type=1;
396        } else if (owl_filterelement_is_not(fe)) {
397          x=i;
398          score=1;
399          type=2;
400        }
401      } else if (score==1) {
402        if (type==1) {
403          if (owl_filterelement_is_and(fe) || owl_filterelement_is_or(fe)) {
404            score=2;
405            y=i;
406          } else {
407            /* it's not a valid binary expression */
408            x=y=z=score=0;
409          }
410        } else if (type==2) {
411          if (owl_filterelement_is_value(fe)) {
412            /* valid unary expression, we're done */
413            y=i;
414            break;
415          }
416        }
417      } else if (score==2) {
418        if (owl_filterelement_is_value(fe)) {
419          /* valid binary expression, we're done */
420          z=i;
421          break;
422        } else {
423          x=y=z=score=0;
424        }
425      }
426    }
427
428    /* simplify AND / OR */
429    if ((type==1) && (z>0)) {
430      fe=owl_list_get_element(fes, x);
431      if (owl_filterelement_is_true(fe)) {
432        a=1;
433      } else if (owl_filterelement_is_false(fe)) {
434        a=0;
435      }
436
437      fe=owl_list_get_element(fes, z);
438      if (owl_filterelement_is_true(fe)) {
439        b=1;
440      } else if (owl_filterelement_is_false(fe)) {
441        b=0;
442      }
443
444      fe=owl_list_get_element(fes, y);
445      if (owl_filterelement_is_and(fe)) {
446        if (a && b) {
447          tmpfe=owl_global_get_filterelement_true(&g);
448        } else {
449          tmpfe=owl_global_get_filterelement_false(&g);
450        }
451      } else if (owl_filterelement_is_or(fe)) {
452        if (a || b) {
453          tmpfe=owl_global_get_filterelement_true(&g);
454        } else {
455          tmpfe=owl_global_get_filterelement_false(&g);
456        }
457      }
458      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
459      owl_list_replace_element(fes, y, tmpfe);
460      owl_list_replace_element(fes, z, owl_global_get_filterelement_null(&g));
461    } else if ((type==2) && (y>0)) {
462      /* simplify NOT */
463      fe=owl_list_get_element(fes, y);
464      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
465      if (owl_filterelement_is_false(fe)) {
466        owl_list_replace_element(fes, y, owl_global_get_filterelement_true(&g));
467      } else {
468        owl_list_replace_element(fes, y, owl_global_get_filterelement_false(&g));
469      }
470    } else {
471      break;
472    }
473  }
474  return(0);
475
476}
477
478void owl_filter_print(owl_filter *f, char *out)
479{
480  int i, j;
481  owl_filterelement *fe;
482  char *tmp;
483
484  strcpy(out, owl_filter_get_name(f));
485  strcat(out, ": ");
486
487  if (f->color!=OWL_COLOR_DEFAULT) {
488    strcat(out, "-c ");
489    strcat(out, owl_util_color_to_string(f->color));
490    strcat(out, " ");
491  }
492
493  j=owl_list_get_size(&(f->fes));
494  for (i=0; i<j; i++) {
495    fe=owl_list_get_element(&(f->fes), i);
496    tmp=owl_filterelement_to_string(fe);
497    strcat(out, tmp);
498    owl_free(tmp);
499  }
500  strcat(out, "\n");
501}
502
503/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */
504int owl_filter_equiv(owl_filter *a, owl_filter *b)
505{
506  char buff[LINE], buff2[LINE];
507
508  owl_filter_print(a, buff);
509  owl_filter_print(b, buff2);
510
511  if (!strcmp(buff, buff2)) return(1);
512  return(0);
513}
514
515/* Private
516 * 'list' should already be allocated and initialized
517 * This function places into list the string names of all filters
518 * used in the filter expression for 'f'.
519 * Caller must do a full free on 'list', including elements.
520 */
521void _owl_filter_get_subfilter_names(owl_filter *f, owl_list *list)
522{
523  int i, j;
524  owl_filterelement *fe;
525
526  j=owl_list_get_size(&(f->fes));
527  for (i=0; i<j; i++) {
528    fe=owl_list_get_element(&(f->fes), i);
529    if (owl_filterelement_is_filter(fe)) {
530      owl_list_append_element(list, owl_strdup(owl_filterelement_get_filtername(fe)));
531    }
532  }
533}
534
535int owl_filter_is_toodeep(owl_filter *f)
536{
537  owl_list seen, tocheck, tmp;
538  int i, j, x, y;
539  owl_filter *subfilter;
540
541  owl_list_create(&seen);
542  owl_list_create(&tocheck);
543  owl_list_create(&tmp);
544
545  /* seed 'tocheck' with the first set of filters */
546  _owl_filter_get_subfilter_names(f, &tmp);
547  j=owl_list_get_size(&tmp);
548  for (i=0; i<j; i++) {
549    owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, i));
550  }
551  owl_list_free_all(&tmp, owl_free);
552  owl_list_create(&tmp);
553
554  /* add this list to the 'seen' list */
555  owl_list_append_element(&seen, owl_strdup(owl_filter_get_name(f)));
556
557  for (i=0; i<OWL_FILTER_MAXRECURSE; i++) {
558    /* if anything in 'tocheck' is in 'seen' we have a loop */
559    if (owl_util_common_strings_in_lists(&tocheck, &seen)) {
560      owl_list_free_all(&tmp, owl_free);
561      owl_list_free_all(&tocheck, owl_free);
562      owl_list_free_all(&seen, owl_free);
563      return(1);
564    }
565
566    /* if there's nothing to check, we're done */
567    y=owl_list_get_size(&tocheck);
568    if (y==0) {
569      owl_list_free_all(&tmp, owl_free);
570      owl_list_free_all(&tocheck, owl_free);
571      owl_list_free_all(&seen, owl_free);
572      return(0);
573    }
574
575    /* add everything in 'tocheck' to the 'seen' list */
576    /* y=owl_list_get_size(&tocheck); */
577    for (x=0; x<y; x++) {
578      owl_list_append_element(&seen, owl_strdup(owl_list_get_element(&tocheck, x)));
579    }
580
581    /* make a new list into 'tmp' with the children of 'tocheck' */
582    /* y=owl_list_get_size(&tocheck); */
583    for (x=0; x<y; x++) {
584      subfilter=owl_global_get_filter(&g, owl_list_get_element(&tocheck, x));
585      if (!subfilter) return(0);
586      _owl_filter_get_subfilter_names(subfilter, &tmp);
587    }
588
589    /* clean out 'tocheck' */
590    owl_list_free_all(&tocheck, owl_free);
591    owl_list_create(&tocheck);
592
593    /* put 'tmp' into 'tocheck' */
594    y=owl_list_get_size(&tmp);
595    for (x=0; x<y; x++) {
596      owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, x));
597    }
598
599    /* clean out 'tmp' */
600    owl_list_free_all(&tmp, owl_free);
601    owl_list_create(&tmp);
602  }
603
604  owl_list_free_all(&tmp, owl_free);
605  owl_list_free_all(&tocheck, owl_free);
606  owl_list_free_all(&seen, owl_free);
607
608  return(1);
609}
610
611void owl_filter_free(owl_filter *f)
612{
613  void (*func)();
614
615  func=&owl_filterelement_free;
616 
617  if (f->name) owl_free(f->name);
618  owl_list_free_all(&(f->fes), func);
619}
Note: See TracBrowser for help on using the repository browser.