Merge from emacs-23
[bpt/emacs.git] / lisp / mail / rmail-spam-filter.el
CommitLineData
8ad667fa 1;;; rmail-spam-filter.el --- spam filter for Rmail, the Emacs mail reader
f5ea26f8 2
5df4f04c 3;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
415046c8 4;; Free Software Foundation, Inc.
97d3e8cd 5;; Keywords: email, spam, filter, rmail
f5ea26f8 6;; Author: Eli Tziperman <eli AT deas.harvard.edu>
aad4679e 7;; Package: rmail
97d3e8cd
PR
8
9;; This file is part of GNU Emacs.
10
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
97d3e8cd 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
97d3e8cd
PR
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
97d3e8cd
PR
23
24;;; Commentary:
f5ea26f8 25;;; -----------
97d3e8cd 26
f5ea26f8
RS
27;;; Automatically recognize and delete junk email before it is
28;;; displayed in rmail/rmail-summary. Spam emails are defined by
29;;; specifying one or more of the sender, subject and contents.
88fcbeaa 30;;; URL: http://www.weizmann.ac.il/~eli/Downloads/rmail-spam-filter/
97d3e8cd 31
f5ea26f8
RS
32;;; Usage:
33;;; ------
97d3e8cd 34
f5ea26f8 35;;; put in your .emacs:
97d3e8cd 36
415046c8 37;;; (require 'rmail-spam-filter)
97d3e8cd 38
f5ea26f8 39;;; and use customize (in rmail-spam-filter group) to:
97d3e8cd 40
f5ea26f8 41;;; (*) turn on the variable rmail-use-spam-filter,
97d3e8cd 42
f5ea26f8
RS
43;;; (*) specify in variable rsf-definitions-alist what sender,
44;;; subject and contents make an email be considered spam.
97d3e8cd 45
f5ea26f8 46;;; in addition, you may:
97d3e8cd 47
f5ea26f8
RS
48;;; (*) Block future mail with the subject or sender of a message
49;;; while reading it in RMAIL: just click on the "Spam" item on the
50;;; menubar, and add the subject or sender to the list of spam
51;;; definitions using the mouse and the appropriate menu item. You
52;;; need to later also save the list of spam definitions using the
53;;; same menu item, or alternatively, see variable
54;;; `rsf-autosave-newly-added-definitions'.
97d3e8cd 55
f5ea26f8
RS
56;;; (*) specify if blind-cc'ed mail (no "To:" header field) is to be
57;;; treated as spam (variable rsf-no-blind-cc; Thanks to Ethan
58;;; Brown <ethan@gso.saic.com> for this).
97d3e8cd 59
f5ea26f8
RS
60;;; (*) specify if rmail-spam-filter should ignore case of spam
61;;; definitions (variable rsf-ignore-case; Thanks to
62;;; Ethan Brown <ethan@gso.saic.com> for the suggestion).
97d3e8cd 63
f5ea26f8
RS
64;;; (*) Specify a "white-list" of trusted senders. If any
65;;; rsf-white-list string matches a substring of the "From"
66;;; header, the message is flagged as a valid, non-spam message (Ethan
67;;; Brown <ethan@gso.saic.com>).
97d3e8cd 68
f5ea26f8
RS
69;;; (*) rmail-spam-filter is best used with a general purpose spam
70;;; filter such as the procmail-based http://www.spambouncer.org/.
71;;; Spambouncer is set to only mark messages as spam/blocked/bulk/OK
72;;; via special headers, and these headers may then be defined in
73;;; rmail-spam-filter such that the spam is rejected by
74;;; rmail-spam-filter itself.
2528f9c4 75
97d3e8cd 76(require 'rmail)
415046c8 77(require 'rmailsum)
97d3e8cd 78
97d3e8cd 79(defgroup rmail-spam-filter nil
8ad667fa 80 "Spam filter for Rmail, the Emacs mail reader."
97d3e8cd
PR
81 :group 'rmail)
82
83(defcustom rmail-use-spam-filter nil
8ad667fa
GM
84 "Non-nil to activate the Rmail spam filter.
85Set `rsf-definitions-alist' to define what you consider spam emails."
97d3e8cd 86 :type 'boolean
8ad667fa 87 :group 'rmail-spam-filter)
97d3e8cd 88
f5ea26f8 89(defcustom rsf-file "~/XRMAIL-SPAM"
8ad667fa
GM
90 "Name of Rmail file for optionally saving some of the spam.
91You can either just delete spam, or save it in this file for
92later review. Which action to take for each spam definition is
93specified by the \"action\" element of the definition."
97d3e8cd 94 :type 'string
8ad667fa 95 :group 'rmail-spam-filter)
97d3e8cd 96
f5ea26f8 97(defcustom rsf-no-blind-cc nil
8ad667fa 98 "Non-nil means mail with no explicit To: or Cc: is spam."
97d3e8cd 99 :type 'boolean
8ad667fa 100 :group 'rmail-spam-filter)
97d3e8cd 101
f5ea26f8 102(defcustom rsf-ignore-case nil
8ad667fa 103 "Non-nil means to ignore case in `rsf-definitions-alist'."
97d3e8cd 104 :type 'boolean
8ad667fa 105 :group 'rmail-spam-filter)
97d3e8cd 106
f5ea26f8 107(defcustom rsf-beep nil
8ad667fa 108 "Non-nil means to beep if spam is found."
97d3e8cd 109 :type 'boolean
8ad667fa 110 :group 'rmail-spam-filter)
97d3e8cd 111
f5ea26f8 112(defcustom rsf-sleep-after-message 2.0
8ad667fa 113 "Seconds to wait after displaying a message that spam was found."
97d3e8cd 114 :type 'number
8ad667fa 115 :group 'rmail-spam-filter)
2528f9c4 116
f5ea26f8 117(defcustom rsf-min-region-to-spam-list 7
415046c8 118 "Minimum size of region that you can add to the spam list.
8ad667fa
GM
119The aim is to avoid adding too short a region, which could result
120in false positive identification of a valid message as spam."
f5ea26f8 121 :type 'integer
8ad667fa 122 :group 'rmail-spam-filter)
f5ea26f8 123
f5ea26f8 124(defcustom rsf-autosave-newly-added-definitions nil
8ad667fa
GM
125 "Non-nil to auto-save new spam entries.
126Any time you add an entry via the \"Spam\" menu, immediately saves
127the custom file."
97d3e8cd 128 :type 'boolean
8ad667fa 129 :group 'rmail-spam-filter)
97d3e8cd 130
f5ea26f8 131(defcustom rsf-white-list nil
8ad667fa
GM
132 "List of regexps to identify valid senders.
133If any element matches the \"From\" header, the message is
134flagged as a valid, non-spam message. E.g., if your domain is
135\"emacs.com\" then including \"emacs\\\\.com\" in this list would
136flag all mail (purporting to be) from your colleagues as valid."
97d3e8cd 137 :type '(repeat string)
8ad667fa 138 :group 'rmail-spam-filter)
97d3e8cd 139
f5ea26f8 140(defcustom rsf-definitions-alist nil
8ad667fa
GM
141 "A list of rules (definitions) matching spam messages.
142Each rule is an alist, with elements of the form (FIELD . REGEXP).
143The recognized FIELDS are: from, to, subject, content-type,
144x-spam-status, and contents. The \"contents\" element refers to
145the entire text of the message; all the other elements refer to
146message headers of the same name.
147
148Using an empty-string for REGEXP is the same as omitting that
149element altogether.
150
151Each rule should contain one \"action\" element, saying what to do
152if the rule is matched. This has the form (action . CHOICE), where
153CHOICE may be either `output-and-delete' (save to `rsf-file', then delete),
154or `delete-spam' (just delete).
155
156A rule matches only if all the specified elements match."
e84b4b86 157 :type '(repeat
97d3e8cd
PR
158 (list :format "%v"
159 (cons :format "%v" :value (from . "")
160 (const :format "" from)
161 (string :tag "From" ""))
162 (cons :format "%v" :value (to . "")
163 (const :format "" to)
164 (string :tag "To" ""))
165 (cons :format "%v" :value (subject . "")
166 (const :format "" subject)
167 (string :tag "Subject" ""))
f5ea26f8
RS
168 (cons :format "%v" :value (content-type . "")
169 (const :format "" content-type)
170 (string :tag "Content-Type" ""))
97d3e8cd
PR
171 (cons :format "%v" :value (contents . "")
172 (const :format "" contents)
173 (string :tag "Contents" ""))
765e147a
BG
174 (cons :format "%v" :value (x-spam-status . "")
175 (const :format "" x-spam-status)
176 (string :tag "X-Spam-Status" ""))
97d3e8cd
PR
177 (cons :format "%v" :value (action . output-and-delete)
178 (const :format "" action)
e84b4b86 179 (choice :tag "Action selection"
8ad667fa
GM
180 (const :tag "Output and delete" output-and-delete)
181 (const :tag "Delete" delete-spam)
182 ))))
97d3e8cd
PR
183 :group 'rmail-spam-filter)
184
8ad667fa 185;; FIXME nothing uses this, and it could just be let-bound.
f5ea26f8 186(defvar rsf-scanning-messages-now nil
415046c8 187 "Non-nil when `rmail-spam-filter' scans messages.")
f5ea26f8
RS
188
189;; the advantage over the automatic filter definitions is the AND conjunction
190;; of in-one-definition-elements
415046c8 191(defun rsf-check-field (field-symbol message-data definition result)
8ad667fa
GM
192 "Check if a message appears to be spam.
193FIELD-SYMBOL is one of the possible keys of a `rsf-definitions-alist'
194rule; e.g. from, to. MESSAGE-DATA is a string giving the value of
195FIELD-SYMBOL in the current message. DEFINITION is the element of
196`rsf-definitions-alist' currently being checked.
197
198RESULT is a cons of the form (MAYBE-SPAM . IS-SPAM). If the car
199is nil, or if the entry for FIELD-SYMBOL in this DEFINITION is
200absent or the empty string, this function does nothing.
201
202Otherwise, if MESSAGE-DATA is non-nil and the entry matches it,
203the cdr is set to t. Else, the car is set to nil."
f5ea26f8 204 (let ((definition-field (cdr (assoc field-symbol definition))))
8ad667fa 205 ;; Only in this case can maybe-spam change from t to nil.
415046c8 206 (if (and (car result) (> (length definition-field) 0))
8ad667fa
GM
207 ;; If FIELD-SYMBOL field appears in the message, and also in
208 ;; spam definition list, this is potentially a spam.
f5ea26f8
RS
209 (if (and message-data
210 (string-match definition-field message-data))
8ad667fa
GM
211 ;; If we do not get a contradiction from another field, this is spam
212 (setcdr result t)
213 ;; The message data contradicts the specification, this is not spam.
214 ;; Note that the total absence of a header specified in the
215 ;; rule means this cannot be spam.
216 (setcar result nil)))))
97d3e8cd
PR
217
218(defun rmail-spam-filter (msg)
8ad667fa
GM
219 "Return nil if message number MSG is spam based on `rsf-definitions-alist'.
220If spam, optionally output message to a file `rsf-file' and delete
97d3e8cd
PR
221it from rmail file. Called for each new message retrieved by
222`rmail-get-new-mail'."
8ad667fa
GM
223 (let ((return-value)
224 ;; maybe-spam is in the car, this-is-a-spam-email in cdr.
225 (maybe-spam '(nil . nil))
226 message-sender message-to message-cc message-recipients
227 message-subject message-content-type message-spam-status
228 (num-spam-definition-elements (safe-length rsf-definitions-alist))
97d3e8cd
PR
229 (num-element 0)
230 (exit-while-loop nil)
8ad667fa
GM
231 ;; Do we want to ignore case in spam definitions.
232 (case-fold-search rsf-ignore-case)
415046c8
GM
233 ;; make sure bbdb does not create entries for messages while spam
234 ;; filter is scanning the rmail file:
235 (bbdb/mail_auto_create_p nil)
8ad667fa
GM
236 ;; Other things may wish to know if we are running (nothing
237 ;; uses this at present).
238 (rsf-scanning-messages-now t))
97d3e8cd 239 (save-excursion
8ad667fa
GM
240 ;; Narrow buffer to header of message and get Sender and
241 ;; Subject fields to be used below:
97d3e8cd 242 (save-restriction
8ad667fa
GM
243 (goto-char (rmail-msgbeg msg))
244 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
245 (setq message-sender (mail-fetch-field "From"))
246 (setq message-to (mail-fetch-field "To")
247 message-cc (mail-fetch-field "Cc")
248 message-recipients (or (and message-to message-cc
249 (concat message-to ", " message-cc))
250 message-to
251 message-cc))
252 (setq message-subject (mail-fetch-field "Subject"))
253 (setq message-content-type (mail-fetch-field "Content-Type"))
254 (setq message-spam-status (mail-fetch-field "X-Spam-Status")))
255 ;; Check for blind CC condition. Set vars such that while
256 ;; loop will be bypassed and spam condition will trigger.
257 (and rsf-no-blind-cc
258 (null message-recipients)
259 (setq exit-while-loop t
260 maybe-spam '(t . t)))
261 ;; Check white list, and likewise cause while loop bypass.
262 (and message-sender
263 (let ((white-list rsf-white-list)
264 (found nil))
265 (while (and (not found) white-list)
266 (if (string-match (car white-list) message-sender)
267 (setq found t)
268 (setq white-list (cdr white-list))))
269 found)
270 (setq exit-while-loop t
271 maybe-spam '(nil . nil)))
272 ;; Scan all elements of the list rsf-definitions-alist.
273 (while (and (< num-element num-spam-definition-elements)
274 (not exit-while-loop))
275 (let ((definition (nth num-element rsf-definitions-alist)))
276 ;; Initialize car, which is set to t in one of two cases:
277 ;; (1) unspecified definition-elements are found in
278 ;; rsf-definitions-alist, (2) empty field is found in the
279 ;; message being scanned (e.g. empty subject, sender,
280 ;; recipients, etc). It is set to nil if a non-empty field
281 ;; of the scanned message does not match a specified field
282 ;; in rsf-definitions-alist.
283 ;; FIXME the car is never set to t?!
284
285 ;; Initialize cdr to nil. This is set to t if one of the
286 ;; spam definitions matches a field in the scanned message.
287 (setq maybe-spam (cons t nil))
288
289 ;; Maybe the different fields should also be done in a
290 ;; loop to make the whole thing more flexible.
291
292 ;; If sender field is not specified in message being
293 ;; scanned, AND if "from" field does not appear in spam
294 ;; definitions for this element, this may still be spam due
295 ;; to another element...
296 (rsf-check-field 'from message-sender definition maybe-spam)
297 ;; Next, if spam was not ruled out already, check recipients:
298 (rsf-check-field 'to message-recipients definition maybe-spam)
299 ;; Next, if spam was not ruled out already, check subject:
300 (rsf-check-field 'subject message-subject definition maybe-spam)
301 ;; Next, if spam was not ruled out already, check content-type:
302 (rsf-check-field 'content-type message-content-type
303 definition maybe-spam)
304 ;; Next, if spam was not ruled out already, check contents:
305 ;; If contents field is not specified, this may still be
306 ;; spam due to another element...
307 (rsf-check-field 'contents
308 (buffer-substring-no-properties
309 (rmail-msgbeg msg) (rmail-msgend msg))
310 definition maybe-spam)
311
312 ;; Finally, check the X-Spam-Status header. You will typically
313 ;; look for the "Yes" string in this header field.
314 (rsf-check-field 'x-spam-status message-spam-status
315 definition maybe-spam)
316
317 ;; If the search in rsf-definitions-alist found
318 ;; that this email is spam, output the email to the spam
319 ;; rmail file, mark the email for deletion, leave the
320 ;; while loop and return nil so that an rmail summary line
321 ;; wont be displayed for this message: (FIXME ?)
322 (if (and (car maybe-spam) (cdr maybe-spam))
323 (setq exit-while-loop t)
324 ;; Else, spam was not yet found, proceed to next element
325 ;; in rsf-definitions-alist:
326 (setq num-element (1+ num-element)))))
327
328 (if (and (car maybe-spam) (cdr maybe-spam))
329 ;; Temporarily set rmail-current-message in order to output
330 ;; and delete the spam msg if needed:
331 (let ((rmail-current-message msg) ; FIXME does this do anything?
332 (action (cdr (assq 'action
c925297d
GM
333 (nth num-element rsf-definitions-alist))))
334 (newfile (not (file-exists-p rsf-file))))
8ad667fa
GM
335 ;; Check action item in rsf-definitions-alist and do it.
336 (cond
337 ((eq action 'output-and-delete)
c925297d 338 ;; Else the prompt to write a new file leaves the raw
8ad667fa 339 ;; mbox buffer visible.
c925297d 340 (and newfile
3e3480fb 341 (rmail-show-message (rmail-first-unseen-message) t))
8ad667fa 342 (rmail-output rsf-file)
c925297d
GM
343 ;; Swap back, else rmail-get-new-mail-1 gets confused.
344 (when newfile
345 (rmail-swap-buffers-maybe)
346 (widen))
8ad667fa
GM
347 ;; Don't delete if automatic deletion after output is on.
348 (or rmail-delete-after-output (rmail-delete-message)))
349 ((eq action 'delete-spam)
350 (rmail-delete-message)))
351 (setq return-value nil))
352 (setq return-value t)))
97d3e8cd
PR
353 return-value))
354
92ef96ee 355(defun rmail-get-new-mail-filter-spam (nnew)
3e3480fb
GM
356 "Check the most NNEW recent messages for spam.
357This is called at the end of `rmail-get-new-mail-1' if there is new mail."
92ef96ee
GM
358 (let* ((nold (- rmail-total-messages nnew))
359 (nspam 0)
3e3480fb
GM
360 (nscan (1+ nold))
361 ;; Save the original deleted state of all the messages.
362 (rdv-old rmail-deleted-vector)
363 errflag)
364 ;; Set all messages undeleted so that the expunge only affects spam.
365 (setq rmail-deleted-vector (make-string (1+ rmail-total-messages) ?\s))
366 (while (and (not errflag) (<= nscan rmail-total-messages))
367 (condition-case nil
368 (or (rmail-spam-filter nscan)
369 (setq nspam (1+ nspam)))
370 (error (setq errflag nscan)))
92ef96ee 371 (setq nscan (1+ nscan)))
3e3480fb
GM
372 (unwind-protect
373 (if errflag
374 (progn
375 (setq rmail-use-spam-filter nil)
376 (if rsf-beep (ding t))
377 (message "Spam filter error for new message %d, disabled" errflag)
378 (sleep-for rsf-sleep-after-message))
379 (when (> nspam 0)
380 ;; Otherwise sleep or expunge prompt leaves raw mbox buffer showing.
381 (rmail-show-message (or (rmail-first-unseen-message) 1) t)
382 (unwind-protect
383 (progn
384 (if rsf-beep (ding t))
385 (message "Rmail spam-filter detected and deleted %d spam \
386message%s"
387 nspam (if (= 1 nspam) "" "s"))
388 (sleep-for rsf-sleep-after-message)
389 (if (rmail-expunge-confirmed) (rmail-only-expunge t)))
390 ;; Swap back, else get-new-mail-1 gets confused.
391 (rmail-swap-buffers-maybe)
392 (widen))))
393 ;; Restore the original deleted state. Character N refers to message N.
394 (setq rmail-deleted-vector
395 (concat (substring rdv-old 0 (1+ nold))
396 ;; This still works if we deleted all the new mail.
397 (substring rmail-deleted-vector (1+ nold)))))
92ef96ee
GM
398 ;; Return a message based on the number of spam messages found.
399 (cond
3e3480fb 400 (errflag ", error in spam filter")
92ef96ee
GM
401 ((zerop nspam) "")
402 ((= 1 nnew) ", and it appears to be spam")
403 ((= nspam nnew) ", and all appear to be spam")
404 (t (format ", and %d appear%s to be spam" nspam
405 (if (= 1 nspam) "s" ""))))))
406
97d3e8cd
PR
407;; define functions for interactively adding sender/subject of a
408;; specific message to the spam definitions while reading it, using
409;; the menubar:
f5ea26f8 410(defun rsf-add-subject-to-spam-list ()
8ad667fa 411 "Add the \"Subject\" header to the spam list."
97d3e8cd 412 (interactive)
8ad667fa
GM
413 (let ((message-subject (regexp-quote (rmail-get-header "Subject"))))
414 ;; Note the use of a backquote and comma on the subject line here,
97d3e8cd 415 ;; to make sure message-subject is actually evaluated and its value
8ad667fa 416 ;; substituted.
f5ea26f8 417 (add-to-list 'rsf-definitions-alist
78edd3b7 418 ;; Note that an empty element is treated the same as
8ad667fa 419 ;; an absent one, so why does it bother to add them?
97d3e8cd
PR
420 (list '(from . "")
421 '(to . "")
422 `(subject . ,message-subject)
f5ea26f8 423 '(content-type . "")
97d3e8cd
PR
424 '(contents . "")
425 '(action . output-and-delete))
426 t)
f5ea26f8
RS
427 (customize-mark-to-save 'rsf-definitions-alist)
428 (if rsf-autosave-newly-added-definitions
97d3e8cd
PR
429 (progn
430 (custom-save-all)
8ad667fa
GM
431 (message "Added subject `%s' to spam list, and saved it"
432 message-subject))
433 (message "Added subject `%s' to spam list (remember to save it)"
434 message-subject))))
97d3e8cd 435
f5ea26f8 436(defun rsf-add-sender-to-spam-list ()
8ad667fa 437 "Add the \"From\" address to the spam list."
97d3e8cd 438 (interactive)
8ad667fa 439 (let ((message-sender (regexp-quote (rmail-get-header "From"))))
f5ea26f8 440 (add-to-list 'rsf-definitions-alist
97d3e8cd
PR
441 (list `(from . ,message-sender)
442 '(to . "")
443 '(subject . "")
f5ea26f8 444 '(content-type . "")
97d3e8cd
PR
445 '(contents . "")
446 '(action . output-and-delete))
447 t)
f5ea26f8
RS
448 (customize-mark-to-save 'rsf-definitions-alist)
449 (if rsf-autosave-newly-added-definitions
97d3e8cd
PR
450 (progn
451 (custom-save-all)
8ad667fa
GM
452 (message "Added sender `%s' to spam list, and saved it"
453 message-sender))
454 (message "Added sender `%s' to spam list (remember to save it)"
455 message-sender))))
97d3e8cd 456
f5ea26f8 457(defun rsf-add-region-to-spam-list ()
8ad667fa
GM
458 "Add the marked region in the Rmail buffer to the spam list.
459Adds to spam definitions as a \"contents\" field."
97d3e8cd
PR
460 (interactive)
461 (set-buffer rmail-buffer)
8ad667fa
GM
462 ;; Check if region is inactive or has zero size.
463 (if (not (and mark-active (not (= (region-beginning) (region-end)))))
464 ;; If inactive, print error message.
465 (message "You must highlight some text in the Rmail buffer")
466 (if (< (- (region-end) (region-beginning)) rsf-min-region-to-spam-list)
467 (message "Region is too small (minimum %d characters)"
468 rsf-min-region-to-spam-list)
469 ;; If region active and long enough, add to list of spam definitions.
470 (let ((region-to-spam-list (regexp-quote
471 (buffer-substring-no-properties
472 (region-beginning) (region-end)))))
473 (add-to-list 'rsf-definitions-alist
474 (list '(from . "")
475 '(to . "")
476 '(subject . "")
477 '(content-type . "")
478 `(contents . ,region-to-spam-list)
479 '(action . output-and-delete))
480 t)
481 (customize-mark-to-save 'rsf-definitions-alist)
482 (if rsf-autosave-newly-added-definitions
483 (progn
484 (custom-save-all)
485 (message "Added highlighted text:\n%s\n\
486to the spam list, and saved it" region-to-spam-list))
487 (message "Added highlighted text:\n%s\n\
488to the spam list (remember to save it)" region-to-spam-list))))))
f5ea26f8
RS
489
490(defun rsf-customize-spam-definitions ()
8ad667fa 491 "Customize `rsf-definitions-alist'."
97d3e8cd 492 (interactive)
8ad667fa 493 (customize-variable 'rsf-definitions-alist))
97d3e8cd 494
f5ea26f8 495(defun rsf-customize-group ()
8ad667fa 496 "Customize the rmail-spam-filter group."
97d3e8cd 497 (interactive)
8ad667fa 498 (customize-group 'rmail-spam-filter))
97d3e8cd 499
f5ea26f8 500(defun rsf-custom-save-all ()
8ad667fa 501 "Interactive version of `custom-save-all'."
97d3e8cd
PR
502 (interactive)
503 (custom-save-all))
504
8ad667fa
GM
505;; Add menu items (and keyboard shortcuts) to both rmail and rmail-summary.
506(dolist (map (list rmail-summary-mode-map rmail-mode-map))
507 (easy-menu-define nil map nil
508 '("Spam"
509 ["Add subject to spam list" rsf-add-subject-to-spam-list]
510 ["Add sender to spam list" rsf-add-sender-to-spam-list]
511 ["Add region to spam list" rsf-add-region-to-spam-list]
512 ["Save spam definitions" rsf-custom-save-all]
513 "--"
514 ["Customize spam definitions" rsf-customize-spam-definitions]
515 ["Browse spam customizations" rsf-customize-group]
516 ))
517 (define-key map "\C-cSt" 'rsf-add-subject-to-spam-list)
518 (define-key map "\C-cSr" 'rsf-add-sender-to-spam-list)
519 (define-key map "\C-cSn" 'rsf-add-region-to-spam-list)
520 (define-key map "\C-cSa" 'rsf-custom-save-all)
521 (define-key map "\C-cSd" 'rsf-customize-spam-definitions)
522 (define-key map "\C-cSg" 'rsf-customize-group))
f5ea26f8
RS
523
524(defun rsf-add-content-type-field ()
8f921acb 525 "Maintain backward compatibility for `rmail-spam-filter'.
8ad667fa
GM
526The most recent version of `rmail-spam-filter' checks the content-type
527field of the incoming mail to see if it is spam. The format of
f5ea26f8 528`rsf-definitions-alist' has therefore changed. This function
8ad667fa 529checks to see if the old format is used, and updates it if necessary."
97d3e8cd 530 (interactive)
f5ea26f8
RS
531 (if (and rsf-definitions-alist
532 (not (assoc 'content-type (car rsf-definitions-alist))))
533 (let ((result nil)
534 (current nil)
535 (definitions rsf-definitions-alist))
536 (while definitions
537 (setq current (car definitions))
538 (setq definitions (cdr definitions))
e84b4b86 539 (setq result
f5ea26f8 540 (append result
e84b4b86 541 (list
f5ea26f8
RS
542 (list (assoc 'from current)
543 (assoc 'to current)
544 (assoc 'subject current)
545 (cons 'content-type "")
546 (assoc 'contents current)
547 (assoc 'action current))))))
548 (setq rsf-definitions-alist result)
549 (customize-mark-to-save 'rsf-definitions-alist)
550 (if rsf-autosave-newly-added-definitions
551 (progn
552 (custom-save-all)
8ad667fa
GM
553 (message "Spam definitions converted to new format, and saved"))
554 (message "Spam definitions converted to new format (remember to save)")))))
97d3e8cd
PR
555
556(provide 'rmail-spam-filter)
557
cbee283d 558;; arch-tag: 03e1d45d-b72f-4dd7-8f04-e7fd78249746
f5ea26f8 559;;; rmail-spam-fitler ends here