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