]> git.madduck.net Git - etc/mailfilter.git/blob - procmail/spamfilter

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

eval formail variable
[etc/mailfilter.git] / procmail / spamfilter
1 #!/usr/bin/procmail
2
3 #TODO: rewrite to use SPAM variable, and do not autotrain spam here, only ham
4
5 PMDIR=${PMDIR:-$HOME/.etc/mailfilter/procmail}
6
7 :0
8 * !PMVAR ?? .
9 {
10   # PMVAR is not defined, so we are being called as filter
11   # thus source the standard defines
12   INCLUDERC=$PMDIR/defines
13   # prevent feeding back to procmail and delete the leading From line
14   PROCMAIL='/bin/cat'
15   # and tell the fucking procmail piece-of-shit to continue to be a filter
16   DEFAULT='|$PROCMAIL'
17 }
18
19 #VERBOSE=yes
20
21 # no need to reprocess messages that went into a spamtrap
22 # UPDATE: retrain them only if diagnosed as non-spam, see below
23 # Note: add E flag to next recipe when uncommenting
24 #:0
25 #* SPAMTRAPPED ?? .
26 #{
27 #  LOG="spamfilter:  skipping checks for spamtrapped message$NL"
28 #  :0 fw
29 #  |$FORMAIL -I"X-Spam: spamtrapped"
30 #}
31
32 # check whether this message is being resubmitted
33 :0
34 *$ $MSG_DEJAVU
35 {
36   :0
37   * TRAINED_AS ?? .
38   {
39     LOG="spamfilter:  skipping already trained $TRAINED_AS$NL"
40     :0
41     * TRAINED_AS ?? spam
42     { IS_SPAM=already-trained }
43   }
44
45   :0 E
46   { LOG="spamfilter:  skipping resubmitted message$NL" }
47 }
48
49 # do not run spamfilters if the message destination is already set
50 :0 E
51 * DEST ?? .
52 {
53   LOG="spamfilter:  message already routed to '$DEST'$NL"
54   SPAM_UNKNOWN=already-destined
55 }
56
57 # let earlier parts of the mailfilter cause bypassing the checks
58 :0 E
59 * SKIP_SPAMCHECKS ?? .
60 {
61   LOG="spamfilter:  skipping checks as requested: $SKIP_SPAMCHECKS$NL"
62   :0 fw
63   |$FORMAIL -I"X-Spam: unknown (skip requested)"
64   SPAM_UNKNOWN=skip-requested
65 }
66
67 # honour skip-spamchecks to exclude certain messages from spam checks
68 # altogether
69 :0 EBH
70 * ? $EGREP -qif $CONF/skip-spamchecks
71 {
72   LOG="spamfilter:  skipping checks as per skip-spamchecks$NL"
73   :0 fw
74   |$FORMAIL -I"X-Spam: unknown (check skipped)"
75   SPAM_UNKNOWN=skip-match
76   SKIP_SPAMCHECKS=match
77 }
78
79 # sanity check on message size
80 :0 E
81 * > $SPAMCHECK_MAX_MESSAGE_SIZE
82 {
83   LOG="spamfilter:  skipping check because message size exceeds $SPAMCHECK_MAX_MESSAGE_SIZE bytes$NL"
84   :0 fw
85   |$FORMAIL -I"X-Spam: unknown (message larger than $SPAMCHECK_MAX_MESSAGE_SIZE bytes)"
86   SPAM_UNKNOWN=too-large
87 }
88
89 # now run the spamfilters
90 :0 E
91 {
92   INCLUDERC=$PMDIR/spamtraps
93   INCLUDERC=$PMDIR/spammers
94   INCLUDERC=$PMDIR/pre-spam-cleanup
95
96   # crm114
97   CRM_SPAM=UNKNOWN
98   CRM_SCORE=0
99   :0
100   * !SKIP_CRM ?? .
101   {
102     :0 fw
103     |$CRM114
104
105     :0
106     * ^X-CRM114-Status: \/[A-Z]+
107     { CRM_SPAM=$MATCH }
108
109     :0
110     * ^X-CRM114-Status: .+\([ ]*\/-?[.0-9]+
111     { CRM_SCORE=$MATCH }
112
113     LOG="crm114:      $CRM_SPAM/$CRM_SCORE$NL"
114   }
115
116   # spamassassin
117   SA_STATUS=Unknown
118   SA_SCORE=0
119   SA_TESTS=none
120   :0
121   * !SKIP_SA ?? .
122   {
123     :0 fw
124     |$SPAMC
125
126     :0
127     * ^X-Spam-Status: \/[A-Za-z]+
128     { SA_SPAM=$MATCH }
129
130     :0
131     * ^X-Spam-Status: .+score=\/-?[.0-9]+
132     { SA_SCORE=$MATCH }
133
134     :0
135     * ^X-Spam-Status: .+tests=\/[^ ]+
136     { SA_TESTS=$MATCH }
137
138     LOG="SA:          $SA_SPAM/$SA_SCORE/$SA_TESTS$NL"
139   }
140
141   ## CASE 0: crm114 is unsure/untrained
142   :0
143   * CRM_SPAM ?? UNSURE
144   {
145     # retrain spamtrapped message
146     :0
147     * SPAMTRAPPED ?? .
148     {
149       LOG="spamfilter:  scheduling retraining with SPAM due to spamtrap$NL"
150       :0 fw
151       |$FORMAIL -A "X-CRM114-Autotrain: spam, due to spamtrap"
152       RETRAIN=spam
153     }
154
155     # retrain as ham
156     :0 E
157     * ? perl -e "$SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
158     {
159       LOG="spamfilter:  scheduling retraining with HAM (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)$NL"
160       :0 fw
161       |$FORMAIL -A "X-CRM114-Autotrain: ham, according to SA (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)"
162       RETRAIN=ham
163     }
164
165     # retrain as spam
166     :0 E
167     * 1^0 ? perl -e "$SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
168     {
169       LOG="spamfilter:  scheduling retraining with SPAM (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
170       :0 fw
171       |$FORMAIL -A "X-CRM114-Autotrain: spam, according to SA (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
172       RETRAIN=spam
173     }
174
175     # skip retraining if SA is not convinced
176     :0 E
177     {
178       LOG="spamfilter:  will not autotrain crm114 because SA is not convinced ($CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM <= $SA_SCORE < $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
179       :0 fw
180       |$FORMAIL -A "X-CRM114-Autotrain: SA is unsure ($CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM <= $SA_SCORE < $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
181       SPAM_UNSURE=sa-unsure
182     }
183   }
184
185   ## CASE 1: disagreement, SA sees ham
186   :0 E
187   * CRM_SPAM ?? SPAM
188   * SA_SPAM ?? No
189   {
190     # message was spamtrapped anyway
191     :0
192     * SPAMTRAPPED ?? .
193     {
194       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
195       RETRAIN=spam
196       :0 fw
197       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
198     }
199
200     # SA is convincing, so retrain crm114
201     :0 E
202     * ? perl -e "$SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
203     {
204       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA is more convincing ($SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)$NL"
205       RETRAIN=ham
206       :0 fw
207       |$FORMAIL -A "X-CRM114-Retrain: ham, according to SA (score $SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)"
208     }
209
210     # SA is not convincing, mark as disagreement
211     :0 E
212     {
213       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA thinks it's ham ($SA_SCORE)$NL"
214       SPAM_DISAGREE=sa-ham
215       :0 fw
216       |$FORMAIL -I "X-Spam: disagree (crm114:spam/$CRM_SCORE SA:ham/$SA_SCORE)"
217     }
218   }
219
220   ## CASE 1: disagreement, SA sees spam
221   :0 E
222   * CRM_SPAM ?? GOOD
223   * SA_SPAM ?? Yes
224   {
225     # message was spamtrapped anyway
226     :0
227     * SPAMTRAPPED ?? .
228     {
229       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
230       RETRAIN=spam
231       :0 fw
232       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
233     }
234
235     # SA is convincing, so retrain crm114
236     :0
237     * ? perl -e "$SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
238     {
239       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA is more convincing ($SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
240       RETRAIN=spam
241       :0 fw
242       |$FORMAIL -A "X-CRM114-Retrain: spam, according to SA (score $SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)"
243     }
244
245     # SA is not convincing, mark as disagreement
246     :0 E
247     {
248       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA thinks it's spam ($SA_SCORE)$NL"
249       SPAM_DISAGREE=sa-spam
250       :0 fw
251       |$FORMAIL -I "X-Spam: disagree (crm114:ham/$CRM_SCORE SA:spam/$SA_SCORE)"
252     }
253   }
254
255   :0 E
256   * CRM_SPAM ?? SPAM
257   * SA_SPAM ?? Yes
258   {
259     IS_SPAM=sa+crm
260     :0 fw
261     |$FORMAIL -I"X-Spam: yes (crm114:$CRM_SCORE SA:$SA_SCORE)"
262   }
263
264   :0 Efw
265   |$FORMAIL -I"X-Spam: no (crm114:$CRM_SCORE SA:$SA_SCORE)"
266 }
267
268 # schedule spamtrapped ham for retraining as spam
269 :0
270 * SPAMTRAPPED ?? .
271 * ! SKIP_SPAMCHECKS ?? .
272 * ! IS_SPAM ?? .
273 {
274   LOG="spamfilter:  found spamtrapped ham, retraining...$NL"
275   :0 fw
276   |$FORMAIL -I"X-Spam: spamtrapped ham"
277   IS_SPAM=spamtrapped-ham
278   RETRAIN=spam
279   SPAM_UNSURE
280 }
281
282 #VERBOSE=no