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