Merged in changes from CVS HEAD
[bpt/emacs.git] / lisp / mail / rmail-spam-filter.el
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
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 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
44 ;;; (*) specify in variable rsf-definitions-alist what sender,
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
52 ;;; definitions using the mouse and the appropriate menu item. You
53 ;;; need to later also save the list of spam definitions using the
54 ;;; same menu item, or alternatively, see variable
55 ;;; `rsf-autosave-newly-added-definitions'.
56
57 ;;; (*) specify if blind-cc'ed mail (no "To:" header field) is to be
58 ;;; treated as spam (variable rsf-no-blind-cc; Thanks to Ethan
59 ;;; Brown <ethan@gso.saic.com> for this).
60
61 ;;; (*) specify if rmail-spam-filter should ignore case of spam
62 ;;; definitions (variable rsf-ignore-case; Thanks to
63 ;;; Ethan Brown <ethan@gso.saic.com> for the suggestion).
64
65 ;;; (*) Specify a "white-list" of trusted senders. If any
66 ;;; rsf-white-list string matches a substring of the "From"
67 ;;; header, the message is flagged as a valid, non-spam message (Ethan
68 ;;; Brown <ethan@gso.saic.com>).
69
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
77 ;;; (*) rmail spam filter also works with bbdb to prevent spam senders
78 ;;; from entering into the .bbdb file. See variable
79 ;;; "rsf-auto-delete-spam-bbdb-entries". This is done
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)
86 (if (> emacs-major-version 20)
87 (require 'rmailsum)
88 (if (not (fboundp 'rmail-make-summary-line)) (load-library "rmailsum")))
89
90 ;; For find-if and other cool common lisp functions we may want to use.
91 (eval-when-compile
92 (require 'cl))
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.
100 Specify `rsf-definitions-alist' to define what you consider spam
101 emails."
102 :type 'boolean
103 :group 'rmail-spam-filter )
104
105 (defcustom rsf-file "~/XRMAIL-SPAM"
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
110 spam, as one of the fields of `rsf-definitions-alist'"
111 :type 'string
112 :group 'rmail-spam-filter )
113
114 (defcustom rsf-no-blind-cc nil
115 "*Non-nil to treat blind CC (no To: header) as spam."
116 :type 'boolean
117 :group 'rmail-spam-filter )
118
119 (defcustom rsf-ignore-case nil
120 "*Non-nil to ignore case in `rsf-definitions-alist'."
121 :type 'boolean
122 :group 'rmail-spam-filter )
123
124 (defcustom rsf-beep nil
125 "*Non-nil to beep if spam is found."
126 :type 'boolean
127 :group 'rmail-spam-filter )
128
129 (defcustom rsf-sleep-after-message 2.0
130 "*Seconds to wait after display of message that spam was found."
131 :type 'number
132 :group 'rmail-spam-filter )
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
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
155 (defcustom rsf-autosave-newly-added-definitions nil
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
164 (defcustom rsf-white-list nil
165 "*List of strings to identify valid senders.
166 If any rsf-white-list string matches a substring of the 'From'
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
169 rsf-white-list would flag all mail from your colleagues as
170 valid."
171 :type '(repeat string)
172 :group 'rmail-spam-filter )
173
174 (defcustom rsf-definitions-alist nil
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
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."
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" ""))
197 (cons :format "%v" :value (content-type . "")
198 (const :format "" content-type)
199 (string :tag "Content-Type" ""))
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
212 (defvar rsf-scanning-messages-now nil
213 "Non nil when rmail-spam-filter scans messages,
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)))))
240
241 (defun rmail-spam-filter (msg)
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
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)
254 (message-content-type)
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)
260 (rsf-saved-bbdb/mail_auto_create_p nil)
261 )
262
263 ;; make sure bbdb does not create entries for messages while spam
264 ;; filter is scanning the rmail file:
265 (setq rsf-saved-bbdb/mail_auto_create_p 'bbdb/mail_auto_create_p)
266 (setq bbdb/mail_auto_create_p nil)
267 ;; let `rsf-bbdb-auto-delete-spam-entries' know that rmail spam
268 ;; filter is running, so that deletion of rmail messages should be
269 ;; ignored for now:
270 (setq rsf-scanning-messages-now t)
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"))
280 (setq message-recipients
281 (concat (mail-fetch-field "To")
282 (if (mail-fetch-field "Cc")
283 (concat ", " (mail-fetch-field "Cc")))))
284 (setq message-subject (mail-fetch-field "Subject"))
285 (setq message-content-type (mail-fetch-field "Content-Type"))
286 )
287 ;; Find number of spam-definition elements in the list
288 ;; rsf-definitions-alist specified by user:
289 (setq num-spam-definition-elements (safe-length
290 rsf-definitions-alist))
291
292 ;;; do we want to ignore case in spam definitions:
293 (setq case-fold-search rsf-ignore-case)
294
295 ;; Check for blind CC condition. Set vars such that while
296 ;; loop will be bypassed and spam condition will trigger
297 (if (and rsf-no-blind-cc
298 (null message-recipients))
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
321 (while (and
322 (< num-element num-spam-definition-elements)
323 (not exit-while-loop))
324 (let ((definition (nth num-element rsf-definitions-alist)))
325 ;; Initialize maybe-spam which is set to t in one of two
326 ;; cases: (1) unspecified definition-elements are found in
327 ;; rsf-definitions-alist, (2) empty field is found
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
332 ;; rsf-definitions-alist.
333
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.
337 (setq maybe-spam (cons t nil))
338
339 ;; start scanning incoming message:
340 ;;---------------------------------
341
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
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...
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)
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...
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
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:
369 (if (and (first maybe-spam) (rest maybe-spam))
370 ;; found that this is spam, no need to look at the
371 ;; rest of the rsf-definitions-alist, exit
372 ;; loop:
373 (setq exit-while-loop t)
374 ;; else, spam was not yet found, increment number of
375 ;; element in rsf-definitions-alist and proceed
376 ;; to next element:
377 (setq num-element (+ num-element 1)))
378 )
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
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)
394 ;; check action item and rsf-definitions-alist
395 ;; and do it:
396 (cond
397 ((equal (cdr (assoc 'action
398 (nth num-element rsf-definitions-alist)))
399 'output-and-delete)
400 (progn
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))
405 ))
406 ((equal (cdr (assoc 'action
407 (nth num-element rsf-definitions-alist)))
408 'delete-spam)
409 (progn
410 (rmail-delete-message)
411 ))
412 )
413 (setq rmail-current-message save-current-msg)
414 (setq bbdb/mail_auto_create_p
415 'rsf-saved-bbdb/mail_auto_create_p)
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)
422 (setq rsf-scanning-messages-now nil)
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:
429 (defun rsf-add-subject-to-spam-list ()
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:
437 (add-to-list 'rsf-definitions-alist
438 (list '(from . "")
439 '(to . "")
440 `(subject . ,message-subject)
441 '(content-type . "")
442 '(contents . "")
443 '(action . output-and-delete))
444 t)
445 (customize-mark-to-save 'rsf-definitions-alist)
446 (if rsf-autosave-newly-added-definitions
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"
454 "Don't forget to save the spam definitions to file using the spam
455 menu"))
456 )))
457
458 (defun rsf-add-sender-to-spam-list ()
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:
466 (add-to-list 'rsf-definitions-alist
467 (list `(from . ,message-sender)
468 '(to . "")
469 '(subject . "")
470 '(content-type . "")
471 '(contents . "")
472 '(action . output-and-delete))
473 t)
474 (customize-mark-to-save 'rsf-definitions-alist)
475 (if rsf-autosave-newly-added-definitions
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."
483 "Don't forget to save the spam definitions to file using the spam
484 menu"))
485 )))
486
487
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."
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")
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 ))))))
529
530
531 (defun rsf-customize-spam-definitions ()
532 (interactive)
533 (customize-variable (quote rsf-definitions-alist)))
534
535 (defun rsf-customize-group ()
536 (interactive)
537 (customize-group (quote rmail-spam-filter)))
538
539 (defun rsf-custom-save-all ()
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]
551 '("Browse customizations of rmail spam filter" . rsf-customize-group))
552 (define-key rmail-mode-map [menu-bar spam customize-group]
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)
556
557 (define-key rmail-summary-mode-map [menu-bar spam customize-spam-list]
558 '("Customize list of spam definitions" . rsf-customize-spam-definitions))
559 (define-key rmail-mode-map [menu-bar spam customize-spam-list]
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)
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]
568 '("save newly added spam definitions to customization file" . rsf-custom-save-all))
569 (define-key rmail-mode-map [menu-bar spam my-custom-save-all]
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)
573
574 (define-key rmail-summary-mode-map [menu-bar spam add-region-to-spam-list]
575 '("add region to spam list" . rsf-add-region-to-spam-list))
576 (define-key rmail-mode-map [menu-bar spam add-region-to-spam-list]
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)
580
581 (define-key rmail-summary-mode-map [menu-bar spam add-sender-to-spam-list]
582 '("add sender to spam list" . rsf-add-sender-to-spam-list))
583 (define-key rmail-mode-map [menu-bar spam add-sender-to-spam-list]
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)
587
588 (define-key rmail-summary-mode-map [menu-bar spam add-subject-to-spam-list]
589 '("add subject to spam list" . rsf-add-subject-to-spam-list))
590 (define-key rmail-mode-map [menu-bar spam add-subject-to-spam-list]
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)
594
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."
603 (interactive)
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 ))))
632
633 (provide 'rmail-spam-filter)
634
635 ;;; arch-tag: 03e1d45d-b72f-4dd7-8f04-e7fd78249746
636 ;;; rmail-spam-fitler ends here