11 Biobuf bin, bout, *cout;
19 char header[Hdrsize+2];
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*);
38 fprint(2, "missing or bad arguments to qer\n");
45 fprint(2, "scanmail: %s\n", s);
60 Realloc(void *p, ulong n)
69 main(int argc, char *argv[])
71 int i, n, nolines, optout;
72 char **args, **a, *cp, *buf;
73 char body[Bodysize+2];
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);
85 for(argc--, argv++; argv[0] && argv[0][0] == '-'; argc--, argv++){
87 case 'c': /* save copy of message */
94 case 'h': /* queue held messages by sender domain */
95 hflag = 1; /* -q flag must be set also */
97 case 'n': /* NOHOLD mode */
100 case 'p': /* pattern file */
101 if(argv[0][2] || argv[1] == 0)
105 strecpy(patfile, patfile+sizeof patfile, *argv);
107 case 'q': /* queue name */
108 if(argv[0][2] || argv[1] == 0)
116 case 's': /* save copy of dumped message */
119 case 't': /* test mode - don't log match
120 * and write message to /dev/null
124 case 'v': /* vebose - print matches */
136 Binit(&bin, 0, OREAD);
137 bp = Bopen(patfile, OREAD);
145 /* copy the rest of argv, acummulating the recipients as we go */
146 for(i = 0; argv[i]; i++){
148 if(i < 4) /* skip queue, 'mail', sender, dest sys */
150 /* recipients and smtp flags - skip the latter*/
151 if(strcmp(argv[i], "-g") == 0){
156 s_append(recips, ", ");
159 s_append(recips, argv[i]);
160 if(optout && !optoutofspamfilter(argv[i]))
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++)
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
177 buf = canon(&bin, header+1, body+1, &n);
181 /* if all users opt out, don't try matches */
184 cout = opencopy(sender);
185 exits(qmail(args, buf, n, cout));
188 /* Turn off line logging, if command line matches */
189 nolines = matchaction(Lineoff, cmd, match);
191 for(i = 0; patterns[i].action; i++){
192 /* Lineoff patterns were already done above */
195 /* don't apply "Line" patterns if excluded above */
196 if(nolines && i == SaveLine)
198 /* apply patterns to the sender/recips, header and body */
199 if(matchaction(i, cmd, match))
201 if(matchaction(i, header+1, match))
205 if(matchaction(i, body+1, match))
208 if(cflag && patterns[i].action == 0) /* no match found - save msg */
209 cout = opencopy(sender);
211 exits(qmail(args, buf, n, cout));
215 qmail(char **argv, char *buf, int n, Biobuf *cout)
218 int i, pid, pipefd[2];
229 for(i = sysfiles(); i >= 3; i--)
231 snprint(path, sizeof(path), "%s/qer", UPASBIN);
236 Binit(&bout, pipefd[1], OWRITE);
239 bp = Bopen("/dev/null", OWRITE);
244 Bwrite(cout, buf, n);
245 n = Bread(&bin, buf, sizeof(buf)-1);
257 if(status == nil || status->pid == pid)
262 strcpy(buf, "wait failed");
264 strcpy(buf, status->msg);
271 canon(Biobuf *bp, char *header, char *body, int *n)
279 raw = readmsg(bp, &hsize, n);
281 if(convert(raw, raw+hsize, header, Hdrsize, 0))
282 conv64(raw+hsize, raw+*n, body, Bodysize); /* base64 */
284 convert(raw+hsize, raw+*n, body, Bodysize, 1); /* text */
290 matchaction(int action, char *message, Resub *m)
295 if(message == 0 || *message == 0)
298 name = patterns[action].action;
299 p = patterns[action].strings;
301 if(matcher(name, p, message, m))
304 for(p = patterns[action].regexps; p; p = p->next)
305 if(matcher(name, p, message, m))
311 matcher(char *action, Pattern *p, char *message, Resub *m)
316 for(cp = message; matchpat(p, cp, m); cp = m->e.ep){
320 xprint(2, action, m);
321 saveline(linefile, sender, m);
328 xprint(2, action, m);
331 cp = strchr(sender, '!');
334 *qname = strdup(sender);
337 *qname = strdup(sender);
342 xprint(2, action, m);
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)));
354 cout = opendump(sender);
364 saveline(char *file, char *sender, Resub *rp)
370 if(rp->s.sp == 0 || rp->e.ep == 0)
372 /* back up approx 20 characters to whitespace */
373 for(p = rp->s.sp, i = 0; *p && i < 20; i++, p--)
375 while(*p && *p != ' ')
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++)
382 while(*q && *q != ' ')
387 bp = sysopen(file, "al", 0644);
389 Bprint(bp, "%s-> %s\n", sender, p);
393 fprint(2, "can't save line: (%s) %s\n", sender, p);
398 opendump(char *sender)
410 sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
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);
421 h = h*257 + *sender++;
422 for(i = 0; i < 50; i++){
424 sprint(cp, "/%lud", h);
425 b = sysopen(buf, "wlc", 0644);
428 fprint(2, "saving in %s\n", buf);
436 opencopy(char *sender)
445 h = h*257 + *sender++;
446 for(i = 0; i < 50; i++){
448 sprint(buf, "%s/%lud", copydir, h);
449 b = sysopen(buf, "wlc", 0600);
457 optoutofspamfilter(char *addr)
462 p = strchr(addr, '!');
469 f = smprint("/mail/box/%s/nospamfiltering", p);
471 rv = access(f, 0)==0;