Commit | Line | Data |
---|---|---|
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 | 102 | Specify `rsf-definitions-alist' to define what you consider spam |
97d3e8cd PR |
103 | emails." |
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. |
109 | Spam may be either just deleted, or saved in a separate spam file to | |
110 | be looked at at a later time. Whether the spam is just deleted or | |
111 | also saved in a separete spam file is specified for each definition of | |
f5ea26f8 | 112 | spam, 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. |
138 | This is a size limit on text that you can specify as | |
139 | indicating a message is spam. The aim is to avoid | |
140 | accidentally adding a too short region, which would result | |
141 | in 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. |
147 | This is done in two ways: (1) bbdb is made not to auto-create entries | |
148 | for messages that are deleted by the `rmail-spam-filter', (2) when a | |
149 | message is deleted in rmail, the user is offered to delete the | |
150 | sender's bbdb entry as well if it was created at the same day. Note | |
151 | that Emacs needs to be restarted after setting this option for it to | |
152 | take 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. |
158 | New entries entered via the spam menu bar item are then saved to | |
159 | customization file immediately after being added via the menu bar, and | |
160 | do not require explicitly saving the file after adding the new | |
161 | entries." | |
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 | 167 | If any rsf-white-list string matches a substring of the 'From' |
97d3e8cd PR |
168 | header, the message is flagged as a valid, non-spam message. Example: |
169 | If your domain is emacs.com then including 'emacs.com' in your | |
f5ea26f8 | 170 | rsf-white-list would flag all mail from your colleagues as |
97d3e8cd PR |
171 | valid." |
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. |
177 | Each definition may contain specifications of one or more of the | |
178 | elements {subject, sender, recipients or contents}, as well as a | |
179 | definition of what to do with the spam (action item). A spam e-mail | |
180 | is defined as one that fits all of the specified elements of any one | |
181 | of the spam definitions. The strings that specify spam subject, | |
182 | sender, etc, may be regexp. For example, to specify that the subject | |
183 | may be either 'this is spam' or 'another spam', use the regexp: 'this | |
f5ea26f8 RS |
184 | is spam\\|another spam' (without the single quotes). To specify that |
185 | if the contents contain both this and that the message is spam, | |
186 | specify '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 | 218 | This 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'. |
224 | Capture maybe-spam and this-is-a-spam-email in a cons in result, | |
e84b4b86 | 225 | where maybe-spam is in first and this-is-a-spam-email is in rest. |
f5ea26f8 RS |
226 | The values are returned by destructively changing result. |
227 | If FIELD-SYMBOL field does not exist AND is not specified, | |
228 | this may still be spam due to another element... | |
229 | if (first result) is nil, we already have a contradiction in another | |
230 | field" | |
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. |
247 | If spam, optionally output msg to a file `rsf-file' and delete | |
97d3e8cd PR |
248 | it 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. | |
502 | Added 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'. |
609 | The most recent version of `rmail-spam-filter' checks the contents | |
f5ea26f8 RS |
610 | field of the incoming mail to see if it spam. The format of |
611 | `rsf-definitions-alist' has therefore changed. This function | |
612 | checks to see if old format is used, and if it is, it converts | |
613 | `rsf-definitions-alist' to the new format. Invoked | |
614 | automatically, 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 |