* spam.el (spam-list-of-processors): use nil for nonexistent processors
[gnus] / lisp / spam-report.el
index fafb891..78f29b6 100644 (file)
@@ -1,5 +1,5 @@
 ;;; spam-report.el --- Reporting spam
-;; Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+;; Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
 
 ;; Author: Teodor Zlatanov <tzz@lifelogs.com>
 ;; Keywords: network
 (require 'gnus)
 (require 'gnus-sum)
 
+(eval-and-compile
+  (autoload 'mm-url-insert "mm-url"))
+
 (defgroup spam-report nil
   "Spam reporting configuration.")
 
 (defcustom spam-report-gmane-regex nil
-  "String matching Gmane newsgroups if wanted, e.g. \"^nntp+.*:gmane.\"
-This is probably handled better with group/topic parameters."
-  :type 'regexp
+  "Regexp matching Gmane newsgroups, e.g. \"^nntp\\+.*:gmane\\.\"
+If you are using spam.el, consider setting gnus-spam-process-newsgroups
+or the gnus-group-spam-exit-processor-report-gmane group/topic parameter
+instead."
+  :type '(radio (const nil)
+               (regexp :format "%t: %v\n" :size 0 :value "^nntp\+.*:gmane\."))
   :group 'spam-report)
 
-(defcustom spam-report-gmane-spam-header 
+(defcustom spam-report-gmane-spam-header
   "^X-Report-Spam: http://\\([^/]+\\)\\(.*\\)$"
   "String matching Gmane spam-reporting header.  Two match groups are needed."
   :type 'regexp
@@ -51,18 +57,74 @@ This is probably handled better with group/topic parameters."
   :type 'boolean
   :group 'spam-report)
 
-(defun spam-report-gmane (article)
+(defcustom spam-report-url-ping-function
+  'spam-report-url-ping-plain
+  "Function to use for url ping spam reporting.
+The function must accept the arguments `host' and `report'."
+  :type '(choice
+         (const :tag "Connect directly"
+                spam-report-url-ping-plain)
+         (const :tag "Use the external program specified in `mm-url-program'"
+                spam-report-url-ping-mm-url)
+         (const :tag "Store request URLs in `spam-report-requests-file'"
+                spam-report-url-to-file)
+         (function :tag "User defined function" nil))
+  :group 'spam-report)
+
+(defcustom spam-report-requests-file
+  (nnheader-concat gnus-directory "spam/" "spam-report-requests.url")
+  ;; Is there a convention for the extension of such a file?
+  ;; Should we use `spam-directory'?
+  "File where spam report request are stored."
+  :type 'file
+  :group 'spam-report)
+
+(defcustom spam-report-resend-to nil
+  "Email address that spam articles are resent to when reporting.
+If not set, the user will be prompted to enter a value which will be
+saved for future use."
+  :type 'string
+  :group 'spam-report)
+
+(defvar spam-report-url-ping-temp-agent-function nil
+  "Internal variable for `spam-report-agentize' and `spam-report-deagentize'.
+This variable will store the value of `spam-report-url-ping-function' from
+before `spam-report-agentize' was run, so that `spam-report-deagentize' can
+undo that change.")
+
+(defun spam-report-resend (articles)
+  "Report an article as spam by resending via email."
+  (dolist (article articles)
+    (gnus-message 6 
+                 "Reporting spam article %d to <%s>..." 
+                 article spam-report-resend-to)
+    (unless spam-report-resend-to
+      (customize-set-variable 
+       spam-report-resend-to
+       (read-from-minibuffer "email address to resend SPAM to? ")))
+    ;; This is ganked from the `gnus-summary-resend-message' function.
+    ;; It involves rendering the SPAM, which is undesirable, but there does
+    ;; not seem to be a nicer way to achieve this.
+    ;; select this particular article
+    (gnus-summary-select-article nil nil nil article)
+    ;; resend it to the destination address
+    (save-excursion
+      (set-buffer gnus-original-article-buffer)
+      (message-resend spam-report-resend-to))))
+
+(defun spam-report-gmane (&rest articles)
   "Report an article as spam through Gmane"
-  (interactive "nEnter the article number: ")
-  (when (and gnus-newsgroup-name
-            (or (null spam-report-gmane-regex)
-                (string-match spam-report-gmane-regex gnus-newsgroup-name)))
-    (gnus-message 6 "Reporting spam article %d to spam.gmane.org..." article)
+  (dolist (article articles)
+    (when (and gnus-newsgroup-name
+              (or (null spam-report-gmane-regex)
+                  (string-match spam-report-gmane-regex gnus-newsgroup-name)))
+      (gnus-message 6 "Reporting spam article %d to spam.gmane.org..." article)
       (if spam-report-gmane-use-article-number
-         (spam-report-url-ping "spam.gmane.org" 
-                   (format "/%s:%d"
-                           (gnus-group-real-name gnus-newsgroup-name)
-                           (gnus-summary-article-number)))
+         (spam-report-url-ping 
+          "spam.gmane.org"
+          (format "/%s:%d"
+                  (gnus-group-real-name gnus-newsgroup-name)
+                  article))
        (with-current-buffer nntp-server-buffer
          (gnus-request-head article gnus-newsgroup-name)
          (goto-char (point-min))
@@ -70,28 +132,142 @@ This is probably handled better with group/topic parameters."
              (let* ((host (match-string 1))
                     (report (match-string 2))
                     (url (format "http://%s%s" host report)))
-               (gnus-message 10 "Reporting spam through URL %s..." url)
+               (gnus-message 7 "Reporting spam through URL %s..." url)
                (spam-report-url-ping host report))
-           (gnus-message 10 "Could not find X-Report-Spam in article %d..."
-                         article))))))
+           (gnus-message 3 "Could not find X-Report-Spam in article %d..."
+                         article)))))))
 
 
 (defun spam-report-url-ping (host report)
-  "Ping a host through HTTP, addressing a specific GET resource"
+  "Ping a host through HTTP, addressing a specific GET resource using
+the function specified by `spam-report-url-ping-function'."
+  (funcall spam-report-url-ping-function host report))
+
+(defun spam-report-url-ping-plain (host report)
+  "Ping a host through HTTP, addressing a specific GET resource."
   (let ((tcp-connection))
     (with-temp-buffer
       (or (setq tcp-connection
-               (open-network-stream 
+               (open-network-stream
                 "URL ping"
                 (buffer-name)
                 host
                 80))
          (error "Could not open connection to %s" host))
       (set-marker (process-mark tcp-connection) (point-min))
-      (process-send-string tcp-connection
-                          (format "GET %s HTTP/1.1\nHost: %s\n\n"
-                                  report host)))))
+      (process-send-string
+       tcp-connection
+       (format "GET %s HTTP/1.1\nUser-Agent: %s (spam-report.el)\nHost: %s\n\n"
+              report (gnus-emacs-version) host)))))
+
+;;;###autoload
+(defun spam-report-process-queue (&optional file keep)
+  "Report all queued requests from `spam-report-requests-file'.
+
+If FILE is given, use it instead of `spam-report-requests-file'.
+If KEEP is t, leave old requests in the file.  If KEEP is the
+symbol `ask', query before flushing the queue file."
+  (interactive
+   (list (read-file-name
+         "File: "
+         (file-name-directory spam-report-requests-file)
+         spam-report-requests-file
+         nil
+         (file-name-nondirectory spam-report-requests-file))
+        current-prefix-arg))
+  (if (eq spam-report-url-ping-function 'spam-report-url-to-file)
+      (error (concat "Cannot process requests when "
+                    "`spam-report-url-ping-function' is "
+                    "`spam-report-url-to-file'."))
+    (gnus-message 7 "Processing requests using `%s'."
+                 spam-report-url-ping-function))
+  (or file (setq file spam-report-requests-file))
+  (save-excursion
+    (set-buffer (find-file-noselect file))
+    (goto-char (point-min))
+    (while (and (not (eobp))
+               (re-search-forward
+                "http://\\([^/]+\\)\\(/.*\\) *$" (point-at-eol) t))
+      (funcall spam-report-url-ping-function (match-string 1) (match-string 2))
+      (forward-line 1))
+    (if (or (eq keep nil)
+           (and (eq keep 'ask)
+                (y-or-n-p
+                 (format
+                  "Flush requests from `%s'? " (current-buffer)))))
+       (progn
+         (gnus-message 7 "Flushing request file `%s'"
+                       spam-report-requests-file)
+         (erase-buffer)
+         (save-buffer)
+         (kill-buffer (current-buffer)))
+      (gnus-message 7 "Keeping requests in `%s'" spam-report-requests-file))))
+
+;;;###autoload
+(defun spam-report-url-ping-mm-url (host report)
+  "Ping a host through HTTP, addressing a specific GET resource. Use
+the external program specified in `mm-url-program' to connect to
+server."
+  (with-temp-buffer
+    (let ((url (concat "http://" host report)))
+      (mm-url-insert url t))))
+
+;;;###autoload
+(defun spam-report-url-to-file (host report)
+  "Collect spam report requests in `spam-report-requests-file'.
+Customize `spam-report-url-ping-function' to use this function."
+  (let ((url (concat "http://" host report))
+       (file spam-report-requests-file))
+    (gnus-make-directory (file-name-directory file))
+    (gnus-message 9 "Writing URL `%s' to file `%s'" url file)
+    (with-temp-buffer
+      (insert url)
+      (newline)
+      (append-to-file (point-min) (point-max) file))))
+
+;;;###autoload
+(defun spam-report-agentize ()
+  "Add spam-report support to the Agent.
+Spam reports will be queued with \\[spam-report-url-to-file] when
+the Agent is unplugged, and will be submitted in a batch when the
+Agent is plugged."
+  (interactive)
+  (add-hook 'gnus-agent-plugged-hook 'spam-report-plug-agent)
+  (add-hook 'gnus-agent-unplugged-hook 'spam-report-unplug-agent))
+
+;;;###autoload
+(defun spam-report-deagentize ()
+  "Remove spam-report support from the Agent.
+Spam reports will be queued with the method used when
+\\[spam-report-agentize] was run."
+  (interactive)
+  (remove-hook 'gnus-agent-plugged-hook 'spam-report-plug-agent)
+  (remove-hook 'gnus-agent-unplugged-hook 'spam-report-unplug-agent))
+
+(defun spam-report-plug-agent ()
+  "Adjust spam report settings for plugged state.
+Process queued spam reports."
+  ;; Process the queue, unless the user only wanted to report to a file
+  ;; anyway.
+  (unless (equal spam-report-url-ping-temp-agent-function
+                'spam-report-url-to-file)
+    (spam-report-process-queue))
+  ;; Set the reporting function, if we have memorized something otherwise,
+  ;; stick with plain URL reporting.
+  (setq spam-report-url-ping-function
+       (or spam-report-url-ping-temp-agent-function
+           'spam-report-url-ping-plain)))
+
+(defun spam-report-unplug-agent ()
+  "Restore spam report settings for unplugged state."
+  ;; save the old value
+  (setq spam-report-url-ping-temp-agent-function
+       spam-report-url-ping-function)
+  ;; store all reports to file
+  (setq spam-report-url-ping-function
+       'spam-report-url-to-file))
 
 (provide 'spam-report)
 
+;;; arch-tag: f6683295-ec89-4ab5-8803-8cc842293022
 ;;; spam-report.el ends here.