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

maildirfilter.c

/*
** Copyright 2000-2003 Double Precision, Inc.
** See COPYING for distribution information.
*/

#include    "config.h"
#include    "maildirfilter.h"
#include    "maildirfiltertypelist.h"
#include    "maildirgetquota.h"
#include    "mailbot.h"

#include    "autoresponse.h"
#include    "numlib/numlib.h"
#include    <stdlib.h>
#include    <string.h>
#include    <stdio.h>
#include    <ctype.h>
#include    <errno.h>
#include    <sys/types.h>
#if   HAVE_SYS_STAT_H
#include    <sys/stat.h>
#endif
#if   HAVE_UNISTD_H
#include    <unistd.h>
#endif

static const char rcsid[]="$Id: maildirfilter.c,v 1.20 2003/04/20 03:44:42 mrsam Exp $";

struct maildirfilterrule *maildir_filter_appendrule(struct maildirfilter *r,
                              const char *name,
                              enum maildirfiltertype type,
                              int flags,
                              const char *header,
                              const char *value,
                              const char *folder,
                              const char *fromhdr,
                              int *errcode)
{
struct maildirfilterrule *p=malloc(sizeof(struct maildirfilterrule));

      *errcode=MF_ERR_INTERNAL;

      if (!p)     return (0);
      memset(p, 0, sizeof(*p));

      if ((p->prev=r->last) != 0)
            p->prev->next=p;
      else
            r->first=p;
      r->last=p;

      if (maildir_filter_ruleupdate(r, p, name, type, flags,
                    header, value, folder, fromhdr, errcode))
      {
            maildir_filter_ruledel(r, p);
            return (0);
      }
      return (p);
}

