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

disabled msgid-index for now
[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 fw
41     |$FORMAIL -I"X-Spam: $TRAINED_AS (already trained)"
42     :0
43     * TRAINED_AS ?? spam
44     { IS_SPAM=already-trained }
45   }
46
47   :0 E
48   {
49     LOG="spamfilter:  skipping resubmitted message$NL"
50     :0 fw
51     |$FORMAIL -I"X-Spam: unknown (resubmitted)"
52   }
53 }
54
55 # do not run spamfilters if the message destination is already set
56 :0 E
57 * DEST ?? .
58 {
59   LOG="spamfilter:  message already routed to '$DEST'$NL"
60   :0 fw
61   |$FORMAIL -I"X-Spam: unknown (already routed)"
62   SPAM_UNKNOWN=already-destined
63 }
64
65 # let earlier parts of the mailfilter cause bypassing the checks
66 :0 E
67 * SKIP_SPAMCHECKS ?? .
68 {
69   LOG="spamfilter:  skipping checks as requested: $SKIP_SPAMCHECKS$NL"
70   :0 fw
71   |$FORMAIL -I"X-Spam: unknown (skip requested)"
72   SPAM_UNKNOWN=skip-requested
73 }
74
75 # honour skip-spamchecks to exclude certain messages from spam checks
76 # altogether
77 :0 EBH
78 * ? $EGREP -qif $CONF/skip-spamchecks
79 {
80   LOG="spamfilter:  skipping checks as per skip-spamchecks$NL"
81   :0 fw
82   |$FORMAIL -I"X-Spam: unknown (check skipped)"
83   SPAM_UNKNOWN=skip-match
84   SKIP_SPAMCHECKS=match
85 }
86
87 # sanity check on message size
88 :0 E
89 * > $SPAMCHECK_MAX_MESSAGE_SIZE
90 {
91   LOG="spamfilter:  skipping check because message size exceeds $SPAMCHECK_MAX_MESSAGE_SIZE bytes$NL"
92   :0 fw
93   |$FORMAIL -I"X-Spam: unknown (message larger than $SPAMCHECK_MAX_MESSAGE_SIZE bytes)"
94   SPAM_UNKNOWN=too-large
95 }
96
97 # now run the spamfilters
98 :0 E
99 {
100   INCLUDERC=$PMDIR/spamtraps
101   INCLUDERC=$PMDIR/spammers
102   INCLUDERC=$PMDIR/pre-spam-cleanup
103
104   # crm114
105   CRM_SPAM=UNKNOWN
106   CRM_SCORE=0
107   :0
108   * !SKIP_CRM ?? .
109   {
110     #TODO: somehow filter out headers we added
111     :0 fw
112     |$CRM114
113
114     :0
115     * ^X-CRM114-Status: \/[A-Z]+
116     { CRM_SPAM=$MATCH }
117
118     :0
119     * ^X-CRM114-Status: .+\([ ]*\/-?[.0-9]+
120     { CRM_SCORE=$MATCH }
121
122     LOG="crm114:      $CRM_SPAM/$CRM_SCORE$NL"
123   }
124
125   # spamassassin
126   SA_STATUS=Unknown
127   SA_SCORE=0
128   SA_TESTS=none
129   :0
130   * !SKIP_SA ?? .
131   {
132     :0 fw
133     |$SPAMC
134
135     :0
136     * ^X-Spam-Status: \/[A-Za-z]+
137     { SA_SPAM=$MATCH }
138
139     :0
140     * ^X-Spam-Status: .+score=\/-?[.0-9]+
141     { SA_SCORE=$MATCH }
142
143     :0
144     * ^X-Spam-Status: .+tests=\/[^ ]+
145     { SA_TESTS=$MATCH }
146
147     LOG="SA:          $SA_SPAM/$SA_SCORE/$SA_TESTS$NL"
148   }
149
150   ## CASE 0: crm114 is unsure/untrained
151   :0
152   * CRM_SPAM ?? UNSURE
153   {
154     # retrain spamtrapped message
155     :0
156     * SPAMTRAPPED ?? .
157     {
158       LOG="spamfilter:  scheduling retraining with SPAM due to spamtrap$NL"
159       :0 fw
160       |$FORMAIL -A "X-CRM114-Autotrain: spam, due to spamtrap"
161       RETRAIN=spam
162     }
163
164     # retrain as ham
165     :0 E
166     * ? perl -e "$SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
167     {
168       LOG="spamfilter:  scheduling retraining with HAM (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)$NL"
169       :0 fw
170       |$FORMAIL -A "X-CRM114-Autotrain: ham, according to SA (score $SA_SCORE <= $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM)"
171       RETRAIN=ham
172     }
173
174     # retrain as spam
175     :0 E
176     * 1^0 ? perl -e "$SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
177     {
178       LOG="spamfilter:  scheduling retraining with SPAM (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
179       :0 fw
180       |$FORMAIL -A "X-CRM114-Autotrain: spam, according to SA (score $SA_SCORE > $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
181       RETRAIN=spam
182     }
183
184     # skip retraining if SA is not convinced
185     :0 E
186     {
187       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"
188       :0 fw
189       |$FORMAIL -A "X-CRM114-Autotrain: SA is unsure ($CRM_UNSURE_SA_AUTOTRAIN_LIMIT_HAM <= $SA_SCORE < $CRM_UNSURE_SA_AUTOTRAIN_LIMIT_SPAM)"
190       SPAM_UNSURE=sa-unsure
191     }
192   }
193
194   ## CASE 1: disagreement, SA sees ham
195   :0 E
196   * CRM_SPAM ?? SPAM
197   * SA_SPAM ?? No
198   {
199     # message was spamtrapped anyway
200     :0
201     * SPAMTRAPPED ?? .
202     {
203       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
204       RETRAIN=spam
205       :0 fw
206       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
207     }
208
209     # SA is convincing, so retrain crm114
210     :0 E
211     * ? perl -e "$SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM || exit 1"
212     {
213       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA is more convincing ($SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)$NL"
214       RETRAIN=ham
215       :0 fw
216       |$FORMAIL -A "X-CRM114-Retrain: ham, according to SA (score $SA_SCORE <= $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_HAM)"
217     }
218
219     # SA is not convincing, mark as disagreement
220     :0 E
221     {
222       LOG="spamfilter:  crm114 found spam ($CRM_SCORE), but SA thinks it's ham ($SA_SCORE)$NL"
223       SPAM_DISAGREE=sa-ham
224       :0 fw
225       |$FORMAIL -I "X-Spam: disagree (crm114:spam/$CRM_SCORE SA:ham/$SA_SCORE)"
226     }
227   }
228
229   ## CASE 1: disagreement, SA sees spam
230   :0 E
231   * CRM_SPAM ?? GOOD
232   * SA_SPAM ?? Yes
233   {
234     # message was spamtrapped anyway
235     :0
236     * SPAMTRAPPED ?? .
237     {
238       LOG="spamfilter:  resolving crm114/SA disagreement due to spamtrap ($CRM_SCORE/$SA_SCORE)$NL"
239       RETRAIN=spam
240       :0 fw
241       |$FORMAIL -A "X-CRM114-Retrain: spam, due to spamtrap"
242     }
243
244     # SA is convincing, so retrain crm114
245     :0
246     * ? perl -e "$SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM || exit 1"
247     {
248       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA is more convincing ($SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)$NL"
249       RETRAIN=spam
250       :0 fw
251       |$FORMAIL -A "X-CRM114-Retrain: spam, according to SA (score $SA_SCORE > $CRM_MISCLASSIFY_SA_AUTOTRAIN_LIMIT_SPAM)"
252     }
253
254     # SA is not convincing, mark as disagreement
255     :0 E
256     {
257       LOG="spamfilter:  crm114 found ham ($CRM_SCORE), but SA thinks it's spam ($SA_SCORE)$NL"
258       SPAM_DISAGREE=sa-spam
259       :0 fw
260       |$FORMAIL -I "X-Spam: disagree (crm114:ham/$CRM_SCORE SA:spam/$SA_SCORE)"
261     }
262   }
263
264   :0 E
265   * CRM_SPAM ?? SPAM
266   * SA_SPAM ?? Yes
267   {
268     IS_SPAM=sa+crm
269     :0 fw
270     |$FORMAIL -I"X-Spam: yes (crm114:$CRM_SCORE SA:$SA_SCORE)"
271   }
272
273   :0 Efw
274   |$FORMAIL -I"X-Spam: no (crm114:$CRM_SCORE SA:$SA_SCORE)"
275 }
276
277 # schedule spamtrapped ham for retraining as spam
278 :0
279 * SPAMTRAPPED ?? .
280 * ! SKIP_SPAMCHECKS ?? .
281 * ! IS_SPAM ?? .
282 {
283   LOG="spamfilter:  found spamtrapped ham, retraining...$NL"
284   :0 fw
285   |$FORMAIL -I"X-Spam: spamtrapped ham"
286   IS_SPAM=spamtrapped-ham
287   RETRAIN=spam
288   SPAM_UNSURE
289 }
290
291 #VERBOSE=no