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