int maildir_filter_ruleupdate(struct maildirfilter *r,
              struct maildirfilterrule *p,
              const char *name,
              enum maildirfiltertype type,
              int flags,
              const char *header,
              const char *value,
              const char *folder,
              const char *fromhdr,
              int *errcode)
{
const char *c;

/*
** Before creating a new rule, validate all input.
*/

      *errcode=0;

      /* rule name: may not contain quotes or control characters. */
      *errcode=MF_ERR_BADRULENAME;
      if (!name || !*name || strlen(name) > 200)
            return (-1);

      for (c=name; *c; c++)
            if ((unsigned char)*c < ' ' || *c == '\'' || *c == '"' ||
                  *c == '`')
                  return (-1);

      /* rule type: we must know what it is */

      switch (type)     {
      case startswith:
      case endswith:
      case contains:
      case hasrecipient:
      case mimemultipart:
      case textplain:
      case islargerthan:
            break;
      default:
            *errcode=MF_ERR_BADRULETYPE;
            break;
      } ;

      /* header: */

      *errcode=MF_ERR_BADRULEHEADER;

      c=header;
      if (c && strlen(c) > 200)     return (-1);
      if (c == 0 || *c == 0)
      {
            switch (type)     {
            case hasrecipient:
            case islargerthan:
            case mimemultipart:
            case textplain:
                  break;
            case contains:
            case startswith:
            case endswith:
                  if (flags & MFR_BODY)
                        break;
                  /* FALLTHRU */
            default:
                  /* required */

                  return (-1);
            }
      }
      else for ( ; *c; c++)
      {
            /* no control characters */
            if (*c <= ' ' || *c == MDIRSEP[0] || *c >= 127 || *c == '\'' ||
                  *c == '\\' || *c == '"' || *c == '`' || *c == '/')
                  return (-1);
      }

      /* rule pattern */

      *errcode=MF_ERR_BADRULEVALUE;

      c=value;
      if (c && strlen(c) > 200)     return (-1);
      if (c == 0 || *c == 0)
      {
            switch (type)     {
            case mimemultipart:
            case textplain:
                  break;
            default:
                  /* required */

                  return (-1);
            }
      }
      else if (!(flags & MFR_PLAINSTRING))
      {
/*
** This is ugly.  We: make sure parenthesis match up, and look out for
** special characters.
*/
      int lparencount=0;

            while (*c)
            {
                  if (*c == '/' || *c == '$' || *c == '!'
                        || *c == '`' || (int)(unsigned char)*c < ' '
                        || *c == '\'' || *c == '"') return (-1);
                                    /* must be escaped */

                  if (type == islargerthan)
                  {
                        if (!isdigit((int)(unsigned char)*c))
                              return (-1);
                  }

                  if (*c == '(')
                  {
                        if (type == hasrecipient)     return (-1);
                        ++lparencount;
                        ++c;
                        if (*c == ')')    return (-1);
                        continue;
                  }
                  if (*c == ')')
                  {
                        if (type == hasrecipient)     return (-1);
                        if (lparencount == 0)   return (-1);
                        ++c;
                        --lparencount;
                        continue;
                  }
                  if (*c == '[')    /* This is a set */
                  {
                        if (type == hasrecipient)     return (-1);
                        ++c;
                        for (;;)
                        {
                              if (*c == '\'' || *c == '"' ||
                                    *c == '`')
                                    return (-1); /* must be quoted*/
                              if (*c == '\\')
                                    ++c;
                              if (!*c)    return (-1);
                              if ((int)(unsigned char)*c < ' ')
                                    return (-1);
                              ++c;
                              if (*c == ']')    break;
                              if (*c != '-')    continue;
                              ++c;

                              if (*c == '\'' || *c == '"' ||
                                    *c == '`')
                                    return (-1); /* must be quoted*/
                              if (*c == '\\')
                                    ++c;
                              if ((int)(unsigned char)*c < ' ')
                                    return (-1);
                              if (!*c)    return (-1);
                              ++c;
                        }
                        ++c;
                        continue;
                  }

                  if (*c == '\\')
                  {
                        if (type == hasrecipient)     return (-1);
                        ++c;
                  }
                  if (!*c)    return (-1);
                  ++c;
            }
            if (lparencount)  return (-1);      /* not balanced */
      }

      /* validate FROM header */

      *errcode=MF_ERR_BADFROMHDR;

      while (fromhdr && *fromhdr && isspace((int)(unsigned char)*fromhdr))
            ++fromhdr;

      for (c=fromhdr; *c; c++)
            if (*c == '\'' || *c == '\\' ||
                (int)(unsigned char)*c < ' ')
                  return (-1);

      *errcode=MF_ERR_BADRULEFOLDER;

      /* validate name of destination folder */

      c=folder;
      if (!c)     return (-1);
      if (strlen(c) > 200)    return (-1);

      if (*c == '*' || *c == '!')
      {
            /* Forward, or bounce with an error */

            ++c;
            for ( ; *c; c++)
            {
                  if (strchr("'\"$\\`;(){}#&<>~", *c) ||
                        (unsigned char)*c < ' ')
                        return (-1);
            }
      }
      else if (*c == '+')     /* Autorespond */
      {
            struct maildir_filter_autoresp_info ai;

            if (maildir_filter_autoresp_info_init_str(&ai, c+1))
                  return (-1);

            maildir_filter_autoresp_info_free(&ai);
      }
      else if (strcmp(c, "."))
      {
            for ( ; *c; c++)
            {
                  if (*c == '.' && (c[1] == '.' || c[1] == 0))
                        return (-1);
                  if ((int)(unsigned char)*c < ' ' || *c == '\\'
                        || *c == '|' || *c == '!')
                        return (-1);
            }
      }

      /* OK, we're good */

      *errcode=MF_ERR_INTERNAL;

      if (p->rulename)  free(p->rulename);
      if ((p->rulename=strdup(name)) == 0)      return (-1);
      p->type=type;
      if (p->fieldname) free(p->fieldname);
      if ((p->fieldname=strdup(header ? header:"")) == 0)   return (-1);
      if (p->fieldvalue)      free(p->fieldvalue);
      if ((p->fieldvalue=strdup(value ? value:"")) == 0)    return (-1);
      if (p->tofolder)  free(p->tofolder);
      if ((p->tofolder=malloc(strlen(folder)+2)) == 0)      return (-1);
      *p->tofolder=0;
      /* Prefix with dot by default */
      if (*folder != '*' && *folder != '!' && *folder != '.'
          && *folder != '+')
            strcpy(p->tofolder, ".");
      strcat(p->tofolder, folder);

      if (p->fromhdr)         free(p->fromhdr);
      if ((p->fromhdr=strdup(fromhdr ? fromhdr:"")) == NULL)
            return (-1);

      p->flags=flags;
      return (0);
}

