Update.
[elisp/wanderlust.git] / elmo / elsp-bsfilter.el
1 ;;; elsp-bsfilter.el --- Bsfilter support for elmo-spam.
2
3 ;; Copyright (C) 2004 Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
4 ;; Copyright (C) 2004 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 (require 'elmo-spam)
33 (require 'luna)
34
35 (defgroup elmo-spam-bsfilter nil
36   "Spam bsfilter configuration."
37   :group 'elmo-spam)
38
39 (defcustom elmo-spam-bsfilter-shell-program "ruby"
40   "*"
41   :type 'string
42   :group 'elmo-spam-bsfilter)
43
44 (defcustom elmo-spam-bsfilter-shell-switch nil
45   "*"
46   :type 'string
47   :group 'elmo-spam-bsfilter)
48
49 (defcustom elmo-spam-bsfilter-program (exec-installed-p "bsfilter")
50   "*Program name of the Bsfilter."
51   :type '(string :tag "Program name of the bsfilter")
52   :group 'elmo-spam-bsfilter)
53
54 (defcustom elmo-spam-bsfilter-args nil
55   "*Argument list for bsfilter."
56   :type '(repeat string)
57   :group 'elmo-spam-bsfilter)
58
59 (defcustom elmo-spam-bsfilter-update-switch "--synchronous-auto-update"
60   "*The switch that Bsfilter uses to update database with classify."
61   :type 'string
62   :group 'elmo-spam-bsfilter)
63
64 (defcustom elmo-spam-bsfilter-database-directory nil
65   "*Directory path of the Bsfilter databases."
66   :type '(choice (directory :tag "Location of the Bsfilter database directory")
67                  (const :tag "Use the default"))
68   :group 'elmo-spam-bsfilter)
69
70 (defcustom elmo-spam-bsfilter-max-files-per-process 100
71   "Number of files processed at once."
72   :type 'integer
73   :group 'elmo-spam-bsfilter)
74
75 (defcustom elmo-spam-bsfilter-max-messages-per-process 30
76   "Number of messages processed at once."
77   :type 'integer
78   :group 'elmo-spam-bsfilter)
79
80 (defcustom elmo-spam-bsfilter-debug nil
81   "Non-nil to debug elmo bsfilter spam backend."
82   :type 'boolean
83   :group 'elmo-spam-bsfilter)
84
85 (eval-and-compile
86   (luna-define-class elsp-bsfilter (elsp-generic)))
87
88 (defsubst elsp-bsfilter-call-bsfilter (&rest args)
89   (apply #'call-process-region
90          (point-min) (point-max)
91          elmo-spam-bsfilter-shell-program
92          nil (if elmo-spam-bsfilter-debug
93                  (get-buffer-create "*Debug ELMO Bsfilter*"))
94          nil
95          (append (if elmo-spam-bsfilter-shell-switch
96                      (list elmo-spam-bsfilter-shell-switch))
97                  (if elmo-spam-bsfilter-program
98                      (list elmo-spam-bsfilter-program))
99                  elmo-spam-bsfilter-args
100                  (if elmo-spam-bsfilter-database-directory
101                      (list "--homedir" elmo-spam-bsfilter-database-directory))
102                  (elmo-flatten args))))
103
104 (luna-define-method elmo-spam-buffer-spam-p ((processor elsp-bsfilter)
105                                              buffer &optional register)
106   (with-current-buffer buffer
107     (= 0 (elsp-bsfilter-call-bsfilter
108           (if register elmo-spam-bsfilter-update-switch)))))
109
110 (defun elsp-bsfilter-list-spam-filter (process output)
111   (when (buffer-live-p (process-buffer process))
112     (with-current-buffer (process-buffer process)
113       (save-excursion
114         (goto-char (process-mark process))
115         (insert output)
116         (set-marker (process-mark process) (point)))
117       (while (re-search-forward "^combined probability.+\r?\n" nil t)
118         (delete-region (match-beginning 0) (match-end 0))
119         (elmo-progress-notify 'elmo-spam-check-spam))
120       (when elmo-spam-bsfilter-debug
121         (with-current-buffer (get-buffer-create "*Debug ELMO Bsfilter*")
122           (goto-char (point-max))
123           (insert output))))))
124
125 (defsubst elsp-bsfilter-start-list-spam (targets)
126   (let ((process
127          (apply #'start-process
128                 "elsp-bsfilter"
129                 (current-buffer)
130                 elmo-spam-bsfilter-shell-program
131                 (append (if elmo-spam-bsfilter-shell-switch
132                             (list elmo-spam-bsfilter-shell-switch))
133                         (if elmo-spam-bsfilter-program
134                             (list elmo-spam-bsfilter-program))
135                         elmo-spam-bsfilter-args
136                         (list "--list-spam")
137                         (if elmo-spam-bsfilter-database-directory
138                             (list "--homedir"
139                                   elmo-spam-bsfilter-database-directory))
140                         targets))))
141     (set-process-filter process #'elsp-bsfilter-list-spam-filter)
142     process))
143
144 (defsubst elsp-bsfilter-read-list-spam (results hash)
145   (goto-char (point-min))
146   (while (not (eobp))
147     (let* ((filename (buffer-substring (point) (save-excursion
148                                                  (end-of-line)
149                                                  (point))))
150            (number (elmo-get-hash-val filename hash)))
151       (when number
152         (setq results (cons number results)))
153       (forward-line)))
154   results)
155
156 (luna-define-method elmo-spam-list-spam-messages :around
157   ((processor elsp-bsfilter) folder &optional numbers)
158   (if (not (elmo-folder-message-file-p folder))
159       (luna-call-next-method)
160     (let* ((nth-of-targets (1- (or elmo-spam-bsfilter-max-files-per-process
161                                    100)))
162            (numbers (or numbers (elmo-folder-list-messages folder t t)))
163            (hash (elmo-make-hash (length numbers)))
164            (targets (mapcar
165                      (lambda (number)
166                        (let ((filename (elmo-message-file-name folder number)))
167                          (elmo-set-hash-val filename number hash)
168                          filename))
169                      numbers))
170            results)
171       (with-temp-buffer
172         (while targets
173           (let* ((last (nthcdr nth-of-targets targets))
174                  (next (cdr last)))
175             (when last
176               (setcdr last nil))
177             (let ((process (elsp-bsfilter-start-list-spam targets)))
178               (while (memq (process-status process) '(open run))
179                 (accept-process-output process 1))
180               (setq results (elsp-bsfilter-read-list-spam results hash)))
181             (erase-buffer)
182             (setq targets next))))
183       results)))
184
185
186 (defsubst elsp-bsfilter-register-buffer (buffer spam restore &optional mbox)
187   (with-current-buffer buffer
188     (elsp-bsfilter-call-bsfilter
189      "--update"
190      (if restore (if spam "--sub-clean" "--sub-spam"))
191      (if spam "--add-spam" "--add-clean")
192      (if mbox "--mbox"))))
193
194 (luna-define-method elmo-spam-register-spam-buffer ((processor elsp-bsfilter)
195                                                     buffer &optional restore)
196   (elsp-bsfilter-register-buffer buffer t restore))
197
198 (luna-define-method elmo-spam-register-good-buffer ((processor elsp-bsfilter)
199                                                     buffer &optional restore)
200   (elsp-bsfilter-register-buffer buffer nil restore))
201
202 (defsubst elmo-spam-bsfilter-register-messages (folder numbers spam restore)
203   (let ((numbers (or numbers (elmo-folder-list-messages folder t t))))
204     (if (and (> (length numbers) 1)
205              elmo-spam-bsfilter-max-messages-per-process
206              (> elmo-spam-bsfilter-max-messages-per-process 0))
207         (elmo-spam-process-messages-as-mbox
208          folder numbers
209          elmo-spam-bsfilter-max-messages-per-process
210          (lambda (count spam restore)
211            (elsp-bsfilter-register-buffer (current-buffer) spam restore 'mbox)
212            (elmo-progress-notify 'elmo-spam-register count))
213          spam restore)
214       (luna-call-next-method))))
215
216 (luna-define-method elmo-spam-register-spam-messages :around
217   ((processor elsp-bsfilter) folder &optional numbers restore)
218   (elmo-spam-bsfilter-register-messages folder numbers t restore))
219
220 (luna-define-method elmo-spam-register-good-messages :around
221   ((processor elsp-bsfilter) folder &optional numbers restore)
222   (elmo-spam-bsfilter-register-messages folder numbers nil restore))
223
224 (require 'product)
225 (product-provide (provide 'elsp-bsfilter) (require 'elmo-version))
226
227 ;;; elsp-bsfilter.el ends here