Sync with latest Gnus. (It's a hard day today. ^^;;)
[elisp/gnus.git-] / lisp / nntp.el
index 32f3a39..dbe1818 100644 (file)
@@ -1,7 +1,9 @@
 ;;; nntp.el --- nntp access for Gnus
-;;; Copyright (C) 1987-90,92-99 Free Software Foundation, Inc.
+;;; Copyright (C) 1987, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99
+;;;   Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;;         Katsumi Yamaoka <yamaoka@jpl.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
 ;;; Code:
 
+(eval-when-compile (require 'cl))
+(eval-when-compile (require 'gnus-clfns))
+
 (require 'nnheader)
 (require 'nnoo)
 (require 'gnus-util)
 
 (nnoo-declare nntp)
 
-(eval-when-compile (require 'cl))
-
 (defvoo nntp-address nil
   "Address of the physical nntp server.")
 
@@ -48,10 +51,10 @@ server spawn an nnrpd server.")
 It is called with no parameters.")
 
 (defvoo nntp-server-action-alist
-  '(("nntpd 1\\.5\\.11t"
-     (remove-hook 'nntp-server-opened-hook 'nntp-send-mode-reader))
-    ("NNRP server Netscape"
-     (setq nntp-server-list-active-group nil)))
+    '(("nntpd 1\\.5\\.11t"
+       (remove-hook 'nntp-server-opened-hook 'nntp-send-mode-reader))
+      ("NNRP server Netscape"
+       (setq nntp-server-list-active-group nil)))
   "Alist of regexps to match on server types and actions to be taken.
 For instance, if you want Gnus to beep every time you connect
 to innd, you could say something like:
@@ -151,12 +154,6 @@ server there that you can connect to.  See also
 (defvoo nntp-warn-about-losing-connection t
   "*If non-nil, beep when a server closes connection.")
 
-(defvoo nntp-coding-system-for-read 'binary
-  "*Coding system to read from NNTP.")
-
-(defvoo nntp-coding-system-for-write 'binary
-  "*Coding system to write to NNTP.")
-
 (defcustom nntp-authinfo-file "~/.authinfo"
   ".netrc-like file that holds nntp authinfo passwords."
   :type
@@ -180,6 +177,10 @@ server there that you can connect to.  See also
   "*Number of seconds to wait before an nntp connection times out.
 If this variable is nil, which is the default, no timers are set.")
 
+(defvoo nntp-prepare-post-hook nil
+  "*Hook run just before posting an article. It is supposed to be used for
+inserting Cancel-Lock headers, signing with Gpg, etc.")
+
 ;;; Internal variables.
 
 (defvar nntp-record-commands nil
@@ -271,13 +272,18 @@ noticing asynchronous data.")
          (nnheader-report 'nntp "Server closed connection"))
         (t
          (goto-char (point-max))
-         (let ((limit (point-min)))
+         (let ((limit (point-min))
+               response)
            (while (not (re-search-backward wait-for limit t))
              (nntp-accept-process-output process)
              ;; We assume that whatever we wait for is less than 1000
              ;; characters long.
              (setq limit (max (- (point-max) 1000) (point-min)))
-             (goto-char (point-max))))
+             (goto-char (point-max)))
+           (setq response (match-string 0))
+           (save-current-buffer
+             (set-buffer nntp-server-buffer)
+             (setq nntp-process-response response)))
          (nntp-decode-text (not decode))
          (unless discard
            (save-excursion
@@ -436,36 +442,36 @@ noticing asynchronous data.")
            (nntp-inhibit-erase t)
            article)
        ;; Send HEAD commands.
-      (while (setq article (pop articles))
-       (nntp-send-command
-        nil
-        "HEAD" (if (numberp article)
-                   (int-to-string article)
-                 ;; `articles' is either a list of article numbers
-                 ;; or a list of article IDs.
-                 article))
-       (incf count)
-       ;; Every 400 requests we have to read the stream in
-       ;; order to avoid deadlocks.
-       (when (or (null articles)       ;All requests have been sent.
-                 (zerop (% count nntp-maximum-request)))
-         (nntp-accept-response)
-         (while (progn
-                  (set-buffer buf)
-                  (goto-char last-point)
-                  ;; Count replies.
-                  (while (nntp-next-result-arrived-p)
-                    (setq last-point (point))
-                    (incf received))
-                  (< received count))
-           ;; If number of headers is greater than 100, give
-           ;;  informative messages.
-           (and (numberp nntp-large-newsgroup)
-                (> number nntp-large-newsgroup)
-                (zerop (% received 20))
-                (nnheader-message 6 "NNTP: Receiving headers... %d%%"
-                                  (/ (* received 100) number)))
-           (nntp-accept-response))))
+       (while (setq article (pop articles))
+         (nntp-send-command
+          nil
+          "HEAD" (if (numberp article)
+                     (int-to-string article)
+                   ;; `articles' is either a list of article numbers
+                   ;; or a list of article IDs.
+                   article))
+         (incf count)
+         ;; Every 400 requests we have to read the stream in
+         ;; order to avoid deadlocks.
+         (when (or (null articles)     ;All requests have been sent.
+                   (zerop (% count nntp-maximum-request)))
+           (nntp-accept-response)
+           (while (progn
+                    (set-buffer buf)
+                    (goto-char last-point)
+                    ;; Count replies.
+                    (while (nntp-next-result-arrived-p)
+                      (setq last-point (point))
+                      (incf received))
+                    (< received count))
+             ;; If number of headers is greater than 100, give
+             ;;  informative messages.
+             (and (numberp nntp-large-newsgroup)
+                  (> number nntp-large-newsgroup)
+                  (zerop (% received 20))
+                  (nnheader-message 6 "NNTP: Receiving headers... %d%%"
+                                    (/ (* received 100) number)))
+             (nntp-accept-response))))
        (and (numberp nntp-large-newsgroup)
             (> number nntp-large-newsgroup)
             (nnheader-message 6 "NNTP: Receiving headers...done"))
@@ -482,6 +488,9 @@ noticing asynchronous data.")
   (nntp-possibly-change-group nil server)
   (when (nntp-find-connection-buffer nntp-server-buffer)
     (save-excursion
+      ;; Erase nntp-server-buffer before nntp-inhibit-erase.
+      (set-buffer nntp-server-buffer)
+      (erase-buffer)
       (set-buffer (nntp-find-connection-buffer nntp-server-buffer))
       ;; The first time this is run, this variable is `try'.  So we
       ;; try.
@@ -492,6 +501,7 @@ noticing asynchronous data.")
            (received 0)
            (last-point (point-min))
            (nntp-inhibit-erase t)
+           (buf (nntp-find-connection-buffer nntp-server-buffer))
            (command (if nntp-server-list-active-group "LIST ACTIVE" "GROUP")))
        (while groups
          ;; Send the command to the server.
@@ -503,6 +513,9 @@ noticing asynchronous data.")
                    (zerop (% count nntp-maximum-request)))
            (nntp-accept-response)
            (while (progn
+                    ;; Search `blue moon' in this file for the
+                    ;; reason why set-buffer here.
+                    (set-buffer buf)
                     (goto-char last-point)
                     ;; Count replies.
                     (while (re-search-forward "^[0-9]" nil t)
@@ -512,10 +525,12 @@ noticing asynchronous data.")
              (nntp-accept-response))))
 
        ;; Wait for the reply from the final command.
+       (set-buffer buf)
        (goto-char (point-max))
        (re-search-backward "^[0-9]" nil t)
        (when (looking-at "^[23]")
          (while (progn
+                  (set-buffer buf)
                   (goto-char (point-max))
                   (if (not nntp-server-list-active-group)
                       (not (re-search-backward "\r?\n" (- (point) 3) t))
@@ -523,6 +538,7 @@ noticing asynchronous data.")
            (nntp-accept-response)))
 
        ;; Now all replies are received.  We remove CRs.
+       (set-buffer buf)
        (goto-char (point-min))
        (while (search-forward "\r" nil t)
          (replace-match "" t t))
@@ -739,7 +755,24 @@ noticing asynchronous data.")
 (deffoo nntp-request-post (&optional server)
   (nntp-possibly-change-group nil server)
   (when (nntp-send-command "^[23].*\r?\n" "POST")
-    (nntp-send-buffer "^[23].*\n")))
+    (let ((response (save-current-buffer
+                     (set-buffer nntp-server-buffer)
+                     nntp-process-response))
+         server-id)
+      (when (and response
+                (string-match "^[23].*\\(<[^\t\n @<>]+@[^\t\n @<>]+>\\)"
+                              response))
+       (setq server-id (match-string 1 response))
+       (narrow-to-region (goto-char (point-min))
+                         (if (search-forward "\n\n" nil t)
+                             (1- (point))
+                           (point-max)))
+       (unless (mail-fetch-field "Message-ID")
+         (goto-char (point-min))
+         (insert "Message-ID: " server-id "\n"))
+       (widen))
+      (run-hooks 'nntp-prepare-post-hook)
+      (nntp-send-buffer "^[23].*\n"))))
 
 (deffoo nntp-request-type (group article)
   'news)
@@ -765,7 +798,7 @@ and a password.
 If SEND-IF-FORCE, only send authinfo to the server if the
 .authinfo file has the FORCE token."
   (let* ((list (gnus-parse-netrc nntp-authinfo-file))
-        (alist (gnus-netrc-machine list nntp-address))
+        (alist (gnus-netrc-machine list nntp-address "nntp"))
         (force (gnus-netrc-get alist "force"))
         (user (or (gnus-netrc-get alist "login") nntp-authinfo-user))
         (passwd (gnus-netrc-get alist "password")))
@@ -777,13 +810,13 @@ If SEND-IF-FORCE, only send authinfo to the server if the
       (unless (member user '(nil ""))
        (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" user)
        (when t                         ;???Should check if AUTHINFO succeeded
-      (nntp-send-command
-       "^2.*\r?\n" "AUTHINFO PASS"
-       (or passwd
-          nntp-authinfo-password
-          (setq nntp-authinfo-password
+         (nntp-send-command
+          "^2.*\r?\n" "AUTHINFO PASS"
+          (or passwd
+              nntp-authinfo-password
+              (setq nntp-authinfo-password
                     (mail-source-read-passwd (format "NNTP (%s@%s) password: "
-                                                user nntp-address))))))))))
+                                                     user nntp-address))))))))))
 
 (defun nntp-send-nosy-authinfo ()
   "Send the AUTHINFO to the nntp server."
@@ -793,7 +826,7 @@ If SEND-IF-FORCE, only send authinfo to the server if the
       (when t                          ;???Should check if AUTHINFO succeeded
        (nntp-send-command "^2.*\r?\n" "AUTHINFO PASS"
                           (mail-source-read-passwd "NNTP (%s@%s) password: "
-                                              user nntp-address))))))
+                                                   user nntp-address))))))
 
 (defun nntp-send-authinfo-from-file ()
   "Send the AUTHINFO to the nntp server.
@@ -830,7 +863,6 @@ password contained in '~/.nntp-authinfo'."
       (format " *server %s %s %s*"
              nntp-address nntp-port-number
              (gnus-buffer-exists-p buffer))))