void maildir_filter_ruledel(struct maildirfilter *r, struct maildirfilterrule *p)
{
      if (p->prev)      p->prev->next=p->next;
      else        r->first=p->next;

      if (p->next)      p->next->prev=p->prev;
      else        r->last=p->prev;

      if (p->rulename)  free(p->rulename);
      if (p->fieldname) free(p->fieldname);
      if (p->fieldvalue)      free(p->fieldvalue);
      if (p->tofolder)  free(p->tofolder);
      if (p->fromhdr)         free(p->fromhdr);
      free(p);
}

void maildir_filter_ruleup(struct maildirfilter *r, struct maildirfilterrule *p)
{
struct maildirfilterrule *q;

      q=p->prev;
      if (!q)     return;
      q->next=p->next;
      if (p->next)      p->next->prev=q;
      else        r->last=q;

      if ((p->prev=q->prev) != 0)   p->prev->next=p;
      else  r->first=p;

      p->next=q;
      q->prev=p;
}

void maildir_filter_ruledown(struct maildirfilter *r, struct maildirfilterrule *p)
{
struct maildirfilterrule *q;

      q=p->next;
      if (!q)     return;
      q->prev=p->prev;
      if (q->prev)      q->prev->next=q;
      else        r->first=q;

      if ((p->next=q->next) != 0)   p->next->prev=p;
      else  r->last=p;

      p->prev=q;
      q->next=p;
}

static void print_pattern(FILE *f, int flags, const char *v)
{
      if (!(flags & MFR_PLAINSTRING))
      {
            fprintf(f, "%s%s",
                  *v && isspace((int)(unsigned char)*v) ? "\\":"", v);
            return;
      }

      while (*v)
      {
            if (!isalnum((int)(unsigned char)*v))
                  putc('\\', f);
            putc((int)(unsigned char)*v, f);
            ++v;
      }
}

