]> 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:

implement locking for logging
[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     #TODO: somehow filter out headers we added
103     :0 fw
104     |$CRM114
105
106     :0
107     * ^X-CRM114-Status: \/[A-Z]+
108     { CRM_SPAM=$MATCH }
109
110     :0
111     * ^X-CRM114-Status: .+\([ ]*\/-?[.0-9]+
112     { CRM_SCORE=$MATCH }
113
114     LOG="crm114:      $CRM_SPAM/$CRM_SCORE$NL"
115   }
116
117   # spamassassin
118   SA_STATUS=Unknown
119   SA_SCORE=0
120   SA_TESTS=none
121   :0
122   * !SKIP_SA ?? .
123   {
124     :0 fw
125     |$SPAMC
126
127     :0
128     * ^X-Spam-Status: \/[A-Za-z]+
129     { SA_SPAM=$MATCH }
130
131     :0
132     * ^X-Spam-Status: .+score=\/-?[.0-9]+
133     { SA_SCORE=$MATCH }
134
135     :0
136     * ^X-Spam-Status: .+tests=\/[^ ]+
137     { SA_TESTS=$MATCH }
138
139     LOG="SA:          $SA_SPAM/$SA_SCORE/$SA_TESTS$NL"
140   }
141
142   ## CASE 0: crm114 is unsure/untrained
143   :0
144   * CRM_SPAM ?? UNSURE
145   {
146     # retrain spamtrapped message
147     :0
148     * SPAMTRAPPED ?? .
149     {
150       LOG="spamfilter:  scheduling retraining with SPAM due to spamtrap$NL"
151       :0 fw
152       |$FORMAIL -A "X-CRM114-Autotrain: spam, due to spamtrap"
153       RETRAIN=spam
154     }
155
156     # retrain as ham
157     :0 E
158     * ? perl -e "$SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
159     {
160       LOG="spamfilter:  scheduling retraining with HAM (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)$NL"
161       :0 fw
162       |$FORMAIL -A "X-CRM114-Autotrain: ham, according to SA (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)"
163       RETRAIN=ham
164     }
165
166     # retrain as spam
167     :0 E
168     * 1^0 ? perl -e "$SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
169     {
170       LOG="spamfilter:  scheduling retraining with SPAM (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
171       :0 fw
172       |$FORMAIL -A "X-CRM114-Autotrain: spam, according to SA (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
173       RETRAIN=spam
174     }
175
176     # skip retraining if SA is not convinced
177     :0 E
178     {
179       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"
180       :0 fw
181       |$FORMAIL -A "X-CRM114-Autotrain: SA is unsure ($CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM <= $SA_SCORE < $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
182       SPAM_UNSURE=sa-unsure
183     }
184   }
185
186   ## CASE 1: disagreement, SA sees ham
187   :0 E
188   * CRM_SPAM ?? SPAM
189   * SA_SPAM ?? No
190   {
191     # message was spamtrapped anyway
192     :0
193     * SPAMTRAPPED ?? .
194     {
195       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
196       RETRAIN=spam
197       :0 fw
198       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
199     }
200
201     # SA is convincing, so retrain crm114
202     :0 E
203     * ? perl -e "$SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
204     {
205       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA is more convincing ($SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)$NL"
206       RETRAIN=ham
207       :0 fw
208       |$FORMAIL -A "X-CRM114-Retrain: ham, according to SA (score $SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)"
209     }
210
211     # SA is not convincing, mark as disagreement
212     :0 E
213     {
214       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA thinks it's ham ($SA_SCORE)$NL"
215       SPAM_DISAGREE=sa-ham
216       :0 fw
217       |$FORMAIL -I "X-Spam: disagree (crm114:spam/$CRM_SCORE SA:ham/$SA_SCORE)"
218     }
219   }
220
221   ## CASE 1: disagreement, SA sees spam
222   :0 E
223   * CRM_SPAM ?? GOOD
224   * SA_SPAM ?? Yes
225   {
226     # message was spamtrapped anyway
227     :0
228     * SPAMTRAPPED ?? .
229     {
230       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
231       RETRAIN=spam
232       :0 fw
233       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
234     }
235
236     # SA is convincing, so retrain crm114
237     :0
238     * ? perl -e "$SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
239     {
240       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA is more convincing ($SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
241       RETRAIN=spam
242       :0 fw
243       |$FORMAIL -A "X-CRM114-Retrain: spam, according to SA (score $SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)"
244     }
245
246     # SA is not convincing, mark as disagreement
247     :0 E
248     {
249       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA thinks it's spam ($SA_SCORE)$NL"
250       SPAM_DISAGREE=sa-spam
251       :0 fw
252       |$FORMAIL -I "X-Spam: disagree (crm114:ham/$CRM_SCORE SA:spam/$SA_SCORE)"
253     }
254   }
255
256   :0 E
257   * CRM_SPAM ?? SPAM
258   * SA_SPAM ?? Yes
259   {
260     IS_SPAM=sa+crm
261     :0 fw
262     |$FORMAIL -I"X-Spam: yes (crm114:$CRM_SCORE SA:$SA_SCORE)"
263   }
264
265   :0 Efw
266   |$FORMAIL -I"X-Spam: no (crm114:$CRM_SCORE SA:$SA_SCORE)"
267 }
268
269 # schedule spamtrapped ham for retraining as spam
270 :0
271 * SPAMTRAPPED ?? .
272 * ! SKIP_SPAMCHECKS ?? .
273 * ! IS_SPAM ?? .
274 {
275   LOG="spamfilter:  found spamtrapped ham, retraining...$NL"
276   :0 fw
277   |$FORMAIL -I"X-Spam: spamtrapped ham"
278   IS_SPAM=spamtrapped-ham
279   RETRAIN=spam
280   SPAM_UNSURE
281 }
282
283 #VERBOSE=no