aa34293f5b13cde066d1cd9496a10c6af9c699fd
[gnus] / lisp / nntp.el
1 ;;; nntp.el --- nntp access for Gnus
2
3 ;; Copyright (C) 1987, 1988, 1989, 1990, 1992, 1993,
4 ;;   1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002,
5 ;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
6
7 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.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, 51 Franklin Street, Fifth Floor, Boston,
25 ;; MA 02110-1301, USA.
26
27 ;;; Commentary:
28
29 ;;; Code:
30
31 (require 'nnheader)
32 (require 'nnoo)
33 (require 'gnus-util)
34 (require 'gnus)
35
36 (nnoo-declare nntp)
37
38 (eval-when-compile (require 'cl))
39
40 (defgroup nntp nil
41   "NNTP access for Gnus."
42   :group 'gnus)
43
44 (defvoo nntp-address nil
45   "Address of the physical nntp server.")
46
47 (defvoo nntp-port-number "nntp"
48   "Port number on the physical nntp server.")
49
50 (defvoo nntp-server-opened-hook '(nntp-send-mode-reader)
51   "*Hook used for sending commands to the server at startup.
52 The default value is `nntp-send-mode-reader', which makes an innd
53 server spawn an nnrpd server.")
54
55 (defvoo nntp-authinfo-function 'nntp-send-authinfo
56   "Function used to send AUTHINFO to the server.
57 It is called with no parameters.")
58
59 (defvoo nntp-server-action-alist
60     '(("nntpd 1\\.5\\.11t"
61        (remove-hook 'nntp-server-opened-hook 'nntp-send-mode-reader))
62       ("NNRP server Netscape"
63        (setq nntp-server-list-active-group nil)))
64   "Alist of regexps to match on server types and actions to be taken.
65 For instance, if you want Gnus to beep every time you connect
66 to innd, you could say something like:
67
68 \(setq nntp-server-action-alist
69        '((\"innd\" (ding))))
70
71 You probably don't want to do that, though.")
72
73 (defvoo nntp-open-connection-function 'nntp-open-network-stream
74   "*Function used for connecting to a remote system.
75 It will be called with the buffer to output in as argument.
76
77 Currently, five such functions are provided (please refer to their
78 respective doc string for more information), three of them establishing
79 direct connections to the nntp server, and two of them using an indirect
80 host.
81
82 Direct connections:
83 - `nntp-open-network-stream' (the default),
84 - `nntp-open-ssl-stream',
85 - `nntp-open-tls-stream',
86 - `nntp-open-telnet-stream'.
87
88 Indirect connections:
89 - `nntp-open-via-rlogin-and-telnet',
90 - `nntp-open-via-rlogin-and-netcat',
91 - `nntp-open-via-telnet-and-telnet'.")
92
93 (defvoo nntp-never-echoes-commands nil
94   "*Non-nil means the nntp server never echoes commands.
95 It is reported that some nntps server doesn't echo commands.  So, you
96 may want to set this to non-nil in the method for such a server setting
97 `nntp-open-connection-function' to `nntp-open-ssl-stream' for example.
98 Note that the `nntp-open-connection-functions-never-echo-commands'
99 variable overrides the nil value of this variable.")
100
101 (defvoo nntp-open-connection-functions-never-echo-commands
102     '(nntp-open-network-stream)
103   "*List of functions that never echo commands.
104 Add or set a function which you set to `nntp-open-connection-function'
105 to this list if it does not echo commands.  Note that a non-nil value
106 of the `nntp-never-echoes-commands' variable overrides this variable.")
107
108 (defvoo nntp-pre-command nil
109   "*Pre-command to use with the various nntp-open-via-* methods.
110 This is where you would put \"runsocks\" or stuff like that.")
111
112 (defvoo nntp-telnet-command "telnet"
113   "*Telnet command used to connect to the nntp server.
114 This command is used by the methods `nntp-open-telnet-stream',
115 `nntp-open-via-rlogin-and-telnet' and `nntp-open-via-telnet-and-telnet'.")
116
117 (defvoo nntp-telnet-switches '("-8")
118   "*Switches given to the telnet command `nntp-telnet-command'.")
119
120 (defvoo nntp-end-of-line "\r\n"
121   "*String to use on the end of lines when talking to the NNTP server.
122 This is \"\\r\\n\" by default, but should be \"\\n\" when using an indirect
123 connection method (nntp-open-via-*).")
124
125 (defvoo nntp-via-rlogin-command "rsh"
126   "*Rlogin command used to connect to an intermediate host.
127 This command is used by the methods `nntp-open-via-rlogin-and-telnet'
128 and `nntp-open-via-rlogin-and-netcat'.  The default is \"rsh\", but \"ssh\"
129 is a popular alternative.")
130
131 (defvoo nntp-via-rlogin-command-switches nil
132   "*Switches given to the rlogin command `nntp-via-rlogin-command'.
133 If you use \"ssh\" for `nntp-via-rlogin-command', you may set this to
134 \(\"-C\") in order to compress all data connections, otherwise set this
135 to \(\"-t\" \"-e\" \"none\") or (\"-C\" \"-t\" \"-e\" \"none\") if the telnet
136 command requires a pseudo-tty allocation on an intermediate host.")
137
138 (defvoo nntp-via-telnet-command "telnet"
139   "*Telnet command used to connect to an intermediate host.
140 This command is used by the `nntp-open-via-telnet-and-telnet' method.")
141
142 (defvoo nntp-via-telnet-switches '("-8")
143   "*Switches given to the telnet command `nntp-via-telnet-command'.")
144
145 (defvoo nntp-via-netcat-command "nc"
146   "*Netcat command used to connect to the nntp server.
147 This command is used by the `nntp-open-via-rlogin-and-netcat' method.")
148
149 (defvoo nntp-via-netcat-switches nil
150   "*Switches given to the netcat command `nntp-via-netcat-command'.")
151
152 (defvoo nntp-via-user-name nil
153   "*User name to log in on an intermediate host with.
154 This variable is used by the various nntp-open-via-* methods.")
155
156 (defvoo nntp-via-user-password nil
157   "*Password to use to log in on an intermediate host with.
158 This variable is used by the `nntp-open-via-telnet-and-telnet' method.")
159
160 (defvoo nntp-via-address nil
161   "*Address of an intermediate host to connect to.
162 This variable is used by the various nntp-open-via-* methods.")
163
164 (defvoo nntp-via-envuser nil
165   "*Whether both telnet client and server support the ENVIRON option.
166 If non-nil, there will be no prompt for a login name.")
167
168 (defvoo nntp-via-shell-prompt "bash\\|\$ *\r?$\\|> *\r?"
169   "*Regular expression to match the shell prompt on an intermediate host.
170 This variable is used by the `nntp-open-via-telnet-and-telnet' method.")
171
172 (defvoo nntp-large-newsgroup 50
173   "*The number of articles which indicates a large newsgroup.
174 If the number of articles is greater than the value, verbose
175 messages will be shown to indicate the current status.")
176
177 (defvoo nntp-maximum-request 400
178   "*The maximum number of the requests sent to the NNTP server at one time.
179 If Emacs hangs up while retrieving headers, set the variable to a
180 lower value.")
181
182 (defvoo nntp-nov-is-evil nil
183   "*If non-nil, nntp will never attempt to use XOVER when talking to the server.")
184
185 (defvoo nntp-xover-commands '("XOVER" "XOVERVIEW")
186   "*List of strings that are used as commands to fetch NOV lines from a server.
187 The strings are tried in turn until a positive response is gotten.  If
188 none of the commands are successful, nntp will just grab headers one
189 by one.")
190
191 (defvoo nntp-nov-gap 5
192   "*Maximum allowed gap between two articles.
193 If the gap between two consecutive articles is bigger than this
194 variable, split the XOVER request into two requests.")
195
196 (defvoo nntp-prepare-server-hook nil
197   "*Hook run before a server is opened.
198 If can be used to set up a server remotely, for instance.  Say you
199 have an account at the machine \"other.machine\".  This machine has
200 access to an NNTP server that you can't access locally.  You could
201 then use this hook to rsh to the remote machine and start a proxy NNTP
202 server there that you can connect to.  See also
203 `nntp-open-connection-function'")
204
205 (defvoo nntp-coding-system-for-read 'binary
206   "*Coding system to read from NNTP.")
207
208 (defvoo nntp-coding-system-for-write 'binary
209   "*Coding system to write to NNTP.")
210
211 ;; Marks
212 (defvoo nntp-marks-is-evil nil
213   "*If non-nil, Gnus will never generate and use marks file for nntp groups.
214 See `nnml-marks-is-evil' for more information.")
215
216 (defvoo nntp-marks-file-name ".marks")
217 (defvoo nntp-marks nil)
218 (defvar nntp-marks-modtime (gnus-make-hashtable))
219
220 (defcustom nntp-marks-directory
221   (nnheader-concat gnus-directory "marks/")
222   "*The directory where marks for nntp groups will be stored."
223   :group 'nntp
224   :type 'directory)
225
226 (defcustom nntp-authinfo-file "~/.authinfo"
227   ".netrc-like file that holds nntp authinfo passwords."
228   :group 'nntp
229   :type
230   '(choice file
231            (repeat :tag "Entries"
232                    :menu-tag "Inline"
233                    (list :format "%v"
234                          :value ("" ("login" . "") ("password" . ""))
235                          (string :tag "Host")
236                          (checklist :inline t
237                                     (cons :format "%v"
238                                           (const :format "" "login")
239                                           (string :format "Login: %v"))
240                                     (cons :format "%v"
241                                           (const :format "" "password")
242                                           (string :format "Password: %v")))))))
243
244 \f
245
246 (defvoo nntp-connection-timeout nil
247   "*Number of seconds to wait before an nntp connection times out.
248 If this variable is nil, which is the default, no timers are set.
249 NOTE: This variable is never seen to work in Emacs 20 and XEmacs 21.")
250
251 (defvoo nntp-prepare-post-hook nil
252   "*Hook run just before posting an article.  It is supposed to be used
253 to insert Cancel-Lock headers.")
254
255 ;;; Internal variables.
256
257 (defvar nntp-record-commands nil
258   "*If non-nil, nntp will record all commands in the \"*nntp-log*\" buffer.")
259
260 (defvar nntp-have-messaged nil)
261
262 (defvar nntp-process-wait-for nil)
263 (defvar nntp-process-to-buffer nil)
264 (defvar nntp-process-callback nil)
265 (defvar nntp-process-decode nil)
266 (defvar nntp-process-start-point nil)
267 (defvar nntp-inside-change-function nil)
268 (defvoo nntp-last-command-time nil)
269 (defvoo nntp-last-command nil)
270 (defvoo nntp-authinfo-password nil)
271 (defvoo nntp-authinfo-user nil)
272 (defvoo nntp-authinfo-force nil)
273
274 (defvar nntp-connection-list nil)
275
276 (defvoo nntp-server-type nil)
277 (defvoo nntp-connection-alist nil)
278 (defvoo nntp-status-string "")
279 (defconst nntp-version "nntp 5.0")
280 (defvoo nntp-inhibit-erase nil)
281 (defvoo nntp-inhibit-output nil)
282
283 (defvoo nntp-server-xover 'try)
284 (defvoo nntp-server-list-active-group 'try)
285
286 (defvar nntp-async-needs-kluge
287   (string-match "^GNU Emacs 20\\.3\\." (emacs-version))
288   "*When non-nil, nntp will poll asynchronous connections
289 once a second.  By default, this is turned on only for Emacs
290 20.3, which has a bug that breaks nntp's normal method of
291 noticing asynchronous data.")
292
293 (defvar nntp-async-timer nil)
294 (defvar nntp-async-process-list nil)
295
296 (defvar nntp-ssl-program
297   "openssl s_client -quiet -ssl3 -connect %s:%p"
298 "A string containing commands for SSL connections.
299 Within a string, %s is replaced with the server address and %p with
300 port number on server.  The program should accept IMAP commands on
301 stdin and return responses to stdout.")
302
303 (defvar nntp-authinfo-rejected nil
304 "A custom error condition used to report 'Authentication Rejected' errors.  
305 Condition handlers that match just this condition ensure that the nntp 
306 backend doesn't catch this error.")
307 (put 'nntp-authinfo-rejected 'error-conditions '(error nntp-authinfo-rejected))
308 (put 'nntp-authinfo-rejected 'error-message "Authorization Rejected")
309
310 \f
311
312 ;;; Internal functions.
313
314 (defsubst nntp-send-string (process string)
315   "Send STRING to PROCESS."
316   ;; We need to store the time to provide timeouts, and
317   ;; to store the command so the we can replay the command
318   ;; if the server gives us an AUTHINFO challenge.
319   (setq nntp-last-command-time (current-time)
320         nntp-last-command string)
321   (when nntp-record-commands
322     (nntp-record-command string))
323   (process-send-string process (concat string nntp-end-of-line))
324   (or (memq (process-status process) '(open run))
325       (nntp-report "Server closed connection")))
326
327 (defun nntp-record-command (string)
328   "Record the command STRING."
329   (save-excursion
330     (set-buffer (get-buffer-create "*nntp-log*"))
331     (goto-char (point-max))
332     (let ((time (current-time)))
333       (insert (format-time-string "%Y%m%dT%H%M%S" time)
334               "." (format "%03d" (/ (nth 2 time) 1000))
335               " " nntp-address " " string "\n"))))
336
337 (defun nntp-report (&rest args)
338   "Report an error from the nntp backend.  The first string in ARGS
339 can be a format string.  For some commands, the failed command may be
340 retried once before actually displaying the error report."
341
342   (when nntp-record-commands
343     (nntp-record-command "*** CALLED nntp-report ***"))
344
345   (nnheader-report 'nntp args)
346
347   (apply 'error args))
348
349 (defun nntp-report-1 (&rest args)
350   "Throws out to nntp-with-open-group-error so that the connection may
351 be restored and the command retried."
352
353   (when nntp-record-commands
354     (nntp-record-command "*** CONNECTION LOST ***"))
355
356   (throw 'nntp-with-open-group-error t))
357
358 (defsubst nntp-wait-for (process wait-for buffer &optional decode discard)
359   "Wait for WAIT-FOR to arrive from PROCESS."
360
361   (save-excursion
362     (set-buffer (process-buffer process))
363     (goto-char (point-min))
364
365     (while (and (or (not (memq (char-after (point)) '(?2 ?3 ?4 ?5)))
366                     (looking-at "48[02]"))
367                 (memq (process-status process) '(open run)))
368       (cond ((looking-at "480")
369              (nntp-handle-authinfo process))
370             ((looking-at "482")
371              (nnheader-report 'nntp (get 'nntp-authinfo-rejected 'error-message))
372              (signal 'nntp-authinfo-rejected nil))
373             ((looking-at "^.*\n")
374              (delete-region (point) (progn (forward-line 1) (point)))))
375       (nntp-accept-process-output process)
376       (goto-char (point-min)))
377     (prog1
378         (cond
379          ((looking-at "[45]")
380           (progn
381             (nntp-snarf-error-message)
382             nil))
383          ((not (memq (process-status process) '(open run)))
384           (nntp-report "Server closed connection"))
385          (t
386           (goto-char (point-max))
387           (let ((limit (point-min))
388                 response)
389             (while (not (re-search-backward wait-for limit t))
390               (nntp-accept-process-output process)
391               ;; We assume that whatever we wait for is less than 1000
392               ;; characters long.
393               (setq limit (max (- (point-max) 1000) (point-min)))
394               (goto-char (point-max)))
395             (setq response (match-string 0))
396             (with-current-buffer nntp-server-buffer
397               (setq nntp-process-response response)))
398           (nntp-decode-text (not decode))
399           (unless discard
400             (save-excursion
401               (set-buffer buffer)
402               (goto-char (point-max))
403               (insert-buffer-substring (process-buffer process))
404               ;; Nix out "nntp reading...." message.
405               (when nntp-have-messaged
406                 (setq nntp-have-messaged nil)
407                 (nnheader-message 5 ""))))
408           t))
409       (unless discard
410         (erase-buffer)))))
411
412 (defun nntp-kill-buffer (buffer)
413   (when (buffer-name buffer)
414     (kill-buffer buffer)
415     (nnheader-init-server-buffer)))
416
417 (defun nntp-erase-buffer (buffer)
418   "Erase contents of BUFFER."
419   (with-current-buffer buffer
420     (erase-buffer)))
421
422 (defsubst nntp-find-connection (buffer)
423   "Find the connection delivering to BUFFER."
424   (let ((alist nntp-connection-alist)
425         (buffer (if (stringp buffer) (get-buffer buffer) buffer))
426         process entry)
427     (while (and alist (setq entry (pop alist)))
428       (when (eq buffer (cadr entry))
429         (setq process (car entry)
430               alist nil)))
431     (when process
432       (if (memq (process-status process) '(open run))
433           process
434         (nntp-kill-buffer (process-buffer process))
435         (setq nntp-connection-alist (delq entry nntp-connection-alist))