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