do on servers that use strict access control.")
(add-hook 'nntp-server-opened-hook 'nntp-send-mode-reader)
+(defvoo nntp-authinfo-function 'nntp-send-authinfo
+ "Function used to send AUTHINFO to the server.")
+
(defvoo nntp-server-action-alist
'(("nntpd 1\\.5\\.11t"
(remove-hook 'nntp-server-opened-hook 'nntp-send-mode-reader)))
;;; Internal variables.
+(defvar nntp-process-wait-for nil)
+(defvar nntp-process-to-buffer nil)
+(defvar nntp-process-callback nil)
+(defvar nntp-process-decode nil)
+(defvar nntp-process-start-point nil)
+(defvar nntp-inside-change-function nil)
+
(defvoo nntp-server-type nil)
(defvoo nntp-connection-alist nil)
(defvoo nntp-status-string "")
(copy-to-buffer nntp-server-buffer (point-min) (point-max))
'headers))))
+(deffoo nntp-retrieve-groups (groups &optional server)
+ "Retrieve group info on GROUPS."
+ (nntp-possibly-change-group nil server)
+ (save-excursion
+ (set-buffer (nntp-find-connection-buffer nntp-server-buffer))
+ ;; The first time this is run, this variable is `try'. So we
+ ;; try.
+ (when (eq nntp-server-list-active-group 'try)
+ (nntp-try-list-active (car groups)))
+ (erase-buffer)
+ (let ((count 0)
+ (received 0)
+ (last-point (point-min))
+ (command (if nntp-server-list-active-group "LIST ACTIVE" "GROUP")))
+ (while groups
+ ;; Send the command to the server.
+ (nntp-send-command nil command (car groups))
+ (setq groups (cdr groups))
+ (setq count (1+ count))
+ ;; Every 400 requests we have to read the stream in
+ ;; order to avoid deadlocks.
+ (when (or (null groups) ;All requests have been sent.
+ (zerop (% count nntp-maximum-request)))
+ (nntp-accept-response)
+ (while (progn
+ (goto-char last-point)
+ ;; Count replies.
+ (while (re-search-forward "^[0-9]" nil t)
+ (setq received (1+ received)))
+ (setq last-point (point))
+ (< received count))
+ (nntp-accept-response))))
+
+ ;; Wait for the reply from the final command.
+ (when nntp-server-list-active-group
+ (goto-char (point-max))
+ (re-search-backward "^[0-9]" nil t)
+ (when (looking-at "^[23]")
+ (while (progn
+ (goto-char (- (point-max) 3))
+ (not (looking-at "^\\.\r?\n")))
+ (nntp-accept-response))))
+
+ ;; Now all replies are received. We remove CRs.
+ (goto-char (point-min))
+ (while (search-forward "\r" nil t)
+ (replace-match "" t t))
+
+ (if (not nntp-server-list-active-group)
+ 'group
+ ;; We have read active entries, so we just delete the
+ ;; superfluos gunk.
+ (goto-char (point-min))
+ (while (re-search-forward "^[.2-5]" nil t)
+ (delete-region (match-beginning 0)
+ (progn (forward-line 1) (point))))
+ (copy-to-buffer nntp-server-buffer (point-min) (point-max))
+ 'active))))
+
+(defun nntp-try-list-active (group)
+ (nntp-list-active-group group)
+ (save-excursion
+ (goto-char (point-min))
+ (cond ((looking-at "5[0-9]+")
+ (setq nntp-server-list-active-group nil))
+ (t
+ (setq nntp-server-list-active-group t)))))
+
+(deffoo nntp-list-active-group (group &optional server)
+ "Return the active info on GROUP (which can be a regexp."
+ (nntp-possibly-change-group group server)
+ (nntp-send-command "^.*\r?\n" "LIST ACTIVE" group))
+
(deffoo nntp-request-article (article &optional group server buffer command)
(nntp-possibly-change-group group server)
(when (nntp-send-command-and-decode
"\r?\n\\.\r?\n" "ARTICLE"
(if (numberp article) (int-to-string article) article))
- (when buffer
+ (when (and buffer
+ (not (equal buffer nntp-server-buffer)))
(save-excursion
(set-buffer nntp-server-buffer)
(copy-to-buffer buffer (point-min) (point-max))
(while (setq process (car (pop nntp-connection-alist)))
(when (memq (process-status process) '(open run))
(set-process-sentinel process nil)
- (set-process-filter process nil)
(nntp-send-string process "QUIT"))
(when (buffer-name (process-buffer process))
(kill-buffer (process-buffer process))))
(deffoo nntp-request-newgroups (date &optional server)
(nntp-possibly-change-group nil server)
- (let* ((date (timezone-parse-date date))
- (time-string
- (format "%s%02d%02d %s%s%s"
- (substring (aref date 0) 2) (string-to-int (aref date 1))
- (string-to-int (aref date 2)) (substring (aref date 3) 0 2)
- (substring
- (aref date 3) 3 5) (substring (aref date 3) 6 8))))
- (prog1
- (nntp-send-command "^\\.\r?\n" "NEWGROUPS" time-string)
- (nntp-decode-text))))
-
-(deffoo nntp-asynchronous-p ()
- t)
+ (save-excursion
+ (set-buffer nntp-server-buffer)
+ (let* ((date (timezone-parse-date date))
+ (time-string
+ (format "%s%02d%02d %s%s%s"
+ (substring (aref date 0) 2) (string-to-int (aref date 1))
+ (string-to-int (aref date 2)) (substring (aref date 3) 0 2)
+ (substring
+ (aref date 3) 3 5) (substring (aref date 3) 6 8))))
+ (prog1
+ (nntp-send-command "^\\.\r?\n" "NEWGROUPS" time-string)
+ (nntp-decode-text)))))
(deffoo nntp-request-post (&optional server)
(nntp-possibly-change-group nil server)
(deffoo nntp-request-type (group article)
'news)
+(deffoo nntp-asynchronous-p ()
+ t)
+
;;; Hooky functions.
(defun nntp-send-mode-reader ()
(nntp-send-command "^.*\r?\n" "AUTHINFO USER"
(read-string "NNTP user name: "))
(nntp-send-command "^.*\r?\n" "AUTHINFO PASS"
- (read-string "NNTP password: ")))
+ (nnmail-read-passwd "NNTP password: ")))
(defun nntp-send-authinfo ()
"Send the AUTHINFO to the nntp server.
(when process
(process-buffer process))))
+(defun nntp-make-process-buffer (buffer)
+ "Create a new, fresh buffer usable for nntp process connections."
+ (save-excursion
+ (set-buffer
+ (generate-new-buffer
+ (format " *server %s %s %s*"
+ nntp-address nntp-port-number
+ (buffer-name (get-buffer buffer)))))
+ (buffer-disable-undo (current-buffer))
+ (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)
+ (set (make-local-variable 'nntp-process-to-buffer) nil)
+ (set (make-local-variable 'nntp-process-start-point) nil)
+ (set (make-local-variable 'nntp-process-decode) nil)
+ (current-buffer)))
+
(defun nntp-open-connection (buffer)
"Open a connection to PORT on ADDRESS delivering output to BUFFER."
(run-hooks 'nntp-prepare-server-hook)
- (let* ((pbuffer (save-excursion
- (set-buffer
- (generate-new-buffer
- (format " *server %s %s %s*"
- nntp-address nntp-port-number
- (buffer-name (get-buffer buffer)))))
- (buffer-disable-undo (current-buffer))
- (current-buffer)))
+ (let* ((pbuffer (nntp-make-process-buffer buffer))
(process
(condition-case ()
(funcall
(eval (cadr entry))
(funcall (cadr entry)))))))
-(defvar nntp-tmp-first)
-(defvar nntp-tmp-wait-for)
-(defvar nntp-tmp-callback)
-(defvar nntp-tmp-buffer)
-
-(defun nntp-make-process-filter (wait-for callback buffer decode)
- `(lambda (proc string)
- (let ((nntp-tmp-wait-for ,wait-for)
- (nntp-tmp-callback ,callback)
- (nntp-tmp-buffer ,buffer))
- (nntp-process-filter proc string))))
-
-(defun nntp-process-filter (proc string)
- "Process filter used for waiting a calling back."
- (let ((old-buffer (current-buffer)))
- (unwind-protect
- (let (point)
- (set-buffer (process-buffer proc))
- ;; Insert the text, moving the process-marker.
- (setq point (goto-char (process-mark proc)))
- (insert string)
- (set-marker (process-mark proc) (point))
- (if (and (= point (point-min))
- (string-match "^45" string))
- (progn
- (nntp-snarf-error-message)
- (set-process-filter proc nil)
- (funcall nntp-tmp-callback nil))
- (setq nntp-tmp-first nil)
- (if (re-search-backward nntp-tmp-wait-for nil t)
- (progn
- (if (buffer-name (get-buffer nntp-tmp-buffer))
- (save-excursion
- (set-buffer (get-buffer nntp-tmp-buffer))
- (goto-char (point-max))
- (insert-buffer-substring (process-buffer proc))))
- (set-process-filter proc nil)
- (erase-buffer)
- (funcall nntp-tmp-callback t)))))
- (set-buffer old-buffer))))
+(defun nntp-after-change-function (beg end len)
+ (when nntp-process-callback
+ (save-match-data
+ (if (and (= beg (point-min))
+ (memq (char-after beg) '(?4 ?5)))
+ ;; Report back error messages.
+ (save-excursion
+ (goto-char beg)
+ (if (looking-at "480")
+ (funcall nntp-authinfo-function)
+ (nntp-snarf-error-message)
+ (funcall nntp-process-callback nil)))
+ (goto-char end)
+ (when (and (> (point) nntp-process-start-point)
+ (re-search-backward nntp-process-wait-for
+ nntp-process-start-point t))
+ (when (buffer-name (get-buffer nntp-process-to-buffer))
+ (let ((cur (current-buffer))
+ (start nntp-process-start-point))
+ (save-excursion
+ (set-buffer (get-buffer nntp-process-to-buffer))
+ (goto-char (point-max))
+ (let ((b (point)))
+ (insert-buffer-substring cur start)
+ (narrow-to-region b (point-max))
+ (nntp-decode-text)
+ (goto-char (point-min))
+ (gnus-delete-line)
+ (widen)))))
+ (goto-char end)
+ (let ((callback nntp-process-callback)
+ (nntp-inside-change-function t))
+ (setq nntp-process-callback nil)
+ (save-excursion
+ (funcall callback t))))))))
(defun nntp-retrieve-data (command address port buffer
&optional wait-for callback decode)
(nntp-open-connection buffer))))
(if (not process)
(nnheader-report 'nntp "Couldn't open connection to %a" address)
- (unless nntp-inhibit-erase
+ (unless (or nntp-inhibit-erase nnheader-callback-function)
(save-excursion
(set-buffer (process-buffer process))
(erase-buffer)))
((eq callback 'ignore)
t)
((and callback wait-for)
- (set-process-filter
- process (nntp-make-process-filter wait-for callback buffer decode))
+ (save-excursion
+ (set-buffer (process-buffer process))
+ (unless nntp-inside-change-function
+ (erase-buffer))
+ (setq nntp-process-decode decode
+ nntp-process-to-buffer buffer
+ nntp-process-wait-for wait-for
+ nntp-process-callback callback
+ nntp-process-start-point (point-max)
+ after-change-functions (list 'nntp-after-change-function)))
t)
(wait-for
- (set-process-filter process nil)
(nntp-wait-for process wait-for buffer decode))
(t t)))))
(save-excursion
(set-buffer (process-buffer process))
(goto-char (point-min))
- (while (not (looking-at "[2345]"))
+ (while (or (not (memq (following-char) '(?2 ?3 ?4 ?5)))
+ (looking-at "480"))
+ (when (looking-at "480")
+ (erase-buffer)
+ (funcall nntp-authinfo-function))
(nntp-accept-process-output process)
(goto-char (point-min)))
(prog1