int maildir_filter_saverules(struct maildirfilter *r, const char *filename,
             const char *maildir,
             const char *maildirpath, const char *fromaddr)
{
FILE  *f=fopen(filename, "w");
struct maildirfilterrule *p;

      if (!f)     return (-1);

      fprintf(f,  "#MFMAILDROP=2\n"
                  "#\n"
                  "# DO NOT EDIT THIS FILE.  This is an automatically"
                                    " generated filter.\n"
                  "\n");

      for (fprintf(f, "FROM='"); *fromaddr; fromaddr++)
      {
            if (*fromaddr != '"' && *fromaddr != '\'' && *fromaddr != '\\')
                  putc(*fromaddr, f);
      }
      fprintf(f, "\'\nimport SENDER\nif ($SENDER eq \"\")\n{\n SENDER=$FROM\n}\n\n");

      for (p=r->first; p; p=p->next)
      {
      const char *fieldname=p->fieldname ? p->fieldname:"";
      const char *fieldvalue=p->fieldvalue ? p->fieldvalue:"";
      const char *tofolder=p->tofolder ? p->tofolder:"";

            fprintf(f, "##Op:%s\n",
                  typelist[p->type].name);
            fprintf(f, "##Header:%s\n", fieldname);
            fprintf(f, "##Value:%s\n", fieldvalue);
            fprintf(f, "##Folder:%s\n", tofolder);
            fprintf(f, "##From:%s\n", p->fromhdr ? p->fromhdr:"");

            if (p->flags & MFR_PLAINSTRING)
                  fprintf(f, "##PlainString\n");
            if (p->flags & MFR_DOESNOT)
                  fprintf(f, "##DoesNot\n");
            if (p->flags & MFR_BODY)
                  fprintf(f, "##Body\n");
            if (p->flags & MFR_CONTINUE)
                  fprintf(f, "##Continue\n");

            fprintf(f, "##Name:%s\n\n", p->rulename ? p->rulename:"");

            fprintf(f, "\nif (");

            if (p->flags & MFR_DOESNOT)
                  fprintf(f, "!");
            fprintf(f, "(");

            switch (p->type)  {
            case startswith:
                  if (p->flags & MFR_BODY)
                  {
                        fprintf(f, "/^");
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "/:b");
                  }
                  else
                  {
                        fprintf(f, "/^%s: *", fieldname);
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "/");
                  }
                  break;
            case endswith:
                  if (p->flags & MFR_BODY)
                  {
                        fprintf(f, "/");
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "$/:b");
                  }
                  else
                  {
                        fprintf(f, "/^%s:.*", fieldname);
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "$/");
                  }
                  break;
            case contains:
                  if (p->flags & MFR_BODY)
                  {
                        fprintf(f, "/");
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "/:b");
                  }
                  else
                  {
                        fprintf(f, "/^%s:.*", fieldname);
                        print_pattern(f, p->flags, fieldvalue);
                        fprintf(f, "/");
                  }
                  break;
            case hasrecipient:
                  fprintf(f, "hasaddr(\"%s\")", fieldvalue);
                  break;
            case mimemultipart:
                  fprintf(f, "/^Content-Type: *multipart\\/mixed/");
                  break;
            case textplain:
                  fprintf(f, " (! /^Content-Type:/) || "
                              "/^Content-Type: text\\/plain$/ || "
                              "/^Content-Type: text\\/plain;/");
                  break;
            case islargerthan:
                  fprintf(f, "$SIZE > %s", fieldvalue);
                  break;
            }
            fprintf(f, "))\n"
                  "{\n");

            if (*tofolder == '!')
            {
                  fprintf(f, "    %s \"| $SENDMAIL -f \" '\"$SENDER\"' \" %s\"\n",
                        p->flags & MFR_CONTINUE ? "cc":"to",
                              tofolder+1);
            }
            else if (*tofolder == '*')
            {
                  fprintf(f, "    echo \"%s\"\n"
                        "    EXITCODE=77\n"
                        "    exit\n", tofolder+1);
            }
            else if (*tofolder == '+')
            {
                  struct maildir_filter_autoresp_info ai;

                  if (maildir_filter_autoresp_info_init_str(&ai, tofolder+1) == 0)
                  {

                        if (p->fromhdr && p->fromhdr[0])
                              fprintf(f, "    AUTOREPLYFROM='%s'\n",
                                    p->fromhdr);
                        else
                              fprintf(f, "    AUTOREPLYFROM=$FROM\n"
                                    );

                        fprintf(f, "   `%s -A \"X-Sender: $FROM\""
                              " -A \"From: $AUTOREPLYFROM\"",
                              MAILBOT);
                        if (ai.dsnflag)
                              fprintf(f, " -M \"$FROM\"");
                        fprintf(f, " -m \"%s/autoresponses/%s\"",
                              maildirpath, ai.name);
                        if (ai.days > 0)
                              fprintf(f,
                                    " -d \"%s/autoresponses/"
                                    "%s.dat\" -D %u",
                              maildirpath, ai.name, ai.days);
                        fprintf(f, " $SENDMAIL -t -f \"\"`\n");
                        maildir_filter_autoresp_info_free(&ai);
                  }
            }
            else
            {
                  if (*tofolder == '.')   ++tofolder;
                        /* Strip leading dot */

                  fprintf(f,
                        "   %s \"%s%s%s/.\"\n",
                        p->flags & MFR_CONTINUE ? "cc":"to",
                        maildirpath,
                        *tofolder ? "/.":"",
                        tofolder);
            }
            fprintf(f, "}\n\n");
      }
      fflush(f);
      if (ferror(f))
      {
            fclose(f);
            return (-1);
      }
      fprintf(f, "to \"%s/.\"\n", maildirpath);
      if (fclose(f))
            return (-1);
      if (chmod(filename, 0600))
            return (-1);

      return (0);
}

