1;;; rmail-spam-filter.el --- spam filter for rmail, the emacs mail reader. 2 3;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 4;; Keywords: email, spam, filter, rmail 5;; Author: Eli Tziperman <eli AT deas.harvard.edu> 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 11;; the Free Software Foundation; either version 2, or (at your option) 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 21;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22;; Boston, MA 02110-1301, USA. 23 24;;; Commentary: 25;;; ----------- 26 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. 30;;; URL: http://www.weizmann.ac.il/~eli/Downloads/rmail-spam-filter/ 31 32;;; Usage: 33;;; ------ 34 35;;; put in your .emacs: 36 37;;; (load "rmail-spam-filter.el") 38 39;;; and use customize (in rmail-spam-filter group) to: 40 41;;; (*) turn on the variable rmail-use-spam-filter, 42 43;;; (*) specify in variable rsf-definitions-alist what sender, 44;;; subject and contents make an email be considered spam. 45 46;;; in addition, you may: 47 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'. 55 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). 59 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). 63 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>). 68 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. 75 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. 83 84(require 'rmail) 85(if (> emacs-major-version 20) 86 (require 'rmailsum) 87 (if (not (fboundp 'rmail-make-summary-line)) (load-library "rmailsum"))) 88 89(defvar bbdb/mail_auto_create_p) 90(defvar rmail-summary-mode-map) 91 92;; For find-if and other cool common lisp functions we may want to use. 93(eval-when-compile 94 (require 'cl)) 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. 102Specify `rsf-definitions-alist' to define what you consider spam 103emails." 104 :type 'boolean 105 :group 'rmail-spam-filter ) 106 107(defcustom rsf-file "~/XRMAIL-SPAM" 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 112spam, as one of the fields of `rsf-definitions-alist'" 113 :type 'string 114 :group 'rmail-spam-filter ) 115 116(defcustom rsf-no-blind-cc nil 117 "*Non-nil to treat blind CC (no To: header) as spam." 118 :type 'boolean 119 :group 'rmail-spam-filter ) 120 121(defcustom rsf-ignore-case nil 122 "*Non-nil to ignore case in `rsf-definitions-alist'." 123 :type 'boolean 124 :group 'rmail-spam-filter ) 125 126(defcustom rsf-beep nil 127 "*Non-nil to beep if spam is found." 128 :type 'boolean 129 :group 'rmail-spam-filter ) 130 131(defcustom rsf-sleep-after-message 2.0 132 "*Seconds to wait after display of message that spam was found." 133 :type 'number 134 :group 'rmail-spam-filter ) 135 136(defcustom rsf-min-region-to-spam-list 7 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." 142 :type 'integer 143 :group 'rmail-spam-filter ) 144 145(defcustom rsf-auto-delete-spam-bbdb-entries nil 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 156(defcustom rsf-autosave-newly-added-definitions nil 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 165(defcustom rsf-white-list nil 166 "*List of strings to identify valid senders. 167If any rsf-white-list string matches a substring of the 'From' 168header, the message is flagged as a valid, non-spam message. Example: 169If your domain is emacs.com then including 'emacs.com' in your 170rsf-white-list would flag all mail from your colleagues as 171valid." 172 :type '(repeat string) 173 :group 'rmail-spam-filter ) 174 175(defcustom rsf-definitions-alist nil 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 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." 187 :type '(repeat 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" "")) 198 (cons :format "%v" :value (content-type . "") 199 (const :format "" content-type) 200 (string :tag "Content-Type" "")) 201 (cons :format "%v" :value (contents . "") 202 (const :format "" contents) 203 (string :tag "Contents" "")) 204 (cons :format "%v" :value (action . output-and-delete) 205 (const :format "" action) 206 (choice :tag "Action selection" 207 (const :tag "output to spam folder and delete" output-and-delete) 208 (const :tag "delete spam" delete-spam) 209 )) 210 )) 211 :group 'rmail-spam-filter) 212 213(defvar rsf-scanning-messages-now nil 214 "Non-nil when `rmail-spam-filter' scans messages. 215This is for interaction with `rsf-bbdb-auto-delete-spam-entries'.") 216 217;; the advantage over the automatic filter definitions is the AND conjunction 218;; of in-one-definition-elements 219(defun check-field (field-symbol message-data definition result) 220 "Check if field-symbol is in `rsf-definitions-alist'. 221Capture maybe-spam and this-is-a-spam-email in a cons in result, 222where maybe-spam is in first and this-is-a-spam-email is in rest. 223The values are returned by destructively changing result. 224If FIELD-SYMBOL field does not exist AND is not specified, 225this may still be spam due to another element... 226if (first result) is nil, we already have a contradiction in another 227field" 228 (let ((definition-field (cdr (assoc field-symbol definition)))) 229 (if (and (first result) (> (length definition-field) 0)) 230 ;; only in this case can maybe-spam change from t to nil 231 ;; ... else, if FIELD-SYMBOL field does appear in the message, 232 ;; and it also appears in spam definition list, this 233 ;; is potentially a spam: 234 (if (and message-data 235 (string-match definition-field message-data)) 236 ;; if we do not get a contradiction from another field, this is 237 ;; spam 238 (setf (rest result) t) 239 ;; the message data contradicts the specification, this is no spam 240 (setf (first result) nil))))) 241 242(defun rmail-spam-filter (msg) 243 "Return nil if msg is spam based on rsf-definitions-alist. 244If spam, optionally output msg to a file `rsf-file' and delete 245it from rmail file. Called for each new message retrieved by 246`rmail-get-new-mail'." 247 248 (let ((old-message) 249 (return-value) 250 (this-is-a-spam-email) 251 (maybe-spam) 252 (message-sender) 253 (message-recipients) 254 (message-subject) 255 (message-content-type) 256 (num-spam-definition-elements) 257 (num-element 0) 258 (exit-while-loop nil) 259 (saved-case-fold-search case-fold-search) 260 (save-current-msg) 261 (rsf-saved-bbdb/mail_auto_create_p nil) 262 ) 263 264 ;; make sure bbdb does not create entries for messages while spam 265 ;; filter is scanning the rmail file: 266 (setq rsf-saved-bbdb/mail_auto_create_p 'bbdb/mail_auto_create_p) 267 (setq bbdb/mail_auto_create_p nil) 268 ;; let `rsf-bbdb-auto-delete-spam-entries' know that rmail spam 269 ;; filter is running, so that deletion of rmail messages should be 270 ;; ignored for now: 271 (setq rsf-scanning-messages-now t) 272 (save-excursion 273 (save-restriction 274 (setq this-is-a-spam-email nil) 275 ;; Narrow buffer to header of message and get Sender and 276 ;; Subject fields to be used below: 277 (save-restriction 278 (goto-char (rmail-msgbeg msg)) 279 (narrow-to-region (point) (progn (search-forward "\n\n") (point))) 280 (setq message-sender (mail-fetch-field "From")) 281 (setq message-recipients 282 (concat (mail-fetch-field "To") 283 (if (mail-fetch-field "Cc") 284 (concat ", " (mail-fetch-field "Cc"))))) 285 (setq message-subject (mail-fetch-field "Subject")) 286 (setq message-content-type (mail-fetch-field "Content-Type")) 287 ) 288 ;; Find number of spam-definition elements in the list 289 ;; rsf-definitions-alist specified by user: 290 (setq num-spam-definition-elements (safe-length 291 rsf-definitions-alist)) 292 293 ;;; do we want to ignore case in spam definitions: 294 (setq case-fold-search rsf-ignore-case) 295 296 ;; Check for blind CC condition. Set vars such that while 297 ;; loop will be bypassed and spam condition will trigger 298 (if (and rsf-no-blind-cc 299 (null message-recipients)) 300 (setq exit-while-loop t 301 maybe-spam t 302 this-is-a-spam-email t)) 303 304 ;; Check white list, and likewise cause while loop 305 ;; bypass. 306 (if (and message-sender 307 (let ((white-list rsf-white-list) 308 (found nil)) 309 (while (and (not found) white-list) 310 (if (string-match (car white-list) message-sender) 311 (setq found t) 312 (setq white-list (cdr white-list)))) 313 found)) 314 (setq exit-while-loop t 315 maybe-spam nil 316 this-is-a-spam-email nil)) 317 318 ;; maybe-spam is in first, this-is-a-spam-email in rest, this 319 ;; simplifies the call to check-field 320 (setq maybe-spam (cons maybe-spam this-is-a-spam-email)) 321 322 ;; scan all elements of the list rsf-definitions-alist 323 (while (and 324 (< num-element num-spam-definition-elements) 325 (not exit-while-loop)) 326 (let ((definition (nth num-element rsf-definitions-alist))) 327 ;; Initialize maybe-spam which is set to t in one of two 328 ;; cases: (1) unspecified definition-elements are found in 329 ;; rsf-definitions-alist, (2) empty field is found 330 ;; in the message being scanned (e.g. empty subject, 331 ;; sender, recipients, etc). The variable is set to nil 332 ;; if a non empty field of the scanned message does not 333 ;; match a specified field in 334 ;; rsf-definitions-alist. 335 336 ;; initialize this-is-a-spam-email to nil. This variable 337 ;; is set to t if one of the spam definitions matches a 338 ;; field in the scanned message. 339 (setq maybe-spam (cons t nil)) 340 341 ;; start scanning incoming message: 342 ;;--------------------------------- 343 344 ;; Maybe the different fields should also be done in a 345 ;; loop to make the whole thing more flexible 346 ;; if sender field is not specified in message being 347 ;; scanned, AND if "from" field does not appear in spam 348 ;; definitions for this element, this may still be spam 349 ;; due to another element... 350 (check-field 'from message-sender definition maybe-spam) 351 ;; next, if spam was not ruled out already, check recipients: 352 (check-field 'to message-recipients definition maybe-spam) 353 ;; next, if spam was not ruled out already, check subject: 354 (check-field 'subject message-subject definition maybe-spam) 355 ;; next, if spam was not ruled out already, check content-type: 356 (check-field 'content-type message-content-type 357 definition maybe-spam) 358 ;; next, if spam was not ruled out already, check 359 ;; contents: if contents field is not specified, this may 360 ;; still be spam due to another element... 361 (check-field 'contents 362 (buffer-substring 363 (rmail-msgbeg msg) (rmail-msgend msg)) 364 definition maybe-spam) 365 366 ;; if the search in rsf-definitions-alist found 367 ;; that this email is spam, output the email to the spam 368 ;; rmail file, mark the email for deletion, leave the 369 ;; while loop and return nil so that an rmail summary line 370 ;; wont be displayed for this message: 371 (if (and (first maybe-spam) (rest maybe-spam)) 372 ;; found that this is spam, no need to look at the 373 ;; rest of the rsf-definitions-alist, exit 374 ;; loop: 375 (setq exit-while-loop t) 376 ;; else, spam was not yet found, increment number of 377 ;; element in rsf-definitions-alist and proceed 378 ;; to next element: 379 (setq num-element (+ num-element 1))) 380 ) 381 ) 382 383 ;; (BK) re-set originally used variables 384 (setq this-is-a-spam-email (rest maybe-spam) 385 maybe-spam (first maybe-spam)) 386 387 (if (and this-is-a-spam-email maybe-spam) 388 (progn 389 ;;(message "Found spam!") 390 ;;(ding 1) (sleep-for 2) 391 392 ;; temprarily set rmail-current-message in order to 393 ;; output and delete the spam msg if needed: 394 (setq save-current-msg rmail-current-message) 395 (setq rmail-current-message msg) 396 ;; check action item and rsf-definitions-alist 397 ;; and do it: 398 (cond 399 ((equal (cdr (assoc 'action 400 (nth num-element rsf-definitions-alist))) 401 'output-and-delete) 402 (progn 403 (rmail-output-to-rmail-file rsf-file 1 t) 404 ;; Don't delete if automatic deletion after output 405 ;; is turned on 406 (unless rmail-delete-after-output (rmail-delete-message)) 407 )) 408 ((equal (cdr (assoc 'action 409 (nth num-element rsf-definitions-alist))) 410 'delete-spam) 411 (progn 412 (rmail-delete-message) 413 )) 414 ) 415 (setq rmail-current-message save-current-msg) 416 (setq bbdb/mail_auto_create_p 417 'rsf-saved-bbdb/mail_auto_create_p) 418 ;; set return value. These lines must be last in the 419 ;; function, so that they will determine the value 420 ;; returned by rmail-spam-filter: 421 (setq return-value nil)) 422 (setq return-value t)))) 423 (setq case-fold-search saved-case-fold-search) 424 (setq rsf-scanning-messages-now nil) 425 return-value)) 426 427 428;; define functions for interactively adding sender/subject of a 429;; specific message to the spam definitions while reading it, using 430;; the menubar: 431(defun rsf-add-subject-to-spam-list () 432 (interactive) 433 (set-buffer rmail-buffer) 434 (let ((message-subject)) 435 (setq message-subject (mail-fetch-field "Subject")) 436 ;; note the use of a backquote and comma on the subject line here, 437 ;; to make sure message-subject is actually evaluated and its value 438 ;; substituted: 439 (add-to-list 'rsf-definitions-alist 440 (list '(from . "") 441 '(to . "") 442 `(subject . ,message-subject) 443 '(content-type . "") 444 '(contents . "") 445 '(action . output-and-delete)) 446 t) 447 (customize-mark-to-save 'rsf-definitions-alist) 448 (if rsf-autosave-newly-added-definitions 449 (progn 450 (custom-save-all) 451 (message "%s" (concat "added subject \n <<< \n" message-subject 452 " \n >>> \n to list of spam definitions. \n" 453 "and saved the spam definitions to file."))) 454 (message "%s" (concat "added subject \n <<< \n" message-subject 455 " \n >>> \n to list of spam definitions. \n" 456 "Don't forget to save the spam definitions to file using the spam 457 menu")) 458 ))) 459 460(defun rsf-add-sender-to-spam-list () 461 (interactive) 462 (set-buffer rmail-buffer) 463 (let ((message-sender)) 464 (setq message-sender (mail-fetch-field "From")) 465 ;; note the use of a backquote and comma on the "from" line here, 466 ;; to make sure message-sender is actually evaluated and its value 467 ;; substituted: 468 (add-to-list 'rsf-definitions-alist 469 (list `(from . ,message-sender) 470 '(to . "") 471 '(subject . "") 472 '(content-type . "") 473 '(contents . "") 474 '(action . output-and-delete)) 475 t) 476 (customize-mark-to-save 'rsf-definitions-alist) 477 (if rsf-autosave-newly-added-definitions 478 (progn 479 (custom-save-all) 480 (message "%s" (concat "added sender \n <<< \n" message-sender 481 " \n >>> \n to list of spam definitions. \n" 482 "and saved the spam definitions to file."))) 483 (message "%s" (concat "added sender \n <<< \n " message-sender 484 " \n >>> \n to list of spam definitions." 485 "Don't forget to save the spam definitions to file using the spam 486 menu")) 487 ))) 488 489 490(defun rsf-add-region-to-spam-list () 491 "Add the region makred by user in the rmail buffer to spam list. 492Added to spam definitions as a contents field." 493 (interactive) 494 (set-buffer rmail-buffer) 495 (let ((region-to-spam-list)) 496 ;; check if region is inactive or has zero size: 497 (if (not (and mark-active (not (= (region-beginning) (region-end))))) 498 ;; if inactive, print error message: 499 (message "you need to first highlight some text in the rmail buffer") 500 (if (< (- (region-end) (region-beginning)) rsf-min-region-to-spam-list) 501 (message 502 (concat "highlighted region is too small; min length set by variable \n" 503 "rsf-min-region-to-spam-list" 504 " is " (number-to-string rsf-min-region-to-spam-list))) 505 ;; if region active and long enough, add to list of spam definisions: 506 (progn 507 (setq region-to-spam-list (buffer-substring (region-beginning) (region-end))) 508 ;; note the use of a backquote and comma on the "from" line here, 509 ;; to make sure message-sender is actually evaluated and its value 510 ;; substituted: 511 (add-to-list 'rsf-definitions-alist 512 (list '(from . "") 513 '(to . "") 514 '(subject . "") 515 '(content-type . "") 516 `(contents . ,region-to-spam-list) 517 '(action . output-and-delete)) 518 t) 519 (customize-mark-to-save 'rsf-definitions-alist) 520 (if rsf-autosave-newly-added-definitions 521 (progn 522 (custom-save-all) 523 (message "%s" (concat "added highlighted text \n <<< \n" region-to-spam-list 524 " \n >>> \n to list of spam definitions. \n" 525 "and saved the spam definitions to file."))) 526 (message "%s" (concat "added highlighted text \n <<< \n " region-to-spam-list 527 " \n >>> \n to list of spam definitions." 528 "Don't forget to save the spam definitions to file using the 529 spam menu")) 530 )))))) 531 532 533(defun rsf-customize-spam-definitions () 534 (interactive) 535 (customize-variable (quote rsf-definitions-alist))) 536 537(defun rsf-customize-group () 538 (interactive) 539 (customize-group (quote rmail-spam-filter))) 540 541(defun rsf-custom-save-all () 542 (interactive) 543 (custom-save-all)) 544 545;; add the actual menu items and keyboard shortcuts to both rmail and 546;; rmail-summary menu-bars:: 547(define-key rmail-summary-mode-map [menu-bar spam] 548 (cons "Spam" (make-sparse-keymap "Spam"))) 549(define-key rmail-mode-map [menu-bar spam] 550 (cons "Spam" (make-sparse-keymap "Spam"))) 551 552(define-key rmail-summary-mode-map [menu-bar spam customize-group] 553 '("Browse customizations of rmail spam filter" . rsf-customize-group)) 554(define-key rmail-mode-map [menu-bar spam customize-group] 555 '("Browse customizations of rmail spam filter" . rsf-customize-group)) 556(define-key rmail-summary-mode-map "\C-cSg" 'rsf-customize-group) 557(define-key rmail-mode-map "\C-cSg" 'rsf-customize-group) 558 559(define-key rmail-summary-mode-map [menu-bar spam customize-spam-list] 560 '("Customize list of spam definitions" . rsf-customize-spam-definitions)) 561(define-key rmail-mode-map [menu-bar spam customize-spam-list] 562 '("Customize list of spam definitions" . rsf-customize-spam-definitions)) 563(define-key rmail-summary-mode-map "\C-cSd" 'rsf-customize-spam-definitions) 564(define-key rmail-mode-map "\C-cSd" 'rsf-customize-spam-definitions) 565 566(define-key rmail-summary-mode-map [menu-bar spam lambda] '("----")) 567(define-key rmail-mode-map [menu-bar spam lambda] '("----")) 568 569(define-key rmail-summary-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-mode-map [menu-bar spam my-custom-save-all] 572 '("save newly added spam definitions to customization file" . rsf-custom-save-all)) 573(define-key rmail-summary-mode-map "\C-cSa" 'rsf-custom-save-all) 574(define-key rmail-mode-map "\C-cSa" 'rsf-custom-save-all) 575 576(define-key rmail-summary-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-mode-map [menu-bar spam add-region-to-spam-list] 579 '("add region to spam list" . rsf-add-region-to-spam-list)) 580(define-key rmail-summary-mode-map "\C-cSn" 'rsf-add-region-to-spam-list) 581(define-key rmail-mode-map "\C-cSn" 'rsf-add-region-to-spam-list) 582 583(define-key rmail-summary-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-mode-map [menu-bar spam add-sender-to-spam-list] 586 '("add sender to spam list" . rsf-add-sender-to-spam-list)) 587(define-key rmail-summary-mode-map "\C-cSr" 'rsf-add-sender-to-spam-list) 588(define-key rmail-mode-map "\C-cSr" 'rsf-add-sender-to-spam-list) 589 590(define-key rmail-summary-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-mode-map [menu-bar spam add-subject-to-spam-list] 593 '("add subject to spam list" . rsf-add-subject-to-spam-list)) 594(define-key rmail-summary-mode-map "\C-cSt" 'rsf-add-subject-to-spam-list) 595(define-key rmail-mode-map "\C-cSt" 'rsf-add-subject-to-spam-list) 596 597(defun rsf-add-content-type-field () 598 "Maintain backward compatibility for `rmail-spam-filter'. 599The most recent version of `rmail-spam-filter' checks the contents 600field of the incoming mail to see if it spam. The format of 601`rsf-definitions-alist' has therefore changed. This function 602checks to see if old format is used, and if it is, it converts 603`rsf-definitions-alist' to the new format. Invoked 604automatically, no user input is required." 605 (interactive) 606 (if (and rsf-definitions-alist 607 (not (assoc 'content-type (car rsf-definitions-alist)))) 608 (let ((result nil) 609 (current nil) 610 (definitions rsf-definitions-alist)) 611 (while definitions 612 (setq current (car definitions)) 613 (setq definitions (cdr definitions)) 614 (setq result 615 (append result 616 (list 617 (list (assoc 'from current) 618 (assoc 'to current) 619 (assoc 'subject current) 620 (cons 'content-type "") 621 (assoc 'contents current) 622 (assoc 'action current)))))) 623 (setq rsf-definitions-alist result) 624 (customize-mark-to-save 'rsf-definitions-alist) 625 (if rsf-autosave-newly-added-definitions 626 (progn 627 (custom-save-all) 628 (message (concat "converted spam definitions to new format\n" 629 "and saved the spam definitions to file."))) 630 (message (concat "converted spam definitions to new format\n" 631 "Don't forget to save the spam definitions to file using the 632 spam menu")) 633 )))) 634 635(provide 'rmail-spam-filter) 636 637;;; arch-tag: 03e1d45d-b72f-4dd7-8f04-e7fd78249746 638;;; rmail-spam-fitler ends here 639