URI:
       tscanmail.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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 }