* wl-vars.el (wl-summary-new-uncached-mark): Renamed from
[elisp/wanderlust.git] / wl / wl-spam.el
1 ;;; wl-spam.el --- Spam filtering interface for Wanderlust.
2
3 ;; Copyright (C) 2003 Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
4 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
5
6 ;; Author: Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news, spam
8
9 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
10
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)
14 ;; any later version.
15 ;;
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.
20 ;;
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.
25 ;;
26
27 ;;; Commentary:
28 ;;
29
30 ;;; Code:
31 ;;
32
33 (eval-when-compile (require 'cl))
34
35 (require 'elmo-spam)
36 (require 'wl-summary)
37 (require 'wl-action)
38 (require 'wl-highlight)
39
40 (defgroup wl-spam nil
41   "Spam configuration for wanderlust."
42   :group 'wl)
43
44 (defcustom wl-spam-folder-name "+spam"
45   "*Spam folder."
46   :type 'string
47   :group 'wl-spam)
48
49 (defcustom wl-spam-undecided-folder-regexp-list '("inbox")
50   "*List of folder regexp which is contained undecided domain."
51   :type '(repeat (regexp :tag "Folder Regexp"))
52   :group 'wl-spam)
53
54 (defcustom wl-spam-ignored-folder-regexp-list
55   (list (regexp-opt (list wl-draft-folder
56                           wl-trash-folder
57                           wl-queue-folder)))
58   "*List of folder regexp which is contained ignored domain."
59   :type '(repeat (regexp :tag "Folder Regexp"))
60   :group 'wl-spam)
61
62 (defcustom wl-spam-auto-check-folder-regexp-list '("[+.]inbox")
63   "*List of Folder regexp which check spam automatically."
64   :type '(repeat (regexp :tag "Folder Regexp"))
65   :group 'wl-spam)
66
67 (defcustom wl-spam-auto-check-marks
68   (list wl-summary-new-uncached-mark
69         wl-summary-new-cached-mark)
70   "Persistent marks to check spam automatically."
71   :type '(choice (const :tag "All marks" all)
72                  (repeat (string :tag "Mark")))
73   :group 'wl-spam)
74
75 (wl-defface wl-highlight-summary-spam-face
76   '((((type tty)
77       (background dark))
78      (:foreground "blue"))
79     (((class color))
80      (:foreground "LightSlateGray")))
81   "Face used for displaying messages mark as spam."
82   :group 'wl-summary-faces
83   :group 'wl-faces)
84
85 (defcustom wl-spam-mark-action-list
86   '(("s"
87      spam
88      nil
89      wl-summary-register-temp-mark
90      wl-summary-exec-action-spam
91      wl-highlight-summary-spam-face
92      "Mark messages as spam."))
93   "A variable to define Mark & Action for spam.
94 Append this value to `wl-summary-mark-action-list' by `wl-spam-setup'.
95
96 See `wl-summary-mark-action-list' for the detail of element."
97   :type '(repeat (string :tag "Temporary mark")
98                  (symbol :tag "Set mark function")
99                  (symbol :tag "Unset mark function")
100                  (symbol :tag "Exec function")
101                  (symbol :tag "Face symbol")
102                  (string :tag "Document string"))
103   :group 'wl-spam)
104
105 (defun wl-spam-domain (folder-name)
106   (cond ((string= folder-name wl-spam-folder-name)
107          'spam)
108         ((wl-string-match-member folder-name
109                                  wl-spam-undecided-folder-regexp-list)
110          'undecided)
111         ((wl-string-match-member folder-name
112                                  wl-spam-ignored-folder-regexp-list)
113          'ignore)
114         (t
115          'good)))
116
117 (defsubst wl-spam-auto-check-message-p (folder number)
118   (or (eq wl-spam-auto-check-marks 'all)
119       (member (wl-summary-message-mark folder number)
120               wl-spam-auto-check-marks)))
121
122 (defsubst wl-spam-map-spam-messages (folder numbers function &rest args)
123   (let ((total (length numbers)))
124     (message "Checking spam...")
125     (elmo-with-progress-display (> total elmo-display-progress-threshold)
126         (elmo-spam-check-spam total "Checking spam...")
127       (dolist (number (elmo-spam-list-spam-messages (elmo-spam-processor)
128                                                     folder
129                                                     numbers))
130         (apply function number args)))
131     (message "Checking spam...done")))
132
133 (defun wl-spam-register-spam-messages (folder numbers)
134   (let ((total (length numbers)))
135     (message "Registering spam...")
136     (elmo-with-progress-display (> total elmo-display-progress-threshold)
137         (elmo-spam-register total "Registering spam...")
138       (elmo-spam-register-spam-messages (elmo-spam-processor)
139                                         wl-summary-buffer-elmo-folder
140                                         numbers))
141     (message "Registering spam...done")))
142
143 (defun wl-spam-register-good-messages (folder numbers)
144   (let ((total (length numbers)))
145     (message "Registering good...")
146     (elmo-with-progress-display (> total elmo-display-progress-threshold)
147         (elmo-spam-register total "Registering good...")
148       (elmo-spam-register-good-messages (elmo-spam-processor)
149                                         wl-summary-buffer-elmo-folder
150                                         numbers))
151     (message "Registering good...done")))
152
153 (defun wl-spam-save-status (&optional force)
154   (interactive "P")
155   (let ((processor (elmo-spam-processor)))
156     (when (or force (elmo-spam-modified-p processor))
157       (elmo-spam-save-status processor))))
158
159 ;; insinuate into summary mode
160 (defvar wl-summary-spam-map nil)
161
162 (unless wl-summary-spam-map
163   (let ((map (make-sparse-keymap)))
164     (define-key map "m" 'wl-summary-spam)
165     (define-key map "c" 'wl-summary-test-spam)
166     (define-key map "C" 'wl-summary-mark-spam)
167     (define-key map "s" 'wl-summary-register-as-spam)
168     (define-key map "S" 'wl-summary-register-as-spam-all)
169     (define-key map "n" 'wl-summary-register-as-good)
170     (define-key map "N" 'wl-summary-register-as-good-all)
171     (setq wl-summary-spam-map map)))
172
173 (eval-when-compile
174   ;; Avoid compile warnings
175   (defalias-maybe 'wl-summary-spam 'ignore))
176
177 (defun wl-summary-test-spam (&optional folder number)
178   (interactive)
179   (let ((folder (or folder wl-summary-buffer-elmo-folder))
180         (number (or number (wl-summary-message-number)))
181         spam)
182     (message "Cheking spam...")
183     (when (setq spam (elmo-spam-message-spam-p (elmo-spam-processor)
184                                                folder number))
185       (wl-summary-spam number))
186     (message "Cheking spam...done")
187     (when (interactive-p)
188       (message "No: %d is %sa spam message." number (if spam "" "not ")))))
189
190 (defun wl-summary-mark-spam (&optional all)
191   "Set spam mark to messages which is spam classification."
192   (interactive "P")
193   (let (numbers)
194     (if all
195         (setq numbers wl-summary-buffer-number-list)
196       (dolist (number wl-summary-buffer-number-list)
197         (when (wl-spam-auto-check-message-p wl-summary-buffer-elmo-folder
198                                             number)
199           (setq numbers (cons number numbers)))))
200     (if numbers
201         (wl-spam-map-spam-messages wl-summary-buffer-elmo-folder
202                                    numbers
203                                    #'wl-summary-spam)
204       (message "No message to test."))))
205
206 (defun wl-summary-register-as-spam ()
207   (interactive)
208   (let ((number (wl-summary-message-number)))
209     (when number
210       (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
211                                       (list number)))))
212
213 (defun wl-summary-register-as-spam-all ()
214   (interactive)
215   (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
216                                   wl-summary-buffer-number-list))
217
218 (defun wl-summary-target-mark-register-as-spam ()
219   (interactive)
220   (save-excursion
221     (goto-char (point-min))
222     (let ((inhibit-read-only t)
223           (buffer-read-only nil)
224           wl-summary-buffer-disp-msg)
225       (wl-spam-register-spam-messages wl-summary-buffer-elmo-folder
226                                       wl-summary-buffer-target-mark-list)
227       (dolist (number wl-summary-buffer-target-mark-list)
228         (wl-summary-unset-mark number)))))
229
230 (defun wl-summary-register-as-good ()
231   (interactive)
232   (let ((number (wl-summary-message-number)))
233     (when number
234       (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
235                                       (list number)))))
236
237 (defun wl-summary-register-as-good-all ()
238   (interactive)
239   (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
240                                   wl-summary-buffer-number-list))
241
242 (defun wl-summary-target-mark-register-as-good ()
243   (interactive)
244   (save-excursion
245     (goto-char (point-min))
246     (let ((inhibit-read-only t)
247           (buffer-read-only nil)
248           wl-summary-buffer-disp-msg)
249       (wl-spam-register-good-messages wl-summary-buffer-elmo-folder
250                                       wl-summary-buffer-target-mark-list)
251       (dolist (number wl-summary-buffer-target-mark-list)
252         (wl-summary-unset-mark number)))))
253
254 ;; hook functions and other
255 (defun wl-summary-auto-check-spam ()
256   (when (elmo-string-match-member (wl-summary-buffer-folder-name)
257                                   wl-spam-auto-check-folder-regexp-list)
258     (wl-summary-mark-spam)))
259
260 (defun wl-summary-exec-action-spam (mark-list)
261   (let ((domain (wl-spam-domain (elmo-folder-name-internal
262                                  wl-summary-buffer-elmo-folder)))
263         (total (length mark-list)))
264     (when (memq domain '(undecided good))
265       (message "Registering spam...")
266       (elmo-with-progress-display (> total elmo-display-progress-threshold)
267           (elmo-spam-register total "Registering spam...")
268         (elmo-spam-register-spam-messages (elmo-spam-processor)
269                                           wl-summary-buffer-elmo-folder
270                                           (mapcar #'car mark-list)
271                                           (eq domain 'good)))
272       (message "Registering spam...done"))
273     (wl-summary-move-mark-list-messages mark-list
274                                         wl-spam-folder-name
275                                         "Refiling spam...")))
276
277 (defun wl-summary-exec-action-refile-with-register (mark-list)
278   (let* ((processor (elmo-spam-processor))
279          (folder wl-summary-buffer-elmo-folder)
280          (domain (wl-spam-domain (elmo-folder-name-internal folder)))
281          spam-list good-list total)
282     (unless (eq domain 'ignore)
283       (dolist (info mark-list)
284         (case (wl-spam-domain (nth 2 info))
285           (spam
286            (setq spam-list (cons (car info) spam-list)))
287           (good
288            (setq good-list (cons (car info) good-list)))))
289       (case domain
290         (spam (setq spam-list nil))
291         (good (setq good-list nil)))
292       (when (or spam-list good-list)
293         (message "Registering spam...")
294         (setq total (+ (length spam-list) (length good-list)))
295         (elmo-with-progress-display (> total elmo-display-progress-threshold)
296             (elmo-spam-register total "Registering spam...")
297           (when spam-list
298             (elmo-spam-register-spam-messages processor folder spam-list
299                                               (eq domain 'good)))
300           (when good-list
301             (elmo-spam-register-good-messages processor folder good-list
302                                               (eq domain 'spam))))
303         (message "Registering spam...done")))
304     ;; execute refile messages
305     (wl-summary-exec-action-refile mark-list)))
306
307 (defun wl-message-check-spam ()
308   (let ((original (wl-message-get-original-buffer))
309         (number wl-message-buffer-cur-number)
310         spam)
311     (message "Cheking spam...")
312     (when (elmo-spam-buffer-spam-p (elmo-spam-processor) original)
313       (with-current-buffer wl-message-buffer-cur-summary-buffer
314         (wl-summary-spam number)))
315     (message "Cheking spam...done")
316     (when (interactive-p)
317       (message "No: %d is %sa spam message." number (if spam "" "not ")))))
318
319 (defun wl-refile-guess-by-spam (entity)
320   (when (elmo-spam-message-spam-p (elmo-spam-processor)
321                                   wl-summary-buffer-elmo-folder
322                                   (elmo-message-entity-number entity))
323     wl-spam-folder-name))
324
325 (defun wl-spam-setup ()
326   (when wl-spam-mark-action-list
327     (setq wl-summary-mark-action-list (append
328                                        wl-summary-mark-action-list
329                                        wl-spam-mark-action-list))
330     (dolist (action wl-spam-mark-action-list)
331       (setq wl-summary-reserve-mark-list
332             (cons (wl-summary-action-mark action)
333                   wl-summary-reserve-mark-list))
334       (setq wl-summary-skip-mark-list
335             (cons (wl-summary-action-mark action)
336                   wl-summary-skip-mark-list))))
337   (define-key wl-summary-mode-map "k" wl-summary-spam-map)
338   (define-key wl-summary-mode-map "ms" 'wl-summary-target-mark-register-as-spam)
339   (define-key wl-summary-mode-map "mn" 'wl-summary-target-mark-register-as-good))
340
341 (require 'product)
342 (product-provide (provide 'wl-spam) (require 'wl-version))
343
344 (unless noninteractive
345   (wl-spam-setup))
346
347 ;;; wl-spam.el ends here