13401
|
1 ;;; nntp.el --- nntp access for Gnus
|
|
2 ;; Copyright (C) 1987,88,89,90,92,93,94,95 Free Software Foundation, Inc.
|
|
3
|
|
4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
|
5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
|
6 ;; Keywords: news
|
|
7
|
|
8 ;; This file is part of GNU Emacs.
|
|
9
|
|
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
11 ;; it under the terms of the GNU General Public License as published by
|
|
12 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
13 ;; any later version.
|
|
14
|
|
15 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 ;; GNU General Public License for more details.
|
|
19
|
|
20 ;; You should have received a copy of the GNU General Public License
|
|
21 ;; along with GNU Emacs; see the file COPYING. If not, write to
|
|
22 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
23
|
|
24 ;;; Commentary:
|
|
25
|
|
26 ;;; Code:
|
|
27
|
|
28 (require 'rnews)
|
|
29 (require 'sendmail)
|
|
30 (require 'nnheader)
|
|
31
|
|
32 (eval-when-compile (require 'cl))
|
|
33
|
|
34 (eval-and-compile
|
|
35 (autoload 'news-setup "rnewspost")
|
|
36 (autoload 'news-reply-mode "rnewspost")
|
|
37 (autoload 'nnmail-request-post-buffer "nnmail")
|
|
38 (autoload 'cancel-timer "timer")
|
|
39 (autoload 'telnet "telnet" nil t)
|
|
40 (autoload 'telnet-send-input "telnet" nil t)
|
|
41 (autoload 'timezone-parse-date "timezone"))
|
|
42
|
|
43 (defvar nntp-server-hook nil
|
|
44 "*Hooks for the NNTP server.
|
|
45 If the kanji code of the NNTP server is different from the local kanji
|
|
46 code, the correct kanji code of the buffer associated with the NNTP
|
|
47 server must be specified as follows:
|
|
48
|
|
49 \(setq nntp-server-hook
|
|
50 (function
|
|
51 (lambda ()
|
|
52 ;; Server's Kanji code is EUC (NEmacs hack).
|
|
53 (make-local-variable 'kanji-fileio-code)
|
|
54 (setq kanji-fileio-code 0))))
|
|
55
|
|
56 If you'd like to change something depending on the server in this
|
|
57 hook, use the variable `nntp-address'.")
|
|
58
|
|
59 (defvar nntp-server-opened-hook nil
|
|
60 "*Hook used for sending commands to the server at startup.
|
|
61 The default value is `nntp-send-mode-reader', which makes an innd
|
|
62 server spawn an nnrpd server. Another useful function to put in this
|
|
63 hook might be `nntp-send-authinfo', which will prompt for a password
|
|
64 to allow posting from the server. Note that this is only necessary to
|
|
65 do on servers that use strict access control.")
|
|
66 (add-hook 'nntp-server-opened-hook 'nntp-send-mode-reader)
|
|
67
|
|
68 (defvar nntp-open-server-function 'nntp-open-network-stream
|
|
69 "*Function used for connecting to a remote system.
|
|
70 It will be called with the address of the remote system.
|
|
71
|
|
72 Two pre-made functions are `nntp-open-network-stream', which is the
|
|
73 default, and simply connects to some port or other on the remote
|
|
74 system (see nntp-port-number). The other is `nntp-open-rlogin', which
|
|
75 does an rlogin on the remote system, and then does a telnet to the
|
|
76 NNTP server available there (see nntp-rlogin-parameters).")
|
|
77
|
|
78 (defvar nntp-rlogin-parameters '("telnet" "${NNTPSERVER:=localhost}" "nntp")
|
|
79 "*Parameters to `nntp-open-login'.
|
|
80 That function may be used as `nntp-open-server-function'. In that
|
|
81 case, this list will be used as the parameter list given to rsh.")
|
|
82
|
|
83 (defvar nntp-rlogin-user-name nil
|
|
84 "*User name on remote system when using the rlogin connect method.")
|
|
85
|
|
86 (defvar nntp-address nil
|
|
87 "*The name of the NNTP server.")
|
|
88
|
|
89 (defvar nntp-port-number "nntp"
|
|
90 "*Port number to connect to.")
|
|
91
|
|
92 (defvar nntp-large-newsgroup 50
|
|
93 "*The number of the articles which indicates a large newsgroup.
|
|
94 If the number of the articles is greater than the value, verbose
|
|
95 messages will be shown to indicate the current status.")
|
|
96
|
|
97 (defvar nntp-buggy-select (memq system-type '(fujitsu-uts))
|
|
98 "*t if your select routine is buggy.
|
|
99 If the select routine signals error or fall into infinite loop while
|
|
100 waiting for the server response, the variable must be set to t. In
|
|
101 case of Fujitsu UTS, it is set to T since `accept-process-output'
|
|
102 doesn't work properly.")
|
|
103
|
|
104 (defvar nntp-maximum-request 400
|
|
105 "*The maximum number of the requests sent to the NNTP server at one time.
|
|
106 If Emacs hangs up while retrieving headers, set the variable to a
|
|
107 lower value.")
|
|
108
|
|
109 (defvar nntp-debug-read 10000
|
|
110 "*Display '...' every 10Kbytes of a message being received if it is non-nil.
|
|
111 If it is a number, dots are displayed per the number.")
|
|
112
|
|
113 (defvar nntp-nov-is-evil nil
|
|
114 "*If non-nil, nntp will never attempt to use XOVER when talking to the server.")
|
|
115
|
|
116 (defvar nntp-xover-commands '("XOVER" "XOVERVIEW")
|
|
117 "*List of strings that are used as commands to fetch NOV lines from a server.
|
|
118 The strings are tried in turn until a positive response is gotten. If
|
|
119 none of the commands are successful, nntp will just grab headers one
|
|
120 by one.")
|
|
121
|
|
122 (defvar nntp-nov-gap 20
|
|
123 "*Maximum allowed gap between two articles.
|
|
124 If the gap between two consecutive articles is bigger than this
|
|
125 variable, split the XOVER request into two requests.")
|
|
126
|
|
127 (defvar nntp-connection-timeout nil
|
|
128 "*Number of seconds to wait before an nntp connection times out.
|
|
129 If this variable is nil, which is the default, no timers are set.")
|
|
130
|
|
131 (defvar nntp-news-default-headers nil
|
|
132 "*If non-nil, override `mail-default-headers' when posting news.")
|
|
133
|
|
134 (defvar nntp-prepare-server-hook nil
|
|
135 "*Hook run before a server is opened.
|
|
136 If can be used to set up a server remotely, for instance. Say you
|
|
137 have an account at the machine \"other.machine\". This machine has
|
|
138 access to an NNTP server that you can't access locally. You could
|
|
139 then use this hook to rsh to the remote machine and start a proxy NNTP
|
|
140 server there that you can connect to.")
|
|
141
|
|
142 (defvar nntp-async-number 5
|
|
143 "*How many articles should be prefetched when in asynchronous mode.")
|
|
144
|
|
145
|
|
146
|
|
147
|
|
148 (defconst nntp-version "nntp 4.0"
|
|
149 "Version numbers of this version of NNTP.")
|
|
150
|
|
151 (defvar nntp-server-buffer nil
|
|
152 "Buffer associated with the NNTP server process.")
|
|
153
|
|
154 (defvar nntp-server-process nil
|
|
155 "The NNTP server process.
|
|
156 You'd better not use this variable in NNTP front-end program, but
|
|
157 instead use `nntp-server-buffer'.")
|
|
158
|
|
159 (defvar nntp-status-string nil
|
|
160 "Save the server response message.
|
|
161 You'd better not use this variable in NNTP front-end program but
|
|
162 instead call function `nntp-status-message' to get status message.")
|
|
163
|
|
164 (defvar nntp-opened-connections nil
|
|
165 "All (possibly) opened connections.")
|
|
166
|
|
167 (defvar nntp-server-xover 'try)
|
|
168 (defvar nntp-server-list-active-group 'try)
|
|
169 (defvar nntp-current-group "")
|
|
170 (defvar nntp-timeout-servers nil)
|
|
171
|
|
172 (defvar nntp-async-process nil)
|
|
173 (defvar nntp-async-buffer nil)
|
|
174 (defvar nntp-async-articles nil)
|
|
175 (defvar nntp-async-fetched nil)
|
|
176 (defvar nntp-async-group-alist nil)
|
|
177
|
|
178
|
|
179
|
|
180 (defvar nntp-current-server nil)
|
|
181 (defvar nntp-server-alist nil)
|
|
182 (defvar nntp-server-variables
|
|
183 (list
|
|
184 (list 'nntp-server-hook nntp-server-hook)
|
|
185 (list 'nntp-server-opened-hook nntp-server-opened-hook)
|
|
186 (list 'nntp-port-number nntp-port-number)
|
|
187 (list 'nntp-address nntp-address)
|
|
188 (list 'nntp-large-newsgroup nntp-large-newsgroup)
|
|
189 (list 'nntp-buggy-select nntp-buggy-select)
|
|
190 (list 'nntp-maximum-request nntp-maximum-request)
|
|
191 (list 'nntp-debug-read nntp-debug-read)
|
|
192 (list 'nntp-nov-is-evil nntp-nov-is-evil)
|
|
193 (list 'nntp-xover-commands nntp-xover-commands)
|
|
194 (list 'nntp-connection-timeout nntp-connection-timeout)
|
|
195 (list 'nntp-news-default-headers nntp-news-default-headers)
|
|
196 (list 'nntp-prepare-server-hook nntp-prepare-server-hook)
|
|
197 (list 'nntp-async-number nntp-async-number)
|
|
198 '(nntp-async-process nil)
|
|
199 '(nntp-async-buffer nil)
|
|
200 '(nntp-async-articles nil)
|
|
201 '(nntp-async-fetched nil)
|
|
202 '(nntp-async-group-alist nil)
|
|
203 '(nntp-server-process nil)
|
|
204 '(nntp-status-string nil)
|
|
205 '(nntp-server-xover try)
|
|
206 '(nntp-server-list-active-group try)
|
|
207 '(nntp-current-group "")))
|
|
208
|
|
209
|
|
210 ;;; Interface functions.
|
|
211
|
|
212 (defun nntp-retrieve-headers (sequence &optional newsgroup server)
|
|
213 "Retrieve the headers to the articles in SEQUENCE."
|
|
214 (nntp-possibly-change-server newsgroup server)
|
|
215 (save-excursion
|
|
216 (set-buffer nntp-server-buffer)
|
|
217 (erase-buffer)
|
|
218 (if (and (not gnus-nov-is-evil)
|
|
219 (not nntp-nov-is-evil)
|
|
220 (nntp-retrieve-headers-with-xover sequence))
|
|
221 'nov
|
|
222 (let ((number (length sequence))
|
|
223 (count 0)
|
|
224 (received 0)
|
|
225 (last-point (point-min)))
|
|
226 ;; Send HEAD command.
|
|
227 (while sequence
|
|
228 (nntp-send-strings-to-server
|
|
229 "HEAD" (if (numberp (car sequence)) (int-to-string (car sequence))
|
|
230 (car sequence)))
|
|
231 (setq sequence (cdr sequence)
|
|
232 count (1+ count))
|
|
233 ;; Every 400 header requests we have to read stream in order
|
|
234 ;; to avoid deadlock.
|
|
235 (if (or (null sequence) ;All requests have been sent.
|
|
236 (zerop (% count nntp-maximum-request)))
|
|
237 (progn
|
|
238 (nntp-accept-response)
|
|
239 (while (progn
|
|
240 (goto-char last-point)
|
|
241 ;; Count replies.
|
|
242 (while (re-search-forward "^[0-9]" nil t)
|
|
243 (setq received (1+ received)))
|
|
244 (setq last-point (point))
|
|
245 (< received count))
|
|
246 ;; If number of headers is greater than 100, give
|
|
247 ;; informative messages.
|
|
248 (and (numberp nntp-large-newsgroup)
|
|
249 (> number nntp-large-newsgroup)
|
|
250 (zerop (% received 20))
|
|
251 (message "NNTP: Receiving headers... %d%%"
|
|
252 (/ (* received 100) number)))
|
|
253 (nntp-accept-response)))))
|
|
254 ;; Wait for text of last command.
|
|
255 (goto-char (point-max))
|
|
256 (re-search-backward "^[0-9]" nil t)
|
|
257 (if (looking-at "^[23]")
|
|
258 (while (progn
|
|
259 (goto-char (- (point-max) 3))
|
|
260 (not (looking-at "^\\.\r?\n")))
|
|
261 (nntp-accept-response)))
|
|
262 (and (numberp nntp-large-newsgroup)
|
|
263 (> number nntp-large-newsgroup)
|
|
264 (message "NNTP: Receiving headers...done"))
|
|
265
|
|
266 ;; Now all of replies are received.
|
|
267 (setq received number)
|
|
268 ;; First, fold continuation lines.
|
|
269 (goto-char (point-min))
|
|
270 (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
|
271 (replace-match " "))
|
|
272 ;; Remove all "\r"'s
|
|
273 (goto-char (point-min))
|
|
274 (while (search-forward "\r" nil t)
|
|
275 (replace-match ""))
|
|
276 'headers))))
|
|
277
|
|
278
|
|
279 (defun nntp-retrieve-groups (groups &optional server)
|
|
280 (nntp-possibly-change-server nil server)
|
|
281 (save-excursion
|
|
282 (set-buffer nntp-server-buffer)
|
|
283 (and (eq nntp-server-list-active-group 'try)
|
|
284 (nntp-try-list-active (car groups)))
|
|
285 (erase-buffer)
|
|
286 (let ((count 0)
|
|
287 (received 0)
|
|
288 (last-point (point-min))
|
|
289 (command (if nntp-server-list-active-group
|
|
290 "LIST ACTIVE" "GROUP")))
|
|
291 (while groups
|
|
292 (nntp-send-strings-to-server command (car groups))
|
|
293 (setq groups (cdr groups))
|
|
294 (setq count (1+ count))
|
|
295 ;; Every 400 requests we have to read the stream in
|
|
296 ;; order to avoid deadlocks.
|
|
297 (if (or (null groups) ;All requests have been sent.
|
|
298 (zerop (% count nntp-maximum-request)))
|
|
299 (progn
|
|
300 (nntp-accept-response)
|
|
301 (while (progn
|
|
302 (goto-char last-point)
|
|
303 ;; Count replies.
|
|
304 (while (re-search-forward "^[0-9]" nil t)
|
|
305 (setq received (1+ received)))
|
|
306 (setq last-point (point))
|
|
307 (< received count))
|
|
308 (nntp-accept-response)))))
|
|
309
|
|
310 ;; Wait for the reply from the final command.
|
|
311 (if nntp-server-list-active-group
|
|
312 (progn
|
|
313 (goto-char (point-max))
|
|
314 (re-search-backward "^[0-9]" nil t)
|
|
315 (if (looking-at "^[23]")
|
|
316 (while (progn
|
|
317 (goto-char (- (point-max) 3))
|
|
318 (not (looking-at "^\\.\r?\n")))
|
|
319 (nntp-accept-response)))))
|
|
320
|
|
321 ;; Now all replies are received. We remove CRs.
|
|
322 (goto-char (point-min))
|
|
323 (while (search-forward "\r" nil t)
|
|
324 (replace-match "" t t))
|
|
325
|
|
326 (if nntp-server-list-active-group
|
|
327 (progn
|
|
328 ;; We have read active entries, so we just delete the
|
|
329 ;; superfluos gunk.
|
|
330 (goto-char (point-min))
|
|
331 (while (re-search-forward "^[.2-5]" nil t)
|
|
332 (delete-region (match-beginning 0)
|
|
333 (progn (forward-line 1) (point))))
|
|
334 'active)
|
|
335 'group))))
|
|
336
|
|
337 (defun nntp-open-server (server &optional defs)
|
|
338 (nnheader-init-server-buffer)
|
|
339 (if (nntp-server-opened server)
|
|
340 t
|
|
341 (if (or (stringp (car defs))
|
|
342 (numberp (car defs)))
|
|
343 (setq defs (cons (list 'nntp-port-number (car defs)) (cdr defs))))
|
|
344 (or (assq 'nntp-address defs)
|
|
345 (setq defs (append defs (list (list 'nntp-address server)))))
|
|
346 (if (and nntp-current-server
|
|
347 (not (equal server nntp-current-server)))
|
|
348 (setq nntp-server-alist
|
|
349 (cons (list nntp-current-server
|
|
350 (nnheader-save-variables nntp-server-variables))
|
|
351 nntp-server-alist)))
|
|
352 (let ((state (assoc server nntp-server-alist)))
|
|
353 (if state
|
|
354 (progn
|
|
355 (nnheader-restore-variables (nth 1 state))
|
|
356 (setq nntp-server-alist (delq state nntp-server-alist)))
|
|
357 (nnheader-set-init-variables nntp-server-variables defs)))
|
|
358 (setq nntp-current-server server)
|
|
359 (or (nntp-server-opened server)
|
|
360 (progn
|
|
361 (if (member nntp-address nntp-timeout-servers)
|
|
362 nil
|
|
363 (run-hooks 'nntp-prepare-server-hook)
|
|
364 (nntp-open-server-semi-internal nntp-address nntp-port-number))))))
|
|
365
|
|
366 (defun nntp-close-server (&optional server)
|
|
367 "Close connection to SERVER."
|
|
368 (nntp-possibly-change-server nil server)
|
|
369 (unwind-protect
|
|
370 (progn
|
|
371 ;; Un-set default sentinel function before closing connection.
|
|
372 (and nntp-server-process
|
|
373 (eq 'nntp-default-sentinel
|
|
374 (process-sentinel nntp-server-process))
|
|
375 (set-process-sentinel nntp-server-process nil))
|
|
376 ;; We cannot send QUIT command unless the process is running.
|
|
377 (if (nntp-server-opened)
|
|
378 (nntp-send-command nil "QUIT")))
|
|
379 (nntp-close-server-internal server)
|
|
380 (setq nntp-timeout-servers (delete server nntp-timeout-servers))))
|
|
381
|
|
382 (defalias 'nntp-request-quit (symbol-function 'nntp-close-server))
|
|
383
|
|
384 (defun nntp-request-close ()
|
|
385 "Close all server connections."
|
|
386 (let (proc)
|
|
387 (while nntp-opened-connections
|
|
388 (setq proc (pop nntp-opened-connections))
|
|
389 (and proc (delete-process proc)))
|
|
390 (and nntp-async-buffer
|
|
391 (get-buffer nntp-async-buffer)
|
|
392 (kill-buffer nntp-async-buffer))
|
|
393 (while nntp-server-alist
|
|
394 (and (setq proc (nth 1 (assq 'nntp-async-buffer
|
|
395 (car nntp-server-alist))))
|
|
396 (buffer-name proc)
|
|
397 (kill-buffer proc))
|
|
398 (setq nntp-server-alist (cdr nntp-server-alist)))
|
|
399 (setq nntp-current-server nil
|
|
400 nntp-timeout-servers nil
|
|
401 nntp-async-group-alist nil)))
|
|
402
|
|
403 (defun nntp-server-opened (&optional server)
|
|
404 "Say whether a connection to SERVER has been opened."
|
|
405 (and (equal server nntp-current-server)
|
|
406 nntp-server-buffer
|
|
407 (buffer-name nntp-server-buffer)
|
|
408 nntp-server-process
|
|
409 (memq (process-status nntp-server-process) '(open run))))
|
|
410
|
|
411 (defun nntp-status-message (&optional server)
|
|
412 "Return server status as a string."
|
|
413 (if (and nntp-status-string
|
|
414 ;; NNN MESSAGE
|
|
415 (string-match "[0-9][0-9][0-9][ \t]+\\([^\r]*\\).*$"
|
|
416 nntp-status-string))
|
|
417 (substring nntp-status-string (match-beginning 1) (match-end 1))
|
|
418 ;; Empty message if nothing.
|
|
419 (or nntp-status-string "")))
|
|
420
|
|
421 (defun nntp-request-article (id &optional newsgroup server buffer)
|
|
422 "Request article ID (message-id or number)."
|
|
423 (nntp-possibly-change-server newsgroup server)
|
|
424
|
|
425 (let (found)
|
|
426
|
|
427 ;; First we see whether we can get the article from the async buffer.
|
|
428 (if (and (numberp id)
|
|
429 nntp-async-articles
|
|
430 (memq id nntp-async-fetched))
|
|
431 (save-excursion
|
|
432 (set-buffer nntp-async-buffer)
|
|
433 (let ((opoint (point))
|
|
434 (art (if (numberp id) (int-to-string id) id))
|
|
435 beg end)
|
|
436 (if (and (or (re-search-forward (concat "^2.. +" art) nil t)
|
|
437 (progn
|
|
438 (goto-char (point-min))
|
|
439 (re-search-forward (concat "^2.. +" art) opoint t)))
|
|
440 (progn
|
|
441 (beginning-of-line)
|
|
442 (setq beg (point)
|
|
443 end (re-search-forward "^\\.\r?\n" nil t))))
|
|
444 (progn
|
|
445 (setq found t)
|
|
446 (save-excursion
|
|
447 (set-buffer (or buffer nntp-server-buffer))
|
|
448 (erase-buffer)
|
|
449 (insert-buffer-substring nntp-async-buffer beg end)
|
|
450 (let ((nntp-server-buffer (current-buffer)))
|
|
451 (nntp-decode-text)))
|
|
452 (delete-region beg end)
|
|
453 (and nntp-async-articles
|
|
454 (nntp-async-fetch-articles id)))))))
|
|
455
|
|
456 (if found
|
|
457 t
|
|
458 ;; The article was not in the async buffer, so we fetch it now.
|
|
459 (unwind-protect
|
|
460 (progn
|
|
461 (if buffer (set-process-buffer nntp-server-process buffer))
|
|
462 (let ((nntp-server-buffer (or buffer nntp-server-buffer))
|
|
463 (art (or (and (numberp id) (int-to-string id)) id)))
|
|
464 ;; If NEmacs, end of message may look like: "\256\215" (".^M")
|
|
465 (prog1
|
|
466 (nntp-send-command "^\\.\r?\n" "ARTICLE" art)
|
|
467 (nntp-decode-text)
|
|
468 (and nntp-async-articles (nntp-async-fetch-articles id)))))
|
|
469 (if buffer (set-process-buffer
|
|
470 nntp-server-process nntp-server-buffer))))))
|
|
471
|
|
472 (defun nntp-request-body (id &optional newsgroup server)
|
|
473 "Request body of article ID (message-id or number)."
|
|
474 (nntp-possibly-change-server newsgroup server)
|
|
475 (prog1
|
|
476 ;; If NEmacs, end of message may look like: "\256\215" (".^M")
|
|
477 (nntp-send-command
|
|
478 "^\\.\r?\n" "BODY" (or (and (numberp id) (int-to-string id)) id))
|
|
479 (nntp-decode-text)))
|
|
480
|
|
481 (defun nntp-request-head (id &optional newsgroup server)
|
|
482 "Request head of article ID (message-id or number)."
|
|
483 (nntp-possibly-change-server newsgroup server)
|
|
484 (prog1
|
|
485 (nntp-send-command
|
|
486 "^\\.\r?\n" "HEAD" (or (and (numberp id) (int-to-string id)) id))
|
|
487 (nntp-decode-text)))
|
|
488
|
|
489 (defun nntp-request-stat (id &optional newsgroup server)
|
|
490 "Request STAT of article ID (message-id or number)."
|
|
491 (nntp-possibly-change-server newsgroup server)
|
|
492 (nntp-send-command
|
|
493 "^[23].*\r?\n" "STAT" (or (and (numberp id) (int-to-string id)) id)))
|
|
494
|
|
495 (defun nntp-request-group (group &optional server dont-check)
|
|
496 "Select GROUP."
|
|
497 (nntp-send-command "^.*\r?\n" "GROUP" group)
|
|
498 (setq nntp-current-group group)
|
|
499 (save-excursion
|
|
500 (set-buffer nntp-server-buffer)
|
|
501 (goto-char (point-min))
|
|
502 (looking-at "[23]")))
|
|
503
|
|
504 (defun nntp-request-asynchronous (group &optional server articles)
|
|
505 (and nntp-async-articles (nntp-async-request-group group))
|
|
506 (and
|
|
507 nntp-async-number
|
|
508 (if (not (or (nntp-async-server-opened)
|
|
509 (nntp-async-open-server)))
|
|
510 (progn
|
|
511 (message "Can't open second connection to %s" nntp-address)
|
|
512 (ding)
|
|
513 (setq nntp-async-articles nil)
|
|
514 (sit-for 2))
|
|
515 (setq nntp-async-articles articles)
|
|
516 (setq nntp-async-fetched nil)
|
|
517 (save-excursion
|
|
518 (set-buffer nntp-async-buffer)
|
|
519 (erase-buffer))
|
|
520 (nntp-async-send-strings "GROUP" group)
|
|
521 t)))
|
|
522
|
|
523 (defun nntp-list-active-group (group &optional server)
|
|
524 (nntp-send-command "^.*\r?\n" "LIST ACTIVE" group))
|
|
525
|
|
526 (defun nntp-request-group-description (group &optional server)
|
|
527 "Get description of GROUP."
|
|
528 (if (nntp-possibly-change-server nil server)
|
|
529 (prog1
|
|
530 (nntp-send-command "^.*\r?\n" "XGTITLE" group)
|
|
531 (nntp-decode-text))))
|
|
532
|
|
533 (defun nntp-close-group (group &optional server)
|
|
534 (setq nntp-current-group nil)
|
|
535 t)
|
|
536
|
|
537 (defun nntp-request-list (&optional server)
|
|
538 "List active groups."
|
|
539 (nntp-possibly-change-server nil server)
|
|
540 (prog1
|
|
541 (nntp-send-command "^\\.\r?\n" "LIST")
|
|
542 (nntp-decode-text)))
|
|
543
|
|
544 (defun nntp-request-list-newsgroups (&optional server)
|
|
545 "List groups."
|
|
546 (nntp-possibly-change-server nil server)
|
|
547 (prog1
|
|
548 (nntp-send-command "^\\.\r?\n" "LIST NEWSGROUPS")
|
|
549 (nntp-decode-text)))
|
|
550
|
|
551 (defun nntp-request-newgroups (date &optional server)
|
|
552 "List new groups."
|
|
553 (nntp-possibly-change-server nil server)
|
|
554 (let* ((date (timezone-parse-date date))
|
|
555 (time-string
|
|
556 (format "%s%02d%02d %s%s%s"
|
|
557 (substring (aref date 0) 2) (string-to-int (aref date 1))
|
|
558 (string-to-int (aref date 2)) (substring (aref date 3) 0 2)
|
|
559 (substring
|
|
560 (aref date 3) 3 5) (substring (aref date 3) 6 8))))
|
|
561 (prog1
|
|
562 (nntp-send-command "^\\.\r?\n" "NEWGROUPS" time-string)
|
|
563 (nntp-decode-text))))
|
|
564
|
|
565 (defun nntp-request-list-distributions (&optional server)
|
|
566 "List distributions."
|
|
567 (nntp-possibly-change-server nil server)
|
|
568 (prog1
|
|
569 (nntp-send-command "^\\.\r?\n" "LIST DISTRIBUTIONS")
|
|
570 (nntp-decode-text)))
|
|
571
|
|
572 (defun nntp-request-last (&optional newsgroup server)
|
|
573 "Decrease the current article pointer."
|
|
574 (nntp-possibly-change-server newsgroup server)
|
|
575 (nntp-send-command "^[23].*\r?\n" "LAST"))
|
|
576
|
|
577 (defun nntp-request-next (&optional newsgroup server)
|
|
578 "Advance the current article pointer."
|
|
579 (nntp-possibly-change-server newsgroup server)
|
|
580 (nntp-send-command "^[23].*\r?\n" "NEXT"))
|
|
581
|
|
582 (defun nntp-request-post (&optional server)
|
|
583 "Post the current buffer."
|
|
584 (nntp-possibly-change-server nil server)
|
|
585 (if (nntp-send-command "^[23].*\r?\n" "POST")
|
|
586 (progn
|
|
587 (nntp-encode-text)
|
|
588 (nntp-send-region-to-server (point-min) (point-max))
|
|
589 ;; 1.2a NNTP's post command is buggy. "^M" (\r) is not
|
|
590 ;; appended to end of the status message.
|
|
591 (nntp-wait-for-response "^[23].*\n"))))
|
|
592
|
|
593 (defun nntp-request-post-buffer
|
|
594 (post group subject header article-buffer info follow-to respect-poster)
|
|
595 "Request a buffer suitable for composing an article.
|
|
596 If POST, this is an original article; otherwise it's a followup.
|
|
597 GROUP is the group to be posted to, the article should have subject
|
|
598 SUBJECT. HEADER is a Gnus header vector. ARTICLE-BUFFER contains the
|
|
599 article being followed up. INFO is a Gnus info list. If FOLLOW-TO,
|
|
600 post to this group instead. If RESPECT-POSTER, heed the special
|
|
601 \"poster\" value of the Followup-to header."
|
|
602 (if (assq 'to-address (nth 5 info))
|
|
603 (nnmail-request-post-buffer
|
|
604 post group subject header article-buffer info follow-to respect-poster)
|
|
605 (let ((mail-default-headers
|
|
606 (or nntp-news-default-headers mail-default-headers))
|
|
607 from date to followup-to newsgroups message-of
|
|
608 references distribution message-id)
|
|
609 (save-excursion
|
|
610 (set-buffer (get-buffer-create "*post-news*"))
|
|
611 (news-reply-mode)
|
|
612 (if (and (buffer-modified-p)
|
|
613 (> (buffer-size) 0)
|
|
614 (not (y-or-n-p "Unsent article being composed; erase it? ")))
|
|
615 ()
|
|
616 (erase-buffer)
|
|
617 (if post
|
|
618 (news-setup nil subject nil group nil)
|
|
619 (save-excursion
|
|
620 (set-buffer article-buffer)
|
|
621 (goto-char (point-min))
|
|
622 (narrow-to-region (point-min)
|
|
623 (progn (search-forward "\n\n") (point)))
|
|
624 (setq from (mail-header-from header))
|
|
625 (setq date (mail-header-date header))
|
|
626 (and from
|
|
627 (let ((stop-pos
|
|
628 (string-match " *at \\| *@ \\| *(\\| *<" from)))
|
|
629 (setq
|
|
630 message-of
|
|
631 (concat (if stop-pos (substring from 0 stop-pos) from)
|
|
632 "'s message of " date))))
|
|
633 (setq subject (or subject (mail-header-subject header)))
|
|
634 (or (string-match "^[Rr][Ee]:" subject)
|
|
635 (setq subject (concat "Re: " subject)))
|
|
636 (setq followup-to (mail-fetch-field "followup-to"))
|
|
637 (if (or (null respect-poster) ;Ignore followup-to: field.
|
|
638 (string-equal "" followup-to) ;Bogus header.
|
|
639 (string-equal "poster" followup-to);Poster
|
|
640 (and (eq respect-poster 'ask)
|
|
641 followup-to
|
|
642 (not (y-or-n-p (concat "Followup to "
|
|
643 followup-to "? ")))))
|
|
644 (setq followup-to nil))
|
|
645 (setq newsgroups
|
|
646 (or follow-to followup-to (mail-fetch-field "newsgroups")))
|
|
647 (setq references (mail-header-references header))
|
|
648 (setq distribution (mail-fetch-field "distribution"))
|
|
649 ;; Remove bogus distribution.
|
|
650 (and (stringp distribution)
|
|
651 (string-match "world" distribution)
|
|
652 (setq distribution nil))
|
|
653 (setq message-id (mail-header-id header))
|
|
654 (widen))
|
|
655 (setq news-reply-yank-from from)
|
|
656 (setq news-reply-yank-message-id message-id)
|
|
657 (news-setup to subject message-of
|
|
658 (if (stringp newsgroups) newsgroups "")
|
|
659 article-buffer)
|
|
660 (if (and newsgroups (listp newsgroups))
|
|
661 (progn
|
|
662 (goto-char (point-min))
|
|
663 (while newsgroups
|
|
664 (insert (car (car newsgroups)) ": "
|
|
665 (cdr (car newsgroups)) "\n")
|
|
666 (setq newsgroups (cdr newsgroups)))))
|
|
667 (nnheader-insert-references references message-id)
|
|
668 (if distribution
|
|
669 (progn
|
|
670 (mail-position-on-field "Distribution")
|
|
671 (insert distribution)))))
|
|
672 (current-buffer)))))
|
|
673
|
|
674 ;;; Internal functions.
|
|
675
|
|
676 (defun nntp-send-mode-reader ()
|
|
677 "Send the MODE READER command to the nntp server.
|
|
678 This function is supposed to be called from `nntp-server-opened-hook'.
|
|
679 It will make innd servers spawn an nnrpd process to allow actual article
|
|
680 reading."
|
|
681 (nntp-send-command "^.*\r?\n" "MODE READER"))
|
|
682
|
|
683 (defun nntp-send-authinfo ()
|
|
684 "Send the AUTHINFO to the nntp server.
|
|
685 This function is supposed to be called from `nntp-server-opened-hook'.
|
|
686 It will prompt for a password."
|
|
687 (nntp-send-command "^.*\r?\n" "AUTHINFO USER" (user-login-name))
|
|
688 (nntp-send-command "^.*\r?\n" "AUTHINFO PASS"
|
|
689 (read-string "NNTP password: ")))
|
|
690
|
|
691 (defun nntp-send-authinfo-from-file ()
|
|
692 "Send the AUTHINFO to the nntp server.
|
|
693 This function is supposed to be called from `nntp-server-opened-hook'.
|
|
694 It will prompt for a password."
|
|
695 (and (file-exists-p "~/.nntp-authinfo")
|
|
696 (save-excursion
|
|
697 (set-buffer (get-buffer-create " *tull*"))
|
|
698 (insert-file-contents "~/.nntp-authinfo")
|
|
699 (goto-char (point-min))
|
|
700 (nntp-send-command "^.*\r?\n" "AUTHINFO USER" (user-login-name))
|
|
701 (nntp-send-command "^.*\r?\n" "AUTHINFO PASS"
|
|
702 (buffer-substring (point)
|
|
703 (progn (end-of-line) (point))))
|
|
704 (kill-buffer (current-buffer)))))
|
|
705
|
|
706 (defun nntp-default-sentinel (proc status)
|
|
707 "Default sentinel function for NNTP server process."
|
|
708 (let ((servers nntp-server-alist)
|
|
709 server)
|
|
710 ;; Go through the alist of server names and find the name of the
|
|
711 ;; server that the process that sent the signal is connected to.
|
|
712 ;; If you get my drift.
|
|
713 (if (equal proc nntp-server-process)
|
|
714 (setq server nntp-address)
|
|
715 (while (and servers
|
|
716 (not (equal proc (nth 1 (assq 'nntp-server-process
|
|
717 (car servers))))))
|
|
718 (setq servers (cdr servers)))
|
|
719 (setq server (car (car servers))))
|
|
720 (and server
|
|
721 (progn
|
|
722 (message "nntp: Connection closed to server %s" server)
|
|
723 (ding)))))
|
|
724
|
|
725 (defun nntp-kill-connection (server)
|
|
726 (let ((proc (nth 1 (assq 'nntp-server-process
|
|
727 (assoc server nntp-server-alist)))))
|
|
728 (and proc (delete-process (process-name proc)))
|
|
729 (nntp-close-server server)
|
|
730 (setq nntp-timeout-servers (cons server nntp-timeout-servers))
|
|
731 (setq nntp-status-string
|
|
732 (message "Connection timed out to server %s." server))
|
|
733 (ding)
|
|
734 (sit-for 1)))
|
|
735
|
|
736 ;; Encoding and decoding of NNTP text.
|
|
737
|
|
738 (defun nntp-decode-text ()
|
|
739 "Decode text transmitted by NNTP.
|
|
740 0. Delete status line.
|
|
741 1. Delete `^M' at end of line.
|
|
742 2. Delete `.' at end of buffer (end of text mark).
|
|
743 3. Delete `.' at beginning of line."
|
|
744 (save-excursion
|
|
745 (set-buffer nntp-server-buffer)
|
|
746 ;; Insert newline at end of buffer.
|
|
747 (goto-char (point-max))
|
|
748 (or (bolp) (insert "\n"))
|
|
749 ;; Delete status line.
|
|
750 (goto-char (point-min))
|
|
751 (delete-region (point) (progn (forward-line 1) (point)))
|
|
752 ;; Delete `^M' at the end of lines.
|
|
753 (while (not (eobp))
|
|
754 (end-of-line)
|
|
755 (and (= (preceding-char) ?\r)
|
|
756 (delete-char -1))
|
|
757 (forward-line 1))
|
|
758 ;; Delete `.' at end of the buffer (end of text mark).
|
|
759 (goto-char (point-max))
|
|
760 (forward-line -1)
|
|
761 (if (looking-at "^\\.\n")
|
|
762 (delete-region (point) (progn (forward-line 1) (point))))
|
|
763 ;; Replace `..' at beginning of line with `.'.
|
|
764 (goto-char (point-min))
|
|
765 ;; (replace-regexp "^\\.\\." ".")
|
|
766 (while (search-forward "\n.." nil t)
|
|
767 (delete-char -1))))
|
|
768
|
|
769 (defun nntp-encode-text ()
|
|
770 "Encode text in current buffer for NNTP transmission.
|
|
771 1. Insert `.' at beginning of line.
|
|
772 2. Insert `.' at end of buffer (end of text mark)."
|
|
773 (save-excursion
|
|
774 ;; Insert newline at end of buffer.
|
|
775 (goto-char (point-max))
|
|
776 (or (bolp) (insert "\n"))
|
|
777 ;; Replace `.' at beginning of line with `..'.
|
|
778 (goto-char (point-min))
|
|
779 ;; (replace-regexp "^\\." "..")
|
|
780 (while (search-forward "\n." nil t)
|
|
781 (insert "."))
|
|
782 ;; Insert `.' at end of buffer (end of text mark).
|
|
783 (goto-char (point-max))
|
|
784 (insert ".\r\n")))
|
|
785
|
|
786
|
|
787 ;;;
|
|
788 ;;; Synchronous Communication with NNTP Server.
|
|
789 ;;;
|
|
790
|
|
791 (defun nntp-send-command (response cmd &rest args)
|
|
792 "Wait for server RESPONSE after sending CMD and optional ARGS to server."
|
|
793 (save-excursion
|
|
794 ;; Clear communication buffer.
|
|
795 (set-buffer nntp-server-buffer)
|
|
796 (erase-buffer)
|
|
797 (apply 'nntp-send-strings-to-server cmd args)
|
|
798 (if response
|
|
799 (nntp-wait-for-response response)
|
|
800 t)))
|
|
801
|
|
802 (defun nntp-wait-for-response (regexp &optional slow)
|
|
803 "Wait for server response which matches REGEXP."
|
|
804 (save-excursion
|
|
805 (let ((status t)
|
|
806 (wait t)
|
|
807 (dotnum 0) ;Number of "." being displayed.
|
|
808 (dotsize ;How often "." displayed.
|
|
809 (if (numberp nntp-debug-read) nntp-debug-read 10000)))
|
|
810 (set-buffer nntp-server-buffer)
|
|
811 ;; Wait for status response (RFC977).
|
|
812 ;; 1xx - Informative message.
|
|
813 ;; 2xx - Command ok.
|
|
814 ;; 3xx - Command ok so far, send the rest of it.
|
|
815 ;; 4xx - Command was correct, but couldn't be performed for some
|
|
816 ;; reason.
|
|
817 ;; 5xx - Command unimplemented, or incorrect, or a serious
|
|
818 ;; program error occurred.
|
|
819 (nntp-accept-response)
|
|
820 (while wait
|
|
821 (goto-char (point-min))
|
|
822 (if slow
|
|
823 (progn
|
|
824 (cond ((re-search-forward "^[23][0-9][0-9]" nil t)
|
|
825 (setq wait nil))
|
|
826 ((re-search-forward "^[45][0-9][0-9]" nil t)
|
|
827 (setq status nil)
|
|
828 (setq wait nil))
|
|
829 (t (nntp-accept-response)))
|
|
830 (if (not wait) (delete-region (point-min)
|
|
831 (progn (beginning-of-line)
|
|
832 (point)))))
|
|
833 (cond ((looking-at "[23]")
|
|
834 (setq wait nil))
|
|
835 ((looking-at "[45]")
|
|
836 (setq status nil)
|
|
837 (setq wait nil))
|
|
838 (t (nntp-accept-response)))))
|
|
839 ;; Save status message.
|
|
840 (end-of-line)
|
|
841 (setq nntp-status-string
|
|
842 (buffer-substring (point-min) (point)))
|
|
843 (if status
|
|
844 (progn
|
|
845 (setq wait t)
|
|
846 (while wait
|
|
847 (goto-char (point-max))
|
|
848 (forward-line -1) ;(beginning-of-line)
|
|
849 ;;(message (buffer-substring
|
|
850 ;; (point)
|
|
851 ;; (save-excursion (end-of-line) (point))))
|
|
852 (if (looking-at regexp)
|
|
853 (setq wait nil)
|
|
854 (if nntp-debug-read
|
|
855 (let ((newnum (/ (buffer-size) dotsize)))
|
|
856 (if (not (= dotnum newnum))
|
|
857 (progn
|
|
858 (setq dotnum newnum)
|
|
859 (message "NNTP: Reading %s"
|
|
860 (make-string dotnum ?.))))))
|
|
861 (nntp-accept-response)))
|
|
862 ;; Remove "...".
|
|
863 (if (and nntp-debug-read (> dotnum 0))
|
|
864 (message ""))
|
|
865 ;; Successfully received server response.
|
|
866 t)))))
|
|
867
|
|
868
|
|
869
|
|
870 ;;;
|
|
871 ;;; Low-Level Interface to NNTP Server.
|
|
872 ;;;
|
|
873
|
|
874 (defun nntp-retrieve-headers-with-xover (sequence)
|
|
875 (erase-buffer)
|
|
876 (cond
|
|
877
|
|
878 ;; This server does not talk NOV.
|
|
879 ((not nntp-server-xover)
|
|
880 nil)
|
|
881
|
|
882 ;; We don't care about gaps.
|
|
883 ((not nntp-nov-gap)
|
|
884 (nntp-send-xover-command
|
|
885 (car sequence) (nntp-last-element sequence) 'wait)
|
|
886
|
|
887 (goto-char (point-min))
|
|
888 (if (looking-at "[1-5][0-9][0-9] ")
|
|
889 (delete-region (point) (progn (forward-line 1) (point))))
|
|
890 (while (search-forward "\r" nil t)
|
|
891 (replace-match "" t t))
|
|
892 (goto-char (point-max))
|
|
893 (forward-line -1)
|
|
894 (if (looking-at "\\.")
|
|
895 (delete-region (point) (progn (forward-line 1) (point)))))
|
|
896
|
|
897 ;; We do it the hard way. For each gap, an XOVER command is sent
|
|
898 ;; to the server. We do not wait for a reply from the server, we
|
|
899 ;; just send them off as fast as we can. That means that we have
|
|
900 ;; to count the number of responses we get back to find out when we
|
|
901 ;; have gotten all we asked for.
|
|
902 ((numberp nntp-nov-gap)
|
|
903 (let ((count 0)
|
|
904 (received 0)
|
|
905 (last-point (point-min))
|
|
906 (buf (current-buffer))
|
|
907 first)
|
|
908 ;; We have to check `nntp-server-xover'. If it gets set to nil,
|
|
909 ;; that means that the server does not understand XOVER, but we
|
|
910 ;; won't know that until we try.
|
|
911 (while (and nntp-server-xover sequence)
|
|
912 (setq first (car sequence))
|
|
913 ;; Search forward until we find a gap, or until we run out of
|
|
914 ;; articles.
|
|
915 (while (and (cdr sequence)
|
|
916 (< (- (nth 1 sequence) (car sequence)) nntp-nov-gap))
|
|
917 (setq sequence (cdr sequence)))
|
|
918
|
|
919 (if (not (nntp-send-xover-command first (car sequence)))
|
|
920 ()
|
|
921 (setq sequence (cdr sequence)
|
|
922 count (1+ count))
|
|
923
|
|
924 ;; Every 400 requests we have to read the stream in
|
|
925 ;; order to avoid deadlocks.
|
|
926 (if (or (null sequence) ;All requests have been sent.
|
|
927 (zerop (% count nntp-maximum-request)))
|
|
928 (progn
|
|
929 (accept-process-output)
|
|
930 ;; On some Emacs versions the preceding function has
|
|
931 ;; a tendency to change the buffer. Perhaps. It's
|
|
932 ;; quite difficult to reporduce, because it only
|
|
933 ;; seems to happen once in a blue moon.
|
|
934 (set-buffer buf)
|
|
935 (while (progn
|
|
936 (goto-char last-point)
|
|
937 ;; Count replies.
|
|
938 (while (re-search-forward "^[0-9][0-9][0-9] " nil t)
|
|
939 (setq received (1+ received)))
|
|
940 (setq last-point (point))
|
|
941 (< received count))
|
|
942 (accept-process-output)
|
|
943 (set-buffer buf))))))
|
|
944
|
|
945 (if (not nntp-server-xover)
|
|
946 ()
|
|
947 ;; Wait for the reply from the final command.
|
|
948 (goto-char (point-max))
|
|
949 (re-search-backward "^[0-9][0-9][0-9] " nil t)
|
|
950 (if (looking-at "^[23]")
|
|
951 (while (progn
|
|
952 (goto-char (point-max))
|
|
953 (forward-line -1)
|
|
954 (not (looking-at "^\\.\r?\n")))
|
|
955 (nntp-accept-response)))
|
|
956
|
|
957 ;; We remove any "." lines and status lines.
|
|
958 (goto-char (point-min))
|
|
959 (while (search-forward "\r" nil t)
|
|
960 (delete-char -1))
|
|
961 (goto-char (point-min))
|
|
962 (delete-matching-lines "^\\.$\\|^[1-5][0-9][0-9] ")))))
|
|
963
|
|
964 nntp-server-xover)
|
|
965
|
|
966 (defun nntp-send-xover-command (beg end &optional wait-for-reply)
|
|
967 (let ((range (format "%d-%d" beg end)))
|
|
968 (if (stringp nntp-server-xover)
|
|
969 ;; If `nntp-server-xover' is a string, then we just send this
|
|
970 ;; command.
|
|
971 (if wait-for-reply
|
|
972 (nntp-send-command "^\\.\r?\n" nntp-server-xover range)
|
|
973 ;; We do not wait for the reply.
|
|
974 (progn
|
|
975 (nntp-send-strings-to-server nntp-server-xover range)
|
|
976 t))
|
|
977 (let ((commands nntp-xover-commands))
|
|
978 ;; `nntp-xover-commands' is a list of possible XOVER commands.
|
|
979 ;; We try them all until we get at positive response.
|
|
980 (while (and commands (eq nntp-server-xover 'try))
|
|
981 (nntp-send-command "^\\.\r?\n" (car commands) range)
|
|
982 (save-excursion
|
|
983 (set-buffer nntp-server-buffer)
|
|
984 (goto-char (point-min))
|
|
985 (and (looking-at "[23]") ; No error message.
|
|
986 ;; We also have to look at the lines. Some buggy
|
|
987 ;; servers give back simple lines with just the
|
|
988 ;; article number. How... helpful.
|
|
989 (progn
|
|
990 (forward-line 1)
|
|
991 (looking-at "[0-9]+\t...")) ; More text after number.
|
|
992 (setq nntp-server-xover (car commands))))
|
|
993 (setq commands (cdr commands)))
|
|
994 ;; If none of the commands worked, we disable XOVER.
|
|
995 (if (eq nntp-server-xover 'try)
|
|
996 (save-excursion
|
|
997 (set-buffer nntp-server-buffer)
|
|
998 (erase-buffer)
|
|
999 (setq nntp-server-xover nil)))
|
|
1000 nntp-server-xover))))
|
|
1001
|
|
1002 (defun nntp-send-strings-to-server (&rest strings)
|
|
1003 "Send list of STRINGS to news server as command and its arguments."
|
|
1004 (let ((cmd (concat (mapconcat 'identity strings " ") "\r\n")))
|
|
1005 ;; We open the nntp server if it is down.
|
|
1006 (or (nntp-server-opened nntp-current-server)
|
|
1007 (nntp-open-server nntp-current-server)
|
|
1008 (error (nntp-status-message)))
|
|
1009 ;; Send the strings.
|
|
1010 (process-send-string nntp-server-process cmd)))
|
|
1011
|
|
1012 (defun nntp-send-region-to-server (begin end)
|
|
1013 "Send current buffer region (from BEGIN to END) to news server."
|
|
1014 (save-excursion
|
|
1015 ;; We have to work in the buffer associated with NNTP server
|
|
1016 ;; process because of NEmacs hack.
|
|
1017 (copy-to-buffer nntp-server-buffer begin end)
|
|
1018 (set-buffer nntp-server-buffer)
|
|
1019 (setq begin (point-min))
|
|
1020 (setq end (point-max))
|
|
1021 ;; `process-send-region' does not work if text to be sent is very
|
|
1022 ;; large. I don't know maximum size of text sent correctly.
|
|
1023 (let ((last nil)
|
|
1024 (size 100)) ;Size of text sent at once.
|
|
1025 (save-restriction
|
|
1026 (narrow-to-region begin end)
|
|
1027 (goto-char begin)
|
|
1028 (while (not (eobp))
|
|
1029 ;;(setq last (min end (+ (point) size)))
|
|
1030 ;; NEmacs gets confused if character at `last' is Kanji.
|
|
1031 (setq last (save-excursion
|
|
1032 (goto-char (min end (+ (point) size)))
|
|
1033 (or (eobp) (forward-char 1)) ;Adjust point
|
|
1034 (point)))
|
|
1035 (process-send-region nntp-server-process (point) last)
|
|
1036 ;; I don't know whether the next codes solve the known
|
|
1037 ;; problem of communication error of GNU Emacs.
|
|
1038 (accept-process-output)
|
|
1039 ;;(sit-for 0)
|
|
1040 (goto-char last))))
|
|
1041 ;; We cannot erase buffer, because reply may be received.
|
|
1042 (delete-region begin end)))
|
|
1043
|
|
1044 (defun nntp-open-server-semi-internal (server &optional service)
|
|
1045 "Open SERVER.
|
|
1046 If SERVER is nil, use value of environment variable `NNTPSERVER'.
|
|
1047 If SERVICE, this this as the port number."
|
|
1048 (let ((server (or server (getenv "NNTPSERVER")))
|
|
1049 (status nil)
|
|
1050 (timer
|
|
1051 (and nntp-connection-timeout
|
|
1052 (cond
|
|
1053 ((fboundp 'run-at-time)
|
|
1054 (run-at-time nntp-connection-timeout
|
|
1055 nil 'nntp-kill-connection server))
|
|
1056 ((fboundp 'start-itimer)
|
|
1057 ;; Not sure if this will work or not, only one way to
|
|
1058 ;; find out
|
|
1059 (eval '(start-itimer "nntp-timeout"
|
|
1060 (lambda ()
|
|
1061 (nntp-kill-connection server))
|
|
1062 nntp-connection-timeout nil)))))))
|
|
1063 (save-excursion
|
|
1064 (set-buffer nntp-server-buffer)
|
|
1065 (setq nntp-status-string "")
|
|
1066 (message "nntp: Connecting to server on %s..." server)
|
|
1067 (cond ((and server (nntp-open-server-internal server service))
|
|
1068 (setq nntp-address server)
|
|
1069 (setq status
|
|
1070 (condition-case nil
|
|
1071 (nntp-wait-for-response "^[23].*\r?\n" 'slow)
|
|
1072 (error nil)
|
|
1073 (quit nil)))
|
|
1074 (or status (nntp-close-server-internal server))
|
|
1075 (and nntp-server-process
|
|
1076 (progn
|
|
1077 (set-process-sentinel
|
|
1078 nntp-server-process 'nntp-default-sentinel)
|
|
1079 ;; You can send commands at startup like AUTHINFO here.
|
|
1080 ;; Added by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>
|
|
1081 (run-hooks 'nntp-server-opened-hook))))
|
|
1082 ((null server)
|
|
1083 (setq nntp-status-string "NNTP server is not specified."))
|
|
1084 (t ; We couldn't open the server.
|
|
1085 (setq nntp-status-string
|
|
1086 (buffer-substring (point-min) (point-max)))
|
|
1087 (setq nntp-timeout-servers (cons server nntp-timeout-servers))))
|
|
1088 (and timer (cancel-timer timer))
|
|
1089 (message "")
|
|
1090 (or status
|
|
1091 (setq nntp-current-server nil
|
|
1092 nntp-async-number nil))
|
|
1093 status)))
|
|
1094
|
|
1095 (defun nntp-open-server-internal (server &optional service)
|
|
1096 "Open connection to news server on SERVER by SERVICE (default is nntp)."
|
|
1097 (let (proc)
|
|
1098 (save-excursion
|
|
1099 ;; Use TCP/IP stream emulation package if needed.
|
|
1100 (or (fboundp 'open-network-stream)
|
|
1101 (require 'tcp))
|
|
1102 ;; Initialize communication buffer.
|
|
1103 (nnheader-init-server-buffer)
|
|
1104 (set-buffer nntp-server-buffer)
|
|
1105 (if (setq proc
|
|
1106 (condition-case nil
|
|
1107 (funcall nntp-open-server-function server)
|
|
1108 (error nil)))
|
|
1109 (progn
|
|
1110 (setq nntp-server-process proc)
|
|
1111 ;; Suggested by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
|
|
1112 (process-kill-without-query proc)
|
|
1113 (setq nntp-address server)
|
|
1114 ;; It is possible to change kanji-fileio-code in this hook.
|
|
1115 (run-hooks 'nntp-server-hook)
|
|
1116 (push proc nntp-opened-connections)
|
|
1117 nntp-server-process)))))
|
|
1118
|
|
1119 (defun nntp-open-network-stream (server)
|
|
1120 (open-network-stream
|
|
1121 "nntpd" nntp-server-buffer server nntp-port-number))
|
|
1122
|
|
1123 (defun nntp-open-rlogin (server)
|
|
1124 (let ((proc (start-process "nntpd" nntp-server-buffer "rsh" server)))
|
|
1125 (process-send-string proc (mapconcat 'identity nntp-rlogin-parameters
|
|
1126 " "))
|
|
1127 (process-send-string proc "\n")))
|
|
1128
|
|
1129 (defun nntp-telnet-to-machine ()
|
|
1130 (let (b)
|
|
1131 (telnet "localhost")
|
|
1132 (goto-char (point-min))
|
|
1133 (while (not (re-search-forward "^login: *" nil t))
|
|
1134 (sit-for 1)
|
|
1135 (goto-char (point-min)))
|
|
1136 (goto-char (point-max))
|
|
1137 (insert "larsi")
|
|
1138 (telnet-send-input)
|
|
1139 (setq b (point))
|
|
1140 (while (not (re-search-forward ">" nil t))
|
|
1141 (sit-for 1)
|
|
1142 (goto-char b))
|
|
1143 (goto-char (point-max))
|
|
1144 (insert "ls")
|
|
1145 (telnet-send-input)))
|
|
1146
|
|
1147 (defun nntp-close-server-internal (&optional server)
|
|
1148 "Close connection to news server."
|
|
1149 (nntp-possibly-change-server nil server)
|
|
1150 (if nntp-server-process
|
|
1151 (delete-process nntp-server-process))
|
|
1152 (setq nntp-server-process nil)
|
|
1153 (setq nntp-address ""))
|
|
1154
|
|
1155 (defun nntp-accept-response ()
|
|
1156 "Read response of server.
|
|
1157 It is well-known that the communication speed will be much improved by
|
|
1158 defining this function as macro."
|
|
1159 ;; To deal with server process exiting before
|
|
1160 ;; accept-process-output is called.
|
|
1161 ;; Suggested by Jason Venner <jason@violet.berkeley.edu>.
|
|
1162 ;; This is a copy of `nntp-default-sentinel'.
|
|
1163 (let ((buf (current-buffer)))
|
|
1164 (prog1
|
|
1165 (if (or (not nntp-server-process)
|
|
1166 (not (memq (process-status nntp-server-process) '(open run))))
|
|
1167 (error "nntp: Process connection closed; %s" (nntp-status-message))
|
|
1168 (if nntp-buggy-select
|
|
1169 (progn
|
|
1170 ;; We cannot use `accept-process-output'.
|
|
1171 ;; Fujitsu UTS requires messages during sleep-for.
|
|
1172 ;; I don't know why.
|
|
1173 (message "NNTP: Reading...")
|
|
1174 (sleep-for 1)
|
|
1175 (message ""))
|
|
1176 (condition-case errorcode
|
|
1177 (accept-process-output nntp-server-process)
|
|
1178 (error
|
|
1179 (cond ((string-equal "select error: Invalid argument"
|
|
1180 (nth 1 errorcode))
|
|
1181 ;; Ignore select error.
|
|
1182 nil)
|
|
1183 (t
|
|
1184 (signal (car errorcode) (cdr errorcode))))))))
|
|
1185 (set-buffer buf))))
|
|
1186
|
|
1187 (defun nntp-last-element (list)
|
|
1188 "Return last element of LIST."
|
|
1189 (while (cdr list)
|
|
1190 (setq list (cdr list)))
|
|
1191 (car list))
|
|
1192
|
|
1193 (defun nntp-possibly-change-server (newsgroup server)
|
|
1194 ;; We see whether it is necessary to change the newsgroup.
|
|
1195 (and newsgroup
|
|
1196 (progn
|
|
1197 (not (equal newsgroup nntp-current-group))
|
|
1198 (nntp-request-group newsgroup server)))
|
|
1199 (and server
|
|
1200 (or (nntp-server-opened server)
|
|
1201 (nntp-open-server server))))
|
|
1202
|
|
1203 (defun nntp-try-list-active (group)
|
|
1204 (nntp-list-active-group group)
|
|
1205 (save-excursion
|
|
1206 (set-buffer nntp-server-buffer)
|
|
1207 (goto-char (point-min))
|
|
1208 (cond ((looking-at "5[0-9]+")
|
|
1209 (setq nntp-server-list-active-group nil))
|
|
1210 (t
|
|
1211 (setq nntp-server-list-active-group t)))))
|
|
1212
|
|
1213 (defun nntp-async-server-opened ()
|
|
1214 (and nntp-async-process
|
|
1215 (memq (process-status nntp-async-process) '(open run))))
|
|
1216
|
|
1217 (defun nntp-async-open-server ()
|
|
1218 (save-excursion
|
|
1219 (set-buffer (generate-new-buffer " *async-nntp*"))
|
|
1220 (setq nntp-async-buffer (current-buffer))
|
|
1221 (buffer-disable-undo (current-buffer)))
|
|
1222 (let ((nntp-server-process nil)
|
|
1223 (nntp-server-buffer nntp-async-buffer))
|
|
1224 (nntp-open-server-semi-internal nntp-address nntp-port-number)
|
|
1225 (if (not (setq nntp-async-process nntp-server-process))
|
|
1226 (progn
|
|
1227 (setq nntp-async-number nil))
|
|
1228 (set-process-buffer nntp-async-process nntp-async-buffer))))
|
|
1229
|
|
1230 (defun nntp-async-fetch-articles (article)
|
|
1231 (if (stringp article)
|
|
1232 ()
|
|
1233 (let ((articles (cdr (memq (assq article nntp-async-articles)
|
|
1234 nntp-async-articles)))
|
|
1235 (max (cond ((numberp nntp-async-number)
|
|
1236 nntp-async-number)
|
|
1237 ((eq nntp-async-number t)
|
|
1238 (length nntp-async-articles))
|
|
1239 (t 0)))
|
|
1240 nart)
|
|
1241 (while (and (>= (setq max (1- max)) 0)
|
|
1242 articles)
|
|
1243 (or (memq (setq nart (car (car articles))) nntp-async-fetched)
|
|
1244 (progn
|
|
1245 (nntp-async-send-strings "ARTICLE " (int-to-string nart))
|
|
1246 (setq nntp-async-fetched (cons nart nntp-async-fetched))))
|
|
1247 (setq articles (cdr articles))))))
|
|
1248
|
|
1249 (defun nntp-async-send-strings (&rest strings)
|
|
1250 (let ((cmd (concat (mapconcat 'identity strings " ") "\r\n")))
|
|
1251 (or (nntp-async-server-opened)
|
|
1252 (nntp-async-open-server)
|
|
1253 (error (nntp-status-message)))
|
|
1254 (process-send-string nntp-async-process cmd)))
|
|
1255
|
|
1256 (defun nntp-async-request-group (group)
|
|
1257 (if (equal group nntp-current-group)
|
|
1258 ()
|
|
1259 (let ((asyncs (assoc group nntp-async-group-alist)))
|
|
1260 ;; A new group has been selected, so we push the current state
|
|
1261 ;; of async articles on an alist, and pull the old state off.
|
|
1262 (setq nntp-async-group-alist
|
|
1263 (cons (list nntp-current-group
|
|
1264 nntp-async-articles nntp-async-fetched
|
|
1265 nntp-async-process)
|
|
1266 (delq asyncs nntp-async-group-alist)))
|
|
1267 (and asyncs
|
|
1268 (progn
|
|
1269 (setq nntp-async-articles (nth 1 asyncs))
|
|
1270 (setq nntp-async-fetched (nth 2 asyncs))
|
|
1271 (setq nntp-async-process (nth 3 asyncs)))))))
|
|
1272
|
|
1273 (provide 'nntp)
|
|
1274
|
|
1275 ;;; nntp.el ends here
|