Logo Search packages:      
Sourcecode: filter version File versions  Download package

parse.c

static char rcsid[] ="@(#)$Id: parse.c,v 5.13 1993/09/19 23:11:17 syd Exp $";

/*******************************************************************************
 *  The Elm Mail System  -  $Revision: 5.13 $   $State: Exp $
 *
 *                Copyright (c) 1988-1992 USENET Community Trust
 *                Copyright (c) 1986,1987 Dave Taylor
 *******************************************************************************
 * Bug reports, patches, comments, suggestions should be sent to:
 *
 *    Philip Brown    filter@bolthole.com
 *
 *******************************************************************************
 * $Log: parse.c,v $
 *
 * 1998/03/31 phil
 * Added "rcpt-to" support
 *
 * Revision 5.13  1993/09/19  23:11:17  syd
 * strtokq was called with the wrong number of parameters.
 * From: Jan.Djarv@sa.erisoft.se (Jan Djarv)
 *
 * Revision 5.12  1993/08/03  19:28:39  syd
 * Elm tries to replace the system toupper() and tolower() on current
 * BSD systems, which is unnecessary.  Even worse, the replacements
 * collide during linking with routines in isctype.o.  This patch adds
 * a Configure test to determine whether replacements are really needed
 * (BROKE_CTYPE definition).  The <ctype.h> header file is now included
 * globally through hdrs/defs.h and the BROKE_CTYPE patchup is handled
 * there.  Inclusion of <ctype.h> was removed from *all* the individual
 * files, and the toupper() and tolower() routines in lib/opt_utils.c
 * were dropped.
 * From: chip@chinacat.unicom.com (Chip Rosenthal)
 *
 * Revision 5.11  1993/04/12  03:04:01  syd
 * Removed a malloc of a struct condition_rec that is never used.
 * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
 *
 * Revision 5.10  1993/04/12  03:03:07  syd
 * The setting of relop to the default (EQ) was in the wrong place,
 * causing the wrong relation to be inserted in the rule.
 * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
 *
 * Revision 5.9  1993/02/03  16:22:22  syd
 * move strtokq to lib directory
 * From: Syd
 *
 * Revision 5.8  1993/01/27  19:45:15  syd
 * Filter turns spaces in quoted strings into _ and then back again. This destroys
 * any _ that where there in the first place. This patch removes that.
 * Also fixed a minor bug where 'filter -r' wrote out the wrong thing if the
 * relation in a rule was '~'.
 * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
 *
 * Revision 5.7  1993/01/27  19:40:01  syd
 * I implemented a change to filter's default verbose message format
 * including %x %X style date and time along with username
 * From: mark@drd.com (Mark Lawrence)
 *
 * Revision 5.6  1992/12/11  01:45:04  syd
 * remove sys/types.h include, it is now included by defs.h
 * and this routine includes defs.h or indirectly includes defs.h
 * From: Syd
 *
 * Revision 5.5  1992/12/07  05:08:03  syd
 * add sys/types for time_t
 * From: Syd
 *
 * Revision 5.4  1992/11/15  01:40:43  syd
 * Add regexp processing to filter.
 * Add execc operator
 * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
 *
 * Revision 5.3  1992/10/28  14:52:25  syd
 * fix compiler warning
 * From: steve@nshore.org (Stephen J. Walick)
 *
 * Revision 5.2  1992/10/24  14:20:24  syd
 * remove the 25 (MAXRULES) limitation.
 * Basically it mallocs rules in hunks of RULESINC (25) as it goes along.
 * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
 *
 * Revision 5.1  1992/10/03  22:18:09  syd
 * Initial checkin as of 2.4 Release at PL0
 *
 *
 ******************************************************************************/


/** This is the parser for the filter program.  It accepts a wide variety of
    constructs, building the ruleset table as it goes along.  Check the 
    data structure in filter.h for more information on how the rules are
    stored.  The parser is a cunning state-table based program.

**/

#include <stdio.h>

#include "defs.h"
#include "filter.h"
#include "s_filter.h"
#include "utils.h"

#define NONE                  0
#define AND             10

#define NEXT_CONDITION        0
#define GETTING_OP            1
#define READING_ARGUMENT      2
#define READING_ACTION        3
#define ACTION_ARGUMENT       4

