Changeset cb769bb for filter.c


Ignore:
Timestamp:
Dec 26, 2006, 1:05:26 AM (17 years ago)
Author:
Nelson Elhage <nelhage@mit.edu>
Branches:
master, barnowl_perlaim, debian, release-1.10, release-1.4, release-1.5, release-1.6, release-1.7, release-1.8, release-1.9
Children:
446aa2b
Parents:
20eb22c
Message:
 r15874@phanatique:  nelhage | 2006-12-24 18:25:33 -0500
 Don't quit if we can't contact the hostmaster. 
 r15884@phanatique:  nelhage | 2006-12-24 18:56:03 -0500
 Respect the displayoutgoing variable
 r15885@phanatique:  nelhage | 2006-12-24 20:10:44 -0500
 You can now write filters based off arbitrary message attributes
 r15887@phanatique:  nelhage | 2006-12-24 22:59:39 -0500
  r15886@phanatique (orig r476):  nelhage | 2006-12-24 22:59:10 -0500
   r24493@heretique:  nelhage | 2006-12-24 22:59:02 -0500
   Moving zephyr initialization later, so that zephyr works again
  
 
 r15891@phanatique:  nelhage | 2006-12-25 14:40:08 -0500
 * perl messages hashes use `private', not `isprivate'
 * get rid of a perl warning if login fails
 r15900@phanatique:  nelhage | 2006-12-25 21:04:15 -0500
 Merging in filter regression tests from my local branch.
 r15926@phanatique:  nelhage | 2006-12-26 00:57:07 -0500
  r15901@phanatique:  nelhage | 2006-12-25 21:08:47 -0500
  Base framework for the filter rewrite system. Only understands regexes and true/false so far.
 
 r15927@phanatique:  nelhage | 2006-12-26 00:57:08 -0500
  r15902@phanatique:  nelhage | 2006-12-25 23:03:33 -0500
  support for negation and parentheses
 
 r15928@phanatique:  nelhage | 2006-12-26 00:57:08 -0500
  r15924@phanatique:  nelhage | 2006-12-26 00:16:30 -0500
  Now passing all tests except for recursion detection
 
 r15929@phanatique:  nelhage | 2006-12-26 00:57:08 -0500
  r15925@phanatique:  nelhage | 2006-12-26 00:52:09 -0500
  Checking for filter recursion loops
 
File:
1 edited

Legend:

Unmodified
Added
Removed
  • filter.c

    rb699e9b rcb769bb  
    1919int owl_filter_init(owl_filter *f, char *name, int argc, char **argv)
    2020{
    21   int i, j, error;
    22   owl_filterelement *fe;
    23   char *regexstr;
    24   owl_list list;
    25    
    2621  f->name=owl_strdup(name);
    2722  f->polarity=0;
    2823  f->color=OWL_COLOR_DEFAULT;
    2924  f->cachedmsgid=-1;
    30   owl_list_create(&(f->fes));
    31  
     25
    3226  /* first take arguments that have to come first */
    3327  /* set the color */
     
    4135    argv+=2;
    4236  }
    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], "filter")) {
    69         owl_filterelement_create_filter(fe, argv[i+1]);
    70         i++;
    71       } else if (!strcasecmp(argv[i], "perl")) {
    72         owl_filterelement_create_perl(fe, argv[i+1]);
    73         i++;
    74       } else {
    75         regexstr=owl_text_substitute(argv[i+1], "%me%", owl_zephyr_get_sender());
    76         owl_filterelement_create_re(fe, argv[i], regexstr);
    77         owl_free(regexstr);
    78         i++;
    79       }
    80     }
    81 
    82     if (!error) {
    83       owl_list_append_element(&(f->fes), fe);
    84     } else {
    85       owl_free(fe);
    86       owl_filter_free(f);
    87       return(-1);
    88     }
    89   }
    90  
    91   /* Are we trying to use the filter we're creating?  Bad. */
    92   owl_list_create(&list);
    93   _owl_filter_get_subfilter_names(f, &list);
    94   j=owl_list_get_size(&list);
    95   for (i=0; i<j; i++) {
    96     if (!strcasecmp(name, owl_list_get_element(&list, i))) {
    97       owl_function_error("Filter loop.");
    98       owl_filter_free(f);
    99       owl_list_free_all(&list, owl_free);
    100       return(-1);
    101     }
    102   }
    103   owl_list_free_all(&list, owl_free);
    104 
    105   /* Now check for more subtle recursion. */
     37
     38  if(!(f->root = owl_filter_parse_expression(argc, argv, NULL)))
     39    return(-1);
     40
     41  /* Now check for recursion. */
    10642  if (owl_filter_is_toodeep(f)) {
    10743    owl_function_error("Filter loop or exceeds recursion depth");
     
    10945    return(-1);
    11046  }
    111  
     47
    11248  return(0);
     49}
     50
     51
     52/* A primitive expression is one without any toplevel ``and'' or ``or''s*/
     53
     54static owl_filterelement * owl_filter_parse_primitive_expression(int argc, char **argv, int *next)
     55{
     56  if(!argc) return NULL;
     57
     58  owl_filterelement * fe = owl_malloc(sizeof(owl_filterelement));
     59  owl_filterelement *op;
     60
     61  owl_filterelement_create(fe);
     62  int i = 0, skip;
     63
     64  if(!strcasecmp(argv[i], "(")) {
     65    i++;
     66    op = owl_filter_parse_expression(argc-i, argv+i, &skip);
     67    if(!op) goto err;
     68    i += skip;
     69    if(strcasecmp(argv[i++], ")")) goto err;
     70    owl_filterelement_create_group(fe, op);
     71  } else if(!strcasecmp(argv[i], "not")) {
     72    i++;
     73    op = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
     74    if(!op) goto err;
     75    i += skip;
     76    owl_filterelement_create_not(fe, op);
     77  } else if(!strcasecmp(argv[i], "true")) {
     78    i++;
     79    owl_filterelement_create_true(fe);
     80  } else if(!strcasecmp(argv[i], "false")) {
     81    i++;
     82    owl_filterelement_create_false(fe);
     83  } else {
     84    if(argc == 1) goto err;
     85    if(!strcasecmp(*argv, "filter")) {
     86      owl_filterelement_create_filter(fe, *(argv+1));
     87    } else if(!strcasecmp(*argv, "perl")) {
     88      owl_filterelement_create_perl(fe, *(argv+1));
     89    } else {
     90      owl_filterelement_create_re(fe, *argv, *(argv+1));
     91    }
     92    i += 2;
     93  }
     94
     95  if(next) {
     96    *next = i;
     97  } else if(i != argc) {
     98    goto err;
     99  }
     100  return fe;
     101err:
     102  owl_filterelement_free(fe);
     103  owl_free(fe);
     104  return NULL;
     105}
     106
     107owl_filterelement * owl_filter_parse_expression(int argc, char **argv, int *next)
     108{
     109  int i = 0, skip;
     110  owl_filterelement * op1 = NULL, * op2 = NULL;
     111
     112  op1 = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
     113  i += skip;
     114  if(!op1) goto err;
     115
     116  while(i < argc) {
     117    if(strcasecmp(argv[i], "and") &&
     118       strcasecmp(argv[i], "or")) break;
     119    op2 = owl_filter_parse_primitive_expression(argc-i-1, argv+i+1, &skip);
     120    if(!op2) goto err;
     121    owl_filterelement * tmp = owl_malloc(sizeof(owl_filterelement));
     122    if(!strcasecmp(argv[i], "and")) {
     123      owl_filterelement_create_and(tmp, op1, op2);
     124    } else {
     125      owl_filterelement_create_or(tmp, op1, op2);
     126    }
     127    op1 = tmp;
     128    op2 = NULL;
     129    i += skip+1;
     130  }
     131
     132  if(next) {
     133    *next = i;
     134  } else if(i != argc) {
     135    goto err;
     136  }
     137  return op1;
     138err:
     139  if(op1) {
     140    owl_filterelement_free(op1);
     141    owl_free(op1);
     142  }
     143  return NULL;
    113144}
    114145
     
    153184int owl_filter_message_match(owl_filter *f, owl_message *m)
    154185{
    155   int i, j, tmp;
    156   owl_list work_fes, *fes;
    157   owl_filterelement *fe;
    158   char *field, *match;
    159 
    160   /* create the working list of expression elements */
    161   fes=&(f->fes);
    162   owl_list_create(&work_fes);
    163   j=owl_list_get_size(fes);
    164   for (i=0; i<j; i++) {
    165     owl_list_append_element(&work_fes, owl_list_get_element(fes, i));
    166   }
    167 
    168   /* first go thru and evaluate all RE elements to true or false */
    169   match="";
    170   for (i=0; i<j; i++) {
    171     fe=owl_list_get_element(&work_fes, i);
    172     if (!owl_filterelement_is_re(fe)) continue;
    173     field=owl_filterelement_get_field(fe);
    174     if (!strcasecmp(field, "class")) {
    175       match=owl_message_get_class(m);
    176     } else if (!strcasecmp(field, "instance")) {
    177       match=owl_message_get_instance(m);
    178     } else if (!strcasecmp(field, "sender")) {
    179       match=owl_message_get_sender(m);
    180     } else if (!strcasecmp(field, "recipient")) {
    181       match=owl_message_get_recipient(m);
    182     } else if (!strcasecmp(field, "body")) {
    183       match=owl_message_get_body(m);
    184     } else if (!strcasecmp(field, "opcode")) {
    185       match=owl_message_get_opcode(m);
    186     } else if (!strcasecmp(field, "realm")) {
    187       match=owl_message_get_realm(m);
    188     } else if (!strcasecmp(field, "type")) {
    189       match=owl_message_get_type(m);
    190     } else if (!strcasecmp(field, "hostname")) {
    191       match=owl_message_get_hostname(m);
    192     } else if (!strcasecmp(field, "direction")) {
    193       if (owl_message_is_direction_out(m)) {
    194         match="out";
    195       } else if (owl_message_is_direction_in(m)) {
    196         match="in";
    197       } else if (owl_message_is_direction_none(m)) {
    198         match="none";
    199       } else {
    200         match="";
    201       }
    202     } else if (!strcasecmp(field, "login")) {
    203       if (owl_message_is_login(m)) {
    204         match="login";
    205       } else if (owl_message_is_logout(m)) {
    206         match="logout";
    207       } else {
    208         match="none";
    209       }
    210     } else {
    211       match = owl_message_get_attribute_value(m,field);
    212       if(match == NULL) match = "";
    213     }
    214 
    215     tmp=owl_regex_compare(owl_filterelement_get_re(fe), match);
    216     if (!tmp) {
    217       owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
    218     } else {
    219       owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
    220     }
    221   }
    222 
    223   /* now subfilters and perl functions */
    224   for (i=0; i<j; i++) {
    225     owl_filter *subfilter;
    226                            
    227     fe=owl_list_get_element(&work_fes, i);
    228 
    229     if (owl_filterelement_is_filter(fe)) {
    230 
    231       subfilter=owl_global_get_filter(&g, owl_filterelement_get_filtername(fe));
    232       if (!subfilter) {
    233         /* the filter does not exist, maybe because it was deleted.
    234          * Default to not matching
    235          */
    236         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
    237       } else if (owl_filter_message_match(subfilter, m)) {
    238         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
    239       } else {
    240         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
    241       }
    242 
    243     } else if (owl_filterelement_is_perl(fe)) {
    244       char *subname, *perlrv;
    245       int   tf=0;
    246 
    247       subname = owl_filterelement_get_filtername(fe);
    248       if (!owl_perlconfig_is_function(subname)) {
    249         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
    250         continue;
    251       }
    252       perlrv = owl_perlconfig_call_with_message(subname, m);
    253       if (perlrv) {
    254         if (0 == strcmp(perlrv, "1")) {
    255           tf=1;
    256         }
    257         owl_free(perlrv);
    258       }
    259       if (tf) {
    260         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
    261       } else {
    262         owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
    263       }
    264     }
    265   }
    266 
    267   /* call the recrsive helper */
    268   i=_owl_filter_message_match_recurse(f, m, &work_fes, 0, owl_list_get_size(&(f->fes))-1);
    269 
    270   /* now there will be only one TRUE / FALSE, find it among the NULL's */
    271   tmp=0;
    272   for (i=0; i<j; i++) {
    273     fe=owl_list_get_element(&work_fes, i);
    274     if (owl_filterelement_is_null(fe)) continue;
    275     if (owl_filterelement_is_true(fe)) {
    276       tmp=1;
    277       break;
    278     }
    279     if (owl_filterelement_is_false(fe)) {
    280       tmp=0;
    281       break;
    282     }
    283   } 
    284 
    285   /* reverse the answer if negative polarity is in use */
    286   if (f->polarity) tmp=!tmp;
    287 
    288   owl_list_free_simple(&work_fes);
    289   return(tmp);
    290 }
    291 
    292 int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end)
    293 {
    294   int a=0, b=0, i, x, y, z, score, ret, type;
    295   owl_filterelement *fe, *tmpfe=NULL;
    296 
    297   /* Deal with parens first. */
    298   for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
    299     /* Find first open paren and matching close paren, store in x, y */
    300     score=x=y=0;
    301     for (i=start; i<=end; i++) {
    302       fe=owl_list_get_element(fes, i);
    303       if (owl_filterelement_is_openbrace(fe)) {
    304         if (score==0) x=i;
    305         score++;
    306       } else if (owl_filterelement_is_closebrace(fe)) {
    307         score--;
    308         if (score<0) {
    309           /* unblanaced parens */
    310           return(-1);
    311         } else if (score==0) {
    312           y=i; /* this is the matching close paren */
    313           break;
    314         }
    315       }
    316     }
    317     if (score>0) {
    318       /* unblanaced parens */
    319       return(-1);
    320     }
    321 
    322     /* Simply the parens by removing them and evaluating what was in between */
    323     if (y>0) {
    324       /* null out the parens */
    325       owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
    326       owl_list_replace_element(fes, y, owl_global_get_filterelement_null(&g));
    327 
    328       /* evaluate expression in between */
    329       ret=_owl_filter_message_match_recurse(f, m, fes, x+1, y-1);
    330       if (ret<0) return(-1);
    331 
    332       /* there may be more, so we continue */
    333       continue;
    334     } else {
    335       /* otherwise we're done with this part */
    336       break;
    337     }
    338   }
    339   if (i==OWL_FILTER_MAX_DEPTH) {
    340     /* hit the saftey limit, consider it invalid */
    341     return(-1);
    342   }
    343 
    344   /* Find AND / OR / NOT.
    345    *   For binary expressions (AND/OR):
    346    *     "type" is 1
    347    *     "x" will index first val, "y" the operator and "z" the second val
    348    *   For unary expressions (NOT):
    349    *     "type" is 2
    350    *     "x" will index the operator, "y" the value
    351    *   "score" tallys how many expression elements have been found so far
    352    */
    353   for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
    354     type=score=x=y=z=0;
    355     for (i=start; i<=end; i++) {
    356       fe=owl_list_get_element(fes, i);
    357       if (owl_filterelement_is_null(fe)) continue;
    358       if (score==0) {
    359         if (owl_filterelement_is_value(fe)) {
    360           x=i;
    361           score=1;
    362           type=1;
    363         } else if (owl_filterelement_is_not(fe)) {
    364           x=i;
    365           score=1;
    366           type=2;
    367         }
    368       } else if (score==1) {
    369         if (type==1) {
    370           if (owl_filterelement_is_and(fe) || owl_filterelement_is_or(fe)) {
    371             score=2;
    372             y=i;
    373           } else {
    374             /* it's not a valid binary expression */
    375             x=y=z=score=0;
    376           }
    377         } else if (type==2) {
    378           if (owl_filterelement_is_value(fe)) {
    379             /* valid unary expression, we're done */
    380             y=i;
    381             break;
    382           }
    383         }
    384       } else if (score==2) {
    385         if (owl_filterelement_is_value(fe)) {
    386           /* valid binary expression, we're done */
    387           z=i;
    388           break;
    389         } else {
    390           x=y=z=score=0;
    391         }
    392       }
    393     }
    394 
    395     /* simplify AND / OR */
    396     if ((type==1) && (z>0)) {
    397       fe=owl_list_get_element(fes, x);
    398       if (owl_filterelement_is_true(fe)) {
    399         a=1;
    400       } else if (owl_filterelement_is_false(fe)) {
    401         a=0;
    402       }
    403 
    404       fe=owl_list_get_element(fes, z);
    405       if (owl_filterelement_is_true(fe)) {
    406         b=1;
    407       } else if (owl_filterelement_is_false(fe)) {
    408         b=0;
    409       }
    410 
    411       fe=owl_list_get_element(fes, y);
    412       if (owl_filterelement_is_and(fe)) {
    413         if (a && b) {
    414           tmpfe=owl_global_get_filterelement_true(&g);
    415         } else {
    416           tmpfe=owl_global_get_filterelement_false(&g);
    417         }
    418       } else if (owl_filterelement_is_or(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       }
    425       owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
    426       owl_list_replace_element(fes, y, tmpfe);
    427       owl_list_replace_element(fes, z, owl_global_get_filterelement_null(&g));
    428     } else if ((type==2) && (y>0)) {
    429       /* simplify NOT */
    430       fe=owl_list_get_element(fes, y);
    431       owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
    432       if (owl_filterelement_is_false(fe)) {
    433         owl_list_replace_element(fes, y, owl_global_get_filterelement_true(&g));
    434       } else {
    435         owl_list_replace_element(fes, y, owl_global_get_filterelement_false(&g));
    436       }
    437     } else {
    438       break;
    439     }
    440   }
    441   return(0);
    442 
    443 }
     186  if(!f->root) return 0;
     187  int ret = owl_filterelement_match(f->root, m);
     188  if(f->polarity) ret = !ret;
     189  return ret;
     190}
     191
    444192
    445193void owl_filter_print(owl_filter *f, char *out)
    446194{
    447   int i, j;
    448   owl_filterelement *fe;
    449   char *tmp;
    450 
    451   strcpy(out, owl_filter_get_name(f));
    452   strcat(out, ": ");
    453 
    454   if (f->color!=OWL_COLOR_DEFAULT) {
    455     strcat(out, "-c ");
    456     strcat(out, owl_util_color_to_string(f->color));
    457     strcat(out, " ");
    458   }
    459 
    460   j=owl_list_get_size(&(f->fes));
    461   for (i=0; i<j; i++) {
    462     fe=owl_list_get_element(&(f->fes), i);
    463     tmp=owl_filterelement_to_string(fe);
    464     strcat(out, tmp);
    465     owl_free(tmp);
    466   }
     195  strcpy(out, "");
     196  if(!f->root) return;
     197  owl_filterelement_print(f->root, out);
    467198  strcat(out, "\n");
    468199}
     
    480211}
    481212
    482 /* Private
    483  * 'list' should already be allocated and initialized
    484  * This function places into list the string names of all filters
    485  * used in the filter expression for 'f'.
    486  * Caller must do a full free on 'list', including elements.
    487  */
    488 void _owl_filter_get_subfilter_names(owl_filter *f, owl_list *list)
    489 {
    490   int i, j;
    491   owl_filterelement *fe;
    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     if (owl_filterelement_is_filter(fe)) {
    497       owl_list_append_element(list, owl_strdup(owl_filterelement_get_filtername(fe)));
    498     }
    499   }
    500 }
    501213
    502214int owl_filter_is_toodeep(owl_filter *f)
    503215{
    504   owl_list seen, tocheck, tmp;
    505   int i, j, x, y;
    506   owl_filter *subfilter;
    507 
    508   owl_list_create(&seen);
    509   owl_list_create(&tocheck);
    510   owl_list_create(&tmp);
    511 
    512   /* seed 'tocheck' with the first set of filters */
    513   _owl_filter_get_subfilter_names(f, &tmp);
    514   j=owl_list_get_size(&tmp);
    515   for (i=0; i<j; i++) {
    516     owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, i));
    517   }
    518   owl_list_free_all(&tmp, owl_free);
    519   owl_list_create(&tmp);
    520 
    521   /* add this list to the 'seen' list */
    522   owl_list_append_element(&seen, owl_strdup(owl_filter_get_name(f)));
    523 
    524   for (i=0; i<OWL_FILTER_MAXRECURSE; i++) {
    525     /* if anything in 'tocheck' is in 'seen' we have a loop */
    526     if (owl_util_common_strings_in_lists(&tocheck, &seen)) {
    527       owl_list_free_all(&tmp, owl_free);
    528       owl_list_free_all(&tocheck, owl_free);
    529       owl_list_free_all(&seen, owl_free);
    530       return(1);
    531     }
    532 
    533     /* if there's nothing to check, we're done */
    534     y=owl_list_get_size(&tocheck);
    535     if (y==0) {
    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(0);
    540     }
    541 
    542     /* add everything in 'tocheck' to the 'seen' list */
    543     /* y=owl_list_get_size(&tocheck); */
    544     for (x=0; x<y; x++) {
    545       owl_list_append_element(&seen, owl_strdup(owl_list_get_element(&tocheck, x)));
    546     }
    547 
    548     /* make a new list into 'tmp' with the children of 'tocheck' */
    549     /* y=owl_list_get_size(&tocheck); */
    550     for (x=0; x<y; x++) {
    551       subfilter=owl_global_get_filter(&g, owl_list_get_element(&tocheck, x));
    552       if (!subfilter) return(0);
    553       _owl_filter_get_subfilter_names(subfilter, &tmp);
    554     }
    555 
    556     /* clean out 'tocheck' */
    557     owl_list_free_all(&tocheck, owl_free);
    558     owl_list_create(&tocheck);
    559 
    560     /* put 'tmp' into 'tocheck' */
    561     y=owl_list_get_size(&tmp);
    562     for (x=0; x<y; x++) {
    563       owl_util_list_add_unique_string(&tocheck, owl_list_get_element(&tmp, x));
    564     }
    565 
    566     /* clean out 'tmp' */
    567     owl_list_free_all(&tmp, owl_free);
    568     owl_list_create(&tmp);
    569   }
    570 
    571   owl_list_free_all(&tmp, owl_free);
    572   owl_list_free_all(&tocheck, owl_free);
    573   owl_list_free_all(&seen, owl_free);
    574 
    575   return(1);
     216  return owl_filterelement_is_toodeep(f, f->root);
    576217}
    577218
    578219void owl_filter_free(owl_filter *f)
    579220{
    580   void (*func)();
    581 
    582   func=&owl_filterelement_free;
    583  
     221  if(f->root) {
     222    owl_filterelement_free(f->root);
     223    owl_free(f->root);
     224  }
    584225  if (f->name) owl_free(f->name);
    585   owl_list_free_all(&(f->fes), func);
    586 }
     226}
     227
     228/**************************************************************************/
     229/************************* REGRESSION TESTS *******************************/
     230/**************************************************************************/
     231
     232#ifdef OWL_INCLUDE_REG_TESTS
     233
     234int owl_filter_test_string(char * filt, owl_message *m, int shouldmatch) /* noproto */ {
     235  owl_filter f;
     236  int ok;
     237  int failed = 0;
     238  if(owl_filter_init_fromstring(&f, "test-filter", filt)) {
     239    printf("\tFAIL: parse %s\n", filt);
     240    failed = 1;
     241    goto out;
     242  }
     243  ok = owl_filter_message_match(&f, m);
     244  if((shouldmatch && !ok) || (!shouldmatch && ok)) {
     245    printf("\tFAIL: match %s (got %d, expected %d)\n", filt, ok, shouldmatch);
     246    failed = 1;
     247  }
     248 out:
     249  owl_filter_free(&f);
     250  if(!failed) {
     251    printf("\tok  : %s %s\n", shouldmatch ? "matches" : "doesn't match", filt);
     252  }
     253  return failed;
     254}
     255
     256
     257#include "test.h"
     258
     259
     260int owl_filter_regtest(void) {
     261  owl_list_create(&(g.filterlist));
     262  int numfailed=0;
     263  owl_message m;
     264  owl_message_init(&m);
     265  owl_message_set_type_zephyr(&m);
     266  owl_message_set_direction_in(&m);
     267  owl_message_set_class(&m, "owl");
     268  owl_message_set_instance(&m, "tester");
     269  owl_message_set_sender(&m, "owl-user");
     270  owl_message_set_recipient(&m, "joe");
     271  owl_message_set_attribute(&m, "foo", "bar");
     272
     273#define TEST_FILTER(f, e) numfailed += owl_filter_test_string(f, &m, e)
     274
     275
     276  TEST_FILTER("true", 1);
     277  TEST_FILTER("false", 0);
     278  TEST_FILTER("( true )", 1);
     279  TEST_FILTER("not false", 1);
     280  TEST_FILTER("( true ) or ( false )", 1);
     281  TEST_FILTER("true and false", 0);
     282  TEST_FILTER("( true or true ) or ( ( false ) )", 1);
     283
     284  TEST_FILTER("class owl", 1);
     285  TEST_FILTER("class ^owl$", 1);
     286  TEST_FILTER("instance test", 1);
     287  TEST_FILTER("instance ^test$", 0);
     288  TEST_FILTER("instance ^tester$", 1);
     289
     290  TEST_FILTER("foo bar", 1);
     291  TEST_FILTER("class owl and instance tester", 1);
     292  TEST_FILTER("type ^zephyr$ and direction ^in$ and ( class ^owl$ or instance ^owl$ )", 1);
     293
     294  // Order of operations and precedence
     295  TEST_FILTER("not true or false", 0);
     296  TEST_FILTER("true or true and false", 0);
     297  TEST_FILTER("true and true and false or true", 1);
     298  TEST_FILTER("false and false or true", 1);
     299  TEST_FILTER("true and false or false", 0);
     300
     301  owl_filter f1, f2, f3, f4;
     302
     303  owl_filter_init_fromstring(&f1, "f1", "class owl");
     304  owl_global_add_filter(&g, &f1);
     305  TEST_FILTER("filter f1", 1);
     306
     307  // Test recursion prevention
     308  FAIL_UNLESS("self reference", owl_filter_init_fromstring(&f2, "test", "filter test"));
     309
     310  // mutual recursion
     311  owl_filter_init_fromstring(&f3, "f3", "filter f4");
     312  owl_global_add_filter(&g, &f3);
     313  FAIL_UNLESS("mutual recursion",   owl_filter_init_fromstring(&f4, "f4", "filter f3"));
     314
     315  return 0;
     316}
     317
     318
     319#endif /* OWL_INCLUDE_REG_TESTS */
Note: See TracChangeset for help on using the changeset viewer.