Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/spam.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2012 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* spam heuristics
73
*/
74
75
#include "mailx.h"
76
77
#if _PACKAGE_ast
78
#include <tm.h>
79
#endif
80
81
#define strcasecmp(s,p) (!strgrpmatch(s,p,NiL,0,STR_ICASE|STR_MAXIMAL|STR_LEFT|STR_RIGHT))
82
83
#define SPAM_advertisement 0x1 /* Advertisement: found */
84
#define SPAM_authentication_outsider 0x2 /* Authentication: outsider */
85
#define SPAM_authentication_protocol 0x4 /* Authentication: protocol bad */
86
#define SPAM_authentication_warning 0x8 /* Authentication: warning */
87
#define SPAM_content_multipart_related 0x10 /* multipart/related */
88
#define SPAM_content_text_html 0x20 /* text/html */
89
#define SPAM_delay_spam 0x40 /* hop delay looks like spam */
90
#define SPAM_external_spam 0x80 /* external spam checker hit */
91
#define SPAM_from_forged 0x100 /* From: forged */
92
#define SPAM_from_spam 0x200 /* From: spam */
93
#define SPAM_message_id_spam 0x400 /* Message-id: spam */
94
#define SPAM_mime_autoconverted 0x800 /* Mime: autoconverted */
95
#define SPAM_received_forged 0x1000 /* Received: forged */
96
#define SPAM_received_unknown 0x2000 /* Received: unknown */
97
#define SPAM_subject_spam 0x4000 /* Subject: spam */
98
#define SPAM_to_spam 0x8000 /* To: spam */
99
100
#define SPAM_DEFAULT (SPAM_advertisement|SPAM_delay_spam|SPAM_external_spam|SPAM_from_spam|SPAM_message_id_spam|SPAM_received_forged|SPAM_received_unknown|SPAM_subject_spam|SPAM_to_spam)
101
102
static const struct lab spamtest[] =
103
{
104
"advertisement", SPAM_advertisement,
105
"authentication_outsider", SPAM_authentication_outsider,
106
"authentication_protocol", SPAM_authentication_protocol,
107
"authentication_warning", SPAM_authentication_warning,
108
"content_multipart_related", SPAM_content_multipart_related,
109
"content_text_html", SPAM_content_text_html,
110
"delay_spam", SPAM_delay_spam,
111
"external_spam", SPAM_external_spam,
112
"from_forged", SPAM_from_forged,
113
"from_spam", SPAM_from_spam,
114
"message_id_spam", SPAM_message_id_spam,
115
"mime_autoconverted", SPAM_mime_autoconverted,
116
"received_forged", SPAM_received_forged,
117
"received_unknown", SPAM_received_unknown,
118
"subject_spam", SPAM_subject_spam,
119
"to_spam", SPAM_to_spam,
120
};
121
122
/*
123
* Trap spamtest variable assignment.
124
*/
125
126
void
127
set_spamtest(struct var* vp, const char* value)
128
{
129
register char* s;
130
register char* t;
131
register int n;
132
register const struct lab* p;
133
long test;
134
135
s = (char*)value;
136
if (!isdigit(*s)) {
137
test = *((long*)vp->variable);
138
do {
139
for (t = s; *t && *t != ',' && *t != '|'; t++);
140
if (n = t - s)
141
for (p = spamtest;; p++) {
142
if (p >= &spamtest[elementsof(spamtest)]) {
143
if (!strncasecmp(s, "clear", n))
144
test = 0;
145
else if (!strncasecmp(s, "default", n))
146
test = SPAM_DEFAULT;
147
else
148
note(WARNING, "%-.*s: unknown %s value", n, s, vp->name);
149
break;
150
}
151
if (!strncasecmp(s, p->name, n)) {
152
test |= p->type;
153
break;
154
}
155
}
156
s = t;
157
} while (*s++);
158
*((long*)vp->variable) = test;
159
}
160
}
161
162
/*
163
* Return 1 if the intersection of the <,><space> separated
164
* address strings a and b is not empty.
165
*/
166
static int
167
addrmatch(const char* a, const char* b)
168
{
169
register char* ap;
170
register char* ae;
171
register char* bp;
172
register char* be;
173
register char* tp;
174
register int many = 0;
175
register int host;
176
177
ap = (char*)a;
178
for (;;)
179
{
180
while (isspace(*ap))
181
ap++;
182
if (ae = strchr(ap, ','))
183
*ae = 0;
184
ap = skin(ap, GDISPLAY|GCOMPARE);
185
bp = (char*)b;
186
for (;;)
187
{
188
while (isspace(*bp))
189
bp++;
190
if (be = strchr(bp, ','))
191
{
192
*be = 0;
193
many = 1;
194
}
195
bp = skin(bp, GDISPLAY|GCOMPARE);
196
if (TRACING('x'))
197
note(0, "spam: addr check `%s' `%s'", ap, bp);
198
if (host = *bp == '@' && (tp = strchr(ap, '@')))
199
ap = tp + 1;
200
bp++;
201
for (;;)
202
{
203
if (!strcasecmp(ap, bp))
204
{
205
if (ae)
206
*ae = ',';
207
if (be)
208
*be = ',';
209
else if (many)
210
return 0;
211
return 1;
212
}
213
if (!host || !(tp = strchr(ap, '.')))
214
break;
215
ap = tp + 1;
216
}
217
if (!be)
218
break;
219
*be++ = ',';
220
bp = be;
221
}
222
if (!ae)
223
break;
224
*ae++ = ',';
225
ap = ae;
226
}
227
return 0;
228
}
229
230
/*
231
* Return 1 if hosts in list a are part of the address list in b.
232
*/
233
static int
234
hostmatch(const char* a, const char* b)
235
{
236
register char* ap;
237
register char* ae;
238
register char* ad;
239
register char* bp;
240
register char* be;
241
int local = 1;
242
243
ap = (char*)a;
244
for (;;)
245
{
246
if (ae = strchr(ap, ','))
247
*ae = 0;
248
if ((ad = strchr(ap, '.')) && !strchr(++ad, '.'))
249
ad = 0;
250
bp = (char*)b;
251
for (;;)
252
{
253
while (isspace(*bp))
254
bp++;
255
if (be = strchr(bp, ','))
256
*be = 0;
257
if (strchr(bp, ' '))
258
local = 0;
259
else if (bp = strchr(bp, '@'))
260
{
261
local = 0;
262
bp++;
263
if (TRACING('x'))
264
note(0, "spam: host check `%s' `%s'", ap, bp);
265
if (!strcasecmp(ap, bp))
266
goto hit;
267
if (ad)
268
{
269
if (!strcasecmp(ad, bp))
270
goto hit;
271
while (bp = strchr(bp, '.'))
272
{
273
bp++;
274
if (!strcasecmp(ad, bp))
275
goto hit;
276
}
277
}
278
}
279
if (!be)
280
break;
281
bp = be;
282
*bp++ = ',';
283
}
284
if (!ae)
285
return local;
286
ap = ae;
287
*ap++ = ',';
288
}
289
hit:
290
if (be)
291
*be = ',';
292
if (ae)
293
*ae = ',';
294
return 1;
295
}
296
297
/*
298
* Return 1 if string a contains any words whose prefixes
299
* ignorecase match the <,><space> separated lower case
300
* prefix list in b.
301
*/
302
static int
303
wordmatch(const char* a, const char* b)
304
{
305
register char* ab;
306
register char* ap;
307
register char* am;
308
register char* bb;
309
register char* be;
310
register char* bm;
311
register int u;
312
register int l;
313
314
bb = (char*)b;
315
for (;;)
316
{
317
while (isspace(*bb))
318
bb++;
319
if (!(be = strchr(bb, ',')))
320
be = bb + strlen(bb);
321
l = *bb;
322
u = toupper(l);
323
ab = ap = (char*)a;
324
do
325
{
326
if ((*ap == l || *ap == u) && (ap == ab || !isalnum(*(ap - 1))))
327
{
328
am = ap;
329
bm = bb;
330
for (;;)
331
{
332
if (bm >= be && !isalpha(*am))
333
{
334
if (TRACING('x'))
335
note(0, "spam: word match `%-.*s'", bm - bb, bb);
336
return 1;
337
}
338
else if (*am == *bm || *am == toupper(*bm))
339
{
340
am++;
341
bm++;
342
}
343
else if (!isalnum(*am) && !isspace(*am))
344
{
345
if (!*am++)
346
break;
347
}
348
else
349
break;
350
}
351
}
352
} while (*++ap);
353
if (*be == 0)
354
break;
355
bb = be + 1;
356
}
357
return 0;
358
}
359
360
/*
361
* Return 1 if user a is part of the <,><space> separated address strings in b.
362
*/
363
static int
364
usermatch(const char* a, const char* b, int to)
365
{
366
register char* ap;
367
register char* ae;
368
register char* ad;
369
register char* bp;
370
register char* be;
371
register char* td;
372
373
if (!*a || !*b)
374
return 0;
375
ap = (char*)a;
376
while (*ap)
377
{
378
while (isspace(*ap))
379
ap++;
380
if (ae = strchr(ap, ','))
381
*ae = 0;
382
if (strchr(ap, ' '))
383
{
384
if (ae)
385
*ae = ',';
386
return 1;
387
}
388
ad = strchr(ap, '@');
389
bp = (char*)b;
390
for (;;)
391
{
392
while (isspace(*bp))
393
bp++;
394
if (be = strchr(bp, ','))
395
*be = 0;
396
if (TRACING('x'))
397
note(0, "spam: user match `%s' `%s'", ap, bp);
398
if (*bp == 0)
399
/* skip */;
400
else if (*bp == '@')
401
{
402
bp++;
403
for (td = ad; td; td = strchr(td, '.'))
404
if (!strcasecmp(++td, bp))
405
goto hit;
406
}
407
else if (!strcasecmp(ap, bp))
408
goto hit;
409
else if (ad)
410
{
411
*ad = 0;
412
if (to && !strcasecmp(ap, state.var.user))
413
{
414
*ad = '@';
415
if (TRACING('x'))
416
note(0, "spam: user addr check `%s' suspect domain", ap);
417
goto hit;
418
}
419
if (!strcasecmp(ap, bp) || strchr(ap, '!'))
420
{
421
*ad = '@';
422
goto hit;
423
}
424
*ad = '@';
425
}
426
if (!be)
427
{
428
if (ae)
429
*ae = ',';
430
break;
431
}
432
*be++ = ',';
433
bp = be;
434
}
435
if (!ae)
436
return 0;
437
*ae++ = ',';
438
ap = ae;
439
}
440
hit:
441
if (ae)
442
*ae = ',';
443
if (be)
444
*be = ',';
445
return 1;
446
}
447
448
/*
449
* check if s came from inside the domain
450
*/
451
452
static int
453
insider(register char* s, register char* e, int f, char* d1, int n1, char* d2, int n2)
454
{
455
register int n;
456
457
if (!e)
458
e = s + strlen(s);
459
do
460
{
461
n = e - ++s;
462
if (n == n1 && strneq(s, d1, n1) || n == n2 && strneq(s, d2, n2))
463
return 1;
464
} while (s = strchr(s, '.'));
465
return -1;
466
}
467
468
/*
469
* Return 1 if it looks like we've been spammed.
470
*/
471
int
472
spammed(register struct msg* mp)
473
{
474
char* s;
475
char* t;
476
char* e;
477
char* to;
478
char* local;
479
char* domain2;
480
unsigned long q;
481
unsigned long x;
482
unsigned long d;
483
int n;
484
int me;
485
int ok;
486
int no;
487
int ours;
488
int ours2;
489
int fromours;
490
long test;
491
struct parse pp;
492
493
if (!(to = grab(mp, GTO|GCOMPARE|GDISPLAY|GLAST|GUSER, NiL)) || !*to)
494
{
495
if (TRACING('x'))
496
note(0, "spam: To: header missing");
497
return 1;
498
}
499
test = 0;
500
if (t = grab(mp, GSENDER|GCOMPARE|GDISPLAY, NiL))
501
{
502
if (TRACING('x'))
503
note(0, "spam: sender `%s'", t);
504
if (addrmatch(t, state.var.user) || state.var.spamfromok && usermatch(t, state.var.spamfromok, 0))
505
return 0;
506
if (addrmatch(t, to) || state.var.spamfrom && usermatch(t, state.var.spamfrom, 0))
507
test |= SPAM_from_spam;
508
}
509
if (headset(&pp, mp, NiL, NiL, NiL, GFROM))
510
{
511
d = state.var.spamdelay;
512
q = 0;
513
ok = no = fromours = me = 0;
514
if (state.var.domain)
515
{
516
ours = strlen(state.var.domain);
517
if ((domain2 = strchr(state.var.domain, '.')) && strchr(domain2 + 1, '.'))
518
ours2 = strlen(++domain2);
519
else
520
{
521
domain2 = 0;
522
ours2 = 0;
523
}
524
}
525
else
526
{
527
ours = ours2 = 0;
528
domain2 = 0;
529
}
530
if (TRACING('z'))
531
note(0, "spam: ours: %s %s", state.var.domain, domain2);
532
while (headget(&pp))
533
{
534
t = pp.name;
535
if (TRACING('h'))
536
note(0, "spam: head: %s: %s", t, pp.data);
537
if ((*t == 'X' || *t == 'x') && *(t + 1) == '-')
538
t += 2;
539
if (*t == 'A' || *t == 'a')
540
{
541
if (!strcasecmp(t, "Ad") || !strcasecmp(t, "Advertisement"))
542
{
543
if (TRACING('x'))
544
note(0, "spam: advertisement header");
545
test |= SPAM_advertisement;
546
}
547
else if (!strcasecmp(t, "Authentication-Warning"))
548
{
549
test |= SPAM_authentication_warning;
550
if (t = strrchr(pp.data, ' '))
551
{
552
*t++ = 0;
553
if (streq(t, "-f"))
554
{
555
if ((t = strrchr(pp.data, ' ')) && streq(t + 1, "using") && !(*t = 0) && (t = strrchr(pp.data, ' ')) && insider(t + 1, NiL, 0, state.var.domain, ours, domain2, ours2))
556
test |= SPAM_authentication_outsider;
557
}
558
else if (streq(t, "protocol"))
559
test |= SPAM_authentication_protocol;
560
}
561
}
562
}
563
else if (*t == 'C' || *t == 'c')
564
{
565
if (!strcasecmp(t, "Cc"))
566
{
567
t = skin(pp.data, GDISPLAY|GCOMPARE|GFROM);
568
if (TRACING('x'))
569
note(0, "spam: cc `%s'", t);
570
if (addrmatch(state.var.user, t))
571
me = 1;
572
}
573
else if (!strcasecmp(t, "Content-Type"))
574
{
575
t = skin(pp.data, GDISPLAY|GCOMPARE|GFROM);
576
if (TRACING('x'))
577
note(0, "spam: content-type `%s'", t);
578
if (!strncasecmp(t, "text/html", 9))
579
test |= SPAM_content_text_html;
580
else if (!strncasecmp(t, "multipart/related", 17))
581
test |= SPAM_content_multipart_related;
582
}
583
}
584
else if (*t == 'F' || *t == 'f')
585
{
586
if (!strcasecmp(t, "From"))
587
{
588
t = skin(pp.data, GDISPLAY|GCOMPARE|GFROM);
589
if (s = strchr(t, ' '))
590
*s = 0;
591
if (TRACING('x'))
592
note(0, "spam: from `%s'", t);
593
if (addrmatch(t, state.var.user) || state.var.spamfromok && usermatch(t, state.var.spamfromok, 0))
594
return 0;
595
if (addrmatch(t, to) || state.var.spamfrom && usermatch(t, state.var.spamfrom, 0))
596
test |= SPAM_from_spam;
597
if (fromours >= 0)
598
fromours = insider(t, NiL, fromours, state.var.domain, ours, domain2, ours2);
599
}
600
}
601
else if (*t == 'M' || *t == 'm')
602
{
603
if (!strcasecmp(t, "Message-Id"))
604
{
605
t = skin(pp.data, GDISPLAY|GCOMPARE|GFROM);
606
if (TRACING('x'))
607
note(0, "spam: message-id `%s'", t);
608
if (!*t)
609
test |= SPAM_message_id_spam;
610
}
611
else if (!strcasecmp(t, "Mime-Autoconverted"))
612
{
613
if (TRACING('x'))
614
note(0, "spam: mime autoconverted");
615
test |= SPAM_mime_autoconverted;
616
}
617
}
618
else if (*t == 'R' || *t == 'r')
619
{
620
if (!strcasecmp(t, "Received"))
621
{
622
for (t = pp.data; *t; t++)
623
{
624
if (*t == 'u')
625
{
626
if ((t == pp.data || *(t - 1) == '(' || *(t - 1) == ' ') && strneq(t, "unknown ", 8))
627
{
628
if (TRACING('x'))
629
note(0, "spam: unknown host name");
630
test |= SPAM_received_unknown;
631
}
632
}
633
else if (*t == 'f' && (t == pp.data || *(t - 1) == ' '))
634
{
635
if (strneq(t, "forged", 6))
636
{
637
if (TRACING('x'))
638
note(0, "spam: forged");
639
test |= SPAM_received_forged;
640
}
641
else if (ours && strneq(t, "from ", 5))
642
{
643
n = 0;
644
s = t;
645
for (s = t + 5; *s == ' '; s++);
646
while (e = strchr(s, ' '))
647
{
648
if (insider(s, e, 0, state.var.domain, ours, domain2, ours2))
649
{
650
n = 1;
651
break;
652
}
653
if (!(s = strchr(e + 1, '.')))
654
break;
655
}
656
if (!n)
657
{
658
ours = 0;
659
if (TRACING('x'))
660
note(0, "spam: outsider: %s", pp.data);
661
}
662
}
663
}
664
}
665
#if _PACKAGE_ast
666
if (!ours && d && (s = strrchr(pp.data, ';')))
667
{
668
while (*++s && isspace(*s));
669
if (*s)
670
{
671
x = tmdate(s, NiL, NiL);
672
if (q == 0)
673
q = x;
674
else if (((q > x) ? (q - x) : (x - q)) > d)
675
{
676
if (TRACING('x'))
677
note(0, "spam: delay %ld", (q > x) ? (q - x) : (x - q));
678
test |= SPAM_delay_spam;
679
}
680
q = x;
681
}
682
}
683
#endif
684
}
685
}
686
else if (*t == 'S' || *t == 's')
687
{
688
if (!strcasecmp(t, "Spam-Flag"))
689
{
690
t = pp.data;
691
if (*t == 'Y' || *t == 'y' || *t == '1')
692
{
693
if (TRACING('x'))
694
note(0, "spam: external spam check hit");
695
test |= SPAM_external_spam;
696
}
697
}
698
else if (!strcasecmp(t, "Subject"))
699
{
700
s = pp.data;
701
while (n = *s++)
702
if (n != ' ' && n != '\t' && isspace(n))
703
{
704
test |= SPAM_subject_spam;
705
break;
706
}
707
if (!(test & SPAM_subject_spam) && state.var.spamsub)
708
{
709
if (wordmatch(strlower(pp.data), state.var.spamsub))
710
test |= SPAM_subject_spam;
711
}
712
}
713
}
714
else if (*t == 'T' || *t == 't')
715
{
716
if (!strcasecmp(t, "To"))
717
{
718
for (t = pp.data; t = strchr(t, ':'); *t = ',');
719
s = pp.data;
720
do
721
{
722
if (e = strchr(s, ','))
723
*e++ = 0;
724
t = skin(s, GDISPLAY|GCOMPARE);
725
if (TRACING('x'))
726
note(0, "spam: to `%s'", t);
727
if (*t == 0)
728
{
729
test |= SPAM_to_spam;
730
break;
731
}
732
if (addrmatch(state.var.user, t))
733
me = 1;
734
else if (state.var.spamtook && usermatch(t, state.var.spamtook, state.var.local != 0))
735
{
736
if (TRACING('x'))
737
note(0, "spam: spamtook `%s'", t);
738
ok++;
739
}
740
else if (state.var.spamto && usermatch(t, state.var.spamto, state.var.local != 0))
741
{
742
if (TRACING('x'))
743
note(0, "spam: spamto `%s'", t);
744
no++;
745
}
746
} while (s = e);
747
}
748
}
749
}
750
if (fromours > 0 && !ours)
751
test |= SPAM_from_forged;
752
if (TRACING('t') || TRACING('x')) {
753
const struct lab* p;
754
char buf[1024];
755
756
s = buf;
757
e = s + sizeof(buf) - 1;
758
for (p = spamtest; p < &spamtest[elementsof(spamtest)]; p++)
759
if (test & p->type)
760
{
761
for (t = (char*)p->name; *t && s < e; *s++ = *t++);
762
if (s < e)
763
*s++ = '|';
764
}
765
if (s > buf)
766
s--;
767
*s = 0;
768
note(0, "spam: ok=%d no=%d test=%s", ok, no, buf);
769
}
770
if (no > ok)
771
return 1;
772
if (ok > no)
773
return 0;
774
if (me)
775
return 0;
776
if (test & state.var.spamtest)
777
return 1;
778
}
779
if (state.var.local)
780
{
781
local = state.var.local;
782
state.var.local = 0;
783
if (!(s = grab(mp, GTO|GCOMPARE|GDISPLAY|GLAST|GUSER, NiL)))
784
{
785
if (TRACING('x'))
786
note(0, "spam: To: header missing");
787
state.var.local = local;
788
return 1;
789
}
790
if (hostmatch(local, s))
791
{
792
if (TRACING('y'))
793
note(0, "spam: host ok#%d `%s' `%s'", __LINE__, local, s);
794
state.var.local = local;
795
return 0;
796
}
797
if ((s = grab(mp, GCC|GCOMPARE|GDISPLAY, NiL)) && hostmatch(local, s))
798
{
799
if (TRACING('y'))
800
note(0, "spam: host ok#%d `%s' `%s'", __LINE__, local, s);
801
state.var.local = local;
802
return 0;
803
}
804
state.var.local = local;
805
return 1;
806
}
807
return 0;
808
}
809
810