char *whatname(), *actionname();
extern char *date_n_user();

static int
grow(ptr, oldsize, incr)
struct ruleset_record **ptr;
int oldsize, incr;
{
      struct ruleset_record *newptr;
      int newsize;

      if (ptr == 0) return 0;

      newsize = oldsize + incr;
      if (oldsize == 0)
        newptr = (struct ruleset_record *)
              malloc(sizeof(struct ruleset_record)*newsize);
      else
        newptr = (struct ruleset_record *)
              realloc((char *) *ptr, sizeof(struct ruleset_record)*newsize);

      if (newptr == NULL){
        if (outfptr != NULL)
          fprintf(outfptr,catgets(elm_msg_cat,
                          FilterSet,FilterCantAllocRules,
                  "filter (%s): Can't alloc memory for %d rules!\n"),
                  date_n_user(), newsize);
        if (outfptr != NULL) fclose(outfptr);
        exit(1);
      }

      *ptr = newptr;
      return newsize;
}

int
get_filter_rules()
{
      /** Given the users home directory, open and parse their rules table,
          building the data structure as we go along.
          returns -1 if we hit an error of any sort...
      **/

      FILE
           *fd;                     /* the file descriptor     */
      char
           buffer[SLEN],                  /* fd reading buffer       */
           *str,                    /* ptr to read string      */
           *word,                   /* ptr to 'token'          */
           filename[SLEN],                /* the name of the ruleset */
           action_argument[SLEN],         /* action arg, per rule    */
           cond_argument[SLEN];           /* cond arg, per condition */
      int
           not_condition = FALSE,         /* are we in a "not" ??    */
           type=NONE,               /* what TYPE of condition? */
           lasttype=NONE,                 /* and the previous TYPE?  */
           state = NEXT_CONDITION,        /* the current state       */
           relop = NONE,                  /* relational operator     */
           action,                        /* the current action type */
           buflen,                        /* the length of buffer    */
           line = 0;                      /* line number we're on    */
      struct condition_rec
           *cond, *newcond;

      stringcopy(filename,filterfile, SLEN);

      if ((fd = fopen(filename,"r")) == NULL) {
        if (outfptr != NULL)
         fprintf(outfptr,catgets(elm_msg_cat,FilterSet,FilterCouldntReadRules,
         "filter (%s): Couldn't read filter rules file \"%s\"!\n"),date_n_user(),
              filename);
        return(-1);
      }

      if (sizeof_rules == 0)
        sizeof_rules = grow(&rules, sizeof_rules, RULESINC);

      cond_argument[0] = action_argument[0] = '\0';

      /* Now, for each line... **/

      if ((cond = (struct condition_rec *) 
                 malloc(sizeof(struct condition_rec))) == NULL) {
        if (outfptr != NULL)
          fprintf(outfptr,catgets(elm_msg_cat,
                          FilterSet,FilterCouldntMallocFirst,
              "filter (%s): couldn't malloc first condition rec!\n"),
                date_n_user());
        return(-1);
      }
      
      rules[total_rules].condition = cond;      /* hooked in! */

      while (fgets(buffer, SLEN, fd) != NULL) {
        line++;
        buflen = strlen(buffer);
        if (buffer[0] == '#')
          continue;           /* nothing to look at! */
        if (buflen <2)
          continue;

        if (lasttype != AND && lasttype != NONE) {
          lasttype = type;
          type = NONE;
        }
        str = (char *) buffer;

        /** Three pieces to this loop - get the `field', the 'relop' (if
            there) then, if needed, get the argument to check against (not 
            needed for errors or the AND, of course)
        **/

        while ((word = strtokq(str, " ()[]:\t\n", 0)) != NULL) {

          str = (char *) NULL;            /* we can start stomping! */
        
          lowercase(word);

          if (strcmp(word, "if") == 0) {  /* only ONE 'if' allowed */
            if ((word = strtokq(str, " ()[]:\t\n", 0)) == NULL) /* NEXT! */
              continue;
            lowercase(word);
          }
      
          if (state == NEXT_CONDITION) {
            lasttype = type;
            type = NONE;

            if (THE_SAME(word, "not") || THE_SAME(word, "!")) {
              not_condition = TRUE;
              if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
                continue;
            }

            if (THE_SAME(word, "from"))
               type = FROM;
            else if (THE_SAME(word, "rcpt-to"))
               type = RCPT_TO;
            else if (THE_SAME(word, "to"))
               type = TO;
            else if (THE_SAME(word, "mailinglist"))
               type = MAILLIST;
            else if (THE_SAME(word, "subject"))
               type = SUBJECT;
            else if (THE_SAME(word, "alphasubject"))
               type = ALPHASUBJECT;
            else if (THE_SAME(word, "sender"))
               type = SENDER;
            else if (THE_SAME(word, "lines"))
               type = LINES;
            else if (THE_SAME(word, "contains"))
               type = CONTAINS;
            else if (THE_SAME(word, "and") || 
                     THE_SAME(word, "&&"))
               type = AND;
            else if (THE_SAME(word,"?") ||
                   THE_SAME(word, "then") || 
                   THE_SAME(word, "always")) {

            /** shove THIS puppy into the structure and let's continue! **/

              if (lasttype == AND) {
              if (outfptr != NULL)
                  fprintf(outfptr,catgets(elm_msg_cat,
                                FilterSet,FilterErrorReadingRules1,
       "filter (%s): Error reading line %d of rules - badly placed \"and\"\n"),
                date_n_user(), line);
              return(-1);
              }

              if (THE_SAME(word, "always"))
              cond->matchwhat = ALWAYS;   /* so it's a hack... */
            else
              cond->matchwhat = lasttype;

              if (relop == NONE) relop = EQ;    /* otherwise can't do -relop */
              cond->relation  = (not_condition? - (relop) : relop);
            cond->regex = NULL;

            stringcopy(cond->argument1, cond_argument, SLEN);
              cond->next = NULL;

              state = READING_ACTION;
              if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
                continue;
              goto get_outta_loop;
            }

            if (type == NONE) {
            if (outfptr != NULL)
                fprintf(outfptr,catgets(elm_msg_cat,
                              FilterSet,FilterErrorReadingRules2,
      "filter (%s): Error reading line %d of rules - field \"%s\" unknown!\n"),
                 date_n_user(), line, word);
            return(-1);
            }

            if (type == AND) {

            /** shove THIS puppy into the structure and let's continue! **/

            cond->matchwhat = lasttype;
              if (relop == NONE) relop = EQ;    /* otherwise can't do -relop */
              cond->relation  = (not_condition? - (relop) : relop);
            cond->regex = NULL;
            stringcopy(cond->argument1, cond_argument, SLEN);
              if ((newcond = (struct condition_rec *)
                   malloc(sizeof(struct condition_rec))) == NULL) {
              if (outfptr != NULL)
                  fprintf(outfptr,catgets(elm_msg_cat,
                                FilterSet,FilterCouldntMallocNew ,
                  "filter (%s): Couldn't malloc new cond rec!!\n"),
                  date_n_user());
              return(-1);
              }
              cond->next = newcond;
            cond = newcond;
            cond->next = NULL;

              not_condition = FALSE;
              state = NEXT_CONDITION;
            }
            else {
              state = GETTING_OP;
            }
          }

get_outta_loop:   /* jump out when we change state, if needed */

          if (state == GETTING_OP) {

             if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
               continue;

             lowercase(word);

             relop = NONE;

             if (THE_SAME(word, "=") || THE_SAME(word, "in") || 
                   THE_SAME(word, "contains")) {
                 state = READING_ARGUMENT;
               relop = EQ;
             }
             else {
               if (THE_SAME(word, "<>")||
                      THE_SAME(word, "!="))     relop = NE;
               else if (THE_SAME(word, "~")||
                    THE_SAME(word, "matches")) relop = RE;

            /* Everything below here is for number of lines ONLY */

               else if (THE_SAME(word, "<="))   relop = LE;
               else if (THE_SAME(word, ">="))   relop = GE;
               else if (THE_SAME(word, ">"))    relop = GT;
               else if (THE_SAME(word, "<"))    relop = LT;

             switch(relop){
                case GE: case GT: case LT: case LE:
                  if((type!=LINES) && (outfptr != NULL)){
                   fprintf(outfptr,
                         catgets(elm_msg_cat,
                              FilterSet,FilterErrorReadingRules4,
                  "filter (%s): Bad comparison at line %d\n"),
                         date_n_user(),line);
                  return(-1);
                  }
             }
             /* maybe there isn't a relop at all!! */

             state=READING_ARGUMENT;

             }
          }
             
          if (state == READING_ARGUMENT) {
            if (relop == RE){
            /* Special for regular expressions (enclosed between //) */
            cond_argument[0] = '\0';
            for (;;) {
                if ((word = strtokq(str, "/", 0)) == NULL)
                  break;
              strncat(cond_argument, word, SLEN);
              if (word[strlen(word)-1] == '\\') /* If / was escaped ... */
                strncat(cond_argument, "/", SLEN);
              else
                break;
            }
            if (word == NULL)
              continue;

            } else {
            if (relop != NONE) {
                if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
                  continue;
              }
              stringcopy(cond_argument, word, SLEN);
            }
            state = NEXT_CONDITION;
          }

          if (state == READING_ACTION) {
            action = NONE;

            not_condition = FALSE;

            if (THE_SAME(word, "delete"))       action = DELETE_MSG;
            else if (THE_SAME(word, "savec"))   action = SAVECC;
            else if (THE_SAME(word, "save"))    action = SAVE;
            else if (THE_SAME(word, "forwardc")) action = FORWARDC;
            else if (THE_SAME(word, "forward")) action = FORWARD;
            else if (THE_SAME(word, "executec")) action = EXECC;
            else if (THE_SAME(word, "exec"))    action = EXEC;
            else if (THE_SAME(word, "leave"))   action = LEAVE;
            else if (THE_SAME(word, "bounce"))   action = BOUNCE;
            else if (THE_SAME(word, "resend"))   action = RESEND;
            else {
            if (outfptr != NULL)
                fprintf(outfptr,catgets(elm_msg_cat,
                              FilterSet,FilterErrorReadingRules3 ,
      "filter (%s): Error on line %d of rules - action \"%s\" unknown\n"),
                  date_n_user(), line, word);
            }

            if (action == DELETE_MSG || action == LEAVE
              || action == BOUNCE) {
              /** add this to the rules section and alloc next... **/
            /* no further arguments */

              rules[total_rules].action = action;
            rules[total_rules].argument2[0] = '\0';   /* nothing! */
              if (++total_rules >= sizeof_rules)
              sizeof_rules = grow(&rules, sizeof_rules, RULESINC);
               
              if ((cond = (struct condition_rec *)
                 malloc(sizeof(struct condition_rec))) == NULL) {
              if (outfptr != NULL)
                  fprintf(outfptr,catgets(elm_msg_cat,
                                FilterSet,FilterCouldntMallocFirst,
                  "filter (%s): couldn't malloc first condition rec!\n"),
                  date_n_user());
                return(-1);
              }
      
              rules[total_rules].condition = cond;    /* hooked in! */
              state = NEXT_CONDITION;     
            }
            else {
              state = ACTION_ARGUMENT;
            }

            if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
              continue;

          }
      
          if (state == ACTION_ARGUMENT) {
            stringcopy(action_argument, word, SLEN);

            /** add this to the rules section and alloc next... **/

            rules[total_rules].action = action;
            rules[total_rules].line = line;

            /** if we are not printing rules we can't expand macros
              until after we applied the rule, due to the regexp macros
            **/
            if (printing_rules)
              expand_macros(action_argument, rules[total_rules].argument2,
                        line, printing_rules);
            else
            stringcopy(rules[total_rules].argument2, action_argument,SLEN);

            if (++total_rules >= sizeof_rules)
              sizeof_rules = grow(&rules, sizeof_rules, RULESINC);

            if ((cond = (struct condition_rec *)
                 malloc(sizeof(struct condition_rec))) == NULL) {
            if (outfptr != NULL)
                fprintf(outfptr,catgets(elm_msg_cat,
                              FilterSet,FilterCouldntMallocFirst,
                      "filter (%s): couldn't malloc first condition rec!\n"),
                    date_n_user());
              return(-1);
            }
      
            rules[total_rules].condition = cond;      /* hooked in! */

            state = NEXT_CONDITION;
            if ((word = strtokq(str, " ()[]\t\n", 1)) == NULL)
              continue;
          }
        }
      }

      return(0);
}

Generated by  Doxygen 1.6.0   Back to index