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