123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- #include "common.h"
- #include "spam.h"
- int cflag;
- int debug;
- int hflag;
- int nflag;
- int sflag;
- int tflag;
- int vflag;
- Biobuf bin, bout, *cout;
- /* file names */
- char patfile[128];
- char linefile[128];
- char holdqueue[128];
- char copydir[128];
- char header[Hdrsize+2];
- char cmd[1024];
- char **qname;
- char **qdir;
- char *sender;
- String *recips;
- char* canon(Biobuf*, char*, char*, int*);
- int matcher(char*, Pattern*, char*, Resub*);
- int matchaction(int, char*, Resub*);
- Biobuf *opencopy(char*);
- Biobuf *opendump(char*);
- char *qmail(char**, char*, int, Biobuf*);
- void saveline(char*, char*, Resub*);
- int optoutofspamfilter(char*);
- void
- usage(void)
- {
- fprint(2, "missing or bad arguments to qer\n");
- exits("usage");
- }
- void
- regerror(char *s)
- {
- fprint(2, "scanmail: %s\n", s);
- }
- void *
- Malloc(long n)
- {
- void *p;
- p = malloc(n);
- if(p == 0)
- exits("malloc");
- return p;
- }
- void*
- Realloc(void *p, ulong n)
- {
- p = realloc(p, n);
- if(p == 0)
- exits("realloc");
- return p;
- }
- void
- main(int argc, char *argv[])
- {
- int i, n, nolines, optout;
- char **args, **a, *cp, *buf;
- char body[Bodysize+2];
- Resub match[1];
- Biobuf *bp;
- optout = 1;
- a = args = Malloc((argc+1)*sizeof(char*));
- sprint(patfile, "%s/patterns", UPASLIB);
- sprint(linefile, "%s/lines", UPASLOG);
- sprint(holdqueue, "%s/queue.hold", SPOOL);
- sprint(copydir, "%s/copy", SPOOL);
- *a++ = argv[0];
- for(argc--, argv++; argv[0] && argv[0][0] == '-'; argc--, argv++){
- switch(argv[0][1]){
- case 'c': /* save copy of message */
- cflag = 1;
- break;
- case 'd': /* debug */
- debug++;
- *a++ = argv[0];
- break;
- case 'h': /* queue held messages by sender domain */
- hflag = 1; /* -q flag must be set also */
- break;
- case 'n': /* NOHOLD mode */
- nflag = 1;
- break;
- case 'p': /* pattern file */
- if(argv[0][2] || argv[1] == 0)
- usage();
- argc--;
- argv++;
- strecpy(patfile, patfile+sizeof patfile, *argv);
- break;
- case 'q': /* queue name */
- if(argv[0][2] || argv[1] == 0)
- usage();
- *a++ = argv[0];
- argc--;
- argv++;
- qname = a;
- *a++ = argv[0];
- break;
- case 's': /* save copy of dumped message */
- sflag = 1;
- break;
- case 't': /* test mode - don't log match
- * and write message to /dev/null
- */
- tflag = 1;
- break;
- case 'v': /* vebose - print matches */
- vflag = 1;
- break;
- default:
- *a++ = argv[0];
- break;
- }
- }
- if(argc < 3)
- usage();
- Binit(&bin, 0, OREAD);
- bp = Bopen(patfile, OREAD);
- if(bp){
- parsepats(bp);
- Bterm(bp);
- }
- qdir = a;
- sender = argv[2];
- /* copy the rest of argv, acummulating the recipients as we go */
- for(i = 0; argv[i]; i++){
- *a++ = argv[i];
- if(i < 4) /* skip queue, 'mail', sender, dest sys */
- continue;
- /* recipients and smtp flags - skip the latter*/
- if(strcmp(argv[i], "-g") == 0){
- *a++ = argv[++i];
- continue;
- }
- if(recips)
- s_append(recips, ", ");
- else
- recips = s_new();
- s_append(recips, argv[i]);
- if(optout && !optoutofspamfilter(argv[i]))
- optout = 0;
- }
- *a = 0;
- /* construct a command string for matching */
- snprint(cmd, sizeof(cmd)-1, "%s %s", sender, s_to_c(recips));
- cmd[sizeof(cmd)-1] = 0;
- for(cp = cmd; *cp; cp++)
- *cp = tolower(*cp);
- /* canonicalize a copy of the header and body.
- * buf points to orginal message and n contains
- * number of bytes of original message read during
- * canonicalization.
- */
- *body = 0;
- *header = 0;
- buf = canon(&bin, header+1, body+1, &n);
- if (buf == 0)
- exits("read");
- /* if all users opt out, don't try matches */
- if(optout){
- if(cflag)
- cout = opencopy(sender);
- exits(qmail(args, buf, n, cout));
- }
- /* Turn off line logging, if command line matches */
- nolines = matchaction(Lineoff, cmd, match);
- for(i = 0; patterns[i].action; i++){
- /* Lineoff patterns were already done above */
- if(i == Lineoff)
- continue;
- /* don't apply "Line" patterns if excluded above */
- if(nolines && i == SaveLine)
- continue;
- /* apply patterns to the sender/recips, header and body */
- if(matchaction(i, cmd, match))
- break;
- if(matchaction(i, header+1, match))
- break;
- if(i == HoldHeader)
- continue;
- if(matchaction(i, body+1, match))
- break;
- }
- if(cflag && patterns[i].action == 0) /* no match found - save msg */
- cout = opencopy(sender);
- exits(qmail(args, buf, n, cout));
- }
- char*
- qmail(char **argv, char *buf, int n, Biobuf *cout)
- {
- Waitmsg *status;
- int i, pid, pipefd[2];
- char path[512];
- Biobuf *bp;
- pid = 0;
- if(tflag == 0){
- if(pipe(pipefd) < 0)
- exits("pipe");
- pid = fork();
- if(pid == 0){
- dup(pipefd[0], 0);
- for(i = sysfiles(); i >= 3; i--)
- close(i);
- snprint(path, sizeof(path), "%s/qer", UPASBIN);
- *argv=path;
- exec(path, argv);
- exits("exec");
- }
- Binit(&bout, pipefd[1], OWRITE);
- bp = &bout;
- } else
- bp = Bopen("/dev/null", OWRITE);
- while(n > 0){
- Bwrite(bp, buf, n);
- if(cout)
- Bwrite(cout, buf, n);
- n = Bread(&bin, buf, sizeof(buf)-1);
- }
- Bterm(bp);
- if(cout)
- Bterm(cout);
- if(tflag)
- return 0;
- close(pipefd[1]);
- close(pipefd[0]);
- for(;;){
- status = wait();
- if(status == nil || status->pid == pid)
- break;
- free(status);
- }
- if(status == nil)
- strcpy(buf, "wait failed");
- else{
- strcpy(buf, status->msg);
- free(status);
- }
- return buf;
- }
- char*
- canon(Biobuf *bp, char *header, char *body, int *n)
- {
- int hsize;
- char *raw;
- hsize = 0;
- *header = 0;
- *body = 0;
- raw = readmsg(bp, &hsize, n);
- if(raw){
- if(convert(raw, raw+hsize, header, Hdrsize, 0))
- conv64(raw+hsize, raw+*n, body, Bodysize); /* base64 */
- else
- convert(raw+hsize, raw+*n, body, Bodysize, 1); /* text */
- }
- return raw;
- }
- int
- matchaction(int action, char *message, Resub *m)
- {
- char *name;
- Pattern *p;
- if(message == 0 || *message == 0)
- return 0;
- name = patterns[action].action;
- p = patterns[action].strings;
- if(p)
- if(matcher(name, p, message, m))
- return 1;
- for(p = patterns[action].regexps; p; p = p->next)
- if(matcher(name, p, message, m))
- return 1;
- return 0;
- }
- int
- matcher(char *action, Pattern *p, char *message, Resub *m)
- {
- char *cp;
- String *s;
- for(cp = message; matchpat(p, cp, m); cp = m->ep){
- switch(p->action){
- case SaveLine:
- if(vflag)
- xprint(2, action, m);
- saveline(linefile, sender, m);
- break;
- case HoldHeader:
- case Hold:
- if(nflag)
- continue;
- if(vflag)
- xprint(2, action, m);
- *qdir = holdqueue;
- if(hflag && qname){
- cp = strchr(sender, '!');
- if(cp){
- *cp = 0;
- *qname = strdup(sender);
- *cp = '!';
- } else
- *qname = strdup(sender);
- }
- return 1;
- case Dump:
- if(vflag)
- xprint(2, action, m);
- *(m->ep) = 0;
- if(!tflag){
- s = s_new();
- s_append(s, sender);
- s = unescapespecial(s);
- syslog(0, "smtpd", "Dumped %s [%s] to %s", s_to_c(s), m->sp,
- s_to_c(s_restart(recips)));
- s_free(s);
- }
- tflag = 1;
- if(sflag)
- cout = opendump(sender);
- return 1;
- default:
- break;
- }
- }
- return 0;
- }
- void
- saveline(char *file, char *sender, Resub *rp)
- {
- char *p, *q;
- int i, c;
- Biobuf *bp;
- if(rp->sp == 0 || rp->ep == 0)
- return;
- /* back up approx 20 characters to whitespace */
- for(p = rp->sp, i = 0; *p && i < 20; i++, p--)
- ;
- while(*p && *p != ' ')
- p--;
- p++;
- /* grab about 20 more chars beyond the end of the match */
- for(q = rp->ep, i = 0; *q && i < 20; i++, q++)
- ;
- while(*q && *q != ' ')
- q++;
- c = *q;
- *q = 0;
- bp = sysopen(file, "al", 0644);
- if(bp){
- Bprint(bp, "%s-> %s\n", sender, p);
- Bterm(bp);
- }
- else if(debug)
- fprint(2, "can't save line: (%s) %s\n", sender, p);
- *q = c;
- }
- Biobuf*
- opendump(char *sender)
- {
- int i;
- ulong h;
- char buf[512];
- Biobuf *b;
- char *cp;
- cp = ctime(time(0));
- cp[7] = 0;
- cp[10] = 0;
- if(cp[8] == ' ')
- sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
- else
- sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
- cp = buf+strlen(buf);
- if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0){
- syslog(0, "smtpd", "couldn't dump mail from %s: %r", sender);
- return 0;
- }
- h = 0;
- while(*sender)
- h = h*257 + *sender++;
- for(i = 0; i < 50; i++){
- h += lrand();
- sprint(cp, "/%lud", h);
- b = sysopen(buf, "wlc", 0644);
- if(b){
- if(vflag)
- fprint(2, "saving in %s\n", buf);
- return b;
- }
- }
- return 0;
- }
- Biobuf*
- opencopy(char *sender)
- {
- int i;
- ulong h;
- char buf[512];
- Biobuf *b;
- h = 0;
- while(*sender)
- h = h*257 + *sender++;
- for(i = 0; i < 50; i++){
- h += lrand();
- sprint(buf, "%s/%lud", copydir, h);
- b = sysopen(buf, "wlc", 0600);
- if(b)
- return b;
- }
- return 0;
- }
- int
- optoutofspamfilter(char *addr)
- {
- char *p, *f;
- int rv;
- p = strchr(addr, '!');
- if(p)
- p++;
- else
- p = addr;
- rv = 0;
- f = smprint("/mail/box/%s/nospamfiltering", p);
- if(f != nil){
- rv = access(f, 0)==0;
- free(f);
- }
- return rv;
- }
|