Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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->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->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->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->sp == 0 || rp->ep == 0)
371
		return;
372
		/* back up approx 20 characters to whitespace */
373
	for(p = rp->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->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
}