Thanks to John Cummings.
This commit is contained in:
476
src/cmd/upas/scanmail/scanmail.c
Normal file
476
src/cmd/upas/scanmail/scanmail.c
Normal file
@@ -0,0 +1,476 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user