Synch with Oort Gnus.
[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"
768                                                         (- (point) 3) t))
769                              (not (re-search-backward "^\\.\r?\n"
770                                                       (- (point) 4) t)))))
771                (nntp-accept-response)))
772
773            ;; Now all replies are received.  We remove CRs.
774            (unless (gnus-buffer-live-p buf)
775              (nnheader-report 'nntp "Connection to %s is closed." server)
776              (throw 'done nil))
777            (set-buffer buf)
778            (goto-char (point-min))
779            (while (search-forward "\r" nil t)
780              (replace-match "" t t))
781
782            (if (not nntp-server-list-active-group)
783                (progn
784                  (copy-to-buffer nntp-server-buffer (point-min) (point-max))
785                  'group)
786              ;; We have read active entries, so we just delete the
787              ;; superfluous gunk.
788              (goto-char (point-min))
789              (while (re-search-forward "^[.2-5]" nil t)
790                (delete-region (match-beginning 0)
791                               (progn (forward-line 1) (point))))
792              (copy-to-buffer nntp-server-buffer (point-min) (point-max))
793              'active)))))))
794
795 (deffoo nntp-retrieve-articles (articles &optional group server)
796   (nntp-with-open-group
797     group server
798    (save-excursion
799      (let ((number (length articles))
800            (articles articles)
801            (count 0)
802            (received 0)
803            (last-point (point-min))
804            (buf (nntp-find-connection-buffer nntp-server-buffer))
805            (nntp-inhibit-erase t)
806            (map (apply 'vector articles))
807            (point 1)
808            article)
809        (set-buffer buf)
810        (erase-buffer)
811        ;; Send ARTICLE command.
812        (while (setq article (pop articles))
813          (nntp-send-command
814           nil
815           "ARTICLE" (if (numberp article)
816                         (int-to-string article)
817                       ;; `articles' is either a list of article numbers
818                       ;; or a list of article IDs.
819                       article))
820          (incf count)
821          ;; Every 400 requests we have to read the stream in
822          ;; order to avoid deadlocks.
823          (when (or (null articles)      ;All requests have been sent.
824                    (zerop (% count nntp-maximum-request)))
825            (nntp-accept-response)
826            (while (progn
827                     (set-buffer buf)
828                     (goto-char last-point)
829                     ;; Count replies.
830                     (while (nntp-next-result-arrived-p)
831                       (aset map received (cons (aref map received) (point)))
832                       (setq last-point (point))
833                       (incf received))
834                     (< received count))
835              ;; If number of headers is greater than 100, give
836              ;;  informative messages.
837              (and (numberp nntp-large-newsgroup)
838                   (> number nntp-large-newsgroup)
839                   (zerop (% received 20))
840                   (nnheader-message 6 "NNTP: Receiving articles... %d%%"
841                                     (/ (* received 100) number)))
842              (nntp-accept-response))))
843        (and (numberp nntp-large-newsgroup)
844             (> number nntp-large-newsgroup)
845             (nnheader-message 6 "NNTP: Receiving articles...done"))
846
847        ;; Now we have all the responses.  We go through the results,
848        ;; wash it and copy it over to the server buffer.
849        (set-buffer nntp-server-buffer)
850        (erase-buffer)
851        (setq last-point (point-min))
852        (mapcar
853         (lambda (entry)
854           (narrow-to-region
855            (setq point (goto-char (point-max)))
856            (progn
857              (insert-buffer-substring buf last-point (cdr entry))
858              (point-max)))
859           (setq last-point (cdr entry))
860           (nntp-decode-text)
861           (widen)
862           (cons (car entry) point))
863         map)))))
864
865 (defun nntp-try-list-active (group)
866   (nntp-list-active-group group)
867   (save-excursion
868     (set-buffer nntp-server-buffer)
869     (goto-char (point-min))
870     (cond ((or (eobp)
871                (looking-at "5[0-9]+"))
872            (setq nntp-server-list-active-group nil))
873           (t
874            (setq nntp-server-list-active-group t)))))
875
876 (deffoo nntp-list-active-group (group &optional server)
877   "Return the active info on GROUP (which can be a regexp)."
878   (nntp-with-open-group
879    nil server
880    (nntp-send-command "^\\.*\r?\n" "LIST ACTIVE" group)))
881
882 (deffoo nntp-request-group-articles (group &optional server)
883   "Return the list of existing articles in GROUP."
884   (nntp-with-open-group
885    nil server
886    (nntp-send-command "^\\.*\r?\n" "LISTGROUP" group)))
887
888 (deffoo nntp-request-article (article &optional group server buffer command)
889   (nntp-with-open-group
890     group server
891     (when (nntp-send-command-and-decode
892            "\r?\n\\.\r?\n" "ARTICLE"
893            (if (numberp article) (int-to-string article) article))
894       (if (and buffer
895                (not (equal buffer nntp-server-buffer)))
896           (save-excursion
897             (set-buffer nntp-server-buffer)
898             (copy-to-buffer buffer (point-min) (point-max))
899             (nntp-find-group-and-number group))
900         (nntp-find-group-and-number group)))))
901
902 (deffoo nntp-request-head (article &optional group server)
903   (nntp-with-open-group
904    group server
905    (when (nntp-send-command
906           "\r?\n\\.\r?\n" "HEAD"
907           (if (numberp article) (int-to-string article) article))
908      (prog1
909          (nntp-find-group-and-number group)
910        (nntp-decode-text)))))
911
912 (deffoo nntp-request-body (article &optional group server)
913   (nntp-with-open-group
914    group server
915    (nntp-send-command-and-decode
916     "\r?\n\\.\r?\n" "BODY"
917     (if (numberp article) (int-to-string article) article))))
918
919 (deffoo nntp-request-group (group &optional server dont-check)
920   (nntp-with-open-group 
921     nil server
922     (when (nntp-send-command "^[245].*\n" "GROUP" group)
923       (let ((entry (nntp-find-connection-entry nntp-server-buffer)))
924         (setcar (cddr entry) group)))))
925
926 (deffoo nntp-close-group (group &optional server)
927   t)
928
929 (deffoo nntp-server-opened (&optional server)
930   "Say whether a connection to SERVER has been opened."
931   (and (nnoo-current-server-p 'nntp server)
932        nntp-server-buffer
933        (gnus-buffer-live-p nntp-server-buffer)
934        (nntp-find-connection nntp-server-buffer)))
935
936 (deffoo nntp-open-server (server &optional defs connectionless)
937   (nnheader-init-server-buffer)
938   (if (nntp-server-opened server)
939       t
940     (when (or (stringp (car defs))
941               (numberp (car defs)))
942       (setq defs (cons (list 'nntp-port-number (car defs)) (cdr defs))))
943     (unless (assq 'nntp-address defs)
944       (setq defs (append defs (list (list 'nntp-address server)))))
945     (nnoo-change-server 'nntp server defs)
946     (unless connectionless
947       (or (nntp-find-connection nntp-server-buffer)
948           (nntp-open-connection nntp-server-buffer)))))
949
950 (deffoo nntp-close-server (&optional server)
951   (nntp-possibly-change-group nil server t)
952   (let ((process (nntp-find-connection nntp-server-buffer)))
953     (while process
954       (when (memq (process-status process) '(open run))
955         (ignore-errors
956           (nntp-send-string process "QUIT")
957           (unless (eq nntp-open-connection-function 'nntp-open-network-stream)
958             ;; Ok, this is evil, but when using telnet and stuff
959             ;; as the connection method, it's important that the
960             ;; QUIT command actually is sent out before we kill
961             ;; the process.
962             (sleep-for 1))))
963       (nntp-kill-buffer (process-buffer process))
964       (setq process (car (pop nntp-connection-alist))))
965     (nnoo-close-server 'nntp)))
966
967 (deffoo nntp-request-close ()
968   (let (process)
969     (while (setq process (pop nntp-connection-list))
970       (when (memq (process-status process) '(open run))
971         (ignore-errors
972           (nntp-send-string process "QUIT")
973           (unless (eq nntp-open-connection-function 'nntp-open-network-stream)
974             ;; Ok, this is evil, but when using telnet and stuff
975             ;; as the connection method, it's important that the
976             ;; QUIT command actually is sent out before we kill
977             ;; the process.
978             (sleep-for 1))))
979       (nntp-kill-buffer (process-buffer process)))))
980
981 (deffoo nntp-request-list (&optional server)
982   "List active groups.  If `nntp-list-options' is non-nil, the listing
983 output from the server will be restricted to the specified newsgroups.
984 If `nntp-options-subscribe' is non-nil, remove newsgroups that do not
985 match the regexp.  If `nntp-options-not-subscribe' is non-nil, remove
986 newsgroups that match the regexp."
987   (nntp-with-open-group
988    nil server
989    (with-current-buffer nntp-server-buffer
990      (prog1
991          (if (not nntp-list-options)
992              (nntp-send-command-and-decode "\r?\n\\.\r?\n" "LIST")
993            (let ((options (if (consp nntp-list-options)
994                               nntp-list-options
995                             (list nntp-list-options)))
996                  (ret t))
997              (erase-buffer)
998              (while options
999                (goto-char (point-max))
1000                (narrow-to-region (point) (point))
1001                (setq ret (and ret
1002                               (nntp-send-command-nodelete
1003                                "\r?\n\\.\r?\n"
1004                                (format "LIST ACTIVE %s" (car options))))
1005                      options (cdr options))
1006                (nntp-decode-text))
1007              (widen)
1008              ret))
1009        (when (and (stringp nntp-options-subscribe)
1010                   (not (string-equal "" nntp-options-subscribe)))
1011          (goto-char (point-min))
1012          (keep-lines nntp-options-subscribe))
1013        (when (and (stringp nntp-options-not-subscribe)
1014                   (not (string-equal "" nntp-options-not-subscribe)))
1015          (goto-char (point-min))
1016          (flush-lines nntp-options-subscribe))))))
1017
1018 (deffoo nntp-request-list-newsgroups (&optional server)
1019   (nntp-with-open-group
1020    nil server
1021    (nntp-send-command "\r?\n\\.\r?\n" "LIST NEWSGROUPS")))
1022
1023 (deffoo nntp-request-newgroups (date &optional server)
1024   (nntp-with-open-group
1025    nil server
1026    (save-excursion
1027      (set-buffer nntp-server-buffer)
1028      (let* ((time (date-to-time date))
1029             (ls (- (cadr time) (nth 8 (decode-time time)))))
1030        (cond ((< ls 0)
1031               (setcar time (1- (car time)))
1032               (setcar (cdr time) (+ ls 65536)))
1033              ((>= ls 65536)
1034               (setcar time (1+ (car time)))
1035               (setcar (cdr time) (- ls 65536)))
1036              (t
1037               (setcar (cdr time) ls)))
1038        (prog1
1039            (nntp-send-command
1040             "^\\.\r?\n" "NEWGROUPS"
1041             (format-time-string "%y%m%d %H%M%S" time)
1042             "GMT")
1043          (nntp-decode-text))))))
1044
1045 (deffoo nntp-request-post (&optional server)
1046   (nntp-with-open-group
1047    nil server
1048    (when (nntp-send-command "^[23].*\r?\n" "POST")
1049      (let ((response (with-current-buffer nntp-server-buffer
1050                        nntp-process-response))
1051            server-id)
1052        (when (and response
1053                   (string-match "^[23].*\\(<[^\t\n @<>]+@[^\t\n @<>]+>\\)"
1054                                 response))
1055          (setq server-id (match-string 1 response))
1056          (narrow-to-region (goto-char (point-min))
1057                            (if (search-forward "\n\n" nil t)
1058                                (1- (point))
1059                              (point-max)))
1060          (unless (mail-fetch-field "Message-ID")
1061            (goto-char (point-min))
1062            (insert "Message-ID: " server-id "\n"))
1063          (widen))
1064        (run-hooks 'nntp-prepare-post-hook)
1065        (nntp-send-buffer "^[23].*\n")))))
1066
1067 (deffoo nntp-request-type (group article)
1068   'news)
1069
1070 (deffoo nntp-asynchronous-p ()
1071   t)
1072
1073 ;;; Hooky functions.
1074
1075 (defun nntp-send-mode-reader ()
1076   "Send the MODE READER command to the nntp server.
1077 This function is supposed to be called from `nntp-server-opened-hook'.
1078 It will make innd servers spawn an nnrpd process to allow actual article
1079 reading."
1080   (nntp-send-command "^.*\n" "MODE READER"))
1081
1082 (defun nntp-send-authinfo (&optional send-if-force)
1083   "Send the AUTHINFO to the nntp server.
1084 It will look in the \"~/.authinfo\" file for matching entries.  If
1085 nothing suitable is found there, it will prompt for a user name
1086 and a password.
1087
1088 If SEND-IF-FORCE, only send authinfo to the server if the
1089 .authinfo file has the FORCE token."
1090   (let* ((list (gnus-parse-netrc nntp-authinfo-file))
1091          (alist (gnus-netrc-machine list nntp-address "nntp"))
1092          (force (gnus-netrc-get alist "force"))
1093          (user (or (gnus-netrc-get alist "login") nntp-authinfo-user))
1094          (passwd (gnus-netrc-get alist "password")))
1095     (when (or (not send-if-force)
1096               force)
1097       (unless user
1098         (setq user (read-string (format "NNTP (%s) user name: " nntp-address))
1099               nntp-authinfo-user user))
1100       (unless (member user '(nil ""))
1101         (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" user)
1102         (when t                         ;???Should check if AUTHINFO succeeded
1103           (nntp-send-command
1104            "^2.*\r?\n" "AUTHINFO PASS"
1105            (or passwd
1106                nntp-authinfo-password
1107                (setq nntp-authinfo-password
1108                      (mail-source-read-passwd
1109                       (format "NNTP (%s@%s) password: "
1110                               user nntp-address))))))))))
1111
1112 (defun nntp-send-nosy-authinfo ()
1113   "Send the AUTHINFO to the nntp server."
1114   (let ((user (read-string (format "NNTP (%s) user name: " nntp-address))))
1115     (unless (member user '(nil ""))
1116       (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" user)
1117       (when t                           ;???Should check if AUTHINFO succeeded
1118         (nntp-send-command "^2.*\r?\n" "AUTHINFO PASS"
1119                            (mail-source-read-passwd "NNTP (%s@%s) password: "
1120                                                     user nntp-address))))))
1121
1122 (defun nntp-send-authinfo-from-file ()
1123   "Send the AUTHINFO to the nntp server.
1124
1125 The authinfo login name is taken from the user's login name and the
1126 password contained in '~/.nntp-authinfo'."
1127   (when (file-exists-p "~/.nntp-authinfo")
1128     (with-temp-buffer
1129       (insert-file-contents "~/.nntp-authinfo")
1130       (goto-char (point-min))
1131       (nntp-send-command "^3.*\r?\n" "AUTHINFO USER" (user-login-name))
1132       (nntp-send-command
1133        "^2.*\r?\n" "AUTHINFO PASS"
1134        (buffer-substring (point) (progn (end-of-line) (point)))))))
1135
1136 ;;; Internal functions.
1137
1138 (defun nntp-handle-authinfo (process)
1139   "Take care of an authinfo response from the server."
1140   (let ((last nntp-last-command))
1141     (funcall nntp-authinfo-function)
1142     ;; We have to re-send the function that was interrupted by
1143     ;; the authinfo request.
1144     (save-excursion
1145       (set-buffer nntp-server-buffer)
1146       (erase-buffer))
1147     (nntp-send-string process last)))
1148
1149 (defun nntp-make-process-buffer (buffer)
1150   "Create a new, fresh buffer usable for nntp process connections."
1151   (save-excursion
1152     (set-buffer
1153      (generate-new-buffer
1154       (format " *server %s %s %s*"
1155               nntp-address nntp-port-number
1156               (gnus-buffer-exists-p buffer))))
1157     (set (make-local-variable 'after-change-functions) nil)
1158     (set (make-local-variable 'nntp-process-wait-for) nil)
1159     (set (make-local-variable 'nntp-process-callback) nil)
1160     (set (make-local-variable 'nntp-process-to-buffer) nil)
1161     (set (make-local-variable 'nntp-process-start-point) nil)
1162     (set (make-local-variable 'nntp-process-decode) nil)
1163     (current-buffer)))
1164
1165 (defun nntp-open-connection (buffer)
1166   "Open a connection to PORT on ADDRESS delivering output to BUFFER."
1167   (run-hooks 'nntp-prepare-server-hook)
1168   (let* ((pbuffer (nntp-make-process-buffer buffer))
1169          (timer
1170           (and nntp-connection-timeout
1171                (nnheader-run-at-time
1172                 nntp-connection-timeout nil
1173                 `(lambda ()
1174                    (nntp-kill-buffer ,pbuffer)))))
1175          (process
1176           (condition-case ()
1177               (funcall nntp-open-connection-function pbuffer)
1178             (error nil)
1179             (quit
1180              (message "Quit opening connection")
1181              (nntp-kill-buffer pbuffer)
1182              (signal 'quit nil)
1183              nil))))
1184     (when timer
1185       (nnheader-cancel-timer timer))
1186     (unless process
1187       (nntp-kill-buffer pbuffer))
1188     (when (and (buffer-name pbuffer)
1189                process)
1190       (process-kill-without-query process)
1191       (if (and (nntp-wait-for process "^2.*\n" buffer nil t)
1192                (memq (process-status process) '(open run)))
1193           (prog1
1194               (caar (push (list process buffer nil) nntp-connection-alist))
1195             (push process nntp-connection-list)
1196             (save-excursion
1197               (set-buffer pbuffer)
1198               (nntp-read-server-type)
1199               (erase-buffer)
1200               (set-buffer nntp-server-buffer)
1201               (let ((nnheader-callback-function nil))
1202                 (run-hooks 'nntp-server-opened-hook)
1203                 (nntp-send-authinfo t))))
1204         (nntp-kill-buffer (process-buffer process))
1205         nil))))
1206
1207 (defun nntp-open-network-stream (buffer)
1208   (open-network-stream-as-binary
1209    "nntpd" buffer nntp-address nntp-port-number))
1210
1211 (defun nntp-open-ssl-stream (buffer)
1212   (let ((proc (open-ssl-stream "nntpd" buffer nntp-address nntp-port-number)))
1213     (save-excursion
1214       (set-buffer buffer)
1215       (nntp-wait-for-string "^\r*20[01]")
1216       (beginning-of-line)
1217       (delete-region (point-min) (point))
1218       proc)))
1219
1220 (defun nntp-read-server-type ()
1221   "Find out what the name of the server we have connected to is."
1222   ;; Wait for the status string to arrive.
1223   (setq nntp-server-type (buffer-string))
1224   (let ((alist nntp-server-action-alist)
1225         (case-fold-search t)
1226         entry)
1227     ;; Run server-specific commands.
1228     (while alist
1229       (setq entry (pop alist))
1230       (when (string-match (car entry) nntp-server-type)
1231         (if (and (listp (cadr entry))
1232                  (not (eq 'lambda (caadr entry))))
1233             (eval (cadr entry))
1234           (funcall (cadr entry)))))))
1235
1236 (defun nntp-async-wait (process wait-for buffer decode callback)
1237   (save-excursion
1238     (set-buffer (process-buffer process))
1239     (unless nntp-inside-change-function
1240       (erase-buffer))
1241     (setq nntp-process-wait-for wait-for
1242           nntp-process-to-buffer buffer
1243           nntp-process-decode decode
1244           nntp-process-callback callback
1245           nntp-process-start-point (point-max))
1246     (setq after-change-functions '(nntp-after-change-function))
1247     (if nntp-async-needs-kluge
1248         (nntp-async-kluge process))))
1249
1250 (defun nntp-async-kluge (process)
1251   ;; emacs 20.3 bug: process output with encoding 'binary
1252   ;; doesn't trigger after-change-functions.
1253   (unless nntp-async-timer
1254     (setq nntp-async-timer
1255           (nnheader-run-at-time 1 1 'nntp-async-timer-handler)))
1256   (add-to-list 'nntp-async-process-list process))
1257
1258 (defun nntp-async-timer-handler ()
1259   (mapcar
1260    (lambda (proc)
1261      (if (memq (process-status proc) '(open run))
1262          (nntp-async-trigger proc)
1263        (nntp-async-stop proc)))
1264    nntp-async-process-list))
1265
1266 (defun nntp-async-stop (proc)
1267   (setq nntp-async-process-list (delq proc nntp-async-process-list))
1268   (when (and nntp-async-timer (not nntp-async-process-list))
1269     (nnheader-cancel-timer nntp-async-timer)
1270     (setq nntp-async-timer nil)))
1271
1272 (defun nntp-after-change-function (beg end len)
1273   (unwind-protect
1274       ;; we only care about insertions at eob
1275       (when (and (eq 0 len) (eq (point-max) end))
1276         (save-match-data
1277           (let ((proc (get-buffer-process (current-buffer))))
1278             (when proc
1279               (nntp-async-trigger proc)))))
1280     ;; any throw from after-change-functions will leave it
1281     ;; set to nil.  so we reset it here, if necessary.
1282     (when quit-flag
1283       (setq after-change-functions '(nntp-after-change-function)))))
1284
1285 (defun nntp-async-trigger (process)
1286   (save-excursion
1287     (set-buffer (process-buffer process))
1288     (when nntp-process-callback
1289       ;; do we have an error message?
1290       (goto-char nntp-process-start-point)
1291       (if (memq (following-char) '(?4 ?5))
1292           ;; wants credentials?
1293           (if (looking-at "480")
1294               (nntp-handle-authinfo process)
1295             ;; report error message.
1296             (nntp-snarf-error-message)
1297             (nntp-do-callback nil))
1298
1299         ;; got what we expect?
1300         (goto-char (point-max))
1301         (when (re-search-backward
1302                nntp-process-wait-for nntp-process-start-point t)
1303           (let ((response (match-string 0)))
1304             (with-current-buffer nntp-server-buffer
1305               (setq nntp-process-response response)))
1306           (nntp-async-stop process)
1307           ;; convert it.
1308           (when (gnus-buffer-exists-p nntp-process-to-buffer)
1309             (let ((buf (current-buffer))
1310                   (start nntp-process-start-point)
1311                   (decode nntp-process-decode))
1312               (save-excursion
1313                 (set-buffer nntp-process-to-buffer)
1314                 (goto-char (point-max))
1315                 (save-restriction
1316                   (narrow-to-region (point) (point))
1317                   (insert-buffer-substring buf start)
1318                   (when decode
1319                     (nntp-decode-text))))))
1320           ;; report it.
1321           (goto-char (point-max))
1322           (nntp-do-callback
1323            (buffer-name (get-buffer nntp-process-to-buffer))))))))
1324
1325 (defun nntp-do-callback (arg)
1326   (let ((callback nntp-process-callback)
1327         (nntp-inside-change-function t))
1328     (setq nntp-process-callback nil)
1329     (funcall callback arg)))
1330
1331 (defun nntp-snarf-error-message ()
1332   "Save the error message in the current buffer."
1333   (let ((message (buffer-string)))
1334     (while (string-match "[\r\n]+" message)
1335       (setq message (replace-match " " t t message)))
1336     (nnheader-report 'nntp message)
1337     message))
1338
1339 (defun nntp-accept-process-output (process &optional timeout)
1340   "Wait for output from PROCESS and message some dots."
1341   (save-excursion
1342     (set-buffer (or (nntp-find-connection-buffer nntp-server-buffer)
1343                     nntp-server-buffer))
1344     (let ((len (/ (point-max) 1024))
1345           message-log-max)
1346       (unless (< len 10)
1347         (setq nntp-have-messaged t)
1348         (nnheader-message 7 "nntp read: %dk" len)))
1349     (if timeout
1350         (accept-process-output process timeout)
1351       (accept-process-output process 0 100))
1352     ;; accept-process-output may update status of process to indicate
1353     ;; that the server has closed the connection.  This MUST be
1354     ;; handled here as the buffer restored by the save-excursion may
1355     ;; be the process's former output buffer (i.e. now killed)
1356     (or (and process 
1357             (memq (process-status process) '(open run)))
1358         (nntp-report "Server closed connection"))))
1359
1360 (defun nntp-accept-response ()
1361   "Wait for output from the process that outputs to BUFFER."
1362   (nntp-accept-process-output (nntp-find-connection nntp-server-buffer)))
1363
1364 (defun nntp-possibly-change-group (group server &optional connectionless)
1365   (let ((nnheader-callback-function nil))
1366     (when server
1367       (or (nntp-server-opened server)
1368           (nntp-open-server server nil connectionless)))
1369
1370     (unless connectionless
1371       (or (nntp-find-connection nntp-server-buffer)
1372           (nntp-open-connection nntp-server-buffer))))
1373
1374   (when group
1375     (let ((entry (nntp-find-connection-entry nntp-server-buffer)))
1376       (when (not (equal group (caddr entry)))
1377         (save-excursion
1378           (set-buffer (process-buffer (car entry)))
1379           (erase-buffer)
1380           (nntp-send-command "^[245].*\n" "GROUP" group)
1381           (setcar (cddr entry) group)
1382           (erase-buffer)
1383           (save-excursion
1384             (set-buffer nntp-server-buffer)
1385             (erase-buffer)))))))
1386
1387 (defun nntp-decode-text (&optional cr-only)
1388   "Decode the text in the current buffer."
1389   (goto-char (point-min))
1390   (while (search-forward "\r" nil t)
1391     (delete-char -1))
1392   (unless cr-only
1393     ;; Remove trailing ".\n" end-of-transfer marker.
1394     (goto-char (point-max))
1395     (forward-line -1)
1396     (when (looking-at ".\n")
1397       (delete-char 2))
1398     ;; Delete status line.
1399     (goto-char (point-min))
1400     (while (looking-at "[1-5][0-9][0-9] .*\n")
1401       ;; For some unknown reason, there is more than one status line.
1402       (delete-region (point) (progn (forward-line 1) (point))))
1403     ;; Remove "." -> ".." encoding.
1404     (while (search-forward "\n.." nil t)
1405       (delete-char -1))))
1406
1407 (defun nntp-encode-text ()
1408   "Encode the text in the current buffer."
1409   (save-excursion
1410     ;; Replace "." at beginning of line with "..".
1411     (goto-char (point-min))
1412     (while (re-search-forward "^\\." nil t)
1413       (insert "."))
1414     (goto-char (point-max))
1415     ;; Insert newline at the end of the buffer.
1416     (unless (bolp)
1417       (insert "\n"))
1418     ;; Insert `.' at end of buffer (end of text mark).
1419     (goto-char (point-max))
1420     (insert ".\n")
1421     (goto-char (point-min))
1422     (while (not (eobp))
1423       (end-of-line)
1424       (delete-char 1)
1425       (insert nntp-end-of-line))))
1426
1427 (defun nntp-retrieve-headers-with-xover (articles &optional fetch-old)
1428   (set-buffer nntp-server-buffer)
1429   (erase-buffer)
1430   (cond
1431
1432    ;; This server does not talk NOV.
1433    ((not nntp-server-xover)
1434     nil)
1435
1436    ;; We don't care about gaps.
1437    ((or (not nntp-nov-gap)
1438         fetch-old)
1439     (nntp-send-xover-command
1440      (if fetch-old
1441          (if (numberp fetch-old)
1442              (max 1 (- (car articles) fetch-old))
1443            1)
1444        (car articles))
1445      (car (last articles)) 'wait)
1446
1447     (goto-char (point-min))
1448     (when (looking-at "[1-5][0-9][0-9] .*\n")
1449       (delete-region (point) (progn (forward-line 1) (point))))
1450     (while (search-forward "\r" nil t)
1451       (replace-match "" t t))
1452     (goto-char (point-max))
1453     (forward-line -1)
1454     (when (looking-at "\\.")
1455       (delete-region (point) (progn (forward-line 1) (point)))))
1456
1457    ;; We do it the hard way.  For each gap, an XOVER command is sent
1458    ;; to the server.  We do not wait for a reply from the server, we
1459    ;; just send them off as fast as we can.  That means that we have
1460    ;; to count the number of responses we get back to find out when we
1461    ;; have gotten all we asked for.
1462    ((numberp nntp-nov-gap)
1463     (let ((count 0)
1464           (received 0)
1465           last-point
1466           in-process-buffer-p
1467           (buf nntp-server-buffer)
1468           (process-buffer (nntp-find-connection-buffer nntp-server-buffer))
1469           first
1470           last)
1471       ;; We have to check `nntp-server-xover'.  If it gets set to nil,
1472       ;; that means that the server does not understand XOVER, but we
1473       ;; won't know that until we try.
1474       (while (and nntp-server-xover articles)
1475         (setq first (car articles))
1476         ;; Search forward until we find a gap, or until we run out of
1477         ;; articles.
1478         (while (and (cdr articles)
1479                     (< (- (nth 1 articles) (car articles)) nntp-nov-gap))
1480           (setq articles (cdr articles)))
1481
1482         (setq in-process-buffer-p (stringp nntp-server-xover))
1483         (nntp-send-xover-command first (setq last (car articles)))
1484         (setq articles (cdr articles))
1485
1486         (when (and nntp-server-xover in-process-buffer-p)
1487           ;; Don't count tried request.
1488           (setq count (1+ count))
1489
1490           ;; Every 400 requests we have to read the stream in
1491           ;; order to avoid deadlocks.
1492           (when (or (null articles)     ;All requests have been sent.
1493                     (= 1 (% count nntp-maximum-request)))
1494
1495             (nntp-accept-response)
1496             ;; On some Emacs versions the preceding function has a
1497             ;; tendency to change the buffer.  Perhaps.  It's quite
1498             ;; difficult to reproduce, because it only seems to happen
1499             ;; once in a blue moon.
1500             (set-buffer process-buffer)
1501             (while (progn
1502                      (goto-char (or last-point (point-min)))
1503                      ;; Count replies.
1504                      (while (re-search-forward "^[0-9][0-9][0-9] .*\n" nil t)
1505                        (incf received))
1506                      (setq last-point (point))
1507                      (or (< received count)
1508                          ;; I haven't started reading the final response
1509                          (progn
1510                            (goto-char (point-max))
1511                            (forward-line -1)
1512                            (not (looking-at "^\\.\r?\n")))))
1513               ;; I haven't read the end of the final response
1514               (nntp-accept-response)
1515               (set-buffer process-buffer))))
1516
1517         ;; Some nntp servers seem to have an extension to the XOVER
1518         ;; extension.  On these servers, requesting an article range
1519         ;; preceeding the active range does not return an error as
1520         ;; specified in the RFC.  What we instead get is the NOV entry
1521         ;; for the first available article.  Obviously, a client can
1522         ;; use that entry to avoid making unnecessary requests.  The
1523         ;; only problem is for a client that assumes that the response
1524         ;; will always be within the requested ranage.  For such a
1525         ;; client, we can get N copies of the same entry (one for each
1526         ;; XOVER command sent to the server).
1527
1528         (when (<= count 1)
1529           (goto-char (point-min))
1530           (when (re-search-forward "^[0-9][0-9][0-9] .*\n\\([0-9]+\\)" nil t)
1531             (let ((low-limit (string-to-int
1532                               (buffer-substring (match-beginning 1) 
1533                                                 (match-end 1)))))
1534               (while (and articles (<= (car articles) low-limit))
1535                 (setq articles (cdr articles))))))
1536         (set-buffer buf))
1537
1538       (when nntp-server-xover
1539         (when in-process-buffer-p
1540           (set-buffer buf)
1541           (goto-char (point-max))
1542           (insert-buffer-substring process-buffer)
1543           (set-buffer process-buffer)
1544           (erase-buffer)
1545           (set-buffer buf))
1546
1547         ;; We remove any "." lines and status lines.
1548         (goto-char (point-min))
1549         (while (search-forward "\r" nil t)
1550           (delete-char -1))
1551         (goto-char (point-min))
1552         (delete-matching-lines "^\\.$\\|^[1-5][0-9][0-9] ")
1553         t))))
1554
1555   nntp-server-xover)
1556
1557 (defun nntp-send-xover-command (beg end &optional wait-for-reply)
1558   "Send the XOVER command to the server."
1559   (let ((range (format "%d-%d" beg end))
1560         (nntp-inhibit-erase t))
1561     (if (stringp nntp-server-xover)
1562         ;; If `nntp-server-xover' is a string, then we just send this
1563         ;; command.
1564         (if wait-for-reply
1565             (nntp-send-command-nodelete
1566              "\r?\n\\.\r?\n" nntp-server-xover range)
1567           ;; We do not wait for the reply.
1568           (nntp-send-command-nodelete nil nntp-server-xover range))
1569       (let ((commands nntp-xover-commands))
1570         ;; `nntp-xover-commands' is a list of possible XOVER commands.
1571         ;; We try them all until we get at positive response.
1572         (while (and commands (eq nntp-server-xover 'try))
1573           (nntp-send-command-nodelete "\r?\n\\.\r?\n" (car commands) range)
1574           (save-excursion
1575             (set-buffer nntp-server-buffer)
1576             (goto-char (point-min))
1577             (and (looking-at "[23]")    ; No error message.
1578                  ;; We also have to look at the lines.  Some buggy
1579                  ;; servers give back simple lines with just the
1580                  ;; article number.  How... helpful.
1581                  (progn
1582                    (forward-line 1)
1583                    (looking-at "[0-9]+\t...")) ; More text after number.
1584                  (setq nntp-server-xover (car commands))))
1585           (setq commands (cdr commands)))
1586         ;; If none of the commands worked, we disable XOVER.
1587         (when (eq nntp-server-xover 'try)
1588           (save-excursion
1589             (set-buffer nntp-server-buffer)
1590             (erase-buffer)
1591             (setq nntp-server-xover nil)))
1592         nntp-server-xover))))
1593
1594 (defun nntp-find-group-and-number (&optional group)
1595   (save-excursion
1596     (save-restriction
1597       (set-buffer nntp-server-buffer)
1598       (narrow-to-region (goto-char (point-min))
1599                         (or (search-forward "\n\n" nil t) (point-max)))
1600       (goto-char (point-min))
1601       ;; We first find the number by looking at the status line.
1602       (let ((number (and (looking-at "2[0-9][0-9] +\\([0-9]+\\) ")
1603                          (string-to-int
1604                           (buffer-substring (match-beginning 1)
1605                                             (match-end 1)))))
1606             newsgroups xref)
1607         (and number (zerop number) (setq number nil))
1608         (if number
1609             ;; Then we find the group name.
1610             (setq group
1611                   (cond
1612                    ;; If there is only one group in the Newsgroups
1613                    ;; header, then it seems quite likely that this
1614                    ;; article comes from that group, I'd say.
1615                    ((and (setq newsgroups
1616                                (mail-fetch-field "newsgroups"))
1617                          (not (string-match "," newsgroups)))
1618                     newsgroups)
1619                    ;; If there is more than one group in the
1620                    ;; Newsgroups header, then the Xref header should
1621                    ;; be filled out.  We hazard a guess that the group
1622                    ;; that has this article number in the Xref header
1623                    ;; is the one we are looking for.  This might very
1624                    ;; well be wrong if this article happens to have
1625                    ;; the same number in several groups, but that's
1626                    ;; life.
1627                    ((and (setq xref (mail-fetch-field "xref"))
1628                          number
1629                          (string-match
1630                           (format "\\([^ :]+\\):%d" number) xref))
1631                     (match-string 1 xref))
1632                    (t "")))
1633           (cond
1634            ((and (setq xref (mail-fetch-field "xref"))
1635                  (string-match
1636                   (if group
1637                       (concat "\\(" (regexp-quote group) "\\):\\([0-9]+\\)")
1638                     "\\([^ :]+\\):\\([0-9]+\\)")
1639                   xref))
1640             (setq group (match-string 1 xref)
1641                   number (string-to-int (match-string 2 xref))))
1642            ((and (setq newsgroups
1643                        (mail-fetch-field "newsgroups"))
1644                  (not (string-match "," newsgroups)))
1645             (setq group newsgroups))
1646            (group)
1647            (t (setq group ""))))
1648         (when (string-match "\r" group)
1649           (setq group (substring group 0 (match-beginning 0))))
1650         (cons group number)))))
1651
1652 (defun nntp-wait-for-string (regexp)
1653   "Wait until string arrives in the buffer."
1654   (let ((buf (current-buffer))
1655         proc)
1656     (goto-char (point-min))
1657     (while (and (setq proc (get-buffer-process buf))
1658                 (memq (process-status proc) '(open run))
1659                 (not (re-search-forward regexp nil t)))
1660       (accept-process-output proc)
1661       (set-buffer buf)
1662       (goto-char (point-min)))))
1663
1664
1665 ;; ==========================================================================
1666 ;; Obsolete nntp-open-* connection methods -- drv
1667 ;; ==========================================================================
1668
1669 (defvoo nntp-open-telnet-envuser nil
1670   "*If non-nil, telnet session (client and server both) will support the ENVIRON option and not prompt for login name.")
1671
1672 (defvoo nntp-telnet-shell-prompt "bash\\|\$ *\r?$\\|> *\r?"
1673   "*Regular expression to match the shell prompt on the remote machine.")
1674
1675 (defvoo nntp-rlogin-program "rsh"
1676   "*Program used to log in on remote machines.
1677 The default is \"rsh\", but \"ssh\" is a popular alternative.")
1678
1679 (defvoo nntp-rlogin-parameters '("telnet" "-8" "${NNTPSERVER:=news}" "nntp")
1680   "*Parameters to `nntp-open-rlogin'.
1681 That function may be used as `nntp-open-connection-function'.  In that
1682 case, this list will be used as the parameter list given to rsh.")
1683
1684 (defvoo nntp-rlogin-user-name nil
1685   "*User name on remote system when using the rlogin connect method.")
1686
1687 (defvoo nntp-telnet-parameters
1688     '("exec" "telnet" "-8" "${NNTPSERVER:=news}" "nntp")
1689   "*Parameters to `nntp-open-telnet'.
1690 That function may be used as `nntp-open-connection-function'.  In that
1691 case, this list will be executed as a command after logging in
1692 via telnet.")
1693
1694 (defvoo nntp-telnet-user-name nil
1695   "User name to log in via telnet with.")
1696
1697 (defvoo nntp-telnet-passwd nil
1698   "Password to use to log in via telnet with.")
1699
1700 (defun nntp-open-telnet (buffer)
1701   (save-excursion
1702     (set-buffer buffer)
1703     (erase-buffer)
1704     (let ((proc (as-binary-process
1705                  (apply
1706                   'start-process
1707                   "nntpd" buffer nntp-telnet-command nntp-telnet-switches)))
1708           (case-fold-search t))
1709       (when (memq (process-status proc) '(open run))
1710         (nntp-wait-for-string "^r?telnet")
1711         (process-send-string proc "set escape \^X\n")
1712         (cond
1713          ((and nntp-open-telnet-envuser nntp-telnet-user-name)
1714           (process-send-string proc (concat "open " "-l" nntp-telnet-user-name
1715                                             nntp-address "\n")))
1716          (t
1717           (process-send-string proc (concat "open " nntp-address "\n"))))
1718         (cond
1719          ((not nntp-open-telnet-envuser)
1720           (nntp-wait-for-string "^\r*.?login:")
1721           (process-send-string
1722            proc (concat
1723                  (or nntp-telnet-user-name
1724                      (setq nntp-telnet-user-name (read-string "login: ")))
1725                  "\n"))))
1726         (nntp-wait-for-string "^\r*.?password:")
1727         (process-send-string
1728          proc (concat
1729                (or nntp-telnet-passwd
1730                    (setq nntp-telnet-passwd
1731                          (mail-source-read-passwd "Password: ")))
1732                "\n"))
1733         (nntp-wait-for-string nntp-telnet-shell-prompt)
1734         (process-send-string
1735          proc (concat (mapconcat 'identity nntp-telnet-parameters " ") "\n"))
1736         (nntp-wait-for-string "^\r*20[01]")
1737         (beginning-of-line)
1738         (delete-region (point-min) (point))
1739         (process-send-string proc "\^]")
1740         (nntp-wait-for-string "^r?telnet")
1741         (process-send-string proc "mode character\n")
1742         (accept-process-output proc 1)
1743         (sit-for 1)
1744         (goto-char (point-min))
1745         (forward-line 1)
1746         (delete-region (point) (point-max)))
1747       proc)))
1748
1749 (defun nntp-open-rlogin (buffer)
1750   "Open a connection to SERVER using rsh."
1751   (let ((proc (if nntp-rlogin-user-name
1752                   (as-binary-process
1753                    (apply 'start-process
1754                           "nntpd" buffer nntp-rlogin-program
1755                           nntp-address "-l" nntp-rlogin-user-name
1756                           nntp-rlogin-parameters))
1757                 (as-binary-process
1758                  (apply 'start-process
1759                         "nntpd" buffer nntp-rlogin-program nntp-address
1760                         nntp-rlogin-parameters)))))
1761     (save-excursion
1762       (set-buffer buffer)
1763       (nntp-wait-for-string "^\r*20[01]")
1764       (beginning-of-line)
1765       (delete-region (point-min) (point))
1766       proc)))
1767
1768
1769 ;; ==========================================================================
1770 ;; Replacements for the nntp-open-* functions -- drv
1771 ;; ==========================================================================
1772
1773 (defun nntp-open-telnet-stream (buffer)
1774   "Open a nntp connection by telnet'ing the news server.
1775
1776 Please refer to the following variables to customize the connection:
1777 - `nntp-pre-command',
1778 - `nntp-telnet-command',
1779 - `nntp-telnet-switches',
1780 - `nntp-address',
1781 - `nntp-port-number',
1782 - `nntp-end-of-line'."
1783   (let ((command `(,nntp-telnet-command
1784                    ,@nntp-telnet-switches
1785                    ,nntp-address ,nntp-port-number))
1786         proc)
1787     (and nntp-pre-command
1788          (push nntp-pre-command command))
1789     (setq proc (apply 'start-process "nntpd" buffer command))
1790     (save-excursion
1791       (set-buffer buffer)
1792       (nntp-wait-for-string "^\r*20[01]")
1793       (beginning-of-line)
1794       (delete-region (point-min) (point))
1795       proc)))
1796
1797 (defun nntp-open-via-rlogin-and-telnet (buffer)
1798   "Open a connection to an nntp server through an intermediate host.
1799 First rlogin to the remote host, and then telnet the real news server
1800 from there.
1801
1802 Please refer to the following variables to customize the connection:
1803 - `nntp-pre-command',
1804 - `nntp-via-rlogin-command',
1805 - `nntp-via-rlogin-command-switches',
1806 - `nntp-via-user-name',
1807 - `nntp-via-address',
1808 - `nntp-telnet-command',
1809 - `nntp-telnet-switches',
1810 - `nntp-address',
1811 - `nntp-port-number',
1812 - `nntp-end-of-line'."
1813   (let ((command `(,nntp-via-address
1814                    ,nntp-telnet-command
1815                    ,@nntp-telnet-switches))
1816         proc)
1817     (when nntp-via-user-name
1818       (setq command `("-l" ,nntp-via-user-name ,@command)))
1819     (when nntp-via-rlogin-command-switches
1820       (setq command (append nntp-via-rlogin-command-switches command)))
1821     (push nntp-via-rlogin-command command)
1822     (and nntp-pre-command
1823          (push nntp-pre-command command))
1824     (setq proc (as-binary-process
1825                 (apply 'start-process "nntpd" buffer command)))
1826     (save-excursion
1827       (set-buffer buffer)
1828       (nntp-wait-for-string "^r?telnet")
1829       (process-send-string proc (concat "open " nntp-address
1830                                         " " nntp-port-number "\n"))
1831       (nntp-wait-for-string "^\r*20[01]")
1832       (beginning-of-line)
1833       (delete-region (point-min) (point))
1834       proc)))
1835
1836 (defun nntp-open-via-telnet-and-telnet (buffer)
1837   "Open a connection to an nntp server through an intermediate host.
1838 First telnet the remote host, and then telnet the real news server
1839 from there.
1840
1841 Please refer to the following variables to customize the connection:
1842 - `nntp-pre-command',
1843 - `nntp-via-telnet-command',
1844 - `nntp-via-telnet-switches',
1845 - `nntp-via-address',
1846 - `nntp-via-envuser',
1847 - `nntp-via-user-name',
1848 - `nntp-via-user-password',
1849 - `nntp-via-shell-prompt',
1850 - `nntp-telnet-command',
1851 - `nntp-telnet-switches',
1852 - `nntp-address',
1853 - `nntp-port-number',
1854 - `nntp-end-of-line'."
1855   (save-excursion
1856     (set-buffer buffer)
1857     (erase-buffer)
1858     (let ((command `(,nntp-via-telnet-command ,@nntp-via-telnet-switches))
1859           (case-fold-search t)
1860           proc)
1861       (and nntp-pre-command (push nntp-pre-command command))
1862       (setq proc (apply 'start-process "nntpd" buffer command))
1863       (when (memq (process-status proc) '(open run))
1864         (nntp-wait-for-string "^r?telnet")
1865         (process-send-string proc "set escape \^X\n")
1866         (cond
1867          ((and nntp-via-envuser nntp-via-user-name)
1868           (process-send-string proc (concat "open " "-l" nntp-via-user-name
1869                                             nntp-via-address "\n")))
1870          (t
1871           (process-send-string proc (concat "open " nntp-via-address
1872                                             "\n"))))
1873         (when (not nntp-via-envuser)
1874           (nntp-wait-for-string "^\r*.?login:")
1875           (process-send-string proc
1876                                (concat
1877                                 (or nntp-via-user-name
1878                                     (setq nntp-via-user-name
1879                                           (read-string "login: ")))
1880                                 "\n")))
1881         (nntp-wait-for-string "^\r*.?password:")
1882         (process-send-string proc
1883                              (concat
1884                               (or nntp-via-user-password
1885                                   (setq nntp-via-user-password
1886                                         (mail-source-read-passwd
1887                                          "Password: ")))
1888                               "\n"))
1889         (nntp-wait-for-string nntp-via-shell-prompt)
1890         (let ((real-telnet-command `("exec"
1891                                      ,nntp-telnet-command
1892                                      ,@nntp-telnet-switches
1893                                      ,nntp-address
1894                                      ,nntp-port-number)))
1895           (process-send-string proc
1896                                (concat (mapconcat 'identity
1897                                                   real-telnet-command " ")
1898                                        "\n")))
1899         (nntp-wait-for-string "^\r*20[01]")
1900         (beginning-of-line)
1901         (delete-region (point-min) (point))
1902         (process-send-string proc "\^]")
1903         (nntp-wait-for-string "^r?telnet")
1904         (process-send-string proc "mode character\n")
1905         (accept-process-output proc 1)
1906         (sit-for 1)
1907         (goto-char (point-min))
1908         (forward-line 1)
1909         (delete-region (point) (point-max)))
1910       proc)))
1911
1912 (provide 'nntp)
1913
1914 ;;; nntp.el ends here