Commit | Line | Data |
---|---|---|
f5ea26f8 RS |
1 | ;;; rmail-spam-filter.el --- spam filter for rmail, the emacs mail reader. |
2 | ||
d7a0267c | 3 | ;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 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" "")) | |
204 | (cons :format "%v" :value (action . output-and-delete) | |
205 | (const :format "" action) | |
e84b4b86 | 206 | (choice :tag "Action selection" |
97d3e8cd PR |
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 | ||
f5ea26f8 | 213 | (defvar rsf-scanning-messages-now nil |
a553e689 | 214 | "Non-nil when `rmail-spam-filter' scans messages. |
8f921acb | 215 | This is for interaction with `rsf-bbdb-auto-delete-spam-entries'.") |
f5ea26f8 RS |
216 | |
217 | ;; the advantage over the automatic filter definitions is the AND conjunction | |
218 | ;; of in-one-definition-elements | |
88fcbeaa | 219 | (defun check-field (field-symbol message-data definition result) |
f5ea26f8 RS |
220 | "Check if field-symbol is in `rsf-definitions-alist'. |
221 | Capture maybe-spam and this-is-a-spam-email in a cons in result, | |
e84b4b86 | 222 | where maybe-spam is in first and this-is-a-spam-email is in rest. |
f5ea26f8 RS |
223 | The values are returned by destructively changing result. |
224 | If FIELD-SYMBOL field does not exist AND is not specified, | |
225 | this may still be spam due to another element... | |
226 | if (first result) is nil, we already have a contradiction in another | |
227 | field" | |
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))))) | |
97d3e8cd PR |
241 | |
242 | (defun rmail-spam-filter (msg) | |
f5ea26f8 RS |
243 | "Return nil if msg is spam based on rsf-definitions-alist. |
244 | If spam, optionally output msg to a file `rsf-file' and delete | |
97d3e8cd PR |
245 | it 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) | |
f5ea26f8 | 255 | (message-content-type) |
97d3e8cd PR |
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) | |
f5ea26f8 | 261 | (rsf-saved-bbdb/mail_auto_create_p nil) |
97d3e8cd | 262 | ) |
e84b4b86 | 263 | |
97d3e8cd PR |
264 | ;; make sure bbdb does not create entries for messages while spam |
265 | ;; filter is scanning the rmail file: | |
f5ea26f8 | 266 | (setq rsf-saved-bbdb/mail_auto_create_p 'bbdb/mail_auto_create_p) |
97d3e8cd | 267 | (setq bbdb/mail_auto_create_p nil) |
f5ea26f8 | 268 | ;; let `rsf-bbdb-auto-delete-spam-entries' know that rmail spam |
97d3e8cd PR |
269 | ;; filter is running, so that deletion of rmail messages should be |
270 | ;; ignored for now: | |
f5ea26f8 | 271 | (setq rsf-scanning-messages-now t) |
97d3e8cd PR |
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")) | |
f5ea26f8 RS |
281 | (setq message-recipients |
282 | (concat (mail-fetch-field "To") | |
283 | (if (mail-fetch-field "Cc") | |
284 | (concat ", " (mail-fetch-field "Cc"))))) | |
97d3e8cd | 285 | (setq message-subject (mail-fetch-field "Subject")) |
f5ea26f8 | 286 | (setq message-content-type (mail-fetch-field "Content-Type")) |
97d3e8cd PR |
287 | ) |
288 | ;; Find number of spam-definition elements in the list | |
f5ea26f8 | 289 | ;; rsf-definitions-alist specified by user: |
97d3e8cd | 290 | (setq num-spam-definition-elements (safe-length |
f5ea26f8 | 291 | rsf-definitions-alist)) |
97d3e8cd PR |
292 | |
293 | ;;; do we want to ignore case in spam definitions: | |
f5ea26f8 | 294 | (setq case-fold-search rsf-ignore-case) |
e84b4b86 | 295 | |
97d3e8cd | 296 | ;; Check for blind CC condition. Set vars such that while |
f5ea26f8 RS |
297 | ;; loop will be bypassed and spam condition will trigger |
298 | (if (and rsf-no-blind-cc | |
97d3e8cd | 299 | (null message-recipients)) |
f5ea26f8 RS |
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. | |
7c7d4075 EZ |
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)) | |
f5ea26f8 RS |
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 | |
88fcbeaa | 319 | ;; simplifies the call to check-field |
f5ea26f8 RS |
320 | (setq maybe-spam (cons maybe-spam this-is-a-spam-email)) |
321 | ||
322 | ;; scan all elements of the list rsf-definitions-alist | |
97d3e8cd PR |
323 | (while (and |
324 | (< num-element num-spam-definition-elements) | |
325 | (not exit-while-loop)) | |
f5ea26f8 | 326 | (let ((definition (nth num-element rsf-definitions-alist))) |
97d3e8cd PR |
327 | ;; Initialize maybe-spam which is set to t in one of two |
328 | ;; cases: (1) unspecified definition-elements are found in | |
f5ea26f8 | 329 | ;; rsf-definitions-alist, (2) empty field is found |
97d3e8cd PR |
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 | |
f5ea26f8 RS |
334 | ;; rsf-definitions-alist. |
335 | ||
97d3e8cd PR |
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. | |
f5ea26f8 | 339 | (setq maybe-spam (cons t nil)) |
97d3e8cd PR |
340 | |
341 | ;; start scanning incoming message: | |
342 | ;;--------------------------------- | |
e84b4b86 | 343 | |
f5ea26f8 RS |
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 | |
97d3e8cd PR |
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... | |
88fcbeaa | 350 | (check-field 'from message-sender definition maybe-spam) |
f5ea26f8 | 351 | ;; next, if spam was not ruled out already, check recipients: |
88fcbeaa | 352 | (check-field 'to message-recipients definition maybe-spam) |
f5ea26f8 | 353 | ;; next, if spam was not ruled out already, check subject: |
88fcbeaa | 354 | (check-field 'subject message-subject definition maybe-spam) |
f5ea26f8 | 355 | ;; next, if spam was not ruled out already, check content-type: |
e84b4b86 | 356 | (check-field 'content-type message-content-type |
f5ea26f8 | 357 | definition maybe-spam) |
97d3e8cd PR |
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... | |
e84b4b86 | 361 | (check-field 'contents |
f5ea26f8 RS |
362 | (buffer-substring |
363 | (rmail-msgbeg msg) (rmail-msgend msg)) | |
364 | definition maybe-spam) | |
e84b4b86 | 365 | |
f5ea26f8 | 366 | ;; if the search in rsf-definitions-alist found |
97d3e8cd PR |
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: | |
f5ea26f8 | 371 | (if (and (first maybe-spam) (rest maybe-spam)) |
97d3e8cd | 372 | ;; found that this is spam, no need to look at the |
f5ea26f8 | 373 | ;; rest of the rsf-definitions-alist, exit |
97d3e8cd PR |
374 | ;; loop: |
375 | (setq exit-while-loop t) | |
376 | ;; else, spam was not yet found, increment number of | |
f5ea26f8 | 377 | ;; element in rsf-definitions-alist and proceed |
97d3e8cd PR |
378 | ;; to next element: |
379 | (setq num-element (+ num-element 1))) | |
380 | ) | |
f5ea26f8 | 381 | ) |
e84b4b86 | 382 | |
f5ea26f8 RS |
383 | ;; (BK) re-set originally used variables |
384 | (setq this-is-a-spam-email (rest maybe-spam) | |
385 | maybe-spam (first maybe-spam)) | |
386 | ||
97d3e8cd PR |
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) | |
f5ea26f8 | 396 | ;; check action item and rsf-definitions-alist |
97d3e8cd PR |
397 | ;; and do it: |
398 | (cond | |
399 | ((equal (cdr (assoc 'action | |
f5ea26f8 | 400 | (nth num-element rsf-definitions-alist))) |
97d3e8cd PR |
401 | 'output-and-delete) |
402 | (progn | |
f5ea26f8 RS |
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)) | |
97d3e8cd PR |
407 | )) |
408 | ((equal (cdr (assoc 'action | |
f5ea26f8 | 409 | (nth num-element rsf-definitions-alist))) |
97d3e8cd PR |
410 | 'delete-spam) |
411 | (progn | |
412 | (rmail-delete-message) | |
413 | )) | |
414 | ) | |
415 | (setq rmail-current-message save-current-msg) | |
f5ea26f8 RS |
416 | (setq bbdb/mail_auto_create_p |
417 | 'rsf-saved-bbdb/mail_auto_create_p) | |
97d3e8cd PR |
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) | |
f5ea26f8 | 424 | (setq rsf-scanning-messages-now nil) |
97d3e8cd PR |
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: | |
f5ea26f8 | 431 | (defun rsf-add-subject-to-spam-list () |
97d3e8cd PR |
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: | |
f5ea26f8 | 439 | (add-to-list 'rsf-definitions-alist |
97d3e8cd PR |
440 | (list '(from . "") |
441 | '(to . "") | |
442 | `(subject . ,message-subject) | |
f5ea26f8 | 443 | '(content-type . "") |
97d3e8cd PR |
444 | '(contents . "") |
445 | '(action . output-and-delete)) | |
446 | t) | |
f5ea26f8 RS |
447 | (customize-mark-to-save 'rsf-definitions-alist) |
448 | (if rsf-autosave-newly-added-definitions | |
97d3e8cd PR |
449 | (progn |
450 | (custom-save-all) | |
83816142 | 451 | (message "%s" (concat "added subject \n <<< \n" message-subject |
97d3e8cd PR |
452 | " \n >>> \n to list of spam definitions. \n" |
453 | "and saved the spam definitions to file."))) | |
83816142 | 454 | (message "%s" (concat "added subject \n <<< \n" message-subject |
97d3e8cd | 455 | " \n >>> \n to list of spam definitions. \n" |
f5ea26f8 RS |
456 | "Don't forget to save the spam definitions to file using the spam |
457 | menu")) | |
97d3e8cd PR |
458 | ))) |
459 | ||
f5ea26f8 | 460 | (defun rsf-add-sender-to-spam-list () |
97d3e8cd PR |
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: | |
f5ea26f8 | 468 | (add-to-list 'rsf-definitions-alist |
97d3e8cd PR |
469 | (list `(from . ,message-sender) |
470 | '(to . "") | |
471 | '(subject . "") | |
f5ea26f8 | 472 | '(content-type . "") |
97d3e8cd PR |
473 | '(contents . "") |
474 | '(action . output-and-delete)) | |
475 | t) | |
f5ea26f8 RS |
476 | (customize-mark-to-save 'rsf-definitions-alist) |
477 | (if rsf-autosave-newly-added-definitions | |
97d3e8cd PR |
478 | (progn |
479 | (custom-save-all) | |
83816142 | 480 | (message "%s" (concat "added sender \n <<< \n" message-sender |
97d3e8cd PR |
481 | " \n >>> \n to list of spam definitions. \n" |
482 | "and saved the spam definitions to file."))) | |
83816142 | 483 | (message "%s" (concat "added sender \n <<< \n " message-sender |
97d3e8cd | 484 | " \n >>> \n to list of spam definitions." |
f5ea26f8 RS |
485 | "Don't forget to save the spam definitions to file using the spam |
486 | menu")) | |
97d3e8cd PR |
487 | ))) |
488 | ||
489 | ||
f5ea26f8 RS |
490 | (defun rsf-add-region-to-spam-list () |
491 | "Add the region makred by user in the rmail buffer to spam list. | |
492 | Added to spam definitions as a contents field." | |
97d3e8cd PR |
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") | |
f5ea26f8 RS |
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) | |
83816142 | 523 | (message "%s" (concat "added highlighted text \n <<< \n" region-to-spam-list |
f5ea26f8 RS |
524 | " \n >>> \n to list of spam definitions. \n" |
525 | "and saved the spam definitions to file."))) | |
83816142 | 526 | (message "%s" (concat "added highlighted text \n <<< \n " region-to-spam-list |
f5ea26f8 RS |
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 () | |
97d3e8cd | 534 | (interactive) |
f5ea26f8 | 535 | (customize-variable (quote rsf-definitions-alist))) |
97d3e8cd | 536 | |
f5ea26f8 | 537 | (defun rsf-customize-group () |
97d3e8cd PR |
538 | (interactive) |
539 | (customize-group (quote rmail-spam-filter))) | |
540 | ||
f5ea26f8 | 541 | (defun rsf-custom-save-all () |
97d3e8cd PR |
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] | |
f5ea26f8 | 553 | '("Browse customizations of rmail spam filter" . rsf-customize-group)) |
97d3e8cd | 554 | (define-key rmail-mode-map [menu-bar spam customize-group] |
f5ea26f8 RS |
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) | |
97d3e8cd PR |
558 | |
559 | (define-key rmail-summary-mode-map [menu-bar spam customize-spam-list] | |
f5ea26f8 | 560 | '("Customize list of spam definitions" . rsf-customize-spam-definitions)) |
97d3e8cd | 561 | (define-key rmail-mode-map [menu-bar spam customize-spam-list] |
f5ea26f8 RS |
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) | |
97d3e8cd PR |
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] | |
f5ea26f8 | 570 | '("save newly added spam definitions to customization file" . rsf-custom-save-all)) |
97d3e8cd | 571 | (define-key rmail-mode-map [menu-bar spam my-custom-save-all] |
f5ea26f8 RS |
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) | |
97d3e8cd PR |
575 | |
576 | (define-key rmail-summary-mode-map [menu-bar spam add-region-to-spam-list] | |
f5ea26f8 | 577 | '("add region to spam list" . rsf-add-region-to-spam-list)) |
97d3e8cd | 578 | (define-key rmail-mode-map [menu-bar spam add-region-to-spam-list] |
f5ea26f8 RS |
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) | |
97d3e8cd PR |
582 | |
583 | (define-key rmail-summary-mode-map [menu-bar spam add-sender-to-spam-list] | |
f5ea26f8 | 584 | '("add sender to spam list" . rsf-add-sender-to-spam-list)) |
97d3e8cd | 585 | (define-key rmail-mode-map [menu-bar spam add-sender-to-spam-list] |
f5ea26f8 RS |
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) | |
97d3e8cd PR |
589 | |
590 | (define-key rmail-summary-mode-map [menu-bar spam add-subject-to-spam-list] | |
f5ea26f8 | 591 | '("add subject to spam list" . rsf-add-subject-to-spam-list)) |
97d3e8cd | 592 | (define-key rmail-mode-map [menu-bar spam add-subject-to-spam-list] |
f5ea26f8 RS |
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 () | |
8f921acb RS |
598 | "Maintain backward compatibility for `rmail-spam-filter'. |
599 | The most recent version of `rmail-spam-filter' checks the contents | |
f5ea26f8 RS |
600 | field of the incoming mail to see if it spam. The format of |
601 | `rsf-definitions-alist' has therefore changed. This function | |
602 | checks to see if old format is used, and if it is, it converts | |
603 | `rsf-definitions-alist' to the new format. Invoked | |
604 | automatically, no user input is required." | |
97d3e8cd | 605 | (interactive) |
f5ea26f8 RS |
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)) | |
e84b4b86 | 614 | (setq result |
f5ea26f8 | 615 | (append result |
e84b4b86 | 616 | (list |
f5ea26f8 RS |
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 | )))) | |
97d3e8cd PR |
634 | |
635 | (provide 'rmail-spam-filter) | |
636 | ||
d366d62b | 637 | ;;; arch-tag: 03e1d45d-b72f-4dd7-8f04-e7fd78249746 |
f5ea26f8 | 638 | ;;; rmail-spam-fitler ends here |