int maildir_filter_loadrules(struct maildirfilter *r, const char *filename)
{
FILE  *f=fopen(filename, "r");
char  buf[BUFSIZ];
char  *p;

enum  maildirfiltertype new_type;
char  new_header[256];
char  new_value[256];
char  new_folder[256];
char  new_autoreplyfrom[512];

int   flags;

      if (!f)     return (MF_LOADNOTFOUND);

      if (fgets(buf, sizeof(buf), f) == 0 ||
            strncmp(buf, "#MFMAILDROP=", 12))
      {
            fclose(f);
            return (MF_LOADFOREIGN);
      }

      flags=atoi(buf+12);
      if (flags != 1 && flags != 2)
      {
            fclose(f);
            return (MF_LOADFOREIGN);
      }

      new_type=contains;
      new_header[0]=0;
      new_value[0]=0;
      new_folder[0]=0;
      new_autoreplyfrom[0]=0;
      flags=0;

#define     SET(f,b) { f[0]=0; strncat( (f), (b), sizeof(f)-1); }

      while ( fgets(buf, sizeof(buf), f))
      {
      int   i;

            p=strchr(buf, '\n');
            if (p)      *p=0;
            if (strncmp(buf, "##", 2))    continue;
            p=buf+2;
            while ( *p && isspace((int)(unsigned char)*p))
                  ++p;

            if (strncasecmp(p, "From:", 5) == 0)
            {
                  p += 5;
                  SET(new_autoreplyfrom, p);
                  continue;
            }


            if (strncasecmp(p, "Op:", 3) == 0)
            {
                  p += 3;

                  for (i=0; typelist[i].name; i++)
                        if (strcasecmp(typelist[i].name, p) == 0)
                              break;
                  if (!typelist[i].name)
                  {
                        fclose(f);
                        return (MF_LOADFOREIGN);
                  }
                  new_type=typelist[i].type;
                  continue;
            }

            if (strncasecmp(p, "Header:", 7) == 0)
            {
                  p += 7;
                  SET(new_header, p);
                  continue;
            }

            if (strncasecmp(p, "Value:", 6) == 0)
            {
                  p += 6;
                  SET(new_value, p);
                  continue;
            }

            if (strncasecmp(p, "Folder:", 7) == 0)
            {
                  p += 7;
                  SET(new_folder, p);
                  continue;
            }

            if (strcasecmp(p, "plainstring") == 0)
            {
                  flags |= MFR_PLAINSTRING;
                  continue;
            }

            if (strcasecmp(p, "doesnot") == 0)
            {
                  flags |= MFR_DOESNOT;
                  continue;
            }

            if (strcasecmp(p, "continue") == 0)
            {
                  flags |= MFR_CONTINUE;
                  continue;
            }

            if (strcasecmp(p, "body") == 0)
            {
                  flags |= MFR_BODY;
                  continue;
            }

            if (strncasecmp(p, "Name:", 5) == 0)
            {
            int dummy;
            struct maildirfilterrule *rr;

                  p += 5;
                  if ((rr=maildir_filter_appendrule(r, p, new_type, flags,
                                    new_header,
                                    new_value, new_folder,
                                    new_autoreplyfrom, &dummy)) == 0)
                  {
                        fclose(f);
                        return (MF_LOADERROR);
                  }
                  new_type=contains;
                  new_header[0]=0;
                  new_value[0]=0;
                  new_folder[0]=0;
                  new_autoreplyfrom[0]=0;
                  flags=0;
            }
      }
      fclose(f);
      return (MF_LOADOK);
}

int maildir_filter_autoresp_info_init(struct maildir_filter_autoresp_info *i, const char *c)
{
      memset(i, 0, sizeof(*i));

      if (maildir_autoresponse_validate(NULL, c))
            return (-1);
      i->name=strdup(c);
      if (!(i->name))
            return (-1);
      return (0);
}

int maildir_filter_autoresp_info_init_str(struct maildir_filter_autoresp_info *i, const char *c)
{
      char *p;

      memset(i, 0, sizeof(*i));
      i->name=strdup(c);
      if (!(i->name))
            return (-1);

      if (strtok(i->name, " \t\r\n") == NULL)
      {
            errno=EINVAL;
            free(i->name);
            i->name=0;
            return (-1);
      }

      while ((p=strtok(NULL, " \t\r\n")) != NULL)
      {
            if (strncmp(p, "dsn=", 4) == 0)
                  i->dsnflag=atoi(p+4) ? 1:0;
            else if (strncmp(p, "days=", 5) == 0)
                  i->days=atoi(p+5);
      }
      return (0);
}

void maildir_filter_autoresp_info_free(struct maildir_filter_autoresp_info *i)
{
      if (i->name)
      {
            free(i->name);
            i->name=0;
      }
}

char *maildir_filter_autoresp_info_asstr(struct maildir_filter_autoresp_info *i)
{
      char days_buf[NUMBUFSIZE+10];

      const char *dsn_arg="";
      const char *days_arg="";
      char *p;

      if (i->dsnflag)
            dsn_arg=" dsn=1";
      if (i->days > 0)
      {
            strcpy(days_buf, " days=");
            libmail_str_size_t(i->days, days_buf+6);
            days_arg=days_buf;
      }

      p=malloc(strlen(i->name)+1+strlen(dsn_arg)+strlen(days_arg));
      if (!p)
            return (NULL);

      strcat(strcat(strcpy(p, i->name), dsn_arg), days_arg);
      return (p);
}

Generated by  Doxygen 1.6.0   Back to index