fb329d86f8ab43adc893a2ca18890ce5674f025e
[elisp/gnus.git-] / lisp / nntp.el
1 ;;; nntp.el --- nntp access for Gnus
2
3 ;; Copyright (C) 1987, 1988, 1989, 1990, 1992, 1993, 1994, 1995, 1996,
4 ;; 1997, 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;;         Katsumi Yamaoka <yamaoka@jpl.org>
8 ;; Keywords: news
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published
14 ;; by the Free Software Foundation; either version 2, or (at your
15 ;; option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ;; General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 ;;; Commentary:
27
28 ;;; Code:
29
30 (eval-when-compile (require 'cl))
31 (eval-when-compile (require 'gnus-clfns))
32
33 (require 'nnheader)
34 (require 'nnoo)
35 (require 'gnus-util)
36
37 (nnoo-declare nntp)
38
39 (defvoo nntp-address nil
40   "Address of the physical nntp server.")
41
42 (defvoo nntp-port-number "nntp"
43   "Port number on the physical nntp server.")
44
45 (defvoo nntp-list-options nil
46   "List of newsgroup name used for a option of the LIST command to
47 restrict the listing output to only the specified newsgroups.
48 Each newsgroup name can be a shell-style wildcard, for instance,
49 \"fj.*\", \"japan.*\", etc.  Fortunately, if the server can accept
50 such a option, it will probably make gnus run faster.  You may
51 use it as a server variable as follows:
52
53 \(setq gnus-select-method
54       '(nntp \"news.somewhere.edu\"
55              (nntp-list-options (\"fj.*\" \"japan.*\"))))")
56
57 (defvoo nntp-options-subscribe nil
58   "Regexp matching the newsgroup names which will be subscribed
59 unconditionally.  It may be effective as well as `nntp-list-options'
60 even though the server could not accept a shell-style wildcard as a
61 option of the LIST command.  You may use it as a server variable as
62 follows:
63
64 \(setq gnus-select-method
65       '(nntp \"news.somewhere.edu\"
66              (nntp-options-subscribe \"^fj\\\\.\\\\|^japan\\\\.\")))")
67
68 (defvoo nntp-options-not-subscribe nil
69   "Regexp matching the newsgroup names which will not be subscribed
70 unconditionally.  It may be effective as well as `nntp-list-options'
71 even though the server could not accept a shell-style wildcard as a
72 option of the LIST command.  You may use it as a server variable as
73 follows:
74
75 \(setq gnus-select-method
76       '(nntp \"news.somewhere.edu\"
77              (nntp-options-not-subscribe \"\\\\.binaries\\\\.\")))")
78
79 (defvoo nntp-server-opened-hook '(nntp-send-mode-reader)
80   "*Hook used for sending commands to the server at startup.
81 The default value is `nntp-send-mode-reader', which makes an innd
82 server spawn an nnrpd server.")
83
84 (defvoo nntp-authinfo-function 'nntp-send-authinfo
85   "Function used to send AUTHINFO to the server.
86 It is called with no parameters.")
87
88 (defvoo nntp-server-action-alist
89     '(("nntpd 1\\.5\\.11t"
90        (remove-hook 'nntp-server-opened-hook 'nntp-send-mode-reader))
91       ("NNRP server Netscape"
92        (setq nntp-server-list-active-group nil)))
93   "Alist of regexps to match on server types and actions to be taken.
94 For instance, if you want Gnus to beep every time you connect
95 to innd, you could say something like:
96
97 \(setq nntp-server-action-alist
98        '((\"innd\" (ding))))
99
100 You probably don't want to do that, though.")
101
102 (defvoo nntp-open-connection-function 'nntp-open-network-stream
103   "*Function used for connecting to a remote system.
104 It will be called with the buffer to output in as argument.
105
106 Currently, five such functions are provided (please refer to their
107 respective doc string for more information), three of them establishing
108 direct connections to the nntp server, and two of them using an indirect
109 host.
110
111 Direct connections:
112 - `nntp-open-network-stream' (the default),
113 - `nntp-open-ssl-stream',
114 - `nntp-open-telnet-stream'.
115
116 Indirect connections:
117 - `nntp-open-via-rlogin-and-telnet',
118 - `nntp-open-via-telnet-and-telnet'.")
119
120 (defvoo nntp-pre-command nil
121   "*Pre-command to use with the various nntp-open-via-* methods.
122 This is where you would put \"runsocks\" or stuff like that.")
123
124 (defvoo nntp-telnet-command "telnet"
125   "*Telnet command used to connect to the nntp server.
126 This command is used by the various nntp-open-via-* methods.")
127
128 (defvoo nntp-telnet-switches '("-8")
129   "*Switches given to the telnet command `nntp-telnet-command'.")
130
131 (defvoo nntp-end-of-line "\r\n"
132   "*String to use on the end of lines when talking to the NNTP server.
133 This is \"\\r\\n\" by default, but should be \"\\n\" when
134 using and indirect connection method (nntp-open-via-*).")
135
136 (defvoo nntp-via-rlogin-command "rsh"
137   "*Rlogin command used to connect to an intermediate host.
138 This command is used by the `nntp-open-via-rlogin-and-telnet' method.
139 The default is \"rsh\", but \"ssh\" is a popular alternative.")
140
141 (defvoo nntp-via-rlogin-command-switches nil
142   "*Switches given to the rlogin command `nntp-via-rlogin-command'.
143 If you use \"ssh\" for `nntp-via-rlogin-command', you may set this to
144 \(\"-C\") in order to compress all data connections, otherwise set this
145 to \(\"-t\") or (\"-C\" \"-t\") if the telnet command requires a pseudo-tty
146 allocation on an intermediate host.")
147
148 (defvoo nntp-via-telnet-command "telnet"
149   "*Telnet command used to connect to an intermediate host.
150 This command is used by the `nntp-open-via-telnet-and-telnet' method.")
151
152 (defvoo nntp-via-telnet-switches '("-8")
153   "*Switches given to the telnet command `nntp-via-telnet-command'.")
154
155 (defvoo nntp-via-user-name nil
156   "*User name to log in on an intermediate host with.
157 This variable is used by the `nntp-open-via-telnet-and-telnet' method.")
158
159 (defvoo nntp-via-user-password nil
160   "*Password to use to log in on an intermediate host with.
161 This variable is used by the `nntp-open-via-telnet-and-telnet' method.")
162
163 (defvoo nntp-via-address nil
164   "*Address of an intermediate host to connect to.
165 This variable is used by the `nntp-open-via-rlogin-and-telnet' and
166 `nntp-open-via-telnet-and-telnet' methods.")
167
168 (defvoo nntp-via-envuser nil
169   "*Whether both telnet client and server support the ENVIRON option.
170 If non-nil, there will be no prompt for a login name.")
171
172 (defvoo nntp-via-shell-prompt "bash\\|\$ *\r?$\\|> *\r?"
173   "*Regular expression to match the shell prompt on an intermediate host.
174 This variable is used by the `nntp-open-via-telnet-and-telnet' method.")
175
176 (defvoo nntp-large-newsgroup 50
177   "*The number of the articles which indicates a large newsgroup.
178 If the number of the articles is greater than the value, verbose
179 messages will be shown to indicate the current status.")
180
181 (defvoo nntp-maximum-request 400
182   "*The maximum number of the requests sent to the NNTP server at one time.
183 If Emacs hangs up while retrieving headers, set the variable to a
184 lower value.")
185
186 (defvoo nntp-nov-is-evil nil
187   "*If non-nil, nntp will never attempt to use XOVER when talking to the server.")
188
189 (defvoo nntp-xover-commands '("XOVER" "XOVERVIEW")
190   "*List of strings that are used as commands to fetch NOV lines from a server.
191 The strings are tried in turn until a positive response is gotten.  If
192 none of the commands are successful, nntp will just grab headers one
193 by one.")
194
195 (defvoo nntp-nov-gap 5
196   "*Maximum allowed gap between two articles.
197 If the gap between two consecutive articles is bigger than this
198 variable, split the XOVER request into two requests.")
199
200 (defvoo nntp-prepare-server-hook nil
201   "*Hook run before a server is opened.
202 If can be used to set up a server remotely, for instance.  Say you
203 have an account at the machine \"other.machine\".  This machine has
204 access to an NNTP server that you can't access locally.  You could
205 then use this hook to rsh to the remote machine and start a proxy NNTP
206 server there that you can connect to.  See also
207 `nntp-open-connection-function'")
208
209 (defvoo nntp-warn-about-losing-connection t
210   "*If non-nil, beep when a server closes connection.")
211
212 (defcustom nntp-authinfo-file "~/.authinfo"
213   ".netrc-like file that holds nntp authinfo passwords."
214   :type
215   '(choice file
216            (repeat :tag "Entries"
217                    :menu-tag "Inline"
218                    (list :format "%v"
219                          :value ("" ("login" . "") ("password" . ""))
220                          (string :tag "Host")
221                          (checklist :inline t
222                                     (cons :format "%v"
223                                           (const :format "" "login")
224                                           (string :format "Login: %v"))
225                                     (cons :format "%v"
226                                           (const :format "" "password")
227                                           (string :format "Password: %v")))))))
228
229 \f
230
231 (defvoo nntp-connection-timeout nil
232   "*Number of seconds to wait before an nntp connection times out.
233 If this variable is nil, which is the default, no timers are set.
234 NOTE: This variable is never seen to work in Emacs 20 and XEmacs 21.")
235
236 (defvoo nntp-prepare-post-hook nil
237   "*Hook run just before posting an article.  It is supposed to be used
238 to insert Cancel-Lock headers.")
239
240 ;;; Internal variables.
241
242 (defvar nntp-record-commands nil
243   "*If non-nil, nntp will record all commands in the \"*nntp-log*\" buffer.")
244
245 (defvar nntp-have-messaged nil)
246
247 (defvar nntp-process-wait-for nil)
248 (defvar nntp-process-to-buffer nil)
249 (defvar nntp-process-callback nil)
250 (defvar nntp-process-decode nil)
251 (defvar nntp-process-start-point nil)
252 (defvar nntp-inside-change-function nil)
253 (defvoo nntp-last-command-time nil)
254 (defvoo nntp-last-command nil)
255 (defvoo nntp-authinfo-password nil)
256 (defvoo nntp-authinfo-user nil)
257
258 (defvar nntp-connection-list nil)
259
260 (defvoo nntp-server-type nil)
261 (defvoo nntp-connection-alist nil)
262 (defvoo nntp-status-string "")
263 (defconst nntp-version "nntp 5.0")
264 (defvoo nntp-inhibit-erase nil)
265 (defvoo nntp-inhibit-output nil)
266
267 (defvoo nntp-server-xover 'try)
268 (defvoo nntp-server-list-active-group 'try)
269
270 (defvar nntp-async-needs-kluge
271   (string-match "^GNU Emacs 20\\.3\\." (emacs-version))
272   "*When non-nil, nntp will poll asynchronous connections
273 once a second.  By default, this is turned on only for Emacs
274 20.3, which has a bug that breaks nntp's normal method of
275 noticing asynchronous data.")
276
277 (defvar nntp-async-timer nil)
278 (defvar nntp-async-process-list nil)
279
280 (eval-and-compile
281   (autoload 'mail-source-read-passwd "mail-source")
282   (autoload 'open-ssl-stream "ssl"))
283
284 \f
285
286 ;;; Internal functions.
287
288 (defsubst nntp-send-string (process string)
289   "Send STRING to PROCESS."
290   ;; We need to store the time to provide timeouts, and
291   ;; to store the command so the we can replay the command
292   ;; if the server gives us an AUTHINFO challenge.
293   (setq nntp-last-command-time (current-time)
294         nntp-last-command string)
295   (when nntp-record-commands
296     (nntp-record-command string))
297   (process-send-string process (concat string nntp-end-of-line))
298   (or (memq (process-status process) '(open run))
299       (nntp-report "Server closed connection")))
300
301 (defun nntp-record-command (string)
302   "Record the command STRING."
303   (save-excursion
304     (set-buffer (get-buffer-create "*nntp-log*"))
305     (goto-char (point-max))
306     (let ((time (current-time)))
307       (insert (format-time-string "%Y%m%dT%H%M%S" time)
308               "." (format "%03d" (/ (nth 2 time) 1000))
309               " " nntp-address " " string "\n"))))
310
311 (defun nntp-report (&rest args)
312   "Report an error from the nntp backend.  The first string in ARGS
313 can be a format string.  For some commands, the failed command may be
314 retried once before actually displaying the error report."
315
316   (when nntp-record-commands
317     (nntp-record-command "*** CALLED nntp-report ***"))
318
319   (nnheader-report 'nntp args)
320
321   (apply 'error args))
322
323 (defun nntp-report-1 (&rest args)
324   "Throws out to nntp-with-open-group-error so that the connection may
325 be restored and the command retried."
326
327   (when nntp-record-commands
328     (nntp-record-command "*** CONNECTION LOST ***"))
329
330   (throw 'nntp-with-open-group-error t))
331
332 (defsubst nntp-wait-for (process wait-for buffer &optional decode discard)
333   "Wait for WAIT-FOR to arrive from PROCESS."
334   (save-excursion
335     (set-buffer (process-buffer process))
336     (goto-char (point-min))
337     (while (and (or (not (memq (char-after (point)) '(?2 ?3 ?4 ?5)))
338                     (looking-at "480"))
339                 (memq (process-status process) '(open run)))
340       (when (looking-at "480")
341         (nntp-handle-authinfo process))
342       (when (looking-at "^.*\n")
343         (delete-region (point) (progn (forward-line 1) (point))))
344       (nntp-accept-process-output process)
345       (goto-char (point-min)))
346     (prog1
347         (cond
348          ((looking-at "[45]")
349           (progn
350             (nntp-snarf-error-message)
351             nil))
352          ((not (memq (process-status process) '(open run)))
353           (nntp-report "Server closed connection"))
354          (t
355           (goto-char (point-max))
356           (let ((limit (point-min))
357                 response)
358             (while (not (re-search-backward wait-for limit t))
359               (nntp-accept-process-output process)
360               ;; We assume that whatever we wait for is less than 1000
361               ;; characters long.
362               (setq limit (max (- (point-max) 1000) (point-min)))
363               (goto-char (point-max)))
364             (setq response (match-string 0))
365             (with-current-buffer nntp-server-buffer
366               (setq nntp-process-response response)))
367           (nntp-decode-text (not decode))
368           (unless discard
369             (save-excursion
370               (set-buffer buffer)
371               (goto-char (point-max))
372               (insert-buffer-substring (process-buffer process))
373               ;; Nix out "nntp reading...." message.
374               (when nntp-have-messaged
375                 (setq nntp-have-messaged nil)
376                 (nnheader-message 5 ""))))
377           t))
378       (unless discard
379         (erase-buffer)))))
380
381 (defun nntp-kill-buffer (buffer)
382   (when (buffer-name buffer)
383     (kill-buffer buffer)
384     (nnheader-init-server-buffer)))
385
386 (defsubst nntp-find-connection (buffer)
387   "Find the connection delivering to BUFFER."
388   (let ((alist nntp-connection-alist)
389         (buffer (if (stringp buffer) (get-buffer buffer) buffer))
390         process entry)
391     (while (and alist (setq entry (pop alist)))
392       (when (eq buffer (cadr entry))
393         (setq process (car entry)
394               alist nil)))
395     (when process
396       (if (memq (process-status process) '(open run))
397           process
398         (nntp-kill-buffer (process-buffer process))
399         (setq nntp-connection-alist (delq entry nntp-connection-alist))
400         nil))))
401
402 (defsubst nntp-find-connection-entry (buffer)
403   "Return the entry for the connection to BUFFER."
404   (assq (nntp-find-connection buffer) nntp-connection-alist))
405
406 (defun nntp-find-connection-buffer (buffer)
407   "Return the process connection buffer tied to BUFFER."
408   (let ((process (nntp-find-connection buffer)))
409     (when process
410       (process-buffer process))))
411
412 (defsubst nntp-retrieve-data (command address port buffer
413                                       &optional wait-for callback decode)
414   "Use COMMAND to retrieve data into BUFFER from PORT on ADDRESS."
415   (let ((process (or (nntp-find-connection buffer)
416                      (nntp-open-connection buffer))))
417     (if process
418         (progn
419           (unless (or nntp-inhibit-erase nnheader-callback-function)
420             (save-excursion
421               (set-buffer (process-buffer process))
422               (erase-buffer)))
423           (condition-case err
424               (progn
425                 (when command
426                   (nntp-send-string process command))
427                 (cond
428                  ((eq callback 'ignore)
429                   t)
430                  ((and callback wait-for)
431                   (nntp-async-wait process wait-for buffer decode callback)
432                   t)
433                  (wait-for
434                   (nntp-wait-for process wait-for buffer decode))
435                  (t t)))
436             (error
437              (nnheader-report 'nntp "Couldn't open connection to %s: %s"
438                               address err))
439             (quit
440              (message "Quit retrieving data from nntp")
441              (signal 'quit nil)
442              nil)))
443       (nnheader-report 'nntp "Couldn't open connection to %s" address))))
444
445 (defsubst nntp-send-command (wait-for &rest strings)
446   "Send STRINGS to server and wait until WAIT-FOR returns."
447   (when (and (not nnheader-callback-function)
448              (not nntp-inhibit-output))
449     (save-excursion
450       (set-buffer nntp-server-buffer)
451       (erase-buffer)))
452   (let* ((command (mapconcat 'identity strings " "))
453          (process (nntp-find-connection nntp-server-buffer))
454          (buffer (and process (process-buffer process)))
455          (pos (and buffer (with-current-buffer buffer (point)))))
456     (if process
457         (prog1
458             (nntp-retrieve-data command
459                                 nntp-address nntp-port-number
460                                 nntp-server-buffer
461                                 wait-for nnheader-callback-function)
462           ;; If nothing to wait for, still remove possibly echo'ed commands.
463           ;; We don't have echos if nntp-open-connection-function
464           ;; is `nntp-open-network-stream', so we skip this in that case.
465           (unless (or wait-for
466                       (equal nntp-open-connection-function
467                              'nntp-open-network-stream))
468             (nntp-accept-response)
469             (save-excursion
470               (set-buffer buffer)
471               (goto-char pos)
472               (if (looking-at (regexp-quote command))
473                   (delete-region pos (progn (forward-line 1)
474                                             (gnus-point-at-bol))))
475               )))
476       (nnheader-report 'nntp "Couldn't open connection to %s."
477                        nntp-address))))
478
479 (defun nntp-send-command-nodelete (wait-for &rest strings)
480   "Send STRINGS to server and wait until WAIT-FOR returns."
481   (let* ((command (mapconcat 'identity strings " "))
482          (process (nntp-find-connection nntp-server-buffer))
483          (buffer (and process (process-buffer process)))
484          (pos (and buffer (with-current-buffer buffer (point)))))
485     (if process
486         (prog1
487             (nntp-retrieve-data command
488                                 nntp-address nntp-port-number
489                                 nntp-server-buffer
490                                 wait-for nnheader-callback-function)
491           ;; If nothing to wait for, still remove possibly echo'ed commands
492           (unless wait-for
493             (nntp-accept-response)
494             (save-excursion
495               (set-buffer buffer)
496               (goto-char pos)
497               (if (looking-at (regexp-quote command))
498                   (delete-region pos (progn (forward-line 1)
499                                             (gnus-point-at-bol))))
500               )))
501       (nnheader-report 'nntp "Couldn't open connection to %s."
502                        nntp-address))))
503
504 (defun nntp-send-command-and-decode (wait-for &rest strings)
505   "Send STRINGS to server and wait until WAIT-FOR returns."
506   (when (and (not nnheader-callback-function)
507              (not nntp-inhibit-output))
508     (save-excursion
509       (set-buffer nntp-server-buffer)
510       (erase-buffer)))
511   (let* ((command (mapconcat 'identity strings " "))
512          (process (nntp-find-connection nntp-server-buffer))
513          (buffer (and process (process-buffer process)))
514          (pos (and buffer (with-current-buffer buffer (point)))))
515     (if process
516         (prog1
517             (nntp-retrieve-data command
518                                 nntp-address nntp-port-number
519                                 nntp-server-buffer
520                                 wait-for nnheader-callback-function t)
521           ;; If nothing to wait for, still remove possibly echo'ed commands
522           (unless wait-for
523             (nntp-accept-response)
524             (save-excursion
525           (set-buffer buffer)
526           (goto-char pos)
527           (if (looking-at (regexp-quote command))
528               (delete-region pos (progn (forward-line 1) (gnus-point-at-bol))))
529           )))
530       (nnheader-report 'nntp "Couldn't open connection to %s."
531                        nntp-address))))
532
533 (defun nntp-send-buffer (wait-for)
534   "Send the current buffer to server and wait until WAIT-FOR returns."
535   (when (and (not nnheader-callback-function)
536              (not nntp-inhibit-output))
537     (save-excursion
538       (set-buffer (nntp-find-connection-buffer nntp-server-buffer))
539       (erase-buffer)))
540   (nntp-encode-text)
541   (let ((multibyte (and (boundp 'enable-multibyte-characters)
542                         (symbol-value 'enable-multibyte-characters))))
543     (unwind-protect
544         ;; Some encoded unicode text contains character 0x80-0x9f e.g. Euro.
545         (let (default-enable-multibyte-characters mc-flag)
546           ;; `set-buffer-multibyte' will be provided by APEL for all Emacsen.
547           (set-buffer-multibyte nil)
548           (process-send-region (nntp-find-connection nntp-server-buffer)
549                                (point-min) (point-max))))
550     (set-buffer-multibyte multibyte))
551   (nntp-retrieve-data
552    nil nntp-address nntp-port-number nntp-server-buffer
553    wait-for nnheader-callback-function))
554
555 \f
556
557 ;;; Interface functions.
558
559 (nnoo-define-basics nntp)
560
561 (defsubst nntp-next-result-arrived-p ()
562   (cond
563    ;; A result that starts with a 2xx code is terminated by
564    ;; a line with only a "." on it.
565    ((eq (char-after) ?2)
566     (if (re-search-forward "\n\\.\r?\n" nil t)
567         t
568       nil))
569    ;; A result that starts with a 3xx or 4xx code is terminated
570    ;; by a newline.
571    ((looking-at "[34]")
572     (if (search-forward "\n" nil t)
573         t
574       nil))
575    ;; No result here.
576    (t
577     nil)))
578
579 (eval-when-compile
580   (defvar nntp-with-open-group-internal nil)
581   (defvar nntp-report-n nil))
582
583 (defmacro nntp-with-open-group (group server &optional connectionless &rest forms)
584   "Protect against servers that don't like clients that keep idle connections opens.
585 The problem being that these servers may either close a connection or
586 simply ignore any further requests on a connection.  Closed
587 connections are not detected until accept-process-output has updated
588 the process-status.  Dropped connections are not detected until the
589 connection timeouts (which may be several minutes) or
590 nntp-connection-timeout has expired.  When these occur
591 nntp-with-open-group, opens a new connection then re-issues the NNTP
592 command whose response triggered the error."
593   (when (and (listp connectionless)
594              (not (eq connectionless nil)))
595     (setq forms (cons connectionless forms)
596           connectionless nil))
597   `(letf ((nntp-report-n (symbol-function 'nntp-report))
598           ((symbol-function 'nntp-report) (symbol-function 'nntp-report-1))
599           (nntp-with-open-group-internal nil))
600      (while (catch 'nntp-with-open-group-error
601               ;; Open the connection to the server
602               ;; NOTE: Existing connections are NOT tested.
603               (nntp-possibly-change-group ,group ,server ,connectionless)
604
605               (let ((timer
606                      (and nntp-connection-timeout
607                           (nnheader-run-at-time
608                            nntp-connection-timeout nil
609                            '(lambda ()
610                               (let ((process (nntp-find-connection
611                                               nntp-server-buffer))
612                                     (buffer  (and process
613                                                   (process-buffer process))))
614                                         ; when I an able to identify
615                                         ; the connection to the server
616                                         ; AND I've received NO reponse
617                                         ; for nntp-connection-timeout
618                                         ; seconds.
619                                 (when (and buffer (eq 0 (buffer-size buffer)))
620                                         ; Close the connection.  Take
621                                         ; no other action as the
622                                         ; accept input code will
623                                         ; handle the closed
624                                         ; connection.
625                                   (nntp-kill-buffer buffer))))))))
626                 (unwind-protect
627                     (setq nntp-with-open-group-internal
628                           (condition-case nil
629                               (progn ,@forms)
630                             (quit
631                              (nntp-close-server)
632                              (signal 'quit nil)))
633                           )
634                   (when timer
635                     (nnheader-cancel-timer timer)))
636                 nil))
637        (setf (symbol-function 'nntp-report) nntp-report-n))
638      nntp-with-open-group-internal))
639
640 (deffoo nntp-retrieve-headers (articles &optional group server fetch-old)
641   "Retrieve the headers of ARTICLES."
642   (nntp-with-open-group
643    group server
644    (save-excursion
645      (set-buffer (nntp-find-connection-buffer nntp-server-buffer))
646      (erase-buffer)
647      (if (and (not gnus-nov-is-evil)
648               (not nntp-nov-is-evil)
649               (nntp-retrieve-headers-with-xover articles fetch-old))
650          ;; We successfully retrieved the headers via XOVER.
651          'nov
652        ;; XOVER didn't work, so we do it the hard, slow and inefficient
653        ;; way.
654        (let ((number (length articles))
655              (articles articles)
656              (count 0)
657              (received 0)
658              (last-point (point-min))
659              (buf (nntp-find-connection-buffer nntp-server-buffer))
660              (nntp-inhibit-erase t)
661              article)
662          ;; Send HEAD commands.
663          (while (setq article (pop articles))
664            (nntp-send-command
665             nil
666             "HEAD" (if (numberp article)
667                        (int-to-string article)
668                      ;; `articles' is either a list of article numbers
669                      ;; or a list of article IDs.
670                      article))
671            (incf count)
672            ;; Every 400 requests we have to read the stream in
673            ;; order to avoid deadlocks.
674            (when (or (null articles)    ;All requests have been sent.
675                      (zerop (% count nntp-maximum-request)))
676              (nntp-accept-response)
677              (while (progn
678                       (set-buffer buf)
679                       (goto-char last-point)
680                       ;; Count replies.
681                       (while (nntp-next-result-arrived-p)
682                         (setq last-point (point))
683                         (incf received))
684                       (< received count))
685                ;; If number of headers is greater than 100, give
686                ;;  informative messages.
687                (and (numberp nntp-large-newsgroup)
688                     (> number nntp-large-newsgroup)
689                     (zerop (% received 20))
690                     (nnheader-message 6 "NNTP: Receiving headers... %d%%"
691                                       (/ (* received 100) number)))
692                (nntp-accept-response))))
693          (and (numberp nntp-large-newsgroup)
694               (> number nntp-large-newsgroup)
695               (nnheader-message 6 "NNTP: Receiving headers...done"))
696
697          ;; Now all of replies are received.  Fold continuation lines.
698          (nnheader-fold-continuation-lines)
699          ;; Remove all "\r"'s.
700          (nnheader-strip-cr)
701          (copy-to-buffer nntp-server-buffer (point-min) (point-max))
702          'headers)))))
703
704 (deffoo nntp-retrieve-groups (groups &optional server)
705   "Retrieve group info on GROUPS."
706   (nntp-with-open-group
707    nil server
708    (when (nntp-find-connection-buffer nntp-server-buffer)
709      (catch 'done
710        (save-excursion
711          ;; Erase nntp-server-buffer before nntp-inhibit-erase.
712          (set-buffer nntp-server-buffer)
713          (erase-buffer)
714          (set-buffer (nntp-find-connection-buffer nntp-server-buffer))
715          ;; The first time this is run, this variable is `try'.  So we
716          ;; try.
717          (when (eq nntp-server-list-active-group 'try)
718            (nntp-try-list-active (car groups)))
719          (erase-buffer)
720          (let ((count 0)
721                (groups groups)
722                (received 0)
723                (last-point (point-min))
724                (nntp-inhibit-erase t)
725                (buf (nntp-find-connection-buffer nntp-server-buffer))
726                (command (if nntp-server-list-active-group
727                             "LIST ACTIVE" "GROUP")))
728            (while groups
729              ;; Timeout may have killed the buffer.
730              (unless (gnus-buffer-live-p buf)
731                (nnheader-report 'nntp "Connection to %s is closed." server)
732                (throw 'done nil))
733              ;; Send the command to the server.
734              (nntp-send-command nil command (pop groups))
735              (incf count)
736              ;; Every 400 requests we have to read the stream in
737              ;; order to avoid deadlocks.
738              (when (or (null groups)    ;All requests have been sent.
739                        (zerop (% count nntp-maximum-request)))
740                (nntp-accept-response)
741                (while (and (gnus-buffer-live-p buf)
742                            (progn
743                              ;; Search `blue moon' in this file for the
744                              ;; reason why set-buffer here.
745                              (set-buffer buf)
746                              (goto-char last-point)
747                              ;; Count replies.
748                              (while (re-search-forward "^[0-9]" nil t)
749                                (incf received))
750                              (setq last-point (point))
751                              (< received count)))
752                  (nntp-accept-response))))
753
754            ;; Wait for the reply from the final command.
755            (unless (gnus-buffer-live-p buf)
756              (nnheader-report 'nntp "Connection to %s is closed." server)
757              (throw 'done nil))
758            (set-buffer buf)
759            (goto-char (point-max))
760            (re-search-backward "^[0-9]" nil t)
761            (when (looking-at "^[23]")
762              (while (and (gnus-buffer-live-p buf)
763                          (progn
764                            (set-buffer buf)
765                            (goto-char (point-max))
766                            (if (not nntp-server-list-active-group)
767                                (not (re-search-backward "\r?\n" (- (point) 3) t))
768                              (not (re-search-backward "^\\.\r?\n"
769                                                       (- (point) 4) t)))))
770                (nntp-accept-response)))
771
772            ;; Now all replies are received.  We remove CRs.
773            (unless (gnus-buffer-live-p buf)
774              (nnheader-report 'nntp "Connection to %s is closed." server)
775              (throw 'done nil))
776            (set-buffer buf)
777            (goto-char (point-min))
778            (while (search-forward "\r" nil t)
779              (replace-match "" t t))
780
781            (if (not nntp-server-list-active-group)
782                (progn
783                  (copy-to-buffer nntp-server-buffer (point-min) (point-max))
784                  'group)
785              ;; We have read active entries, so we just delete the
786              ;; superfluous gunk.
787              (goto-char (point-min))
788              (while (re-search-forward "^[.2-5]" nil t)
789                (delete-region (match-beginning 0)
790                               (progn (forward-line 1) (point))))
791              (copy-to-buffer nntp-server-buffer (point-min) (point-max))
792              'active)))))))
793
794 (deffoo nntp-retrieve-articles (articles &optional group server)
795   (nntp-with-open-group
796     group server
797    (save-excursion
798      (let ((number (length articles))
799            (articles articles)
800            (count 0)
801            (received 0)
802            (last-point (point-min))
803            (buf (nntp-find-connection-buffer nntp-server-buffer))
804            (nntp-inhibit-erase t)
805            (map (apply 'vector articles))
806            (point 1)
807            article)
808        (set-buffer buf)
809        (erase-buffer)
810        ;; Send ARTICLE command.
811        (while (setq article (pop articles))
812          (nntp-send-command
813           nil
814           "ARTICLE" (if (numberp article)
815                         (int-to-string article)
816                       ;; `articles' is either a list of article numbers
817                       ;; or a list of article IDs.
818                       article))
819          (incf count)
820          ;; Every 400 requests we have to read the stream in
821          ;; order to avoid deadlocks.
822          (when (or (null articles)      ;All requests have been sent.
823                    (zerop (% count nntp-maximum-request)))
824            (nntp-accept-response)
825            (while (progn
826                     (set-buffer buf)
827                     (goto-char last-point)
828                     ;; Count replies.
829                     (while (nntp-next-result-arrived-p)
830                       (aset map received (cons (aref map received) (point)))
831                       (setq last-point (point))
832                       (incf received))
833                     (< received count))
834              ;; If number of headers is greater than 100, give
835              ;;  informative messages.
836              (and (numberp nntp-large-newsgroup)
837                   (> number nntp-large-newsgroup)
838                   (zerop (% received 20))
839                   (nnheader-message 6 "NNTP: Receiving articles... %d%%"
840                                     (/ (* received 100) number)))
841              (nntp-accept-response))))
842        (and (numberp nntp-large-newsgroup)
843             (> number nntp-large-newsgroup)
844             (nnheader-message 6 "NNTP: Receiving articles...done"))
845
846        ;; Now we have all the responses.  We go through the results,
847        ;; wash it and copy it over to the server buffer.
848        (set-buffer nntp-server-buffer)
849        (erase-buffer)
850        (setq last-point (point-min))
851        (mapcar
852         (lambda (entry)
853           (narrow-to-region
854            (setq point (goto-char (point-max)))
855            (progn
856              (insert-buffer-substring buf last-point (cdr entry))
857              (point-max)))
858           (setq last-point (cdr entry))
859           (nntp-decode-text)
860           (widen)
861           (cons (car entry) point))
862         map)))))
863
864 (defun nntp-try-list-active (group)
865   (nntp-list-active-group group)
866   (save-excursion
867     (set-buffer nntp-server-buffer)
868     (goto-char (point-min))
869     (cond ((or (eobp)
870                (looking-at "5[0-9]+"))
871            (setq nntp-server-list-active-group nil))
872           (t
873            (setq nntp-server-list-active-group t)))))
874
875 (deffoo nntp-list-active-group (group &optional server)
876   "Return the active info on GROUP (which can be a regexp)."
877   (nntp-with-open-group
878    nil server
879    (nntp-send-command "^\\.*\r?\n" "LIST ACTIVE" group)))
880
881 (deffoo nntp-request-group-articles (group &optional server)
882   "Return the list of existing articles in GROUP."
883   (nntp-with-open-group
884    nil server
885    (nntp-send-command "^\\.*\r?\n" "LISTGROUP" group)))
886
887 (deffoo nntp-request-article (article &optional group server buffer command)
888   (nntp-with-open-group
889     group server
890     (when (nntp-send-command-and-decode
891            "\r?\n\\.\r?\n" "ARTICLE"
892            (if (numberp article) (int-to-string article) article))
893       (if (and buffer
894                (not (equal buffer nntp-server-buffer)))
895           (save-excursion
896             (set-buffer nntp-server-buffer)
897             (copy-to-buffer buffer (point-min) (point-max))
898             (nntp-find-group-and-number group))
899         (nntp-find-group-and-number group)))))
900
901 (deffoo nntp-request-head (article &optional group server)
902   (nntp-with-open-group
903    group server
904    (when (nntp-send-command
905           "\r?\n\\.\r?\n" "HEAD"
906           (if (numberp article) (int-to-string article) article))
907      (prog1
908          (nntp-find-group-and-number group)
909        (nntp-decode-text)))))
910
911 (deffoo nntp-request-body (article &optional group server)
912   (nntp-with-open-group
913    group server
914    (nntp-send-command-and-decode
915     "\r?\n\\.\r?\n" "BODY"
916     (if (numberp article) (int-to-string article) article))))
917
918 (deffoo nntp-request-group (group &optional server dont-check)
919   (nntp-with-open-group 
920     nil server
921     (when (nntp-send-command "^[245].*\n" "GROUP" group)
922       (let ((entry (nntp-find-connection-entry nntp-server-buffer)))
923         (setcar (cddr entry) group)))))
924
925 (deffoo nntp-close-group (group &optional server)
926   t)
927
928 (deffoo nntp-server-opened (&optional server)
929   "Say whether a connection to SERVER has been opened."
930   (and (nnoo-current-server-p 'nntp server)
931        nntp-server-buffer
932        (gnus-buffer-live-p nntp-server-buffer)
933        (nntp-find-connection nntp-server-buffer)))
934
935 (deffoo nntp-open-server (server &optional defs connectionless)
936   (nnheader-init-server-buffer)
937   (if (nntp-server-opened server)
938       t
939     (when (or (stringp (car defs))
940               (numberp (car defs)))
941       (setq defs (cons (list 'nntp-port-number (car defs)) (cdr defs))))
942     (unless (assq 'nntp-address defs)
943       (setq defs (append defs (list (list 'nntp-address server)))))
944     (nnoo-change-server 'nntp server defs)
945     (unless connectionless
946       (or (nntp-find-connection nntp-server-buffer)
947           (nntp-open-connection nntp-server-buffer)))))
948
949 (deffoo nntp-close-server (&optional server)
950   (nntp-possibly-change-group nil server t)
951   (let ((process (nntp-find-connection nntp-server-buffer)))
952     (while process
953       (when (memq (process-status process) '(open run))
954         (ignore-errors
955           (nntp-send-string process "QUIT")
956           (unless (eq nntp-open-connection-function 'nntp-open-network-stream)
957             ;; Ok, this is evil, but when using telnet and stuff
958             ;; as the connection method, it's important that the
959             ;; QUIT command actually is sent out before we kill
960             ;; the process.
961             (sleep-for 1))))
962       (nntp-kill-buffer (process-buffer process))
963       (setq process (car (pop nntp-connection-alist))))
964     (nnoo-close-server 'nntp)))
965
966 (deffoo nntp-request-close ()
967   (let (process)
968     (while (setq process (pop nntp-connection-list))
969       (when (memq (process-status process) '(open run))
970         (ignore-errors
971           (nntp-send-string process "QUIT")
972           (unless (eq nntp-open-connection-function 'nntp-open-network-stream)
973             ;; Ok, this is evil, but when using telnet and stuff
974             ;; as the connection method, it's important that the
975             ;; QUIT command actually is sent out before we kill
976             ;; the process.
977             (sleep-for 1))))
978       (nntp-kill-buffer (process-buffer process)))))
979
980 (deffoo nntp-request-list (&optional server)
981   "List active groups.  If `nntp-list-options' is non-nil, the listing
982 output from the server will be restricted to the specified newsgroups.
983 If `nntp-options-subscribe' is non-nil, remove newsgroups that do not
984 match the regexp.  If `nntp-options-not-subscribe' is non-nil, remove
985 newsgroups that match the regexp."
986   (nntp-with-open-group
987    nil server
988    (with-current-buffer nntp-server-buffer
989      (prog1
990          (if (not nntp-list-options)
991              (nntp-send-command-and-decode "\r?\n\\.\r?\n" "LIST")
992            (let ((options (if (consp nntp-list-options)
993                               nntp-list-options
994                             (list nntp-list-options)))
995                  (ret t))
996              (erase-buffer)
997              (while options
998                (goto-char (point-max))
999                (narrow-to-region (point) (point))
1000                (setq ret (and ret
1001                               (nntp-send-command-nodelete
1002                                "\r?\n\\.\r?\n"
1003                                (format "LIST ACTIVE %s" (car options))))
1004                      options (cdr options))
1005                (nntp-decode-text))
1006              (widen)
1007              ret))
1008        (when (and (stringp nntp-options-subscribe)
1009                   (not (string-equal "" nntp-options-subscribe)))
1010          (goto-char (point-min))
1011          (keep-lines nntp-options-subscribe))
1012        (when (and (stringp nntp-options-not-subscribe)
1013                   (not (string-equal "" nntp-options-not-subscribe)))
1014          (goto-char (point-min))
1015          (flush-lines nntp-options-subscribe))))))
1016
1017 (deffoo nntp-request-list-newsgroups (&optional server)
1018   (nntp-with-open-group
1019    nil server
1020    (nntp-send-command "\r?\n\\.\r?\n" "LIST NEWSGROUPS")))
1021
1022 (deffoo nntp-request-newgroups (date &optional server)
1023   (nntp-with-open-group
1024    nil server
1025    (save-excursion
1026      (set-buffer nntp-server-buffer)
1027      (let* ((time (date-to-time date))
1028             (ls (- (cadr time) (nth 8 (decode-time time)))))
1029        (cond ((< ls 0)
1030               (setcar time (1- (car time)))
1031               (setcar (cdr time) (+ ls 65536)))
1032              ((>= ls 65536)
1033               (setcar time (1+ (car time)))
1034               (setcar (cdr time) (- ls 65536)))
1035              (t
1036               (setcar (cdr time) ls)))
1037        (prog1
1038            (nntp-send-command
1039             "^\\.\r?\n" "NEWGROUPS"
1040             (format-time-string "%y%m%d %H%M%S" time)
1041             "GMT")
1042          (nntp-decode-text))))))
1043
1044 (deffoo nntp-request-post (&optional server)
1045   (nntp-with-open-group
1046    nil server
1047    (when (nntp-send-command "^[23].*\r?\n" "POST")
1048      (let ((response (with-current-buffer nntp-server-buffer
1049                        nntp-process-response))
1050            server-id)
1051        (when (and response
1052                   (string-match "^[23].*\\(<[^\t\n @<>]+@[^\t\n @<>]+>\\)"
1053                                 response))
1054          (setq server-id (match-string 1 response))
1055          (narrow-to-region (goto-char (point-min))
1056                            (if (search-forward "\n\n" nil t)
1057                                (1- (point))
1058                              (point-max)))
1059          (unless (mail-fetch-field "Message-ID")
1060            (goto-char (point-min))
1061            (insert "Message-ID: " server-id "\n"))
1062          (widen))
1063        (run-hooks 'nntp-prepare-post-hook)
1064        (nntp-send-buffer "^[23].*\n")))))
1065
1066 (deffoo nntp-request-type (group article)
1067   'news)
1068
1069 (deffoo nntp-asynchronous-p ()
1070   t)
1071
1072 ;;; Hooky functions.
1073
1074 (defun nntp-send-mode-reader ()
1075   "Send the MODE READER command to the nntp server.
1076 This function is supposed to be called from `nntp-server-opened-hook'.
1077 It will make innd servers spawn an nnrpd process to allow actual article
1078 reading."
1079   (nntp-send-command "^.*\n" "MODE READER"))
1080
1081 (defun nntp-send-authinfo (&optional send-if-force)
1082   "Send the AUTHINFO to the nntp server.
1083 It will look in the \"~/.authinfo\" file for matching entries.  If
1084 nothing suitable is found there, it will prompt for a user name
1085 and a password.
1086
1087 If SEND-IF-FORCE, only send authinfo to the server if the
1088 .authinfo file has the FORCE token."
1089   (let* ((list (gnus-parse-netrc nntp-authinfo-file))
1090          (alist (gnus-netrc-machine list nntp-address "nntp"))
1091          (force (gnus-netrc-get alist "force"))
1092          (user (or (gnus-netrc-get alist "login") nntp-authinfo-user))
1093          (passwd (gnus-netrc-get alist "password")))
1094     (when (or (not send-if-force)
1095               force)
1096       (unless user
1097         (setq user (read-string (format "NNTP (%s) user name: " nntp-address))
1098               nntp-authinfo-user user))
1099       (unless (member user '(nil ""))
1100         (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" user)
1101         (when t                         ;???Should check if AUTHINFO succeeded
1102           (nntp-send-command
1103            "^2.*\r?\n" "AUTHINFO PASS"
1104            (or passwd
1105                nntp-authinfo-password
1106                (setq nntp-authinfo-password
1107                      (mail-source-read-passwd
1108                       (format "NNTP (%s@%s) password: "
1109                               user nntp-address))))))))))
1110
1111 (defun nntp-send-nosy-authinfo ()
1112   "Send the AUTHINFO to the nntp server."
1113   (let ((user (read-string (format "NNTP (%s) user name: " nntp-address))))
1114     (unless (member user '(nil ""))
1115       (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" user)
1116       (when t                           ;???Should check if AUTHINFO succeeded
1117         (nntp-send-command "^2.*\r?\n" "AUTHINFO PASS"
1118                            (mail-source-read-passwd "NNTP (%s@%s) password: "
1119                                                     user nntp-address))))))
1120
1121 (defun nntp-send-authinfo-from-file ()
1122   "Send the AUTHINFO to the nntp server.
1123
1124 The authinfo login name is taken from the user's login name and the
1125 password contained in '~/.nntp-authinfo'."
1126   (when (file-exists-p "~/.nntp-authinfo")
1127     (with-temp-buffer
1128       (insert-file-contents "~/.nntp-authinfo")
1129       (goto-char (point-min))
1130       (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" (user-login-name))
1131       (nntp-send-command
1132        "^2.*\r?\n" "AUTHINFO PASS"
1133        (buffer-substring (point) (progn (end-of-line) (point)))))))
1134
1135 ;;; Internal functions.
1136
1137 (defun nntp-handle-authinfo (process)
1138   "Take care of an authinfo response from the server."
1139   (let ((last nntp-last-command))
1140     (funcall nntp-authinfo-function)
1141     ;; We have to re-send the function that was interrupted by
1142     ;; the authinfo request.
1143     (save-excursion
1144       (set-buffer nntp-server-buffer)
1145       (erase-buffer))
1146     (nntp-send-string process last)))
1147
1148 (defun nntp-make-process-buffer (buffer)
1149   "Create a new, fresh buffer usable for nntp process connections."
1150   (save-excursion
1151     (set-buffer
1152      (generate-new-buffer
1153       (format " *server %s %s %s*"
1154               nntp-address nntp-port-number
1155               (gnus-buffer-exists-p buffer))))
1156     (set (make-local-variable 'after-change-functions) nil)
1157     (set (make-local-variable 'nntp-process-wait-for) nil)
1158     (set (make-local-variable 'nntp-process-callback) nil)
1159     (set (make-local-variable 'nntp-process-to-buffer) nil)
1160     (set (make-local-variable 'nntp-process-start-point) nil)
1161     (set (make-local-variable 'nntp-process-decode) nil)
1162     (current-buffer)))
1163
1164 (defun nntp-open-connection (buffer)
1165   "Open a connection to PORT on ADDRESS delivering output to BUFFER."
1166   (run-hooks 'nntp-prepare-server-hook)
1167   (let* ((pbuffer (nntp-make-process-buffer buffer))
1168          (timer
1169           (and nntp-connection-timeout
1170                (nnheader-run-at-time
1171                 nntp-connection-timeout nil
1172                 `(lambda ()
1173                    (nntp-kill-buffer ,pbuffer)))))
1174          (process
1175           (condition-case ()
1176               (funcall nntp-open-connection-function pbuffer)
1177             (error nil)
1178             (quit
1179              (message "Quit opening connection")
1180              (nntp-kill-buffer pbuffer)
1181              (signal 'quit nil)
1182              nil))))
1183     (when timer
1184       (nnheader-cancel-timer timer))
1185     (unless process
1186       (nntp-kill-buffer pbuffer))
1187     (when (and (buffer-name pbuffer)
1188                process)
1189       (process-kill-without-query process)
1190       (if (and (nntp-wait-for process "^2.*\n" buffer nil t)
1191                (memq (process-status process) '(open run)))
1192           (prog1
1193               (caar (push (list process buffer nil) nntp-connection-alist))
1194             (push process nntp-connection-list)
1195             (save-excursion
1196               (set-buffer pbuffer)
1197               (nntp-read-server-type)
1198               (erase-buffer)
1199               (set-buffer nntp-server-buffer)
1200               (let ((nnheader-callback-function nil))
1201                 (run-hooks 'nntp-server-opened-hook)
1202                 (nntp-send-authinfo t))))
1203         (nntp-kill-buffer (process-buffer process))
1204         nil))))
1205
1206 (defun nntp-open-network-stream (buffer)
1207   (open-network-stream-as-binary
1208    "nntpd" buffer nntp-address nntp-port-number))
1209
1210 (defun nntp-open-ssl-stream (buffer)
1211   (let ((proc (open-ssl-stream "nntpd" buffer nntp-address nntp-port-number)))
1212     (save-excursion
1213       (set-buffer buffer)
1214       (nntp-wait-for-string "^\r*20[01]")
1215       (beginning-of-line)
1216       (delete-region (point-min) (point))
1217       proc)))
1218
1219 (defun nntp-read-server-type ()
1220   "Find out what the name of the server we have connected to is."
1221   ;; Wait for the status string to arrive.
1222   (setq nntp-server-type (buffer-string))
1223   (let ((alist nntp-server-action-alist)
1224         (case-fold-search t)
1225         entry)
1226     ;; Run server-specific commands.
1227     (while alist
1228       (setq entry (pop alist))
1229       (when (string-match (car entry) nntp-server-type)
1230         (if (and (listp (cadr entry))
1231                  (not (eq 'lambda (caadr entry))))
1232             (eval (cadr entry))
1233           (funcall (cadr entry)))))))
1234
1235 (defun nntp-async-wait (process wait-for buffer decode callback)
1236   (save-excursion
1237     (set-buffer (process-buffer process))
1238     (unless nntp-inside-change-function
1239       (erase-buffer))
1240     (setq nntp-process-wait-for wait-for
1241           nntp-process-to-buffer buffer
1242           nntp-process-decode decode
1243           nntp-process-callback callback
1244           nntp-process-start-point (point-max))
1245     (setq after-change-functions '(nntp-after-change-function))
1246     (if nntp-async-needs-kluge
1247         (nntp-async-kluge process))))
1248
1249 (defun nntp-async-kluge (process)
1250   ;; emacs 20.3 bug: process output with encoding 'binary
1251   ;; doesn't trigger after-change-functions.
1252   (unless nntp-async-timer
1253     (setq nntp-async-timer
1254           (nnheader-run-at-time 1 1 'nntp-async-timer-handler)))
1255   (add-to-list 'nntp-async-process-list process))
1256
1257 (defun nntp-async-timer-handler ()
1258   (mapcar
1259    (lambda (proc)
1260      (if (memq (process-status proc) '(open run))
1261          (nntp-async-trigger proc)
1262        (nntp-async-stop proc)))
1263    nntp-async-process-list))
1264
1265 (defun nntp-async-stop (proc)
1266   (setq nntp-async-process-list (delq proc nntp-async-process-list))
1267   (when (and nntp-async-timer (not nntp-async-process-list))
1268     (nnheader-cancel-timer nntp-async-timer)
1269     (setq nntp-async-timer nil)))
1270
1271 (defun nntp-after-change-function (beg end len)
1272   (unwind-protect
1273       ;; we only care about insertions at eob
1274       (when (and (eq 0 len) (eq (point-max) end))
1275         (save-match-data
1276           (let ((proc (get-buffer-process (current-buffer))))
1277             (when proc
1278               (nntp-async-trigger proc)))))
1279     ;; any throw from after-change-functions will leave it
1280     ;; set to nil.  so we reset it here, if necessary.
1281     (when quit-flag
1282       (setq after-change-functions '(nntp-after-change-function)))))
1283
1284 (defun nntp-async-trigger (process)
1285   (save-excursion
1286     (set-buffer (process-buffer process))
1287     (when nntp-process-callback
1288       ;; do we have an error message?
1289       (goto-char nntp-process-start-point)
1290       (if (memq (following-char) '(?4 ?5))
1291           ;; wants credentials?
1292           (if (looking-at "480")
1293               (nntp-handle-authinfo process)
1294             ;; report error message.
1295             (nntp-snarf-error-message)
1296             (nntp-do-callback nil))
1297
1298         ;; got what we expect?
1299         (goto-char (point-max))
1300         (when (re-search-backward
1301                nntp-process-wait-for nntp-process-start-point t)
1302           (let ((response (match-string 0)))
1303             (with-current-buffer nntp-server-buffer
1304               (setq nntp-process-response response)))
1305           (nntp-async-stop process)
1306           ;; convert it.
1307           (when (gnus-buffer-exists-p nntp-process-to-buffer)
1308             (let ((buf (current-buffer))
1309                   (start nntp-process-start-point)
1310                   (decode nntp-process-decode))
1311               (save-excursion
1312                 (set-buffer nntp-process-to-buffer)
1313                 (goto-char (point-max))
1314                 (save-restriction
1315                   (narrow-to-region (point) (point))
1316                   (insert-buffer-substring buf start)
1317                   (when decode
1318                     (nntp-decode-text))))))
1319           ;; report it.
1320           (goto-char (point-max))
1321           (nntp-do-callback
1322            (buffer-name (get-buffer nntp-process-to-buffer))))))))
1323
1324 (defun nntp-do-callback (arg)
1325   (let ((callback nntp-process-callback)
1326         (nntp-inside-change-function t))
1327     (setq nntp-process-callback nil)
1328     (funcall callback arg)))
1329
1330 (defun nntp-snarf-error-message ()
1331   "Save the error message in the current buffer."
1332   (let ((message (buffer-string)))
1333     (while (string-match "[\r\n]+" message)
1334       (setq message (replace-match " " t t message)))
1335     (nnheader-report 'nntp message)
1336     message))
1337
1338 (defun nntp-accept-process-output (process &optional timeout)
1339   "Wait for output from PROCESS and message some dots."
1340   (save-excursion
1341     (set-buffer (or (nntp-find-connection-buffer nntp-server-buffer)
1342                     nntp-server-buffer))
1343     (let ((len (/ (point-max) 1024))
1344           message-log-max)
1345       (unless (< len 10)
1346         (setq nntp-have-messaged t)
1347         (nnheader-message 7 "nntp read: %dk" len)))
1348     (if timeout
1349         (accept-process-output process timeout)
1350       (accept-process-output process 0 100))
1351     ;; accept-process-output may update status of process to indicate
1352     ;; that the server has closed the connection.  This MUST be
1353     ;; handled here as the buffer restored by the save-excursion may
1354     ;; be the process's former output buffer (i.e. now killed)
1355     (or (and process 
1356             (memq (process-status process) '(open run)))
1357         (nntp-report "Server closed connection"))))
1358
1359 (defun nntp-accept-response ()
1360   "Wait for output from the process that outputs to BUFFER."
1361   (nntp-accept-process-output (nntp-find-connection nntp-server-buffer)))
1362
1363 (defun nntp-possibly-change-group (group server &optional connectionless)
1364   (let ((nnheader-callback-function nil))
1365     (when server
1366       (or (nntp-server-opened server)
1367           (nntp-open-server server nil connectionless)))
1368
1369     (unless connectionless
1370       (or (nntp-find-connection nntp-server-buffer)
1371           (nntp-open-connection nntp-server-buffer))))
1372
1373   (when group
1374     (let ((entry (nntp-find-connection-entry nntp-server-buffer)))
1375       (when (not (equal group (caddr entry)))
1376         (save-excursion
1377           (set-buffer (process-buffer (car entry)))
1378           (erase-buffer)
1379           (nntp-send-command "^[245].*\n" "GROUP" group)
1380           (setcar (cddr entry) group)
1381           (erase-buffer)
1382           (save-excursion
1383             (set-buffer nntp-server-buffer)
1384             (erase-buffer)))))))
1385
1386 (defun nntp-decode-text (&optional cr-only)
1387   "Decode the text in the current buffer."
1388   (goto-char (point-min))
1389   (while (search-forward "\r" nil t)
1390     (delete-char -1))
1391   (unless cr-only
1392     ;; Remove trailing ".\n" end-of-transfer marker.
1393     (goto-char (point-max))
1394     (forward-line -1)
1395     (when (looking-at ".\n")
1396       (delete-char 2))
1397     ;; Delete status line.
1398     (goto-char (point-min))
1399     (while (looking-at "[1-5][0-9][0-9] .*\n")
1400       ;; For some unknown reason, there is more than one status line.
1401       (delete-region (point) (progn (forward-line 1) (point))))
1402     ;; Remove "." -> ".." encoding.
1403     (while (search-forward "\n.." nil t)
1404       (delete-char -1))))
1405
1406 (defun nntp-encode-text ()
1407   "Encode the text in the current buffer."
1408   (save-excursion
1409     ;; Replace "." at beginning of line with "..".
1410     (goto-char (point-min))
1411     (while (re-search-forward "^\\." nil t)
1412       (insert "."))
1413     (goto-char (point-max))
1414     ;; Insert newline at the end of the buffer.
1415     (unless (bolp)
1416       (insert "\n"))
1417     ;; Insert `.' at end of buffer (end of text mark).
1418     (goto-char (point-max))
1419     (insert ".\n")
1420     (goto-char (point-min))
1421     (while (not (eobp))
1422       (end-of-line)
1423       (delete-char 1)
1424       (insert nntp-end-of-line))))
1425
1426 (defun nntp-retrieve-headers-with-xover (articles &optional fetch-old)
1427   (set-buffer nntp-server-buffer)
1428   (erase-buffer)
1429   (cond
1430
1431    ;; This server does not talk NOV.
1432    ((not nntp-server-xover)
1433     nil)
1434
1435    ;; We don't care about gaps.
1436    ((or (not nntp-nov-gap)
1437         fetch-old)
1438     (nntp-send-xover-command
1439      (if fetch-old
1440          (if (numberp fetch-old)
1441              (max 1 (- (car articles) fetch-old))
1442            1)
1443        (car articles))
1444      (car (last articles)) 'wait)
1445
1446     (goto-char (point-min))
1447     (when (looking-at "[1-5][0-9][0-9] .*\n")
1448       (delete-region (point) (progn (forward-line 1) (point))))
1449     (while (search-forward "\r" nil t)
1450       (replace-match "" t t))
1451     (goto-char (point-max))
1452     (forward-line -1)
1453     (when (looking-at "\\.")
1454       (delete-region (point) (progn (forward-line 1) (point)))))
1455
1456    ;; We do it the hard way.  For each gap, an XOVER command is sent
1457    ;; to the server.  We do not wait for a reply from the server, we
1458    ;; just send them off as fast as we can.  That means that we have
1459    ;; to count the number of responses we get back to find out when we
1460    ;; have gotten all we asked for.
1461    ((numberp nntp-nov-gap)
1462     (let ((count 0)
1463           (received 0)
1464           last-point
1465           in-process-buffer-p
1466           (buf nntp-server-buffer)
1467           (process-buffer (nntp-find-connection-buffer nntp-server-buffer))
1468           first
1469           last)
1470       ;; We have to check `nntp-server-xover'.  If it gets set to nil,
1471       ;; that means that the server does not understand XOVER, but we
1472       ;; won't know that until we try.
1473       (while (and nntp-server-xover articles)
1474         (setq first (car articles))
1475         ;; Search forward until we find a gap, or until we run out of
1476         ;; articles.
1477         (while (and (cdr articles)
1478                     (< (- (nth 1 articles) (car articles)) nntp-nov-gap))
1479           (setq articles (cdr articles)))
1480
1481         (setq in-process-buffer-p (stringp nntp-server-xover))
1482         (nntp-send-xover-command first (setq last (car articles)))
1483         (setq articles (cdr articles))
1484
1485         (when (and nntp-server-xover in-process-buffer-p)
1486           ;; Don't count tried request.
1487           (setq count (1+ count))
1488
1489           ;; Every 400 requests we have to read the stream in
1490           ;; order to avoid deadlocks.
1491           (when (or (null articles)     ;All requests have been sent.
1492                     (= 1 (% count nntp-maximum-request)))
1493
1494             (nntp-accept-response)
1495             ;; On some Emacs versions the preceding function has a
1496             ;; tendency to change the buffer.  Perhaps.  It's quite
1497             ;; difficult to reproduce, because it only seems to happen
1498             ;; once in a blue moon.
1499             (set-buffer process-buffer)
1500             (while (progn
1501                      (goto-char (or last-point (point-min)))
1502                      ;; Count replies.
1503                      (while (re-search-forward "^[0-9][0-9][0-9] .*\n" nil t)
1504                        (incf received))
1505                      (setq last-point (point))
1506                      (or (< received count) ;; I haven't started reading the final response
1507                          (progn
1508                            (goto-char (point-max))
1509                            (forward-line -1)
1510                            (not (looking-at "^\\.\r?\n"))) ;; I haven't read the end of the final response
1511                          ))
1512               (nntp-accept-response)
1513               (set-buffer process-buffer))))
1514
1515         ;; Some nntp servers seem to have an extension to the XOVER
1516         ;; extension.  On these servers, requesting an article range
1517         ;; preceeding the active range does not return an error as
1518         ;; specified in the RFC.  What we instead get is the NOV entry
1519         ;; for the first available article.  Obviously, a client can
1520         ;; use that entry to avoid making unnecessary requests.  The
1521         ;; only problem is for a client that assumes that the response
1522         ;; will always be within the requested ranage.  For such a
1523         ;; client, we can get N copies of the same entry (one for each
1524         ;; XOVER command sent to the server).
1525
1526         (when (<= count 1)
1527           (goto-char (point-min))
1528           (when (re-search-forward "^[0-9][0-9][0-9] .*\n\\([0-9]+\\)" nil t)
1529             (let ((low-limit (string-to-int (buffer-substring (match-beginning 1) 
1530                                                               (match-end 1)))))
1531               (while (and articles (<= (car articles) low-limit))
1532                 (setq articles (cdr articles))))))
1533         (set-buffer buf))
1534
1535       (when nntp-server-xover
1536         (when in-process-buffer-p
1537           (set-buffer buf)
1538           (goto-char (point-max))
1539           (insert-buffer-substring process-buffer)
1540           (set-buffer process-buffer)
1541           (erase-buffer)
1542           (set-buffer buf))
1543
1544         ;; We remove any "." lines and status lines.
1545         (goto-char (point-min))
1546         (while (search-forward "\r" nil t)
1547           (delete-char -1))
1548         (goto-char (point-min))
1549         (delete-matching-lines "^\\.$\\|^[1-5][0-9][0-9] ")
1550         t))))
1551
1552   nntp-server-xover)
1553
1554 (defun nntp-send-xover-command (beg end &optional wait-for-reply)
1555   "Send the XOVER command to the server."
1556   (let ((range (format "%d-%d" beg end))
1557         (nntp-inhibit-erase t))
1558     (if (stringp nntp-server-xover)
1559         ;; If `nntp-server-xover' is a string, then we just send this
1560         ;; command.
1561         (if wait-for-reply
1562             (nntp-send-command-nodelete
1563              "\r?\n\\.\r?\n" nntp-server-xover range)
1564           ;; We do not wait for the reply.
1565           (nntp-send-command-nodelete nil nntp-server-xover range))
1566       (let ((commands nntp-xover-commands))
1567         ;; `nntp-xover-commands' is a list of possible XOVER commands.
1568         ;; We try them all until we get at positive response.
1569         (while (and commands (eq nntp-server-xover 'try))
1570           (nntp-send-command-nodelete "\r?\n\\.\r?\n" (car commands) range)
1571           (save-excursion
1572             (set-buffer nntp-server-buffer)
1573             (goto-char (point-min))
1574             (and (looking-at "[23]")    ; No error message.
1575                  ;; We also have to look at the lines.  Some buggy
1576                  ;; servers give back simple lines with just the
1577                  ;; article number.  How... helpful.
1578                  (progn
1579                    (forward-line 1)
1580                    (looking-at "[0-9]+\t...")) ; More text after number.
1581                  (setq nntp-server-xover (car commands))))
1582           (setq commands (cdr commands)))
1583         ;; If none of the commands worked, we disable XOVER.
1584         (when (eq nntp-server-xover 'try)
1585           (save-excursion
1586             (set-buffer nntp-server-buffer)
1587             (erase-buffer)
1588             (setq nntp-server-xover nil)))
1589         nntp-server-xover))))
1590
1591 (defun nntp-find-group-and-number (&optional group)
1592   (save-excursion
1593     (save-restriction
1594       (set-buffer nntp-server-buffer)
1595       (narrow-to-region (goto-char (point-min))
1596                         (or (search-forward "\n\n" nil t) (point-max)))
1597       (goto-char (point-min))
1598       ;; We first find the number by looking at the status line.
1599       (let ((number (and (looking-at "2[0-9][0-9] +\\([0-9]+\\) ")
1600                          (string-to-int
1601                           (buffer-substring (match-beginning 1)
1602                                             (match-end 1)))))
1603             newsgroups xref)
1604         (and number (zerop number) (setq number nil))
1605         (if number
1606             ;; Then we find the group name.
1607             (setq group
1608                   (cond
1609                    ;; If there is only one group in the Newsgroups
1610                    ;; header, then it seems quite likely that this
1611                    ;; article comes from that group, I'd say.
1612                    ((and (setq newsgroups
1613                                (mail-fetch-field "newsgroups"))
1614                          (not (string-match "," newsgroups)))
1615                     newsgroups)
1616                    ;; If there is more than one group in the
1617                    ;; Newsgroups header, then the Xref header should
1618                    ;; be filled out.  We hazard a guess that the group
1619                    ;; that has this article number in the Xref header
1620                    ;; is the one we are looking for.  This might very
1621                    ;; well be wrong if this article happens to have
1622                    ;; the same number in several groups, but that's
1623                    ;; life.
1624                    ((and (setq xref (mail-fetch-field "xref"))
1625                          number
1626                          (string-match
1627                           (format "\\([^ :]+\\):%d" number) xref))
1628                     (match-string 1 xref))
1629                    (t "")))
1630           (cond
1631            ((and (setq xref (mail-fetch-field "xref"))
1632                  (string-match
1633                   (if group
1634                       (concat "\\(" (regexp-quote group) "\\):\\([0-9]+\\)")
1635                     "\\([^ :]+\\):\\([0-9]+\\)")
1636                   xref))
1637             (setq group (match-string 1 xref)
1638                   number (string-to-int (match-string 2 xref))))
1639            ((and (setq newsgroups
1640                        (mail-fetch-field "newsgroups"))
1641                  (not (string-match "," newsgroups)))
1642             (setq group newsgroups))
1643            (group)
1644            (t (setq group ""))))
1645         (when (string-match "\r" group)
1646           (setq group (substring group 0 (match-beginning 0))))
1647         (cons group number)))))
1648
1649 (defun nntp-wait-for-string (regexp)
1650   "Wait until string arrives in the buffer."
1651   (let ((buf (current-buffer))
1652         proc)
1653     (goto-char (point-min))
1654     (while (and (setq proc (get-buffer-process buf))
1655                 (memq (process-status proc) '(open run))
1656                 (not (re-search-forward regexp nil t)))
1657       (accept-process-output proc)
1658       (set-buffer buf)
1659       (goto-char (point-min)))))
1660
1661
1662 ;; ==========================================================================
1663 ;; Obsolete nntp-open-* connection methods -- drv
1664 ;; ==========================================================================
1665
1666 (defvoo nntp-open-telnet-envuser nil
1667   "*If non-nil, telnet session (client and server both) will support the ENVIRON option and not prompt for login name.")
1668
1669 (defvoo nntp-telnet-shell-prompt "bash\\|\$ *\r?$\\|> *\r?"
1670   "*Regular expression to match the shell prompt on the remote machine.")
1671
1672 (defvoo nntp-rlogin-program "rsh"
1673   "*Program used to log in on remote machines.
1674 The default is \"rsh\", but \"ssh\" is a popular alternative.")
1675
1676 (defvoo nntp-rlogin-parameters '("telnet" "-8" "${NNTPSERVER:=news}" "nntp")
1677   "*Parameters to `nntp-open-rlogin'.
1678 That function may be used as `nntp-open-connection-function'.  In that
1679 case, this list will be used as the parameter list given to rsh.")
1680
1681 (defvoo nntp-rlogin-user-name nil
1682   "*User name on remote system when using the rlogin connect method.")
1683
1684 (defvoo nntp-telnet-parameters
1685     '("exec" "telnet" "-8" "${NNTPSERVER:=news}" "nntp")
1686   "*Parameters to `nntp-open-telnet'.
1687 That function may be used as `nntp-open-connection-function'.  In that
1688 case, this list will be executed as a command after logging in
1689 via telnet.")
1690
1691 (defvoo nntp-telnet-user-name nil
1692   "User name to log in via telnet with.")
1693
1694 (defvoo nntp-telnet-passwd nil
1695   "Password to use to log in via telnet with.")
1696
1697 (defun nntp-open-telnet (buffer)
1698   (save-excursion
1699     (set-buffer buffer)
1700     (erase-buffer)
1701     (let ((proc (as-binary-process
1702                  (apply
1703                   'start-process
1704                   "nntpd" buffer nntp-telnet-command nntp-telnet-switches)))
1705           (case-fold-search t))
1706       (when (memq (process-status proc) '(open run))
1707         (nntp-wait-for-string "^r?telnet")
1708         (process-send-string proc "set escape \^X\n")
1709         (cond
1710          ((and nntp-open-telnet-envuser nntp-telnet-user-name)
1711           (process-send-string proc (concat "open " "-l" nntp-telnet-user-name
1712                                             nntp-address "\n")))
1713          (t
1714           (process-send-string proc (concat "open " nntp-address "\n"))))
1715         (cond
1716          ((not nntp-open-telnet-envuser)
1717           (nntp-wait-for-string "^\r*.?login:")
1718           (process-send-string
1719            proc (concat
1720                  (or nntp-telnet-user-name
1721                      (setq nntp-telnet-user-name (read-string "login: ")))
1722                  "\n"))))
1723         (nntp-wait-for-string "^\r*.?password:")
1724         (process-send-string
1725          proc (concat
1726                (or nntp-telnet-passwd
1727                    (setq nntp-telnet-passwd
1728                          (mail-source-read-passwd "Password: ")))
1729                "\n"))
1730         (nntp-wait-for-string nntp-telnet-shell-prompt)
1731         (process-send-string
1732          proc (concat (mapconcat 'identity nntp-telnet-parameters " ") "\n"))
1733         (nntp-wait-for-string "^\r*20[01]")
1734         (beginning-of-line)
1735         (delete-region (point-min) (point))
1736         (process-send-string proc "\^]")
1737         (nntp-wait-for-string "^r?telnet")
1738         (process-send-string proc "mode character\n")
1739         (accept-process-output proc 1)
1740         (sit-for 1)
1741         (goto-char (point-min))
1742         (forward-line 1)
1743         (delete-region (point) (point-max)))
1744       proc)))
1745
1746 (defun nntp-open-rlogin (buffer)
1747   "Open a connection to SERVER using rsh."
1748   (let ((proc (if nntp-rlogin-user-name
1749                   (as-binary-process
1750                    (apply 'start-process
1751                           "nntpd" buffer nntp-rlogin-program
1752                           nntp-address "-l" nntp-rlogin-user-name
1753                           nntp-rlogin-parameters))
1754                 (as-binary-process
1755                  (apply 'start-process
1756                         "nntpd" buffer nntp-rlogin-program nntp-address
1757                         nntp-rlogin-parameters)))))
1758     (save-excursion
1759       (set-buffer buffer)
1760       (nntp-wait-for-string "^\r*20[01]")
1761       (beginning-of-line)
1762       (delete-region (point-min) (point))
1763       proc)))
1764
1765
1766 ;; ==========================================================================
1767 ;; Replacements for the nntp-open-* functions -- drv
1768 ;; ==========================================================================
1769
1770 (defun nntp-open-telnet-stream (buffer)
1771   "Open a nntp connection by telnet'ing the news server.
1772
1773 Please refer to the following variables to customize the connection:
1774 - `nntp-pre-command',
1775 - `nntp-telnet-command',
1776 - `nntp-telnet-switches',
1777 - `nntp-address',
1778 - `nntp-port-number',
1779 - `nntp-end-of-line'."
1780   (let ((command `(,nntp-telnet-command
1781                    ,@nntp-telnet-switches
1782                    ,nntp-address ,nntp-port-number))
1783         proc)
1784     (and nntp-pre-command
1785          (push nntp-pre-command command))
1786     (setq proc (apply 'start-process "nntpd" buffer command))
1787     (save-excursion
1788       (set-buffer buffer)
1789       (nntp-wait-for-string "^\r*20[01]")
1790       (beginning-of-line)
1791       (delete-region (point-min) (point))
1792       proc)))
1793
1794 (defun nntp-open-via-rlogin-and-telnet (buffer)
1795   "Open a connection to an nntp server through an intermediate host.
1796 First rlogin to the remote host, and then telnet the real news server
1797 from there.
1798
1799 Please refer to the following variables to customize the connection:
1800 - `nntp-pre-command',
1801 - `nntp-via-rlogin-command',
1802 - `nntp-via-rlogin-command-switches',
1803 - `nntp-via-user-name',
1804 - `nntp-via-address',
1805 - `nntp-telnet-command',
1806 - `nntp-telnet-switches',
1807 - `nntp-address',
1808 - `nntp-port-number',
1809 - `nntp-end-of-line'."
1810   (let ((command `(,nntp-via-address
1811                    ,nntp-telnet-command
1812                    ,@nntp-telnet-switches))
1813         proc)
1814     (when nntp-via-user-name
1815       (setq command `("-l" ,nntp-via-user-name ,@command)))
1816     (when nntp-via-rlogin-command-switches
1817       (setq command (append nntp-via-rlogin-command-switches command)))
1818     (push nntp-via-rlogin-command command)
1819     (and nntp-pre-command
1820          (push nntp-pre-command command))
1821     (setq proc (as-binary-process
1822                 (apply 'start-process "nntpd" buffer command)))
1823     (save-excursion
1824       (set-buffer buffer)
1825       (nntp-wait-for-string "^r?telnet")
1826       (process-send-string proc (concat "open " nntp-address
1827                                         " " nntp-port-number "\n"))
1828       (nntp-wait-for-string "^\r*20[01]")
1829       (beginning-of-line)
1830       (delete-region (point-min) (point))
1831       proc)))
1832
1833 (defun nntp-open-via-telnet-and-telnet (buffer)
1834   "Open a connection to an nntp server through an intermediate host.
1835 First telnet the remote host, and then telnet the real news server
1836 from there.
1837
1838 Please refer to the following variables to customize the connection:
1839 - `nntp-pre-command',
1840 - `nntp-via-telnet-command',
1841 - `nntp-via-telnet-switches',
1842 - `nntp-via-address',
1843 - `nntp-via-envuser',
1844 - `nntp-via-user-name',
1845 - `nntp-via-user-password',
1846 - `nntp-via-shell-prompt',
1847 - `nntp-telnet-command',
1848 - `nntp-telnet-switches',
1849 - `nntp-address',
1850 - `nntp-port-number',
1851 - `nntp-end-of-line'."
1852   (save-excursion
1853     (set-buffer buffer)
1854     (erase-buffer)
1855     (let ((command `(,nntp-via-telnet-command ,@nntp-via-telnet-switches))
1856           (case-fold-search t)
1857           proc)
1858       (and nntp-pre-command (push nntp-pre-command command))
1859       (setq proc (apply 'start-process "nntpd" buffer command))
1860       (when (memq (process-status proc) '(open run))
1861         (nntp-wait-for-string "^r?telnet")
1862         (process-send-string proc "set escape \^X\n")
1863         (cond
1864          ((and nntp-via-envuser nntp-via-user-name)
1865           (process-send-string proc (concat "open " "-l" nntp-via-user-name
1866                                             nntp-via-address "\n")))
1867          (t
1868           (process-send-string proc (concat "open " nntp-via-address
1869                                             "\n"))))
1870         (when (not nntp-via-envuser)
1871           (nntp-wait-for-string "^\r*.?login:")
1872           (process-send-string proc
1873                                (concat
1874                                 (or nntp-via-user-name
1875                                     (setq nntp-via-user-name
1876                                           (read-string "login: ")))
1877                                 "\n")))
1878         (nntp-wait-for-string "^\r*.?password:")
1879         (process-send-string proc
1880                              (concat
1881                               (or nntp-via-user-password
1882                                   (setq nntp-via-user-password
1883                                         (mail-source-read-passwd
1884                                          "Password: ")))
1885                               "\n"))
1886         (nntp-wait-for-string nntp-via-shell-prompt)
1887         (let ((real-telnet-command `("exec"
1888                                      ,nntp-telnet-command
1889                                      ,@nntp-telnet-switches
1890                                      ,nntp-address
1891                                      ,nntp-port-number)))
1892           (process-send-string proc
1893                                (concat (mapconcat 'identity
1894                                                   real-telnet-command " ")
1895                                        "\n")))
1896         (nntp-wait-for-string "^\r*20[01]")
1897         (beginning-of-line)
1898         (delete-region (point-min) (point))
1899         (process-send-string proc "\^]")
1900         (nntp-wait-for-string "^r?telnet")
1901         (process-send-string proc "mode character\n")
1902         (accept-process-output proc 1)
1903         (sit-for 1)
1904         (goto-char (point-min))
1905         (forward-line 1)
1906         (delete-region (point) (point-max)))
1907       proc)))
1908
1909 (provide 'nntp)
1910
1911 ;;; nntp.el ends here