tscanmail.c - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
tscanmail.c (8754B)
---
     1 #include "common.h"
     2 #include "spam.h"
     3 
     4 int        cflag;
     5 int        debug;
     6 int        hflag;
     7 int        nflag;
     8 int        sflag;
     9 int        tflag;
    10 int        vflag;
    11 Biobuf        bin, bout, *cout;
    12 
    13         /* file names */
    14 char        patfile[128];
    15 char        linefile[128];
    16 char        holdqueue[128];
    17 char        copydir[128];
    18 
    19 char        header[Hdrsize+2];
    20 char        cmd[1024];
    21 char        **qname;
    22 char        **qdir;
    23 char        *sender;
    24 String        *recips;
    25 
    26 char*        canon(Biobuf*, char*, char*, int*);
    27 int        matcher(char*, Pattern*, char*, Resub*);
    28 int        matchaction(int, char*, Resub*);
    29 Biobuf        *opencopy(char*);
    30 Biobuf        *opendump(char*);
    31 char        *qmail(char**, char*, int, Biobuf*);
    32 void        saveline(char*, char*, Resub*);
    33 int        optoutofspamfilter(char*);
    34 
    35 void
    36 usage(void)
    37 {
    38         fprint(2, "missing or bad arguments to qer\n");
    39         exits("usage");
    40 }
    41 
    42 void
    43 regerror(char *s)
    44 {
    45         fprint(2, "scanmail: %s\n", s);
    46 }
    47 
    48 void *
    49 Malloc(long n)
    50 {
    51         void *p;
    52 
    53         p = malloc(n);
    54         if(p == 0)
    55                 exits("malloc");
    56         return p;
    57 }
    58 
    59 void*
    60 Realloc(void *p, ulong n)
    61 {
    62         p = realloc(p, n);
    63         if(p == 0)
    64                 exits("realloc");
    65         return p;
    66 }
    67 
    68 void
    69 main(int argc, char *argv[])
    70 {
    71         int i, n, nolines, optout;
    72         char **args, **a, *cp, *buf;
    73         char body[Bodysize+2];
    74         Resub match[1];
    75         Biobuf *bp;
    76 
    77         optout = 1;
    78         a = args = Malloc((argc+1)*sizeof(char*));
    79         sprint(patfile, "%s/patterns", UPASLIB);
    80         sprint(linefile, "%s/lines", UPASLOG);
    81         sprint(holdqueue, "%s/queue.hold", SPOOL);
    82         sprint(copydir, "%s/copy", SPOOL);
    83 
    84         *a++ = argv[0];
    85         for(argc--, argv++; argv[0] && argv[0][0] == '-'; argc--, argv++){
    86                 switch(argv[0][1]){
    87                 case 'c':                        /* save copy of message */
    88                         cflag = 1;
    89                         break;
    90                 case 'd':                        /* debug */
    91                         debug++;
    92                         *a++ = argv[0];
    93                         break;
    94                 case 'h':                        /* queue held messages by sender domain */
    95                         hflag = 1;                /* -q flag must be set also */
    96                         break;
    97                 case 'n':                        /* NOHOLD mode */
    98                         nflag = 1;
    99                         break;
   100                 case 'p':                        /* pattern file */
   101                         if(argv[0][2] || argv[1] == 0)
   102                                 usage();
   103                         argc--;
   104                         argv++;
   105                         strecpy(patfile, patfile+sizeof patfile, *argv);
   106                         break;
   107                 case 'q':                        /* queue name */
   108                         if(argv[0][2] ||  argv[1] == 0)
   109                                 usage();
   110                         *a++ = argv[0];
   111                         argc--;
   112                         argv++;
   113                         qname = a;
   114                         *a++ = argv[0];
   115                         break;
   116                 case 's':                        /* save copy of dumped message */
   117                         sflag = 1;
   118                         break;
   119                 case 't':                        /* test mode - don't log match
   120                                                  * and write message to /dev/null
   121                                                  */
   122                         tflag = 1;
   123                         break;
   124                 case 'v':                        /* vebose - print matches */
   125                         vflag = 1;
   126                         break;
   127                 default:
   128                         *a++ = argv[0];
   129                         break;
   130                 }
   131         }
   132 
   133         if(argc < 3)
   134                 usage();
   135 
   136         Binit(&bin, 0, OREAD);
   137         bp = Bopen(patfile, OREAD);
   138         if(bp){
   139                 parsepats(bp);
   140                 Bterm(bp);
   141         }
   142         qdir = a;
   143         sender = argv[2];
   144 
   145                 /* copy the rest of argv, acummulating the recipients as we go */
   146         for(i = 0; argv[i]; i++){
   147                 *a++ = argv[i];
   148                 if(i < 4)        /* skip queue, 'mail', sender, dest sys */
   149                         continue;
   150                         /* recipients and smtp flags - skip the latter*/
   151                 if(strcmp(argv[i], "-g") == 0){
   152                         *a++ = argv[++i];
   153                         continue;
   154                 }
   155                 if(recips)
   156                         s_append(recips, ", ");
   157                 else
   158                         recips = s_new();
   159                 s_append(recips, argv[i]);
   160                 if(optout && !optoutofspamfilter(argv[i]))
   161                         optout = 0;
   162         }
   163         *a = 0;
   164                 /* construct a command string for matching */
   165         snprint(cmd, sizeof(cmd)-1, "%s %s", sender, s_to_c(recips));
   166         cmd[sizeof(cmd)-1] = 0;
   167         for(cp = cmd; *cp; cp++)
   168                 *cp = tolower(*cp);
   169 
   170                 /* canonicalize a copy of the header and body.
   171                  * buf points to orginal message and n contains
   172                  * number of bytes of original message read during
   173                  * canonicalization.
   174                  */
   175         *body = 0;
   176         *header = 0;
   177         buf = canon(&bin, header+1, body+1, &n);
   178         if (buf == 0)
   179                 exits("read");
   180 
   181                 /* if all users opt out, don't try matches */
   182         if(optout){
   183                 if(cflag)
   184                         cout = opencopy(sender);
   185                 exits(qmail(args, buf, n, cout));
   186         }
   187 
   188                 /* Turn off line logging, if command line matches */
   189         nolines = matchaction(Lineoff, cmd, match);
   190 
   191         for(i = 0; patterns[i].action; i++){
   192                         /* Lineoff patterns were already done above */
   193                 if(i == Lineoff)
   194                         continue;
   195                         /* don't apply "Line" patterns if excluded above */
   196                 if(nolines && i == SaveLine)
   197                         continue;
   198                         /* apply patterns to the sender/recips, header and body */
   199                 if(matchaction(i, cmd, match))
   200                         break;
   201                 if(matchaction(i, header+1, match))
   202                         break;
   203                 if(i == HoldHeader)
   204                         continue;
   205                 if(matchaction(i, body+1, match))
   206                         break;
   207         }
   208         if(cflag && patterns[i].action == 0)        /* no match found - save msg */
   209                 cout = opencopy(sender);
   210 
   211         exits(qmail(args, buf, n, cout));
   212 }
   213 
   214 char*
   215 qmail(char **argv, char *buf, int n, Biobuf *cout)
   216 {
   217         Waitmsg *status;
   218         int i, pid, pipefd[2];
   219         char path[512];
   220         Biobuf *bp;
   221 
   222         pid = 0;
   223         if(tflag == 0){
   224                 if(pipe(pipefd) < 0)
   225                         exits("pipe");
   226                 pid = fork();
   227                 if(pid == 0){
   228                         dup(pipefd[0], 0);
   229                         for(i = sysfiles(); i >= 3; i--)
   230                                 close(i);
   231                         snprint(path, sizeof(path), "%s/qer", UPASBIN);
   232                         *argv=path;
   233                         exec(path, argv);
   234                         exits("exec");
   235                 }
   236                 Binit(&bout, pipefd[1], OWRITE);
   237                 bp = &bout;
   238         } else
   239                 bp = Bopen("/dev/null", OWRITE);
   240 
   241         while(n > 0){
   242                 Bwrite(bp, buf, n);
   243                 if(cout)
   244                         Bwrite(cout, buf, n);
   245                 n = Bread(&bin, buf, sizeof(buf)-1);
   246         }
   247         Bterm(bp);
   248         if(cout)
   249                 Bterm(cout);
   250         if(tflag)
   251                 return 0;
   252 
   253         close(pipefd[1]);
   254         close(pipefd[0]);
   255         for(;;){
   256                 status = wait();
   257                 if(status == nil || status->pid == pid)
   258                         break;
   259                 free(status);
   260         }
   261         if(status == nil)
   262                 strcpy(buf, "wait failed");
   263         else{
   264                 strcpy(buf, status->msg);
   265                 free(status);
   266         }
   267         return buf;
   268 }
   269 
   270 char*
   271 canon(Biobuf *bp, char *header, char *body, int *n)
   272 {
   273         int hsize;
   274         char *raw;
   275 
   276         hsize = 0;
   277         *header = 0;
   278         *body = 0;
   279         raw = readmsg(bp, &hsize, n);
   280         if(raw){
   281                 if(convert(raw, raw+hsize, header, Hdrsize, 0))
   282                         conv64(raw+hsize, raw+*n, body, Bodysize);        /* base64 */
   283                 else
   284                         convert(raw+hsize, raw+*n, body, Bodysize, 1);        /* text */
   285         }
   286         return raw;
   287 }
   288 
   289 int
   290 matchaction(int action, char *message, Resub *m)
   291 {
   292         char *name;
   293         Pattern *p;
   294 
   295         if(message == 0 || *message == 0)
   296                 return 0;
   297 
   298         name = patterns[action].action;
   299         p = patterns[action].strings;
   300         if(p)
   301                 if(matcher(name, p, message, m))
   302                         return 1;
   303 
   304         for(p = patterns[action].regexps; p; p = p->next)
   305                 if(matcher(name, p, message, m))
   306                         return 1;
   307         return 0;
   308 }
   309 
   310 int
   311 matcher(char *action, Pattern *p, char *message, Resub *m)
   312 {
   313         char *cp;
   314         String *s;
   315 
   316         for(cp = message; matchpat(p, cp, m); cp = m->e.ep){
   317                 switch(p->action){
   318                 case SaveLine:
   319                         if(vflag)
   320                                 xprint(2, action, m);
   321                         saveline(linefile, sender, m);
   322                         break;
   323                 case HoldHeader:
   324                 case Hold:
   325                         if(nflag)
   326                                 continue;
   327                         if(vflag)
   328                                 xprint(2, action, m);
   329                         *qdir = holdqueue;
   330                         if(hflag && qname){
   331                                 cp = strchr(sender, '!');
   332                                 if(cp){
   333                                         *cp = 0;
   334                                         *qname = strdup(sender);
   335                                         *cp = '!';
   336                                 } else
   337                                         *qname = strdup(sender);
   338                         }
   339                         return 1;
   340                 case Dump:
   341                         if(vflag)
   342                                 xprint(2, action, m);
   343                         *m->e.ep = 0;
   344                         if(!tflag){
   345                                 s = s_new();
   346                                 s_append(s, sender);
   347                                 s = unescapespecial(s);
   348                                 syslog(0, "smtpd", "Dumped %s [%s] to %s", s_to_c(s), m->s.sp,
   349                                         s_to_c(s_restart(recips)));
   350                                 s_free(s);
   351                         }
   352                         tflag = 1;
   353                         if(sflag)
   354                                 cout = opendump(sender);
   355                         return 1;
   356                 default:
   357                         break;
   358                 }
   359         }
   360         return 0;
   361 }
   362 
   363 void
   364 saveline(char *file, char *sender, Resub *rp)
   365 {
   366         char *p, *q;
   367         int i, c;
   368         Biobuf *bp;
   369 
   370         if(rp->s.sp == 0 || rp->e.ep == 0)
   371                 return;
   372                 /* back up approx 20 characters to whitespace */
   373         for(p = rp->s.sp, i = 0; *p && i < 20; i++, p--)
   374                         ;
   375         while(*p && *p != ' ')
   376                 p--;
   377         p++;
   378 
   379                 /* grab about 20 more chars beyond the end of the match */
   380         for(q = rp->e.ep, i = 0; *q && i < 20; i++, q++)
   381                         ;
   382         while(*q && *q != ' ')
   383                 q++;
   384 
   385         c = *q;
   386         *q = 0;
   387         bp = sysopen(file, "al", 0644);
   388         if(bp){
   389                 Bprint(bp, "%s-> %s\n", sender, p);
   390                 Bterm(bp);
   391         }
   392         else if(debug)
   393                 fprint(2, "can't save line: (%s) %s\n", sender, p);
   394         *q = c;
   395 }
   396 
   397 Biobuf*
   398 opendump(char *sender)
   399 {
   400         int i;
   401         ulong h;
   402         char buf[512];
   403         Biobuf *b;
   404         char *cp;
   405 
   406         cp = ctime(time(0));
   407         cp[7] = 0;
   408         cp[10] = 0;
   409         if(cp[8] == ' ')
   410                 sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
   411         else
   412                 sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
   413         cp = buf+strlen(buf);
   414         if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0){
   415                 syslog(0, "smtpd", "couldn't dump mail from %s: %r", sender);
   416                 return 0;
   417         }
   418 
   419         h = 0;
   420         while(*sender)
   421                 h = h*257 + *sender++;
   422         for(i = 0; i < 50; i++){
   423                 h += lrand();
   424                 sprint(cp, "/%lud", h);
   425                 b = sysopen(buf, "wlc", 0644);
   426                 if(b){
   427                         if(vflag)
   428                                 fprint(2, "saving in %s\n", buf);
   429                         return b;
   430                 }
   431         }
   432         return 0;
   433 }
   434 
   435 Biobuf*
   436 opencopy(char *sender)
   437 {
   438         int i;
   439         ulong h;
   440         char buf[512];
   441         Biobuf *b;
   442 
   443         h = 0;
   444         while(*sender)
   445                 h = h*257 + *sender++;
   446         for(i = 0; i < 50; i++){
   447                 h += lrand();
   448                 sprint(buf, "%s/%lud", copydir, h);
   449                 b = sysopen(buf, "wlc", 0600);
   450                 if(b)
   451                         return b;
   452         }
   453         return 0;
   454 }
   455 
   456 int
   457 optoutofspamfilter(char *addr)
   458 {
   459         char *p, *f;
   460         int rv;
   461 
   462         p = strchr(addr, '!');
   463         if(p)
   464                 p++;
   465         else
   466                 p = addr;
   467 
   468         rv = 0;
   469         f = smprint("/mail/box/%s/nospamfiltering", p);
   470         if(f != nil){
   471                 rv = access(f, 0)==0;
   472                 free(f);
   473         }
   474 
   475         return rv;
   476 }