X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fnntp.el;h=98393a617648f8df2d38ac742e14080f7ee10d23;hb=283e76bd2d5af17da11a4f5da4443962eab7ae53;hp=5dff3a97aae00dfbe2f5c9cada2da3dabe9a1db6;hpb=1c821fd1e79241aecefaf5273adbf6273446f5de;p=gnus diff --git a/lisp/nntp.el b/lisp/nntp.el index 5dff3a97a..98393a617 100644 --- a/lisp/nntp.el +++ b/lisp/nntp.el @@ -1,33 +1,38 @@ ;;; nntp.el --- nntp access for Gnus -;; Copyright (C) 1987, 1988, 1989, 1990, 1992, 1993, -;; 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, -;; 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. +;; Copyright (C) 1987-1990, 1992-1998, 2000-2012 +;; Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; Keywords: news ;; This file is part of GNU Emacs. -;; GNU Emacs 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. +;; GNU Emacs 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 3 of the License, or +;; (at your option) any later version. -;; GNU Emacs 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. +;; GNU Emacs 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, 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: ;;; Code: +;; For Emacs <22.2 and XEmacs. +(eval-and-compile + (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))) + ;; In Emacs 24, `open-protocol-stream' is an autoloaded alias for + ;; `make-network-stream'. + (unless (fboundp 'open-protocol-stream) + (require 'proto-stream))) + (require 'nnheader) (require 'nnoo) (require 'gnus-util) @@ -38,6 +43,8 @@ (eval-when-compile (require 'cl)) +(autoload 'auth-source-search "auth-source") + (defgroup nntp nil "NNTP access for Gnus." :group 'gnus) @@ -72,24 +79,27 @@ to innd, you could say something like: You probably don't want to do that, though.") (defvoo nntp-open-connection-function 'nntp-open-network-stream - "*Function used for connecting to a remote system. -It will be called with the buffer to output in as argument. - -Currently, five such functions are provided (please refer to their -respective doc string for more information), three of them establishing -direct connections to the nntp server, and two of them using an indirect -host. - -Direct connections: -- `nntp-open-network-stream' (the default), -- `nntp-open-ssl-stream', -- `nntp-open-tls-stream', -- `nntp-open-telnet-stream'. - -Indirect connections: -- `nntp-open-via-rlogin-and-telnet', -- `nntp-open-via-rlogin-and-netcat', -- `nntp-open-via-telnet-and-telnet'.") + "Method for connecting to a remote system. +It should be a function, which is called with the output buffer +as its single argument, or one of the following special values: + +- `nntp-open-network-stream' specifies a network connection, + upgrading to a TLS connection via STARTTLS if possible. +- `nntp-open-plain-stream' specifies an unencrypted network + connection (no STARTTLS upgrade is attempted). +- `nntp-open-ssl-stream' or `nntp-open-tls-stream' specify a TLS + network connection. + +Apart from the above special values, valid functions are as +follows; please refer to their respective doc string for more +information. +For direct connections: +- `nntp-open-netcat-stream' +- `nntp-open-telnet-stream' +For indirect connections: +- `nntp-open-via-rlogin-and-netcat' +- `nntp-open-via-rlogin-and-telnet' +- `nntp-open-via-telnet-and-telnet'") (defvoo nntp-never-echoes-commands nil "*Non-nil means the nntp server never echoes commands. @@ -143,12 +153,13 @@ This command is used by the `nntp-open-via-telnet-and-telnet' method.") (defvoo nntp-via-telnet-switches '("-8") "*Switches given to the telnet command `nntp-via-telnet-command'.") -(defvoo nntp-via-netcat-command "nc" +(defvoo nntp-netcat-command "nc" "*Netcat command used to connect to the nntp server. -This command is used by the `nntp-open-via-rlogin-and-netcat' method.") +This command is used by the `nntp-open-netcat-stream' and +`nntp-open-via-rlogin-and-netcat' methods.") -(defvoo nntp-via-netcat-switches nil - "*Switches given to the netcat command `nntp-via-netcat-command'.") +(defvoo nntp-netcat-switches nil + "*Switches given to the netcat command `nntp-netcat-command'.") (defvoo nntp-via-user-name nil "*User name to log in on an intermediate host with. @@ -194,6 +205,14 @@ by one.") If the gap between two consecutive articles is bigger than this variable, split the XOVER request into two requests.") +(defvoo nntp-xref-number-is-evil nil + "*If non-nil, Gnus never trusts article numbers in the Xref header. +Some news servers, e.g., ones running Diablo, run multiple engines +having the same articles but article numbers are not kept synchronized +between them. If you connect to such a server, set this to a non-nil +value, and Gnus never uses article numbers (that appear in the Xref +header and vary by which engine is chosen) to refer to articles.") + (defvoo nntp-prepare-server-hook nil "*Hook run before a server is opened. If can be used to set up a server remotely, for instance. Say you @@ -242,6 +261,8 @@ See `nnml-marks-is-evil' for more information.") (const :format "" "password") (string :format "Password: %v"))))))) +(make-obsolete 'nntp-authinfo-file nil "Emacs 24.1") + (defvoo nntp-connection-timeout nil @@ -253,8 +274,14 @@ NOTE: This variable is never seen to work in Emacs 20 and XEmacs 21.") "*Hook run just before posting an article. It is supposed to be used to insert Cancel-Lock headers.") +(defvoo nntp-server-list-active-group 'try + "If nil, then always use GROUP instead of LIST ACTIVE. +This is usually slower, but on misconfigured servers that don't +update their active files often, this can help.") + ;;; Internal variables. +(defvoo nntp-retrieval-in-progress nil) (defvar nntp-record-commands nil "*If non-nil, nntp will record all commands in the \"*nntp-log*\" buffer.") @@ -282,28 +309,13 @@ to insert Cancel-Lock headers.") (defvoo nntp-inhibit-output nil) (defvoo nntp-server-xover 'try) -(defvoo nntp-server-list-active-group 'try) - -(defvar nntp-async-needs-kluge - (string-match "^GNU Emacs 20\\.3\\." (emacs-version)) - "*When non-nil, nntp will poll asynchronous connections -once a second. By default, this is turned on only for Emacs -20.3, which has a bug that breaks nntp's normal method of -noticing asynchronous data.") (defvar nntp-async-timer nil) (defvar nntp-async-process-list nil) -(defvar nntp-ssl-program - "openssl s_client -quiet -ssl3 -connect %s:%p" -"A string containing commands for SSL connections. -Within a string, %s is replaced with the server address and %p with -port number on server. The program should accept IMAP commands on -stdin and return responses to stdout.") - (defvar nntp-authinfo-rejected nil -"A custom error condition used to report 'Authentication Rejected' errors. -Condition handlers that match just this condition ensure that the nntp +"A custom error condition used to report 'Authentication Rejected' errors. +Condition handlers that match just this condition ensure that the nntp backend doesn't catch this error.") (put 'nntp-authinfo-rejected 'error-conditions '(error nntp-authinfo-rejected)) (put 'nntp-authinfo-rejected 'error-message "Authorization Rejected") @@ -327,13 +339,10 @@ backend doesn't catch this error.") (defun nntp-record-command (string) "Record the command STRING." - (save-excursion - (set-buffer (get-buffer-create "*nntp-log*")) + (with-current-buffer (get-buffer-create "*nntp-log*") (goto-char (point-max)) - (let ((time (current-time))) - (insert (format-time-string "%Y%m%dT%H%M%S" time) - "." (format "%03d" (/ (nth 2 time) 1000)) - " " nntp-address " " string "\n")))) + (insert (format-time-string "%Y%m%dT%H%M%S.%3N") + " " nntp-address " " string "\n"))) (defun nntp-report (&rest args) "Report an error from the nntp backend. The first string in ARGS @@ -356,11 +365,23 @@ be restored and the command retried." (throw 'nntp-with-open-group-error t)) +(defmacro nntp-copy-to-buffer (buffer start end) + "Copy string from unibyte current buffer to multibyte buffer." + (if (featurep 'xemacs) + `(copy-to-buffer ,buffer ,start ,end) + `(let ((string (buffer-substring ,start ,end))) + (with-current-buffer ,buffer + (erase-buffer) + (insert (if enable-multibyte-characters + (mm-string-to-multibyte string) + string)) + (goto-char (point-min)) + nil)))) + (defsubst nntp-wait-for (process wait-for buffer &optional decode discard) "Wait for WAIT-FOR to arrive from PROCESS." - (save-excursion - (set-buffer (process-buffer process)) + (with-current-buffer (process-buffer process) (goto-char (point-min)) (while (and (or (not (memq (char-after (point)) '(?2 ?3 ?4 ?5))) @@ -369,7 +390,8 @@ be restored and the command retried." (cond ((looking-at "480") (nntp-handle-authinfo process)) ((looking-at "482") - (nnheader-report 'nntp (get 'nntp-authinfo-rejected 'error-message)) + (nnheader-report 'nntp "%s" + (get 'nntp-authinfo-rejected 'error-message)) (signal 'nntp-authinfo-rejected nil)) ((looking-at "^.*\n") (delete-region (point) (progn (forward-line 1) (point))))) @@ -398,10 +420,9 @@ be restored and the command retried." (setq nntp-process-response response))) (nntp-decode-text (not decode)) (unless discard - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char (point-max)) - (insert-buffer-substring (process-buffer process)) + (nnheader-insert-buffer-substring (process-buffer process)) ;; Nix out "nntp reading...." message. (when nntp-have-messaged (setq nntp-have-messaged nil) @@ -412,6 +433,9 @@ be restored and the command retried." (defun nntp-kill-buffer (buffer) (when (buffer-name buffer) + (let ((process (get-buffer-process buffer))) + (when process + (delete-process process))) (kill-buffer buffer) (nnheader-init-server-buffer))) @@ -505,8 +529,7 @@ be restored and the command retried." nntp-open-connection-function nntp-open-connection-functions-never-echo-commands)) (nntp-accept-response) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char pos) (if (looking-at (regexp-quote command)) (delete-region pos (progn (forward-line 1) @@ -529,8 +552,7 @@ be restored and the command retried." ;; If nothing to wait for, still remove possibly echo'ed commands (unless wait-for (nntp-accept-response) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char pos) (if (looking-at (regexp-quote command)) (delete-region pos (progn (forward-line 1) @@ -556,8 +578,7 @@ be restored and the command retried." ;; If nothing to wait for, still remove possibly echo'ed commands (unless wait-for (nntp-accept-response) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char pos) (if (looking-at (regexp-quote command)) (delete-region pos (progn (forward-line 1) (point-at-bol)))) @@ -573,10 +594,12 @@ be restored and the command retried." (nntp-erase-buffer (nntp-find-connection-buffer nntp-server-buffer))) (nntp-encode-text) - (mm-with-unibyte-current-buffer - ;; Some encoded unicode text contains character 0x80-0x9f e.g. Euro. - (process-send-region (nntp-find-connection nntp-server-buffer) - (point-min) (point-max))) + ;; Make sure we did not forget to encode some of the content. + (assert (save-excursion (goto-char (point-min)) + (not (re-search-forward "[^\000-\377]" nil t)))) + (mm-disable-multibyte) + (process-send-region (nntp-find-connection nntp-server-buffer) + (point-min) (point-max)) (nntp-retrieve-data nil nntp-address nntp-port-number nntp-server-buffer wait-for nnheader-callback-function)) @@ -614,67 +637,79 @@ be restored and the command retried." (defvar nntp-with-open-group-internal nil) (defvar nntp-report-n nil)) +(defun nntp-with-open-group-function (-group -server -connectionless -bodyfun) + "Protect against servers that don't like clients that keep idle connections opens. +The problem being that these servers may either close a connection or +simply ignore any further requests on a connection. Closed +connections are not detected until `accept-process-output' has updated +the `process-status'. Dropped connections are not detected until the +connection timeouts (which may be several minutes) or +`nntp-connection-timeout' has expired. When these occur +`nntp-with-open-group', opens a new connection then re-issues the NNTP +command whose response triggered the error." + (letf ((nntp-report-n (symbol-function 'nntp-report)) + ((symbol-function 'nntp-report) (symbol-function 'nntp-report-1)) + (nntp-with-open-group-internal nil)) + (while (catch 'nntp-with-open-group-error + ;; Open the connection to the server + ;; NOTE: Existing connections are NOT tested. + (nntp-possibly-change-group -group -server -connectionless) + + (let ((-timer + (and nntp-connection-timeout + (run-at-time + nntp-connection-timeout nil + (lambda () + (let* ((-process (nntp-find-connection + nntp-server-buffer)) + (-buffer (and -process + (process-buffer -process)))) + ;; When I an able to identify the + ;; connection to the server AND I've + ;; received NO response for + ;; nntp-connection-timeout seconds. + (when (and -buffer (eq 0 (buffer-size -buffer))) + ;; Close the connection. Take no + ;; other action as the accept input + ;; code will handle the closed + ;; connection. + (nntp-kill-buffer -buffer)))))))) + (unwind-protect + (setq nntp-with-open-group-internal + (condition-case nil + (funcall -bodyfun) + (quit + (unless debug-on-quit + (nntp-close-server)) + (signal 'quit nil)))) + (when -timer + (nnheader-cancel-timer -timer))) + nil)) + (setf (symbol-function 'nntp-report) nntp-report-n)) + nntp-with-open-group-internal)) + (defmacro nntp-with-open-group (group server &optional connectionless &rest forms) "Protect against servers that don't like clients that keep idle connections opens. The problem being that these servers may either close a connection or simply ignore any further requests on a connection. Closed -connections are not detected until accept-process-output has updated -the process-status. Dropped connections are not detected until the +connections are not detected until `accept-process-output' has updated +the `process-status'. Dropped connections are not detected until the connection timeouts (which may be several minutes) or -nntp-connection-timeout has expired. When these occur -nntp-with-open-group, opens a new connection then re-issues the NNTP +`nntp-connection-timeout' has expired. When these occur +`nntp-with-open-group', opens a new connection then re-issues the NNTP command whose response triggered the error." + (declare (indent 2) (debug (form form [&optional symbolp] def-body))) (when (and (listp connectionless) (not (eq connectionless nil))) (setq forms (cons connectionless forms) connectionless nil)) - `(letf ((nntp-report-n (symbol-function 'nntp-report)) - ((symbol-function 'nntp-report) (symbol-function 'nntp-report-1)) - (nntp-with-open-group-internal nil)) - (while (catch 'nntp-with-open-group-error - ;; Open the connection to the server - ;; NOTE: Existing connections are NOT tested. - (nntp-possibly-change-group ,group ,server ,connectionless) - - (let ((timer - (and nntp-connection-timeout - (run-at-time - nntp-connection-timeout nil - '(lambda () - (let ((process (nntp-find-connection - nntp-server-buffer)) - (buffer (and process - (process-buffer process)))) - ;; When I an able to identify the - ;; connection to the server AND I've - ;; received NO reponse for - ;; nntp-connection-timeout seconds. - (when (and buffer (eq 0 (buffer-size buffer))) - ;; Close the connection. Take no - ;; other action as the accept input - ;; code will handle the closed - ;; connection. - (nntp-kill-buffer buffer)))))))) - (unwind-protect - (setq nntp-with-open-group-internal - (condition-case nil - (progn ,@forms) - (quit - (unless debug-on-quit - (nntp-close-server)) - (signal 'quit nil)))) - (when timer - (nnheader-cancel-timer timer))) - nil)) - (setf (symbol-function 'nntp-report) nntp-report-n)) - nntp-with-open-group-internal)) + `(nntp-with-open-group-function ,group ,server ,connectionless (lambda () ,@forms))) (deffoo nntp-retrieve-headers (articles &optional group server fetch-old) "Retrieve the headers of ARTICLES." (nntp-with-open-group group server - (save-excursion - (set-buffer (nntp-find-connection-buffer nntp-server-buffer)) + (with-current-buffer (nntp-find-connection-buffer nntp-server-buffer) (erase-buffer) (if (and (not gnus-nov-is-evil) (not nntp-nov-is-evil) @@ -730,14 +765,100 @@ command whose response triggered the error." (nnheader-fold-continuation-lines) ;; Remove all "\r"'s. (nnheader-strip-cr) - (copy-to-buffer nntp-server-buffer (point-min) (point-max)) + (nntp-copy-to-buffer nntp-server-buffer (point-min) (point-max)) 'headers))))) +(deffoo nntp-retrieve-group-data-early (server infos) + "Retrieve group info on INFOS." + (nntp-with-open-group nil server + (let ((buffer (nntp-find-connection-buffer nntp-server-buffer))) + (unless infos + (with-current-buffer buffer + (setq nntp-retrieval-in-progress nil))) + (when (and buffer + infos + (with-current-buffer buffer + (not nntp-retrieval-in-progress))) + ;; 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 + (gnus-group-real-name (gnus-info-group (car infos))))) + (with-current-buffer buffer + (erase-buffer) + ;; Mark this buffer as "in use" in case we try to issue two + ;; retrievals from the same server. This shouldn't happen, + ;; so this is mostly a sanity check. + (setq nntp-retrieval-in-progress t) + (let ((nntp-inhibit-erase t) + (command (if nntp-server-list-active-group + "LIST ACTIVE" "GROUP"))) + (dolist (info infos) + (nntp-send-command + nil command (gnus-group-real-name (gnus-info-group info))))) + (length infos)))))) + +(deffoo nntp-finish-retrieve-group-infos (server infos count) + (nntp-with-open-group nil server + (let ((buf (nntp-find-connection-buffer nntp-server-buffer)) + (method (gnus-find-method-for-group + (gnus-info-group (car infos)) + (car infos))) + (received 0) + (last-point 1)) + (with-current-buffer buf + (setq nntp-retrieval-in-progress nil)) + (when (and buf + count) + (with-current-buffer buf + (while (and (gnus-buffer-live-p buf) + (progn + (goto-char last-point) + ;; Count replies. + (while (re-search-forward + (if nntp-server-list-active-group + "^[.]" + "^[0-9]") + nil t) + (incf received)) + (setq last-point (point)) + (< received count))) + (nntp-accept-response)) + ;; We now have all the entries. Remove CRs. + (nnheader-strip-cr) + (if (not nntp-server-list-active-group) + (progn + (nntp-copy-to-buffer nntp-server-buffer + (point-min) (point-max)) + (gnus-groups-to-gnus-format method gnus-active-hashtb t)) + ;; We have read active entries, so we just delete the + ;; superfluous gunk. + (goto-char (point-min)) + (while (re-search-forward "^[.2-5]" nil t) + (delete-region (match-beginning 0) + (progn (forward-line 1) (point)))) + (nntp-copy-to-buffer nntp-server-buffer (point-min) (point-max)) + (with-current-buffer nntp-server-buffer + (gnus-active-to-gnus-format + ;; Kludge to use the extended method name if you have + ;; an extended one. + (if (consp (gnus-info-method (car infos))) + (gnus-info-method (car infos)) + method) + gnus-active-hashtb nil t)))))))) + (deffoo nntp-retrieve-groups (groups &optional server) "Retrieve group info on GROUPS." (nntp-with-open-group nil server - (when (nntp-find-connection-buffer nntp-server-buffer) + (when (and (nntp-find-connection-buffer nntp-server-buffer) + (with-current-buffer + (nntp-find-connection-buffer nntp-server-buffer) + (if (not nntp-retrieval-in-progress) + t + (message "Warning: Refusing to do retrieval from %s because a retrieval is already happening" + server) + nil))) (catch 'done (save-excursion ;; Erase nntp-server-buffer before nntp-inhibit-erase. @@ -812,7 +933,8 @@ command whose response triggered the error." (if (not nntp-server-list-active-group) (progn - (copy-to-buffer nntp-server-buffer (point-min) (point-max)) + (nntp-copy-to-buffer nntp-server-buffer + (point-min) (point-max)) 'group) ;; We have read active entries, so we just delete the ;; superfluous gunk. @@ -820,7 +942,7 @@ command whose response triggered the error." (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)) + (nntp-copy-to-buffer nntp-server-buffer (point-min) (point-max)) 'active))))))) (deffoo nntp-retrieve-articles (articles &optional group server) @@ -885,7 +1007,7 @@ command whose response triggered the error." (narrow-to-region (setq point (goto-char (point-max))) (progn - (insert-buffer-substring buf last-point (cdr entry)) + (nnheader-insert-buffer-substring buf last-point (cdr entry)) (point-max))) (setq last-point (cdr entry)) (nntp-decode-text) @@ -895,8 +1017,7 @@ command whose response triggered the error." (defun nntp-try-list-active (group) (nntp-list-active-group group) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (cond ((or (eobp) (looking-at "5[0-9]+")) @@ -918,17 +1039,15 @@ command whose response triggered the error." (deffoo nntp-request-article (article &optional group server buffer command) (nntp-with-open-group - group server + group server (when (nntp-send-command-and-decode "\r?\n\\.\r?\n" "ARTICLE" (if (numberp article) (int-to-string article) article)) - (if (and buffer - (not (equal buffer nntp-server-buffer))) - (save-excursion - (set-buffer nntp-server-buffer) - (copy-to-buffer buffer (point-min) (point-max)) - (nntp-find-group-and-number group)) - (nntp-find-group-and-number group))))) + (when (and buffer + (not (equal buffer nntp-server-buffer))) + (with-current-buffer nntp-server-buffer + (copy-to-buffer buffer (point-min) (point-max)))) + (nntp-find-group-and-number group)))) (deffoo nntp-request-head (article &optional group server) (nntp-with-open-group @@ -947,7 +1066,7 @@ command whose response triggered the error." "\r?\n\\.\r?\n" "BODY" (if (numberp article) (int-to-string article) article)))) -(deffoo nntp-request-group (group &optional server dont-check) +(deffoo nntp-request-group (group &optional server dont-check info) (nntp-with-open-group nil server (when (nntp-send-command "^[245].*\n" "GROUP" group) @@ -974,7 +1093,8 @@ command whose response triggered the error." (unless (assq 'nntp-address defs) (setq defs (append defs (list (list 'nntp-address server))))) (nnoo-change-server 'nntp server defs) - (unless connectionless + (if connectionless + t (or (nntp-find-connection nntp-server-buffer) (nntp-open-connection nntp-server-buffer))))) @@ -1022,8 +1142,7 @@ command whose response triggered the error." (deffoo nntp-request-newgroups (date &optional server) (nntp-with-open-group nil server - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (let* ((time (date-to-time date)) (ls (- (cadr time) (nth 8 (decode-time time))))) (cond ((< ls 0) @@ -1070,27 +1189,17 @@ command whose response triggered the error." t) (deffoo nntp-request-set-mark (group actions &optional server) - (unless nntp-marks-is-evil + (when (and (not nntp-marks-is-evil) + nntp-marks-file-name) (nntp-possibly-create-directory group server) (nntp-open-marks group server) - (dolist (action actions) - (let ((range (nth 0 action)) - (what (nth 1 action)) - (marks (nth 2 action))) - (assert (or (eq what 'add) (eq what 'del)) nil - "Unknown request-set-mark action: %s" what) - (dolist (mark marks) - (setq nntp-marks (gnus-update-alist-soft - mark - (funcall (if (eq what 'add) 'gnus-range-add - 'gnus-remove-from-range) - (cdr (assoc mark nntp-marks)) range) - nntp-marks))))) + (setq nntp-marks (nnheader-update-marks-actions nntp-marks actions)) (nntp-save-marks group server)) nil) -(deffoo nntp-request-update-info (group info &optional server) - (unless nntp-marks-is-evil +(deffoo nntp-request-marks (group info &optional server) + (when (and (not nntp-marks-is-evil) + nntp-marks-file-name) (nntp-possibly-create-directory group server) (when (nntp-marks-changed-p group server) (nnheader-message 8 "Updating marks for %s..." group) @@ -1126,6 +1235,11 @@ It will make innd servers spawn an nnrpd process to allow actual article reading." (nntp-send-command "^.*\n" "MODE READER")) +(declare-function netrc-parse "netrc" (&optional file)) +(declare-function netrc-machine "netrc" + (list machine &optional port defaultport)) +(declare-function netrc-get "netrc" (alist type)) + (defun nntp-send-authinfo (&optional send-if-force) "Send the AUTHINFO to the nntp server. It will look in the \"~/.authinfo\" file for matching entries. If @@ -1134,11 +1248,32 @@ and a password. If SEND-IF-FORCE, only send authinfo to the server if the .authinfo file has the FORCE token." + (require 'netrc) (let* ((list (netrc-parse nntp-authinfo-file)) (alist (netrc-machine list nntp-address "nntp")) - (force (or (netrc-get alist "force") nntp-authinfo-force)) - (user (or (netrc-get alist "login") nntp-authinfo-user)) - (passwd (netrc-get alist "password"))) + (auth-info + (nth 0 (auth-source-search :max 1 + :host (list nntp-address + (nnoo-current-server 'nntp)) + :port '("119" "nntp")))) + (auth-user (plist-get auth-info :user)) + (auth-force (plist-get auth-info :force)) + (auth-passwd (plist-get auth-info :secret)) + (auth-passwd (if (functionp auth-passwd) + (funcall auth-passwd) + auth-passwd)) + (force (or (netrc-get alist "force") + nntp-authinfo-force + auth-force)) + (user (or + ;; this is preferred to netrc-* + auth-user + (netrc-get alist "login") + nntp-authinfo-user)) + (passwd (or + ;; this is preferred to netrc-* + auth-passwd + (netrc-get alist "password")))) (when (or (not send-if-force) force) (unless user @@ -1192,19 +1327,19 @@ password contained in '~/.nntp-authinfo'." (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 - (gnus-buffer-exists-p buffer)))) - (mm-enable-multibyte) + (with-current-buffer + (generate-new-buffer + (format " *server %s %s %s*" + nntp-address nntp-port-number + (gnus-buffer-exists-p buffer))) + (mm-disable-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) (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) + (set (make-local-variable 'nntp-retrieval-in-progress) nil) (current-buffer))) (defun nntp-open-connection (buffer) @@ -1218,11 +1353,29 @@ password contained in '~/.nntp-authinfo'." `(lambda () (nntp-kill-buffer ,pbuffer))))) (process - (condition-case () + (condition-case err (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)) - (error nil) + (coding-system-for-write nntp-coding-system-for-write) + (map '((nntp-open-network-stream network) + (network-only plain) ; compat + (nntp-open-plain-stream plain) + (nntp-open-ssl-stream tls) + (nntp-open-tls-stream tls)))) + (if (assoc nntp-open-connection-function map) + (open-protocol-stream + "nntpd" pbuffer nntp-address nntp-port-number + :type (cadr (assoc nntp-open-connection-function map)) + :end-of-command "^\\([2345]\\|[.]\\).*\n" + :capability-command "CAPABILITIES\r\n" + :success "^3" + :starttls-function + (lambda (capabilities) + (if (not (string-match "STARTTLS" capabilities)) + nil + "STARTTLS\r\n"))) + (funcall nntp-open-connection-function pbuffer))) + (error + (nnheader-report 'nntp ">>> %s" err)) (quit (message "Quit opening connection to %s" nntp-address) (nntp-kill-buffer pbuffer) @@ -1230,18 +1383,30 @@ password contained in '~/.nntp-authinfo'." nil)))) (when timer (nnheader-cancel-timer timer)) + (when (and process + (not (memq (process-status process) '(open run)))) + (with-current-buffer pbuffer + (goto-char (point-min)) + (nnheader-report 'nntp "Error when connecting: %s" + (buffer-substring (point) (line-end-position)))) + (setq process nil)) (unless process (nntp-kill-buffer pbuffer)) (when (and (buffer-name pbuffer) process) + (when (and (fboundp 'set-network-process-option) ;; Unavailable in XEmacs. + (fboundp 'process-type) ;; Emacs 22 doesn't provide it. + (eq (process-type process) 'network)) + ;; Use TCP-keepalive so that connections that pass through a NAT router + ;; don't hang when left idle. + (set-network-process-option process :keepalive t)) (gnus-set-process-query-on-exit-flag process nil) (if (and (nntp-wait-for process "^2.*\n" buffer nil t) (memq (process-status process) '(open run))) (prog1 (caar (push (list process buffer nil) nntp-connection-alist)) (push process nntp-connection-list) - (save-excursion - (set-buffer pbuffer) + (with-current-buffer pbuffer (nntp-read-server-type) (erase-buffer) (set-buffer nntp-server-buffer) @@ -1251,43 +1416,6 @@ password contained in '~/.nntp-authinfo'." (nntp-kill-buffer (process-buffer process)) nil)))) -(defun nntp-open-network-stream (buffer) - (open-network-stream "nntpd" buffer nntp-address nntp-port-number)) - -(eval-and-compile - (autoload 'format-spec "format-spec") - (autoload 'format-spec-make "format-spec") - (autoload 'open-tls-stream "tls")) - -(defun nntp-open-ssl-stream (buffer) - (let* ((process-connection-type nil) - (proc (start-process "nntpd" buffer - shell-file-name - shell-command-switch - (format-spec nntp-ssl-program - (format-spec-make - ?s nntp-address - ?p nntp-port-number))))) - (gnus-set-process-query-on-exit-flag proc nil) - (save-excursion - (set-buffer buffer) - (let ((nntp-connection-alist (list proc buffer nil))) - (nntp-wait-for-string "^\r*20[01]")) - (beginning-of-line) - (delete-region (point-min) (point)) - proc))) - -(defun nntp-open-tls-stream (buffer) - (let ((proc (open-tls-stream "nntpd" buffer nntp-address nntp-port-number))) - (gnus-set-process-query-on-exit-flag proc nil) - (save-excursion - (set-buffer buffer) - (let ((nntp-connection-alist (list proc buffer nil))) - (nntp-wait-for-string "^\r*20[01]")) - (beginning-of-line) - (delete-region (point-min) (point)) - proc))) - (defun nntp-read-server-type () "Find out what the name of the server we have connected to is." ;; Wait for the status string to arrive. @@ -1302,8 +1430,7 @@ password contained in '~/.nntp-authinfo'." (funcall (cadr entry))))))) (defun nntp-async-wait (process wait-for buffer decode callback) - (save-excursion - (set-buffer (process-buffer process)) + (with-current-buffer (process-buffer process) (unless nntp-inside-change-function (erase-buffer)) (setq nntp-process-wait-for wait-for @@ -1311,17 +1438,7 @@ password contained in '~/.nntp-authinfo'." nntp-process-decode decode nntp-process-callback callback nntp-process-start-point (point-max)) - (setq after-change-functions '(nntp-after-change-function)) - (if nntp-async-needs-kluge - (nntp-async-kluge process)))) - -(defun nntp-async-kluge (process) - ;; emacs 20.3 bug: process output with encoding 'binary - ;; doesn't trigger after-change-functions. - (unless nntp-async-timer - (setq nntp-async-timer - (run-at-time 1 1 'nntp-async-timer-handler))) - (add-to-list 'nntp-async-process-list process)) + (setq after-change-functions '(nntp-after-change-function)))) (defun nntp-async-timer-handler () (mapcar @@ -1351,8 +1468,7 @@ password contained in '~/.nntp-authinfo'." (setq after-change-functions '(nntp-after-change-function))))) (defun nntp-async-trigger (process) - (save-excursion - (set-buffer (process-buffer process)) + (with-current-buffer (process-buffer process) (when nntp-process-callback ;; do we have an error message? (goto-char nntp-process-start-point) @@ -1377,12 +1493,11 @@ password contained in '~/.nntp-authinfo'." (let ((buf (current-buffer)) (start nntp-process-start-point) (decode nntp-process-decode)) - (save-excursion - (set-buffer nntp-process-to-buffer) + (with-current-buffer nntp-process-to-buffer (goto-char (point-max)) (save-restriction (narrow-to-region (point) (point)) - (insert-buffer-substring buf start) + (nnheader-insert-buffer-substring buf start) (when decode (nntp-decode-text)))))) ;; report it. @@ -1401,7 +1516,7 @@ password contained in '~/.nntp-authinfo'." (let ((message (buffer-string))) (while (string-match "[\r\n]+" message) (setq message (replace-match " " t t message))) - (nnheader-report 'nntp message) + (nnheader-report 'nntp "%s" message) message)) (defun nntp-accept-process-output (process) @@ -1442,8 +1557,7 @@ password contained in '~/.nntp-authinfo'." (cond ((not entry) (nntp-report "Server closed connection")) ((not (equal group (caddr entry))) - (save-excursion - (set-buffer (process-buffer (car entry))) + (with-current-buffer (process-buffer (car entry)) (erase-buffer) (nntp-send-command "^[245].*\n" "GROUP" group) (setcar (cddr entry) group) @@ -1588,12 +1702,12 @@ password contained in '~/.nntp-authinfo'." ;; Some nntp servers seem to have an extension to the XOVER ;; extension. On these servers, requesting an article range - ;; preceeding the active range does not return an error as + ;; preceding the active range does not return an error as ;; specified in the RFC. What we instead get is the NOV entry ;; for the first available article. Obviously, a client can ;; use that entry to avoid making unnecessary requests. The ;; only problem is for a client that assumes that the response - ;; will always be within the requested ranage. For such a + ;; will always be within the requested range. For such a ;; client, we can get N copies of the same entry (one for each ;; XOVER command sent to the server). @@ -1611,7 +1725,7 @@ password contained in '~/.nntp-authinfo'." (when in-process-buffer-p (set-buffer buf) (goto-char (point-max)) - (insert-buffer-substring process-buffer) + (nnheader-insert-buffer-substring process-buffer) (set-buffer process-buffer) (erase-buffer) (set-buffer buf)) @@ -1643,8 +1757,7 @@ password contained in '~/.nntp-authinfo'." ;; We try them all until we get at positive response. (while (and commands (eq nntp-server-xover 'try)) (nntp-send-command-nodelete "\r?\n\\.\r?\n" (car commands) range) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (and (looking-at "[23]") ; No error message. ;; We also have to look at the lines. Some buggy @@ -1665,6 +1778,7 @@ password contained in '~/.nntp-authinfo'." (defun nntp-find-group-and-number (&optional group) (save-excursion (save-restriction + ;; FIXME: This is REALLY FISHY: set-buffer after save-restriction?!? (set-buffer nntp-server-buffer) (narrow-to-region (goto-char (point-min)) (or (search-forward "\n\n" nil t) (point-max))) @@ -1702,7 +1816,8 @@ password contained in '~/.nntp-authinfo'." (match-string 1 xref)) (t ""))) (cond - ((and (setq xref (mail-fetch-field "xref")) + ((and (not nntp-xref-number-is-evil) + (setq xref (mail-fetch-field "xref")) (string-match (if group (concat "\\(" (regexp-quote group) "\\):\\([0-9]+\\)") @@ -1728,7 +1843,7 @@ password contained in '~/.nntp-authinfo'." (while (and (setq proc (get-buffer-process buf)) (memq (process-status proc) '(open run)) (not (re-search-forward regexp nil t))) - (accept-process-output proc) + (accept-process-output proc 0.1) (set-buffer buf) (goto-char (point-min))))) @@ -1768,9 +1883,21 @@ via telnet.") (defvoo nntp-telnet-passwd nil "Password to use to log in via telnet with.") +(defun nntp-service-to-port (svc) + (cond + ((integerp svc) (number-to-string svc)) + ((string-match "\\`[0-9]+\\'" svc) svc) + (t + (with-temp-buffer + (ignore-errors (insert-file-contents "/etc/services")) + (goto-char (point-min)) + (if (re-search-forward (concat "^" (regexp-quote svc) + "[ \t]+\\([0-9]+\\)/tcp")) + (match-string 1) + svc))))) + (defun nntp-open-telnet (buffer) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (erase-buffer) (let ((proc (apply 'start-process @@ -1826,8 +1953,7 @@ via telnet.") (apply 'start-process "nntpd" buffer nntp-rlogin-program nntp-address nntp-rlogin-parameters)))) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (nntp-wait-for-string "^\r*20[01]") (beginning-of-line) (delete-region (point-min) (point)) @@ -1840,6 +1966,8 @@ via telnet.") (defun nntp-open-telnet-stream (buffer) "Open a nntp connection by telnet'ing the news server. +`nntp-open-netcat-stream' is recommended in place of this function +because it is more reliable. Please refer to the following variables to customize the connection: - `nntp-pre-command', @@ -1850,13 +1978,13 @@ Please refer to the following variables to customize the connection: - `nntp-end-of-line'." (let ((command `(,nntp-telnet-command ,@nntp-telnet-switches - ,nntp-address ,nntp-port-number)) + ,nntp-address + ,(nntp-service-to-port nntp-port-number))) proc) (and nntp-pre-command (push nntp-pre-command command)) (setq proc (apply 'start-process "nntpd" buffer command)) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (nntp-wait-for-string "^\r*20[01]") (beginning-of-line) (delete-region (point-min) (point)) @@ -1866,6 +1994,8 @@ Please refer to the following variables to customize the connection: "Open a connection to an nntp server through an intermediate host. First rlogin to the remote host, and then telnet the real news server from there. +`nntp-open-via-rlogin-and-netcat' is recommended in place of this function +because it is more reliable. Please refer to the following variables to customize the connection: - `nntp-pre-command', @@ -1890,11 +2020,11 @@ Please refer to the following variables to customize the connection: (and nntp-pre-command (push nntp-pre-command command)) (setq proc (apply 'start-process "nntpd" buffer command)) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (nntp-wait-for-string "^r?telnet") - (process-send-string proc (concat "open " nntp-address - " " nntp-port-number "\n")) + (process-send-string proc (concat "open " nntp-address " " + (nntp-service-to-port nntp-port-number) + "\n")) (nntp-wait-for-string "^\r*20[01]") (beginning-of-line) (delete-region (point-min) (point)) @@ -1919,24 +2049,46 @@ Please refer to the following variables to customize the connection: - `nntp-via-rlogin-command-switches', - `nntp-via-user-name', - `nntp-via-address', -- `nntp-via-netcat-command', -- `nntp-via-netcat-switches', +- `nntp-netcat-command', +- `nntp-netcat-switches', - `nntp-address', -- `nntp-port-number', -- `nntp-end-of-line'." +- `nntp-port-number'." (let ((command `(,@(when nntp-pre-command (list nntp-pre-command)) ,nntp-via-rlogin-command - ,@(when nntp-via-rlogin-command-switches - nntp-via-rlogin-command-switches) + ,@nntp-via-rlogin-command-switches ,@(when nntp-via-user-name (list "-l" nntp-via-user-name)) ,nntp-via-address - ,nntp-via-netcat-command - ,@nntp-via-netcat-switches + ,nntp-netcat-command + ,@nntp-netcat-switches ,nntp-address - ,nntp-port-number))) - (apply 'start-process "nntpd" buffer command))) + ,(nntp-service-to-port nntp-port-number)))) + ;; A non-nil connection type results in mightily odd behavior where + ;; (process-send-string proc "\^M") ends up sending a "\n" to the + ;; ssh process. --Stef + ;; Also a nil connection allow ssh-askpass to work under X11. + (let ((process-connection-type nil)) + (apply 'start-process "nntpd" buffer command)))) + +(defun nntp-open-netcat-stream (buffer) + "Open a connection to an nntp server through netcat. +I.e. use the `nc' command rather than Emacs's builtin networking code. + +Please refer to the following variables to customize the connection: +- `nntp-pre-command', +- `nntp-netcat-command', +- `nntp-netcat-switches', +- `nntp-address', +- `nntp-port-number'." + (let ((command `(,nntp-netcat-command + ,@nntp-netcat-switches + ,nntp-address + ,(nntp-service-to-port nntp-port-number)))) + (and nntp-pre-command (push nntp-pre-command command)) + (let ((process-connection-type nil)) ;See `nntp-open-via-rlogin-and-netcat'. + (apply 'start-process "nntpd" buffer command)))) + (defun nntp-open-via-telnet-and-telnet (buffer) "Open a connection to an nntp server through an intermediate host. @@ -1957,8 +2109,7 @@ Please refer to the following variables to customize the connection: - `nntp-address', - `nntp-port-number', - `nntp-end-of-line'." - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (erase-buffer) (let ((command `(,nntp-via-telnet-command ,@nntp-via-telnet-switches)) (case-fold-search t) @@ -1995,7 +2146,7 @@ Please refer to the following variables to customize the connection: ,nntp-telnet-command ,@nntp-telnet-switches ,nntp-address - ,nntp-port-number))) + ,(nntp-service-to-port nntp-port-number)))) (process-send-string proc (concat (mapconcat 'identity real-telnet-command " ") @@ -2041,8 +2192,7 @@ Please refer to the following variables to customize the connection: (make-directory (directory-file-name dir) t) (nnheader-message 5 "Creating nntp marks directory %s" dir)))) -(eval-and-compile - (autoload 'time-less-p "time-date")) +(autoload 'time-less-p "time-date") (defun nntp-marks-changed-p (group server) (let ((file (nntp-group-pathname server group nntp-marks-file-name)) @@ -2105,5 +2255,4 @@ Please refer to the following variables to customize the connection: (provide 'nntp) -;;; arch-tag: 8655466a-b1b5-4929-9c45-7b1b2e767271 ;;; nntp.el ends here