1 ;;; wl-spam.el --- Spam filtering interface for Wanderlust.
3 ;; Copyright (C) 2003 Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
4 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Author: Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news, spam
9 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
33 (eval-when-compile (require 'cl
))
38 (require 'wl-highlight
)
41 "Spam configuration for wanderlust."
44 (defcustom wl-spam-folder
"+spam"
49 (defcustom wl-spam-undecided-folder-list nil
50 "*List of folder name which is contained undecided domain.
51 If an element is symbol, use symbol-value instead."
52 :type
'(repeat (choice (string :tag
"Folder name")
53 (variable :tag
"Variable")))
56 (defcustom wl-spam-undecided-folder-regexp-list
'("inbox")
57 "*List of folder regexp which is contained undecided domain."
58 :type
'(repeat (regexp :tag
"Folder Regexp"))
61 (defcustom wl-spam-ignored-folder-list
'(wl-draft-folder
64 "*List of folder name which is contained ignored domain.
65 If an element is symbol, use symbol-value instead."
66 :type
'(repeat (choice (string :tag
"Folder name")
67 (variable :tag
"Variable")))
70 (defcustom wl-spam-ignored-folder-regexp-list nil
71 "*List of folder regexp which is contained ignored domain."
72 :type
'(repeat (regexp :tag
"Folder Regexp"))
75 (defcustom wl-spam-auto-check-folder-regexp-list nil
76 "*List of Folder regexp which check spam automatically."
77 :type
'(repeat (regexp :tag
"Folder Regexp"))
80 (defcustom wl-spam-auto-check-marks
81 (list wl-summary-new-uncached-mark
82 wl-summary-new-cached-mark
)
83 "Persistent marks to check spam automatically."
84 :type
'(choice (const :tag
"All marks" all
)
85 (repeat (string :tag
"Mark")))
88 (wl-defface wl-highlight-summary-spam-face
93 (:foreground
"LightSlateGray")))
94 "Face used for displaying messages mark as spam."
95 :group
'wl-summary-faces
98 (defcustom wl-spam-mark-action-list
102 wl-summary-register-temp-mark
103 wl-summary-exec-action-spam
104 wl-highlight-summary-spam-face
105 "Mark messages as spam."))
106 "A variable to define Mark & Action for spam.
107 Append this value to `wl-summary-mark-action-list' by `wl-spam-setup'.
109 See `wl-summary-mark-action-list' for the detail of element."
111 (string :tag
"Temporary mark")
112 (symbol :tag
"Action name")
113 (symbol :tag
"Argument function")
114 (symbol :tag
"Set mark function")
115 (symbol :tag
"Exec function")
116 (symbol :tag
"Face symbol")
117 (string :tag
"Document string")))
120 (defsubst wl-spam-string-member-p
(string list regexp-list
)
121 (or (wl-string-member string list
)
122 (wl-string-match-member string regexp-list
)))
124 (defun wl-spam-domain (folder-name)
125 (cond ((string= folder-name wl-spam-folder
)
127 ((wl-spam-string-member-p folder-name
128 wl-spam-undecided-folder-list
129 wl-spam-undecided-folder-regexp-list
)
131 ((wl-spam-string-member-p folder-name
132 wl-spam-ignored-folder-list
133 wl-spam-ignored-folder-regexp-list
)
138 (defun wl-spam-split-numbers (folder numbers
)
140 (dolist (number numbers
)
141 (let* ((domain (wl-spam-domain
142 (elmo-folder-name-internal
143 (elmo-message-folder folder number
))))
144 (cell (assq domain alist
)))
146 (setcdr cell
(cons number
(cdr cell
)))
147 (setq alist
(cons (list domain number
) alist
)))))
150 (defsubst wl-spam-auto-check-message-p
(folder number
)
151 (or (eq wl-spam-auto-check-marks
'all
)
152 (member (wl-summary-message-mark folder number
)
153 wl-spam-auto-check-marks
)))
155 (defsubst wl-spam-map-spam-messages
(folder numbers function
&rest args
)
156 (elmo-with-progress-display (elmo-spam-check-spam (length numbers
))
158 (dolist (number (elmo-spam-list-spam-messages (elmo-spam-processor)
161 (apply function number args
))))
163 (defun wl-spam-apply-partitions (folder partitions function msg
)
166 (dolist (partition partitions
)
167 (setq total
(+ total
(length (cdr partition
)))))
168 (elmo-with-progress-display (elmo-spam-register total
) msg
169 (dolist (partition partitions
)
170 (funcall function folder
(cdr partition
) (car partition
)))))))
172 (defun wl-spam-register-spam-messages (folder numbers
)
173 (elmo-with-progress-display (elmo-spam-register (length numbers
))
175 (elmo-spam-register-spam-messages (elmo-spam-processor)
179 (defun wl-spam-register-good-messages (folder numbers
)
180 (elmo-with-progress-display (elmo-spam-register (length numbers
))
182 (elmo-spam-register-good-messages (elmo-spam-processor)
186 (defun wl-spam-save-status (&optional force
)
188 (let ((processor (elmo-spam-processor (not force
))))
190 (and processor
(elmo-spam-modified-p processor
)))
191 (elmo-spam-save-status processor
))))
193 ;; insinuate into summary mode
194 (defvar wl-summary-spam-map nil
)
196 (unless wl-summary-spam-map
197 (let ((map (make-sparse-keymap)))
198 (define-key map
"m" 'wl-summary-spam
)
199 (define-key map
"c" 'wl-summary-test-spam
)
200 (define-key map
"C" 'wl-summary-mark-spam
)
201 (define-key map
"s" 'wl-summary-register-as-spam
)
202 (define-key map
"S" 'wl-summary-register-as-spam-all
)
203 (define-key map
"n" 'wl-summary-register-as-good
)
204 (define-key map
"N" 'wl-summary-register-as-good-all
)
205 (setq wl-summary-spam-map map
)))
208 ;; Avoid compile warnings
209 (defalias-maybe 'wl-summary-spam
'ignore
))
211 (defun wl-summary-test-spam (&optional folder number
)
213 (let ((folder (or folder wl-summary-buffer-elmo-folder
))
214 (number (or number
(wl-summary-message-number)))
216 (message "Checking spam...")
217 (when (setq spam
(elmo-spam-message-spam-p (elmo-spam-processor)
219 (wl-summary-spam number
))
220 (message "Checking spam...done")
221 (when (interactive-p)
222 (message "No: %d is %sa spam message." number
(if spam
"" "not ")))))
224 (defun wl-summary-test-spam-region (beg end
)
226 (let ((numbers (wl-summary-collect-numbers-region beg end
)))
228 (wl-spam-map-spam-messages wl-summary-buffer-elmo-folder
232 (message "No message to test.")))))
234 (defun wl-thread-test-spam (&optional arg
)
236 (wl-thread-call-region-func 'wl-summary-test-spam-region arg
))
238 (defun wl-summary-mark-spam (&optional all
)
239 "Set spam mark to messages which is spam classification."
243 (setq numbers wl-summary-buffer-number-list
)
244 (dolist (number wl-summary-buffer-number-list
)
245 (when (wl-spam-auto-check-message-p wl-summary-buffer-elmo-folder
247 (setq numbers
(cons number numbers
)))))
249 (wl-spam-map-spam-messages wl-summary-buffer-elmo-folder
253 (message "No message to test.")))))
255 (defun wl-summary-register-as-spam ()
257 (let ((number (wl-summary-message-number)))
259 (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
262 (defun wl-summary-register-as-spam-all ()
264 (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
265 wl-summary-buffer-number-list
))
267 (defun wl-summary-target-mark-register-as-spam ()
270 (goto-char (point-min))
271 (let ((inhibit-read-only t
)
272 (buffer-read-only nil
)
273 wl-summary-buffer-disp-msg
)
274 (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
275 wl-summary-buffer-target-mark-list
)
276 (dolist (number wl-summary-buffer-target-mark-list
)
277 (wl-summary-unset-mark number
)))))
279 (defun wl-summary-register-as-good ()
281 (let ((number (wl-summary-message-number)))
283 (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
286 (defun wl-summary-register-as-good-all ()
288 (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
289 wl-summary-buffer-number-list
))
291 (defun wl-summary-target-mark-register-as-good ()
294 (goto-char (point-min))
295 (let ((inhibit-read-only t
)
296 (buffer-read-only nil
)
297 wl-summary-buffer-disp-msg
)
298 (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
299 wl-summary-buffer-target-mark-list
)
300 (dolist (number wl-summary-buffer-target-mark-list
)
301 (wl-summary-unset-mark number
)))))
303 ;; hook functions and other
304 (defun wl-summary-auto-check-spam ()
305 (when (elmo-string-match-member (wl-summary-buffer-folder-name)
306 wl-spam-auto-check-folder-regexp-list
)
307 (wl-summary-mark-spam)))
309 (defun wl-summary-exec-action-spam (mark-list)
310 (let ((folder wl-summary-buffer-elmo-folder
))
311 (wl-folder-confirm-existence (wl-folder-get-elmo-folder wl-spam-folder
))
312 (wl-spam-apply-partitions
314 (wl-filter-associations
316 (wl-spam-split-numbers folder
(mapcar #'car mark-list
)))
317 (lambda (folder numbers domain
)
318 (elmo-spam-register-spam-messages (elmo-spam-processor)
322 (wl-summary-move-mark-list-messages mark-list
326 (defun wl-summary-exec-action-refile-with-register (mark-list)
327 (let ((folder wl-summary-buffer-elmo-folder
)
329 (dolist (info mark-list
)
330 (case (wl-spam-domain (nth 2 info
))
332 (setq spam-list
(cons (car info
) spam-list
)))
334 (setq good-list
(cons (car info
) good-list
)))))
335 (wl-spam-apply-partitions
337 (wl-filter-associations '(undecided good
)
338 (wl-spam-split-numbers folder spam-list
))
339 (lambda (folder numbers domain
)
340 (elmo-spam-register-spam-messages (elmo-spam-processor)
344 (wl-spam-apply-partitions
346 (wl-filter-associations '(undecided spam
)
347 (wl-spam-split-numbers folder good-list
))
348 (lambda (folder numbers domain
)
349 (elmo-spam-register-good-messages (elmo-spam-processor)
353 ;; execute refile messages
354 (wl-summary-exec-action-refile mark-list
)))
356 (defun wl-message-check-spam ()
357 (let ((original (wl-message-get-original-buffer))
358 (number wl-message-buffer-cur-number
)
360 (message "Checking spam...")
361 (when (setq spam
(elmo-spam-buffer-spam-p (elmo-spam-processor) original
))
362 (with-current-buffer wl-message-buffer-cur-summary-buffer
363 (wl-summary-spam number
)))
364 (message "Checking spam...done")
365 (message "No: %d is %sa spam message." number
(if spam
"" "not "))))
367 (defun wl-refile-guess-by-spam (entity)
368 (when (elmo-spam-message-spam-p (elmo-spam-processor)
369 wl-summary-buffer-elmo-folder
370 (elmo-message-entity-number entity
))
373 (defun wl-spam-setup ()
374 (add-hook 'wl-summary-sync-updated-hook
#'wl-summary-auto-check-spam
)
375 (let ((actions wl-summary-mark-action-list
)
378 (setq action
(car actions
)
379 actions
(cdr actions
))
380 (when (eq (wl-summary-action-symbol action
) 'refile
)
381 (setcar (nthcdr 4 action
) 'wl-summary-exec-action-refile-with-register
)
382 (setq actions nil
))))
383 (when wl-spam-mark-action-list
384 (setq wl-summary-mark-action-list
(append
385 wl-summary-mark-action-list
386 wl-spam-mark-action-list
))
387 (dolist (action wl-spam-mark-action-list
)
388 (setq wl-summary-reserve-mark-list
389 (cons (wl-summary-action-mark action
)
390 wl-summary-reserve-mark-list
))
391 (setq wl-summary-skip-mark-list
392 (cons (wl-summary-action-mark action
)
393 wl-summary-skip-mark-list
))))
394 (define-key wl-summary-mode-map
"k" wl-summary-spam-map
)
396 wl-summary-mode-map
"rkm" 'wl-summary-spam-region
)
398 wl-summary-mode-map
"rkc" 'wl-summary-test-spam-region
)
400 wl-summary-mode-map
"tkm" 'wl-thread-spam
)
402 wl-summary-mode-map
"tkc" 'wl-thread-test-spam
)
404 wl-summary-mode-map
"mk" 'wl-summary-target-mark-spam
)
406 wl-summary-mode-map
"ms" 'wl-summary-target-mark-register-as-spam
)
408 wl-summary-mode-map
"mn" 'wl-summary-target-mark-register-as-good
))
411 (product-provide (provide 'wl-spam
) (require 'wl-version
))
413 (unless noninteractive
416 ;;; wl-spam.el ends here