49862
|
1 ;;; rmail-spam-filter.el --- spam filter for rmail, the emacs mail reader.
|
|
2
|
|
3 ;; Copyright (C) 2002
|
|
4 ;; Free Software Foundation, Inc.
|
|
5 ;; Keywords: email, spam, filter, rmail
|
89909
|
6 ;; Author: Eli Tziperman <eli AT deas.harvard.edu>
|
49862
|
7
|
|
8 ;; This file is part of GNU Emacs.
|
|
9
|
|
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
11 ;; it under the terms of the GNU General Public License as published by
|
|
12 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
13 ;; any later version.
|
|
14
|
|
15 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 ;; GNU General Public License for more details.
|
|
19
|
|
20 ;; You should have received a copy of the GNU General Public License
|
|
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
23 ;; Boston, MA 02111-1307, USA.
|
|
24
|
|
25 ;;; Commentary:
|
|
26 ;;; -----------
|
|
27
|
|
28 ;;; Automatically recognize and delete junk email before it is
|
|
29 ;;; displayed in rmail/rmail-summary. Spam emails are defined by
|
|
30 ;;; specifying one or more of the sender, subject and contents.
|
|
31 ;;; URL: http://www.weizmann.ac.il/~eli/Downloads/rmail-spam-filter/
|
|
32
|
|
33 ;;; Usage:
|
|
34 ;;; ------
|
|
35
|
|
36 ;;; put in your .emacs:
|
|
37
|
|
38 ;;; (load "rmail-spam-filter.el")
|
|
39
|
|
40 ;;; and use customize (in rmail-spam-filter group) to:
|
|
41
|
|
42 ;;; (*) turn on the variable rmail-use-spam-filter,
|
|
43
|
89909
|
44 ;;; (*) specify in variable rsf-definitions-alist what sender,
|
49862
|
45 ;;; subject and contents make an email be considered spam.
|
|
46
|
|
47 ;;; in addition, you may:
|
|
48
|
|
49 ;;; (*) Block future mail with the subject or sender of a message
|
|
50 ;;; while reading it in RMAIL: just click on the "Spam" item on the
|
|
51 ;;; menubar, and add the subject or sender to the list of spam
|
89909
|
52 ;;; definitions using the mouse and the appropriate menu item. You
|
49862
|
53 ;;; need to later also save the list of spam definitions using the
|
|
54 ;;; same menu item, or alternatively, see variable
|
89909
|
55 ;;; `rsf-autosave-newly-added-definitions'.
|
49862
|
56
|
|
57 ;;; (*) specify if blind-cc'ed mail (no "To:" header field) is to be
|
89909
|
58 ;;; treated as spam (variable rsf-no-blind-cc; Thanks to Ethan
|
49862
|
59 ;;; Brown <ethan@gso.saic.com> for this).
|
|
60
|
|
61 ;;; (*) specify if rmail-spam-filter should ignore case of spam
|
89909
|
62 ;;; definitions (variable rsf-ignore-case; Thanks to
|
49862
|
63 ;;; Ethan Brown <ethan@gso.saic.com> for the suggestion).
|
|
64
|
|
65 ;;; (*) Specify a "white-list" of trusted senders. If any
|
89909
|
66 ;;; rsf-white-list string matches a substring of the "From"
|
49862
|
67 ;;; header, the message is flagged as a valid, non-spam message (Ethan
|
|
68 ;;; Brown <ethan@gso.saic.com>).
|
|
69
|
89909
|
70 ;;; (*) rmail-spam-filter is best used with a general purpose spam
|
|
71 ;;; filter such as the procmail-based http://www.spambouncer.org/.
|
|
72 ;;; Spambouncer is set to only mark messages as spam/blocked/bulk/OK
|
|
73 ;;; via special headers, and these headers may then be defined in
|
|
74 ;;; rmail-spam-filter such that the spam is rejected by
|
|
75 ;;; rmail-spam-filter itself.
|
|
76
|
49862
|
77 ;;; (*) rmail spam filter also works with bbdb to prevent spam senders
|
|
78 ;;; from entering into the .bbdb file. See variable
|
89909
|
79 ;;; "rsf-auto-delete-spam-bbdb-entries". This is done
|
49862
|
80 ;;; in two ways: (a) bbdb is made not to auto-create entries for
|
|
81 ;;; messages that are deleted by the rmail-spam-filter, (b) when a
|
|
82 ;;; message is deleted in rmail, the user is offered to delete the
|
|
83 ;;; sender's bbdb entry as well _if_ it was created at the same day.
|
|
84
|
|
85 (require 'rmail)
|
89909
|
86 (if (> emacs-major-version 20)
|
|
87 (require 'rmailsum)
|
|
88 (if (not (fboundp 'rmail-make-summary-line)) (load-library "rmailsum")))
|
49862
|
89
|
89909
|
90 ;; For find-if and other cool common lisp functions we may want to use.
|
|
91 (eval-when-compile
|
|
92 (require 'cl))
|
49862
|
93
|
|
94 (defgroup rmail-spam-filter nil
|
|
95 "Spam filter for RMAIL, the mail reader for Emacs."
|
|
96 :group 'rmail)
|
|
97
|
|
98 (defcustom rmail-use-spam-filter nil
|
|
99 "*Non-nil to activate the rmail spam filter.
|
89909
|
100 Specify `rsf-definitions-alist' to define what you consider spam
|
49862
|
101 emails."
|
|
102 :type 'boolean
|
|
103 :group 'rmail-spam-filter )
|
|
104
|
89909
|
105 (defcustom rsf-file "~/XRMAIL-SPAM"
|
49862
|
106 "*Name of rmail file for optionally saving some of the spam.
|
|
107 Spam may be either just deleted, or saved in a separate spam file to
|
|
108 be looked at at a later time. Whether the spam is just deleted or
|
|
109 also saved in a separete spam file is specified for each definition of
|
89909
|
110 spam, as one of the fields of `rsf-definitions-alist'"
|
49862
|
111 :type 'string
|
|
112 :group 'rmail-spam-filter )
|
|
113
|
89909
|
114 (defcustom rsf-no-blind-cc nil
|
49862
|
115 "*Non-nil to treat blind CC (no To: header) as spam."
|
|
116 :type 'boolean
|
|
117 :group 'rmail-spam-filter )
|
|
118
|
89909
|
119 (defcustom rsf-ignore-case nil
|
|
120 "*Non-nil to ignore case in `rsf-definitions-alist'."
|
49862
|
121 :type 'boolean
|
|
122 :group 'rmail-spam-filter )
|
|
123
|
89909
|
124 (defcustom rsf-beep nil
|
49862
|
125 "*Non-nil to beep if spam is found."
|
|
126 :type 'boolean
|
|
127 :group 'rmail-spam-filter )
|
|
128
|
89909
|
129 (defcustom rsf-sleep-after-message 2.0
|
49862
|
130 "*Seconds to wait after display of message that spam was found."
|
|
131 :type 'number
|
|
132 :group 'rmail-spam-filter )
|
89909
|
133
|
|
134 (defcustom rsf-min-region-to-spam-list 7
|
|
135 "*User may highlight a region in an incomming message and use
|
|
136 the menubar to add this region to the spam definitions. This
|
|
137 variable specifies the minimum size of region that may be added
|
|
138 to spam list, to avoid accidentally adding a too short region
|
|
139 which would result in false positive identification of spam
|
|
140 messages."
|
|
141 :type 'integer
|
|
142 :group 'rmail-spam-filter )
|
|
143
|
|
144 (defcustom rsf-auto-delete-spam-bbdb-entries nil
|
49862
|
145 "*Non-nil to make sure no entries are made in bbdb for spam emails.
|
|
146 This is done in two ways: (1) bbdb is made not to auto-create entries
|
|
147 for messages that are deleted by the `rmail-spam-filter', (2) when a
|
|
148 message is deleted in rmail, the user is offered to delete the
|
|
149 sender's bbdb entry as well if it was created at the same day. Note
|
|
150 that Emacs needs to be restarted after setting this option for it to
|
|
151 take an effect."
|
|
152 :type 'boolean
|
|
153 :group 'rmail-spam-filter )
|
|
154
|
89909
|
155 (defcustom rsf-autosave-newly-added-definitions nil
|
49862
|
156 "*Non-nil to auto save new spam entries.
|
|
157 New entries entered via the spam menu bar item are then saved to
|
|
158 customization file immediately after being added via the menu bar, and
|
|
159 do not require explicitly saving the file after adding the new
|
|
160 entries."
|
|
161 :type 'boolean
|
|
162 :group 'rmail-spam-filter )
|
|
163
|
89909
|
164 (defcustom rsf-white-list nil
|
49862
|
165 "*List of strings to identify valid senders.
|
89909
|
166 If any rsf-white-list string matches a substring of the 'From'
|
49862
|
167 header, the message is flagged as a valid, non-spam message. Example:
|
|
168 If your domain is emacs.com then including 'emacs.com' in your
|
89909
|
169 rsf-white-list would flag all mail from your colleagues as
|
49862
|
170 valid."
|
|
171 :type '(repeat string)
|
|
172 :group 'rmail-spam-filter )
|
|
173
|
89909
|
174 (defcustom rsf-definitions-alist nil
|
49862
|
175 "*Alist matching strings defining what messages are considered spam.
|
|
176 Each definition may contain specifications of one or more of the
|
|
177 elements {subject, sender, recipients or contents}, as well as a
|
|
178 definition of what to do with the spam (action item). A spam e-mail
|
|
179 is defined as one that fits all of the specified elements of any one
|
|
180 of the spam definitions. The strings that specify spam subject,
|
|
181 sender, etc, may be regexp. For example, to specify that the subject
|
|
182 may be either 'this is spam' or 'another spam', use the regexp: 'this
|
89909
|
183 is spam\\|another spam' (without the single quotes). To specify that
|
|
184 if the contents contain both this and that the message is spam,
|
|
185 specify 'this\\&that' in the appropriate spam definition field."
|
49862
|
186 :type '(repeat
|
|
187 (list :format "%v"
|
|
188 (cons :format "%v" :value (from . "")
|
|
189 (const :format "" from)
|
|
190 (string :tag "From" ""))
|
|
191 (cons :format "%v" :value (to . "")
|
|
192 (const :format "" to)
|
|
193 (string :tag "To" ""))
|
|
194 (cons :format "%v" :value (subject . "")
|
|
195 (const :format "" subject)
|
|
196 (string :tag "Subject" ""))
|
89909
|
197 (cons :format "%v" :value (content-type . "")
|
|
198 (const :format "" content-type)
|
|
199 (string :tag "Content-Type" ""))
|
49862
|
200 (cons :format "%v" :value (contents . "")
|
|
201 (const :format "" contents)
|
|
202 (string :tag "Contents" ""))
|
|
203 (cons :format "%v" :value (action . output-and-delete)
|
|
204 (const :format "" action)
|
|
205 (choice :tag "Action selection"
|
|
206 (const :tag "output to spam folder and delete" output-and-delete)
|
|
207 (const :tag "delete spam" delete-spam)
|
|
208 ))
|
|
209 ))
|
|
210 :group 'rmail-spam-filter)
|
|
211
|
89909
|
212 (defvar rsf-scanning-messages-now nil
|
49862
|
213 "Non nil when rmail-spam-filter scans messages,
|
89909
|
214 for interaction with `rsf-bbdb-auto-delete-spam-entries'")
|
|
215
|
|
216 ;; the advantage over the automatic filter definitions is the AND conjunction
|
|
217 ;; of in-one-definition-elements
|
|
218 (defun check-field (field-symbol message-data definition result)
|
|
219 "Check if field-symbol is in `rsf-definitions-alist'.
|
|
220 Capture maybe-spam and this-is-a-spam-email in a cons in result,
|
|
221 where maybe-spam is in first and this-is-a-spam-email is in rest.
|
|
222 The values are returned by destructively changing result.
|
|
223 If FIELD-SYMBOL field does not exist AND is not specified,
|
|
224 this may still be spam due to another element...
|
|
225 if (first result) is nil, we already have a contradiction in another
|
|
226 field"
|
|
227 (let ((definition-field (cdr (assoc field-symbol definition))))
|
|
228 (if (and (first result) (> (length definition-field) 0))
|
|
229 ;; only in this case can maybe-spam change from t to nil
|
|
230 ;; ... else, if FIELD-SYMBOL field does appear in the message,
|
|
231 ;; and it also appears in spam definition list, this
|
|
232 ;; is potentially a spam:
|
|
233 (if (and message-data
|
|
234 (string-match definition-field message-data))
|
|
235 ;; if we do not get a contradiction from another field, this is
|
|
236 ;; spam
|
|
237 (setf (rest result) t)
|
|
238 ;; the message data contradicts the specification, this is no spam
|
|
239 (setf (first result) nil)))))
|
49862
|
240
|
|
241 (defun rmail-spam-filter (msg)
|
89909
|
242 "Return nil if msg is spam based on rsf-definitions-alist.
|
|
243 If spam, optionally output msg to a file `rsf-file' and delete
|
49862
|
244 it from rmail file. Called for each new message retrieved by
|
|
245 `rmail-get-new-mail'."
|
|
246
|
|
247 (let ((old-message)
|
|
248 (return-value)
|
|
249 (this-is-a-spam-email)
|
|
250 (maybe-spam)
|
|
251 (message-sender)
|
|
252 (message-recipients)
|
|
253 (message-subject)
|
89909
|
254 (message-content-type)
|
49862
|
255 (num-spam-definition-elements)
|
|
256 (num-element 0)
|
|
257 (exit-while-loop nil)
|
|
258 (saved-case-fold-search case-fold-search)
|
|
259 (save-current-msg)
|
89909
|
260 (rsf-saved-bbdb/mail_auto_create_p nil)
|
49862
|
261 )
|
|
262
|
|
263 ;; make sure bbdb does not create entries for messages while spam
|
|
264 ;; filter is scanning the rmail file:
|
89909
|
265 (setq rsf-saved-bbdb/mail_auto_create_p 'bbdb/mail_auto_create_p)
|
49862
|
266 (setq bbdb/mail_auto_create_p nil)
|
89909
|
267 ;; let `rsf-bbdb-auto-delete-spam-entries' know that rmail spam
|
49862
|
268 ;; filter is running, so that deletion of rmail messages should be
|
|
269 ;; ignored for now:
|
89909
|
270 (setq rsf-scanning-messages-now t)
|
49862
|
271 (save-excursion
|
|
272 (save-restriction
|
|
273 (setq this-is-a-spam-email nil)
|
|
274 ;; Narrow buffer to header of message and get Sender and
|
|
275 ;; Subject fields to be used below:
|
|
276 (save-restriction
|
|
277 (goto-char (rmail-msgbeg msg))
|
|
278 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
|
|
279 (setq message-sender (mail-fetch-field "From"))
|
89909
|
280 (setq message-recipients
|
|
281 (concat (mail-fetch-field "To")
|
|
282 (if (mail-fetch-field "Cc")
|
|
283 (concat ", " (mail-fetch-field "Cc")))))
|
49862
|
284 (setq message-subject (mail-fetch-field "Subject"))
|
89909
|
285 (setq message-content-type (mail-fetch-field "Content-Type"))
|
49862
|
286 )
|
|
287 ;; Find number of spam-definition elements in the list
|
89909
|
288 ;; rsf-definitions-alist specified by user:
|
49862
|
289 (setq num-spam-definition-elements (safe-length
|
89909
|
290 rsf-definitions-alist))
|
49862
|
291
|
|
292 ;;; do we want to ignore case in spam definitions:
|
89909
|
293 (setq case-fold-search rsf-ignore-case)
|
49862
|
294
|
|
295 ;; Check for blind CC condition. Set vars such that while
|
89909
|
296 ;; loop will be bypassed and spam condition will trigger
|
|
297 (if (and rsf-no-blind-cc
|
49862
|
298 (null message-recipients))
|
89909
|
299 (setq exit-while-loop t
|
|
300 maybe-spam t
|
|
301 this-is-a-spam-email t))
|
|
302
|
|
303 ;; Check white list, and likewise cause while loop
|
|
304 ;; bypass.
|
|
305 (if (let ((white-list rsf-white-list)
|
|
306 (found nil))
|
|
307 (while (and (not found) white-list)
|
|
308 (if (string-match (car white-list) message-sender)
|
|
309 (setq found t)
|
|
310 (setq white-list (cdr white-list))))
|
|
311 found)
|
|
312 (setq exit-while-loop t
|
|
313 maybe-spam nil
|
|
314 this-is-a-spam-email nil))
|
|
315
|
|
316 ;; maybe-spam is in first, this-is-a-spam-email in rest, this
|
|
317 ;; simplifies the call to check-field
|
|
318 (setq maybe-spam (cons maybe-spam this-is-a-spam-email))
|
|
319
|
|
320 ;; scan all elements of the list rsf-definitions-alist
|
49862
|
321 (while (and
|
|
322 (< num-element num-spam-definition-elements)
|
|
323 (not exit-while-loop))
|
89909
|
324 (let ((definition (nth num-element rsf-definitions-alist)))
|
49862
|
325 ;; Initialize maybe-spam which is set to t in one of two
|
|
326 ;; cases: (1) unspecified definition-elements are found in
|
89909
|
327 ;; rsf-definitions-alist, (2) empty field is found
|
49862
|
328 ;; in the message being scanned (e.g. empty subject,
|
|
329 ;; sender, recipients, etc). The variable is set to nil
|
|
330 ;; if a non empty field of the scanned message does not
|
|
331 ;; match a specified field in
|
89909
|
332 ;; rsf-definitions-alist.
|
|
333
|
49862
|
334 ;; initialize this-is-a-spam-email to nil. This variable
|
|
335 ;; is set to t if one of the spam definitions matches a
|
|
336 ;; field in the scanned message.
|
89909
|
337 (setq maybe-spam (cons t nil))
|
49862
|
338
|
|
339 ;; start scanning incoming message:
|
|
340 ;;---------------------------------
|
|
341
|
89909
|
342 ;; Maybe the different fields should also be done in a
|
|
343 ;; loop to make the whole thing more flexible
|
|
344 ;; if sender field is not specified in message being
|
49862
|
345 ;; scanned, AND if "from" field does not appear in spam
|
|
346 ;; definitions for this element, this may still be spam
|
|
347 ;; due to another element...
|
89909
|
348 (check-field 'from message-sender definition maybe-spam)
|
|
349 ;; next, if spam was not ruled out already, check recipients:
|
|
350 (check-field 'to message-recipients definition maybe-spam)
|
|
351 ;; next, if spam was not ruled out already, check subject:
|
|
352 (check-field 'subject message-subject definition maybe-spam)
|
|
353 ;; next, if spam was not ruled out already, check content-type:
|
|
354 (check-field 'content-type message-content-type
|
|
355 definition maybe-spam)
|
49862
|
356 ;; next, if spam was not ruled out already, check
|
|
357 ;; contents: if contents field is not specified, this may
|
|
358 ;; still be spam due to another element...
|
89909
|
359 (check-field 'contents
|
|
360 (buffer-substring
|
|
361 (rmail-msgbeg msg) (rmail-msgend msg))
|
|
362 definition maybe-spam)
|
|
363
|
|
364 ;; if the search in rsf-definitions-alist found
|
49862
|
365 ;; that this email is spam, output the email to the spam
|
|
366 ;; rmail file, mark the email for deletion, leave the
|
|
367 ;; while loop and return nil so that an rmail summary line
|
|
368 ;; wont be displayed for this message:
|
89909
|
369 (if (and (first maybe-spam) (rest maybe-spam))
|
49862
|
370 ;; found that this is spam, no need to look at the
|
89909
|
371 ;; rest of the rsf-definitions-alist, exit
|
49862
|
372 ;; loop:
|
|
373 (setq exit-while-loop t)
|
|
374 ;; else, spam was not yet found, increment number of
|
89909
|
375 ;; element in rsf-definitions-alist and proceed
|
49862
|
376 ;; to next element:
|
|
377 (setq num-element (+ num-element 1)))
|
|
378 )
|
89909
|
379 )
|
|
380
|
|
381 ;; (BK) re-set originally used variables
|
|
382 (setq this-is-a-spam-email (rest maybe-spam)
|
|
383 maybe-spam (first maybe-spam))
|
|
384
|
49862
|
385 (if (and this-is-a-spam-email maybe-spam)
|
|
386 (progn
|
|
387 ;;(message "Found spam!")
|
|
388 ;;(ding 1) (sleep-for 2)
|
|
389
|
|
390 ;; temprarily set rmail-current-message in order to
|
|
391 ;; output and delete the spam msg if needed:
|
|
392 (setq save-current-msg rmail-current-message)
|
|
393 (setq rmail-current-message msg)
|
89909
|
394 ;; check action item and rsf-definitions-alist
|
49862
|
395 ;; and do it:
|
|
396 (cond
|
|
397 ((equal (cdr (assoc 'action
|
89909
|
398 (nth num-element rsf-definitions-alist)))
|
49862
|
399 'output-and-delete)
|
|
400 (progn
|
89909
|
401 (rmail-output-to-rmail-file rsf-file 1 t)
|
|
402 ;; Don't delete if automatic deletion after output
|
|
403 ;; is turned on
|
|
404 (unless rmail-delete-after-output (rmail-delete-message))
|
49862
|
405 ))
|
|
406 ((equal (cdr (assoc 'action
|
89909
|
407 (nth num-element rsf-definitions-alist)))
|
49862
|
408 'delete-spam)
|
|
409 (progn
|
|
410 (rmail-delete-message)
|
|
411 ))
|
|
412 )
|
|
413 (setq rmail-current-message save-current-msg)
|
89909
|
414 (setq bbdb/mail_auto_create_p
|
|
415 'rsf-saved-bbdb/mail_auto_create_p)
|
49862
|
416 ;; set return value. These lines must be last in the
|
|
417 ;; function, so that they will determine the value
|
|
418 ;; returned by rmail-spam-filter:
|
|
419 (setq return-value nil))
|
|
420 (setq return-value t))))
|
|
421 (setq case-fold-search saved-case-fold-search)
|
89909
|
422 (setq rsf-scanning-messages-now nil)
|
49862
|
423 return-value))
|
|
424
|
|
425
|
|
426 ;; define functions for interactively adding sender/subject of a
|
|
427 ;; specific message to the spam definitions while reading it, using
|
|
428 ;; the menubar:
|
89909
|
429 (defun rsf-add-subject-to-spam-list ()
|
49862
|
430 (interactive)
|
|
431 (set-buffer rmail-buffer)
|
|
432 (let ((message-subject))
|
|
433 (setq message-subject (mail-fetch-field "Subject"))
|
|
434 ;; note the use of a backquote and comma on the subject line here,
|
|
435 ;; to make sure message-subject is actually evaluated and its value
|
|
436 ;; substituted:
|
89909
|
437 (add-to-list 'rsf-definitions-alist
|
49862
|
438 (list '(from . "")
|
|
439 '(to . "")
|
|
440 `(subject . ,message-subject)
|
89909
|
441 '(content-type . "")
|
49862
|
442 '(contents . "")
|
|
443 '(action . output-and-delete))
|
|
444 t)
|
89909
|
445 (customize-mark-to-save 'rsf-definitions-alist)
|
|
446 (if rsf-autosave-newly-added-definitions
|
49862
|
447 (progn
|
|
448 (custom-save-all)
|
|
449 (message (concat "added subject \n <<< \n" message-subject
|
|
450 " \n >>> \n to list of spam definitions. \n"
|
|
451 "and saved the spam definitions to file.")))
|
|
452 (message (concat "added subject \n <<< \n" message-subject
|
|
453 " \n >>> \n to list of spam definitions. \n"
|
89909
|
454 "Don't forget to save the spam definitions to file using the spam
|
|
455 menu"))
|
49862
|
456 )))
|
|
457
|
89909
|
458 (defun rsf-add-sender-to-spam-list ()
|
49862
|
459 (interactive)
|
|
460 (set-buffer rmail-buffer)
|
|
461 (let ((message-sender))
|
|
462 (setq message-sender (mail-fetch-field "From"))
|
|
463 ;; note the use of a backquote and comma on the "from" line here,
|
|
464 ;; to make sure message-sender is actually evaluated and its value
|
|
465 ;; substituted:
|
89909
|
466 (add-to-list 'rsf-definitions-alist
|
49862
|
467 (list `(from . ,message-sender)
|
|
468 '(to . "")
|
|
469 '(subject . "")
|
89909
|
470 '(content-type . "")
|
49862
|
471 '(contents . "")
|
|
472 '(action . output-and-delete))
|
|
473 t)
|
89909
|
474 (customize-mark-to-save 'rsf-definitions-alist)
|
|
475 (if rsf-autosave-newly-added-definitions
|
49862
|
476 (progn
|
|
477 (custom-save-all)
|
|
478 (message (concat "added sender \n <<< \n" message-sender
|
|
479 " \n >>> \n to list of spam definitions. \n"
|
|
480 "and saved the spam definitions to file.")))
|
|
481 (message (concat "added sender \n <<< \n " message-sender
|
|
482 " \n >>> \n to list of spam definitions."
|
89909
|
483 "Don't forget to save the spam definitions to file using the spam
|
|
484 menu"))
|
49862
|
485 )))
|
|
486
|
|
487
|
89909
|
488 (defun rsf-add-region-to-spam-list ()
|
|
489 "Add the region makred by user in the rmail buffer to spam list.
|
|
490 Added to spam definitions as a contents field."
|
49862
|
491 (interactive)
|
|
492 (set-buffer rmail-buffer)
|
|
493 (let ((region-to-spam-list))
|
|
494 ;; check if region is inactive or has zero size:
|
|
495 (if (not (and mark-active (not (= (region-beginning) (region-end)))))
|
|
496 ;; if inactive, print error message:
|
|
497 (message "you need to first highlight some text in the rmail buffer")
|
89909
|
498 (if (< (- (region-end) (region-beginning)) rsf-min-region-to-spam-list)
|
|
499 (message
|
|
500 (concat "highlighted region is too small; min length set by variable \n"
|
|
501 "rsf-min-region-to-spam-list"
|
|
502 " is " (number-to-string rsf-min-region-to-spam-list)))
|
|
503 ;; if region active and long enough, add to list of spam definisions:
|
|
504 (progn
|
|
505 (setq region-to-spam-list (buffer-substring (region-beginning) (region-end)))
|
|
506 ;; note the use of a backquote and comma on the "from" line here,
|
|
507 ;; to make sure message-sender is actually evaluated and its value
|
|
508 ;; substituted:
|
|
509 (add-to-list 'rsf-definitions-alist
|
|
510 (list '(from . "")
|
|
511 '(to . "")
|
|
512 '(subject . "")
|
|
513 '(content-type . "")
|
|
514 `(contents . ,region-to-spam-list)
|
|
515 '(action . output-and-delete))
|
|
516 t)
|
|
517 (customize-mark-to-save 'rsf-definitions-alist)
|
|
518 (if rsf-autosave-newly-added-definitions
|
|
519 (progn
|
|
520 (custom-save-all)
|
|
521 (message (concat "added highlighted text \n <<< \n" region-to-spam-list
|
|
522 " \n >>> \n to list of spam definitions. \n"
|
|
523 "and saved the spam definitions to file.")))
|
|
524 (message (concat "added highlighted text \n <<< \n " region-to-spam-list
|
|
525 " \n >>> \n to list of spam definitions."
|
|
526 "Don't forget to save the spam definitions to file using the
|
|
527 spam menu"))
|
|
528 ))))))
|
49862
|
529
|
|
530
|
89909
|
531 (defun rsf-customize-spam-definitions ()
|
49862
|
532 (interactive)
|
89909
|
533 (customize-variable (quote rsf-definitions-alist)))
|
49862
|
534
|
89909
|
535 (defun rsf-customize-group ()
|
49862
|
536 (interactive)
|
|
537 (customize-group (quote rmail-spam-filter)))
|
|
538
|
89909
|
539 (defun rsf-custom-save-all ()
|
49862
|
540 (interactive)
|
|
541 (custom-save-all))
|
|
542
|
|
543 ;; add the actual menu items and keyboard shortcuts to both rmail and
|
|
544 ;; rmail-summary menu-bars::
|
|
545 (define-key rmail-summary-mode-map [menu-bar spam]
|
|
546 (cons "Spam" (make-sparse-keymap "Spam")))
|
|
547 (define-key rmail-mode-map [menu-bar spam]
|
|
548 (cons "Spam" (make-sparse-keymap "Spam")))
|
|
549
|
|
550 (define-key rmail-summary-mode-map [menu-bar spam customize-group]
|
89909
|
551 '("Browse customizations of rmail spam filter" . rsf-customize-group))
|
49862
|
552 (define-key rmail-mode-map [menu-bar spam customize-group]
|
89909
|
553 '("Browse customizations of rmail spam filter" . rsf-customize-group))
|
|
554 (define-key rmail-summary-mode-map "\C-cSg" 'rsf-customize-group)
|
|
555 (define-key rmail-mode-map "\C-cSg" 'rsf-customize-group)
|
49862
|
556
|
|
557 (define-key rmail-summary-mode-map [menu-bar spam customize-spam-list]
|
89909
|
558 '("Customize list of spam definitions" . rsf-customize-spam-definitions))
|
49862
|
559 (define-key rmail-mode-map [menu-bar spam customize-spam-list]
|
89909
|
560 '("Customize list of spam definitions" . rsf-customize-spam-definitions))
|
|
561 (define-key rmail-summary-mode-map "\C-cSd" 'rsf-customize-spam-definitions)
|
|
562 (define-key rmail-mode-map "\C-cSd" 'rsf-customize-spam-definitions)
|
49862
|
563
|
|
564 (define-key rmail-summary-mode-map [menu-bar spam lambda] '("----"))
|
|
565 (define-key rmail-mode-map [menu-bar spam lambda] '("----"))
|
|
566
|
|
567 (define-key rmail-summary-mode-map [menu-bar spam my-custom-save-all]
|
89909
|
568 '("save newly added spam definitions to customization file" . rsf-custom-save-all))
|
49862
|
569 (define-key rmail-mode-map [menu-bar spam my-custom-save-all]
|
89909
|
570 '("save newly added spam definitions to customization file" . rsf-custom-save-all))
|
|
571 (define-key rmail-summary-mode-map "\C-cSa" 'rsf-custom-save-all)
|
|
572 (define-key rmail-mode-map "\C-cSa" 'rsf-custom-save-all)
|
49862
|
573
|
|
574 (define-key rmail-summary-mode-map [menu-bar spam add-region-to-spam-list]
|
89909
|
575 '("add region to spam list" . rsf-add-region-to-spam-list))
|
49862
|
576 (define-key rmail-mode-map [menu-bar spam add-region-to-spam-list]
|
89909
|
577 '("add region to spam list" . rsf-add-region-to-spam-list))
|
|
578 (define-key rmail-summary-mode-map "\C-cSn" 'rsf-add-region-to-spam-list)
|
|
579 (define-key rmail-mode-map "\C-cSn" 'rsf-add-region-to-spam-list)
|
49862
|
580
|
|
581 (define-key rmail-summary-mode-map [menu-bar spam add-sender-to-spam-list]
|
89909
|
582 '("add sender to spam list" . rsf-add-sender-to-spam-list))
|
49862
|
583 (define-key rmail-mode-map [menu-bar spam add-sender-to-spam-list]
|
89909
|
584 '("add sender to spam list" . rsf-add-sender-to-spam-list))
|
|
585 (define-key rmail-summary-mode-map "\C-cSr" 'rsf-add-sender-to-spam-list)
|
|
586 (define-key rmail-mode-map "\C-cSr" 'rsf-add-sender-to-spam-list)
|
49862
|
587
|
|
588 (define-key rmail-summary-mode-map [menu-bar spam add-subject-to-spam-list]
|
89909
|
589 '("add subject to spam list" . rsf-add-subject-to-spam-list))
|
49862
|
590 (define-key rmail-mode-map [menu-bar spam add-subject-to-spam-list]
|
89909
|
591 '("add subject to spam list" . rsf-add-subject-to-spam-list))
|
|
592 (define-key rmail-summary-mode-map "\C-cSt" 'rsf-add-subject-to-spam-list)
|
|
593 (define-key rmail-mode-map "\C-cSt" 'rsf-add-subject-to-spam-list)
|
49862
|
594
|
89909
|
595 (defun rsf-add-content-type-field ()
|
|
596 "Maintain backward compatibility with previous versions of rmail-spam-filter.
|
|
597 The most recent version of rmai-spam-filter checks the contents
|
|
598 field of the incoming mail to see if it spam. The format of
|
|
599 `rsf-definitions-alist' has therefore changed. This function
|
|
600 checks to see if old format is used, and if it is, it converts
|
|
601 `rsf-definitions-alist' to the new format. Invoked
|
|
602 automatically, no user input is required."
|
49862
|
603 (interactive)
|
89909
|
604 (if (and rsf-definitions-alist
|
|
605 (not (assoc 'content-type (car rsf-definitions-alist))))
|
|
606 (let ((result nil)
|
|
607 (current nil)
|
|
608 (definitions rsf-definitions-alist))
|
|
609 (while definitions
|
|
610 (setq current (car definitions))
|
|
611 (setq definitions (cdr definitions))
|
|
612 (setq result
|
|
613 (append result
|
|
614 (list
|
|
615 (list (assoc 'from current)
|
|
616 (assoc 'to current)
|
|
617 (assoc 'subject current)
|
|
618 (cons 'content-type "")
|
|
619 (assoc 'contents current)
|
|
620 (assoc 'action current))))))
|
|
621 (setq rsf-definitions-alist result)
|
|
622 (customize-mark-to-save 'rsf-definitions-alist)
|
|
623 (if rsf-autosave-newly-added-definitions
|
|
624 (progn
|
|
625 (custom-save-all)
|
|
626 (message (concat "converted spam definitions to new format\n"
|
|
627 "and saved the spam definitions to file.")))
|
|
628 (message (concat "converted spam definitions to new format\n"
|
|
629 "Don't forget to save the spam definitions to file using the
|
|
630 spam menu"))
|
|
631 ))))
|
49862
|
632
|
|
633 (provide 'rmail-spam-filter)
|
|
634
|
89909
|
635 ;;; arch-tag: 03e1d45d-b72f-4dd7-8f04-e7fd78249746
|
|
636 ;;; rmail-spam-fitler ends here
|