summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/scanmail/scanmail.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/upas/scanmail/scanmail.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/upas/scanmail/scanmail.c')
-rwxr-xr-xsys/src/cmd/upas/scanmail/scanmail.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/sys/src/cmd/upas/scanmail/scanmail.c b/sys/src/cmd/upas/scanmail/scanmail.c
new file mode 100755
index 000000000..444bbcdd2
--- /dev/null
+++ b/sys/src/cmd/upas/scanmail/scanmail.c
@@ -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;
+}