(defcustom elmo-spam-scheme nil
"*Scheme of spam processor implementation. "
:type '(choice (const :tag "none" nil)
- (const :tag "Bogofilter" bogofilter))
+ (const :tag "Bogofilter" bogofilter)
+ (const :tag "Spamfilter" spamfilter)
+ (const :tag "SpamAssassin" sa)
+ (const :tag "Header" header)
+ (const :tag "Bsfilter" bsfilter))
:group 'elmo-spam)
(eval-and-compile
(luna-define-class elsp-generic ()))
;; required method
-(luna-define-generic elmo-spam-buffer-spam-p (processor buffer)
+(luna-define-generic elmo-spam-buffer-spam-p (processor buffer
+ &optional register)
"Return non-nil if contents of BUFFER is spam.
-PROCESSOR is spam processor structure.")
+PROCESSOR is spam processor structure.
+If optional augument REGISTER is non-nil,
+register according to the classification.")
-(luna-define-generic elmo-spam-register-spam-buffer (processor buffer)
+(luna-define-generic elmo-spam-register-spam-buffer (processor
+ buffer
+ &optional restore)
"Register contents of BUFFER as spam.
-PROCESSOR is spam processor structure.")
+PROCESSOR is spam processor structure.
+If optional argument RESTORE is non-nil, unregister from non-spam list.")
-(luna-define-generic elmo-spam-register-good-buffer (processor buffer)
- "Register contents of BUFFER as non spam.
-PROCESSOR is spam processor structure.")
+(luna-define-generic elmo-spam-register-good-buffer (processor
+ buffer
+ &optional restore)
+ "Register contents of BUFFER as non-spam.
+PROCESSOR is spam processor structure.
+If optional argument RESTORE is non-nil, unregister from spam list.")
;; optional method
-(luna-define-generic elmo-spam-message-spam-p (processor folder number)
+(luna-define-generic elmo-spam-modified-p (processor)
+ "Return non-nil if status of PROCESSOR is modified.")
+
+(luna-define-generic elmo-spam-save-status (processor)
+ "Save status of the PROCESSOR.")
+
+(luna-define-generic elmo-spam-message-spam-p (processor folder number
+ &optional register)
"Return non-nil if the message in the FOLDER with NUMBER is spam.
-PROCESSOR is spam processor structure.")
+PROCESSOR is spam processor structure.
+If optional augument REGISTER is non-nil,
+register according to the classification.")
(luna-define-generic elmo-spam-list-spam-messages (processor
folder &optional numbers)
- "Return a list of message numbers which is gussed spam.
+ "Return a list of message numbers which is classified as spam.
PROCESSOR is spam processor structure.
FOLDER is the ELMO folder structure.
If optional argument NUMBERS is specified and is a list of message numbers,
messages are searched from the list.")
(luna-define-generic elmo-spam-register-spam-messages (processor
- folder &optional numbers)
+ folder
+ &optional
+ numbers restore)
"Register contents of messages as spam.
PROCESSOR is spam processor structure.
FOLDER is the ELMO folder structure.
If optional argument NUMBERS is specified and is a list of message numbers,
-messages are searched from the list.")
+messages are searched from the list.
+If optional argument RESTORE is non-nil, unregister from non-spam list.")
(luna-define-generic elmo-spam-register-good-messages (processor
- folder &optional numbers)
+ folder
+ &optional
+ numbers restore)
"Register contents of messages as non spam.
PROCESSOR is spam processor structure.
FOLDER is the ELMO folder structure.
If optional argument NUMBERS is specified and is a list of message numbers,
-messages are searched from the list.")
+messages are searched from the list.
+If optional argument RESTORE is non-nil, unregister from spam list.")
;; for internal use
(defun elmo-spam-message-fetch (folder number)
(let (elmo-message-fetch-threshold)
+ (when enable-multibyte-characters
+ (set-buffer-multibyte nil))
(elmo-message-fetch
folder number
- (elmo-find-fetch-strategy folder
- (elmo-message-entity folder number))
- nil (current-buffer) 'unread)))
+ (elmo-find-fetch-strategy folder number nil 'entire)
+ 'unread)))
+
+(defun elmo-spam-process-messages-as-mbox (folder numbers number-per-process
+ function &rest args)
+ (with-temp-buffer
+ (while numbers
+ (let ((count 0))
+ (while (and numbers (< count number-per-process))
+ (insert "From MAILER-DAEMON@example.com\n")
+ (let ((begin (point)))
+ (insert
+ (with-temp-buffer
+ (elmo-spam-message-fetch folder (car numbers))
+ (buffer-string)))
+ (goto-char begin)
+ (while (re-search-forward "^>*From " nil t)
+ (goto-char (match-beginning 0))
+ (insert ?>)
+ (forward-line))
+ (goto-char (point-max))
+ (insert "\n\n"))
+ (setq count (1+ count)
+ numbers (cdr numbers)))
+ (apply function count args)
+ (erase-buffer)))))
;; generic implement
(luna-define-method elmo-spam-message-spam-p ((processor elsp-generic)
- folder number)
+ folder number &optional register)
(with-temp-buffer
(elmo-spam-message-fetch folder number)
- (elmo-spam-buffer-spam-p processor (current-buffer))))
+ (elmo-spam-buffer-spam-p processor (current-buffer) register)))
(luna-define-method elmo-spam-list-spam-messages ((processor elsp-generic)
folder &optional numbers)
(nreverse spam-list)))
(luna-define-method elmo-spam-register-spam-messages ((processor elsp-generic)
- folder &optional numbers)
+ folder
+ &optional
+ numbers restore)
(let ((numbers (or numbers (elmo-folder-list-messages folder t t))))
(with-temp-buffer
- (buffer-disable-undo (current-buffer))
(dolist (number numbers)
(erase-buffer)
(elmo-spam-message-fetch folder number)
- (elmo-spam-register-spam-buffer processor (current-buffer))
+ (elmo-spam-register-spam-buffer processor (current-buffer) restore)
(elmo-progress-notify 'elmo-spam-register)))))
(luna-define-method elmo-spam-register-good-messages ((processor elsp-generic)
- folder &optional numbers)
+ folder
+ &optional
+ numbers restore)
(let ((numbers (or numbers (elmo-folder-list-messages folder t t))))
(with-temp-buffer
- (buffer-disable-undo (current-buffer))
(dolist (number numbers)
(erase-buffer)
(elmo-spam-message-fetch folder number)
- (elmo-spam-register-good-buffer processor (current-buffer))
+ (elmo-spam-register-good-buffer processor (current-buffer) restore)
(elmo-progress-notify 'elmo-spam-register)))))
(provide 'elsp-generic)
(defvar elmo-spam-processor-internal nil)
-(defun elmo-spam-processor ()
+(defun elmo-spam-processor (&optional if-exists)
(or elmo-spam-processor-internal
- (let* ((scheme (or elmo-spam-scheme 'generic))
- (class (intern (format "elsp-%s" scheme))))
- (require class)
- (setq elmo-spam-processor-internal
- (luna-make-entity class)))))
+ (unless if-exists
+ (let* ((scheme (or elmo-spam-scheme 'generic))
+ (class (intern (format "elsp-%s" scheme))))
+ (require class)
+ (setq elmo-spam-processor-internal
+ (luna-make-entity class))))))
+
+;; Backend for header match
+(eval-and-compile
+ (luna-define-class elsp-header (elsp-generic)))
+
+(defgroup elmo-spam-header nil
+ "Spam header configuration."
+ :group 'elmo-spam)
+
+(defcustom elmo-spam-header-good-alist '(("X-Spam-Flag" . "No"))
+ "*Regular expression for positive header good matches."
+ :type '(repeat (cons (string :tag "Header name")
+ (regexp :tag "Regular expression to match good header")))
+ :group 'elmo-spam-header)
+
+(defcustom elmo-spam-header-spam-alist '(("X-Spam-Flag" . "Yes"))
+ "*Regular expression for positive header spam matches."
+ :type '(repeat (cons (string :tag "Header name")
+ (regexp :tag "Regular expression to match spam header")))
+ :group 'elmo-spam-header)
+
+(defun elmo-spam-header-check-headers (fetch-field-function)
+ (catch 'done
+ (dolist (pair elmo-spam-header-good-alist)
+ (let ((field-body (funcall fetch-field-function (car pair))))
+ (when (and field-body (string-match (cdr pair) field-body))
+ (throw 'done nil))))
+ (dolist (pair elmo-spam-header-spam-alist)
+ (let ((field-body (funcall fetch-field-function (car pair))))
+ (when (and field-body (string-match (cdr pair) field-body))
+ (throw 'done t))))))
+
+(luna-define-method elmo-spam-buffer-spam-p ((processor elsp-header)
+ buffer &optional register)
+ (with-current-buffer buffer
+ (save-restriction
+ (std11-narrow-to-header)
+ (elmo-spam-header-check-headers #'elmo-decoded-field-body))))
+
+(luna-define-method elmo-spam-message-spam-p ((processor elsp-header)
+ folder number &optional register)
+ (let ((entity (elmo-message-entity folder number))
+ buffer)
+ (unwind-protect
+ (save-excursion
+ (elmo-spam-header-check-headers
+ (lambda (field-name)
+ (or (elmo-message-entity-field entity
+ (intern (downcase field-name))
+ 'string)
+ (progn
+ (unless buffer
+ (setq buffer (get-buffer-create
+ (generate-new-buffer-name
+ " *elmo-spam-work*")))
+ (set-buffer buffer)
+ (elmo-spam-message-fetch folder number)
+ (std11-narrow-to-header))
+ (elmo-decoded-field-body field-name))))))
+ (and buffer (kill-buffer buffer)))))
+
+(luna-define-method elmo-spam-register-spam-messages ((processor elsp-header)
+ folder
+ &optional
+ numbers restore)
+ (elmo-progress-notify 'elmo-spam-register (length numbers)))
+
+(luna-define-method elmo-spam-register-good-messages ((processor elsp-header)
+ folder
+ &optional
+ numbers restore)
+ (elmo-progress-notify 'elmo-spam-register (length numbers)))
+
+(provide 'elsp-header)
+
(require 'product)
(product-provide (provide 'elmo-spam) (require 'elmo-version))
-;;; elmo-sapm.el ends here
+;;; elmo-spam.el ends here