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