;;; clamav.el --- clamav anti-virus scanner.

;; Copyright (C) 2004 Yuuichi Teranishi <teranisi@gohome.org>

;; Author: Yuuichi Teranishi <teranisi@gohome.org>
;; Keywords: mail, net news

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;

;;; Commentary:
;;

;;; Code
(require 'pces)

(defgroup clamav nil
  "Clamav anti virus scanner client."
  :group 'mail)

(defcustom clamav-port 3310
  "Server port number of clamav."
  :type 'integer
  :group 'clamav)

(defvar clamav-target-buffer nil)
(make-variable-buffer-local 'clamav-target-buffer)
(defvar clamav-state nil)
(make-variable-buffer-local 'clamav-state)
(defvar clamav-result nil)
(make-variable-buffer-local 'clamav-result)

(defun clamav-process-sentinel (process string)
  "A sentinel for clamav process.  PROCESS, STRING are arguments for sentinel."
  (delete-process process))

(defun clamav-process-filter (process string)
  "Process filter of clamav.  PROCESS and STRING are given."
  (when (buffer-live-p (process-buffer process))
    (with-current-buffer (process-buffer process)
      (goto-char (point-max))
      (insert string)
      (goto-char (point-min))
      (case clamav-state
	(stream
	 (when (re-search-forward "^PORT \\([0-9]+\\)" nil t)
	   (setq clamav-state 'result)
	   (clamav-send-buffer clamav-target-buffer
			       (string-to-int (match-string 1)))))
	(result
	 (when (re-search-forward "^stream: \\(.*\\)$" nil t)
	   (let ((result (match-string 1)))
	     (if (string-match " FOUND" result)
		 (setq clamav-result (substring result 0
						(match-beginning 0)))
	       (setq clamav-result 'ok)))))))))

(defun clamav-send-buffer (buffer port)
  "Send BUFFER to the PORT."
  (let ((process (open-network-stream-as-binary " *clamav data*"
						nil
						"127.0.0.1" port)))
    (with-current-buffer buffer
      (process-send-region process (point-min) (point-max))
      (process-send-eof process))))

(defun clamav-scan-file (file)
  "Scan FILE.  If the file has Virus, return virus name."
  (interactive "fFile: ")
  (with-temp-buffer
    (set-buffer-multibyte nil)
    (insert-file-contents-literally file)
    (let ((result (clamav-scan-buffer (current-buffer))))
      (if (interactive-p)
	  (if (stringp result)
	      (message "Virus `%s' was detected." result)
	    (message "Clean.")))
      result)))

(defun clamav-scan-buffer (buffer)
  "Scan BUFFER.  If the buffer has Virus, return virus name."
  (interactive "bBuffer: ")
  (with-temp-buffer
    (set-buffer-multibyte nil)
    (let ((process (open-network-stream-as-binary " *clamav*"
						  (current-buffer)
						  "127.0.0.1" clamav-port)))
      (setq clamav-target-buffer buffer
	    clamav-state 'stream
	    clamav-result nil)
      (set-process-filter process 'clamav-process-filter)
      (set-process-sentinel process 'clamav-process-sentinel)
      (process-send-string process "STREAM")
      (while (null clamav-result)
	(accept-process-output process))
      (if (interactive-p)
	  (if (stringp clamav-result)
	      (message "Virus `%s' was detected." clamav-result)
	    (message "Clean.")))
      (unless (eq clamav-result 'ok)
	clamav-result))))

(provide 'clamav)

;;; clamav.el ends here