-    (mm-enable-multibyte)
     (set (make-local-variable 'after-change-functions) nil)
     (set (make-local-variable 'nntp-process-wait-for) nil)
     (set (make-local-variable 'nntp-process-callback) nil)
@@ -851,9 +883,7 @@ password contained in '~/.nntp-authinfo'."
                   (nntp-kill-buffer ,pbuffer)))))
         (process
          (condition-case ()
-             (let ((coding-system-for-read nntp-coding-system-for-read)
-                    (coding-system-for-write nntp-coding-system-for-write))
-               (funcall nntp-open-connection-function pbuffer))
+             (funcall nntp-open-connection-function pbuffer)
            (error nil)
            (quit nil))))
     (when timer
@@ -878,7 +908,8 @@ password contained in '~/.nntp-authinfo'."
        nil))))
 
 (defun nntp-open-network-stream (buffer)
-  (open-network-stream "nntpd" buffer nntp-address nntp-port-number))
+  (open-network-stream-as-binary
+   "nntpd" buffer nntp-address nntp-port-number))
 
 (defun nntp-open-ssl-stream (buffer)
   (let* ((ssl-program-arguments '("-connect" (concat host ":" service)))
@@ -973,6 +1004,10 @@ password contained in '~/.nntp-authinfo'."
        (goto-char (point-max))
        (when (re-search-backward
               nntp-process-wait-for nntp-process-start-point t)
+         (let ((response (match-string 0)))
+           (save-current-buffer
+             (set-buffer nntp-server-buffer)
+             (setq nntp-process-response response)))
          (nntp-async-stop process)
          ;; convert it.
          (when (gnus-buffer-exists-p nntp-process-to-buffer)
@@ -1101,7 +1136,7 @@ password contained in '~/.nntp-authinfo'."
      (car (last articles)) 'wait)
 
     (goto-char (point-min))
-    (when (looking-at "[1-5][0-9][0-9] ")
+    (when (looking-at "[1-5][0-9][0-9] .*\n")
       (delete-region (point) (progn (forward-line 1) (point))))
     (while (search-forward "\r" nil t)
       (replace-match "" t t))
@@ -1118,9 +1153,10 @@ password contained in '~/.nntp-authinfo'."
    ((numberp nntp-nov-gap)
     (let ((count 0)
          (received 0)
-         (last-point (point-min))
+         last-point
+         in-process-buffer-p
          (buf nntp-server-buffer)
-         ;;(process-buffer (nntp-find-connection (current-buffer))))
+         (process-buffer (nntp-find-connection-buffer nntp-server-buffer))
          first)
       ;; We have to check `nntp-server-xover'.  If it gets set to nil,
       ;; that means that the server does not understand XOVER, but we
@@ -1133,40 +1169,58 @@ password contained in '~/.nntp-authinfo'."
                    (< (- (nth 1 articles) (car articles)) nntp-nov-gap))
          (setq articles (cdr articles)))
 
-       (when (nntp-send-xover-command first (car articles))
-         (setq articles (cdr articles)
-               count (1+ count))
-
+       (setq in-process-buffer-p (stringp nntp-server-xover))
+       (nntp-send-xover-command first (car articles))
+       (setq articles (cdr articles))
+       
+       (when (and nntp-server-xover in-process-buffer-p)
+         ;; Don't count tried request.
+         (setq count (1+ count))
+         
          ;; Every 400 requests we have to read the stream in
          ;; order to avoid deadlocks.
          (when (or (null articles)     ;All requests have been sent.
                    (zerop (% count nntp-maximum-request)))
-           (accept-process-output)
-           ;; On some Emacs versions the preceding function has
-           ;; a tendency to change the buffer.  Perhaps.  It's
-           ;; quite difficult to reproduce, because it only
-           ;; seems to happen once in a blue moon.
-           (set-buffer buf)
+
+           (nntp-accept-response)
+           ;; On some Emacs versions the preceding function has a
+           ;; tendency to change the buffer.  Perhaps.  It's quite
+           ;; difficult to reproduce, because it only seems to happen
+           ;; once in a blue moon.
+           (set-buffer process-buffer)
            (while (progn
-                    (goto-char last-point)
+                    (goto-char (or last-point (point-min)))
                     ;; Count replies.
-                    (while (re-search-forward "^[0-9][0-9][0-9] " nil t)
-                      (setq received (1+ received)))
+                    (while (re-search-forward "^[0-9][0-9][0-9] .*\n" nil t)
+                      (incf received))
                     (setq last-point (point))
                     (< received count))
-             (accept-process-output)
-             (set-buffer buf)))))
+             (nntp-accept-response)
+             (set-buffer process-buffer))
+           (set-buffer buf))))
 
       (when nntp-server-xover
-       ;; Wait for the reply from the final command.
-       (goto-char (point-max))
-       (re-search-backward "^[0-9][0-9][0-9] " nil t)
-       (when (looking-at "^[23]")
-         (while (progn
-                  (goto-char (point-max))
-                  (forward-line -1)
-                  (not (looking-at "^\\.\r?\n")))
-           (nntp-accept-response)))
+       (when in-process-buffer-p
+         (set-buffer process-buffer)
+         ;; Wait for the reply from the final command.
+         (goto-char (point-max))
+         (while (not (re-search-backward "^[0-9][0-9][0-9] " nil t))
+           (nntp-accept-response)
+           (set-buffer process-buffer)
+           (goto-char (point-max)))
+         (when (looking-at "^[23]")
+           (while (progn
+                    (goto-char (point-max))
+                    (forward-line -1)
+                    (not (looking-at "^\\.\r?\n")))
+             (nntp-accept-response)
+             (set-buffer process-buffer)))
+         (set-buffer buf)
+         (goto-char (point-max))
+         (insert-buffer-substring process-buffer)
+         (set-buffer process-buffer)
+         (erase-buffer)
+         (set-buffer buf))
 
        ;; We remove any "." lines and status lines.
        (goto-char (point-min))
@@ -1189,7 +1243,7 @@ password contained in '~/.nntp-authinfo'."
            (nntp-send-command-nodelete
             "\r?\n\\.\r?\n" nntp-server-xover range)
          ;; We do not wait for the reply.
-         (nntp-send-command-nodelete "\r?\n\\.\r?\n" nntp-server-xover range))
+         (nntp-send-command-nodelete nil nntp-server-xover range))
       (let ((commands nntp-xover-commands))
        ;; `nntp-xover-commands' is a list of possible XOVER commands.
        ;; We try them all until we get at positive response.
@@ -1230,9 +1284,10 @@ password contained in '~/.nntp-authinfo'."
   (save-excursion
     (set-buffer buffer)
     (erase-buffer)
-    (let ((proc (apply
-                'start-process
-                "nntpd" buffer nntp-telnet-command nntp-telnet-switches))
+    (let ((proc (as-binary-process
+                (apply
+                 'start-process
+                 "nntpd" buffer nntp-telnet-command nntp-telnet-switches)))
          (case-fold-search t))
       (when (memq (process-status proc) '(open run))
        (process-send-string proc "set escape \^X\n")
@@ -1257,7 +1312,6 @@ password contained in '~/.nntp-authinfo'."
                   (setq nntp-telnet-passwd
                         (mail-source-read-passwd "Password: ")))
               "\n"))
-       (erase-buffer)
        (nntp-wait-for-string nntp-telnet-shell-prompt)
        (process-send-string
         proc (concat (mapconcat 'identity nntp-telnet-parameters " ") "\n"))
@@ -1277,13 +1331,15 @@ password contained in '~/.nntp-authinfo'."
 (defun nntp-open-rlogin (buffer)
   "Open a connection to SERVER using rsh."
   (let ((proc (if nntp-rlogin-user-name
-                 (apply 'start-process
-                        "nntpd" buffer nntp-rlogin-program
-                        nntp-address "-l" nntp-rlogin-user-name
-                        nntp-rlogin-parameters)
-               (apply 'start-process
-                      "nntpd" buffer nntp-rlogin-program nntp-address
-                      nntp-rlogin-parameters))))
+                 (as-binary-process
+                  (apply 'start-process
+                         "nntpd" buffer nntp-rlogin-program
+                         nntp-address "-l" nntp-rlogin-user-name
+                         nntp-rlogin-parameters))
+               (as-binary-process
+                (apply 'start-process
+                       "nntpd" buffer nntp-rlogin-program nntp-address
+                       nntp-rlogin-parameters)))))
     (save-excursion
       (set-buffer buffer)
       (nntp-wait-for-string "^\r*20[01]")