Regenerate ldefs-boot.el
[bpt/emacs.git] / lisp / gnus / gnus-srvr.el
CommitLineData
eec82323 1;;; gnus-srvr.el --- virtual server support for Gnus
e84b4b86 2
ba318903 3;; Copyright (C) 1995-2014 Free Software Foundation, Inc.
eec82323 4
6748645f 5;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
eec82323
LMI
6;; Keywords: news
7
8;; This file is part of GNU Emacs.
9
5e809f55 10;; GNU Emacs is free software: you can redistribute it and/or modify
eec82323 11;; it under the terms of the GNU General Public License as published by
5e809f55
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
eec82323
LMI
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
5e809f55 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
eec82323
LMI
22
23;;; Commentary:
24
25;;; Code:
26
5ab7173c
RS
27(eval-when-compile (require 'cl))
28
eec82323 29(require 'gnus)
8ccbef23 30(require 'gnus-start)
eec82323
LMI
31(require 'gnus-spec)
32(require 'gnus-group)
33(require 'gnus-int)
34(require 'gnus-range)
35
389b76fa
G
36(autoload 'gnus-group-make-nnir-group "nnir")
37
23f87bed
MB
38(defcustom gnus-server-mode-hook nil
39 "Hook run in `gnus-server-mode' buffers."
40 :group 'gnus-server
41 :type 'hook)
eec82323 42
23f87bed
MB
43(defcustom gnus-server-exit-hook nil
44 "Hook run when exiting the server buffer."
45 :group 'gnus-server
46 :type 'hook)
47
48(defcustom gnus-server-line-format " {%(%h:%w%)} %s%a\n"
eec82323
LMI
49 "Format of server lines.
50It works along the same lines as a normal formatting string,
6748645f
LMI
51with some simple extensions.
52
53The following specs are understood:
54
01c52d31 55%h back end
6748645f
LMI
56%n name
57%w address
23f87bed
MB
58%s status
59%a agent covered
60
61General format specifiers can also be used.
62See Info node `(gnus)Formatting Variables'."
63 :link '(custom-manual "(gnus)Formatting Variables")
64 :group 'gnus-server-visual
65 :type 'string)
66
67(defcustom gnus-server-mode-line-format "Gnus: %%b"
68 "The format specification for the server mode line."
69 :group 'gnus-server-visual
70 :type 'string)
71
72(defcustom gnus-server-browse-in-group-buffer nil
73 "Whether server browsing should take place in the group buffer.
74If nil, a faster, but more primitive, buffer is used instead."
bf247b6e 75 :version "22.1"
23f87bed
MB
76 :group 'gnus-server-visual
77 :type 'boolean)
eec82323
LMI
78
79;;; Internal variables.
80
81(defvar gnus-inserted-opened-servers nil)
82
83(defvar gnus-server-line-format-alist
6748645f
LMI
84 `((?h gnus-tmp-how ?s)
85 (?n gnus-tmp-name ?s)
86 (?w gnus-tmp-where ?s)
23f87bed
MB
87 (?s gnus-tmp-status ?s)
88 (?a gnus-tmp-agent ?s)))
eec82323
LMI
89
90(defvar gnus-server-mode-line-format-alist
6748645f
LMI
91 `((?S gnus-tmp-news-server ?s)
92 (?M gnus-tmp-news-method ?s)
93 (?u gnus-tmp-user-defined ?s)))
eec82323
LMI
94
95(defvar gnus-server-line-format-spec nil)
96(defvar gnus-server-mode-line-format-spec nil)
97(defvar gnus-server-killed-servers nil)
98
99(defvar gnus-server-mode-map)
100
101(defvar gnus-server-menu-hook nil
102 "*Hook run after the creation of the server mode menu.")
103
104(defun gnus-server-make-menu-bar ()
105 (gnus-turn-off-edit-menu 'server)
106 (unless (boundp 'gnus-server-server-menu)
107 (easy-menu-define
108 gnus-server-server-menu gnus-server-mode-map ""
109 '("Server"
23f87bed 110 ["Add..." gnus-server-add-server t]
eec82323
LMI
111 ["Browse" gnus-server-read-server t]
112 ["Scan" gnus-server-scan-server t]
113 ["List" gnus-server-list-servers t]
114 ["Kill" gnus-server-kill-server t]
115 ["Yank" gnus-server-yank-server t]
116 ["Copy" gnus-server-copy-server t]
59e75882 117 ["Show" gnus-server-show-server t]
eec82323
LMI
118 ["Edit" gnus-server-edit-server t]
119 ["Regenerate" gnus-server-regenerate-server t]
01c52d31 120 ["Compact" gnus-server-compact-server t]
eec82323
LMI
121 ["Exit" gnus-server-exit t]))
122
123 (easy-menu-define
124 gnus-server-connections-menu gnus-server-mode-map ""
125 '("Connections"
126 ["Open" gnus-server-open-server t]
127 ["Close" gnus-server-close-server t]
23f87bed 128 ["Offline" gnus-server-offline-server t]
eec82323
LMI
129 ["Deny" gnus-server-deny-server t]
130 "---"
131 ["Open All" gnus-server-open-all-servers t]
132 ["Close All" gnus-server-close-all-servers t]
133 ["Reset All" gnus-server-remove-denials t]))
134
6748645f 135 (gnus-run-hooks 'gnus-server-menu-hook)))
eec82323
LMI
136
137(defvar gnus-server-mode-map nil)
138(put 'gnus-server-mode 'mode-class 'special)
139
140(unless gnus-server-mode-map
141 (setq gnus-server-mode-map (make-sparse-keymap))
142 (suppress-keymap gnus-server-mode-map)
143
6748645f 144 (gnus-define-keys gnus-server-mode-map
23f87bed 145 " " gnus-server-read-server-in-server-buffer
6748645f
LMI
146 "\r" gnus-server-read-server
147 gnus-mouse-2 gnus-server-pick-server
148 "q" gnus-server-exit
149 "l" gnus-server-list-servers
150 "k" gnus-server-kill-server
151 "y" gnus-server-yank-server
152 "c" gnus-server-copy-server
153 "a" gnus-server-add-server
154 "e" gnus-server-edit-server
59e75882 155 "S" gnus-server-show-server
6748645f
LMI
156 "s" gnus-server-scan-server
157
158 "O" gnus-server-open-server
159 "\M-o" gnus-server-open-all-servers
160 "C" gnus-server-close-server
161 "\M-c" gnus-server-close-all-servers
162 "D" gnus-server-deny-server
23f87bed 163 "L" gnus-server-offline-server
6748645f
LMI
164 "R" gnus-server-remove-denials
165
16409b0b
GM
166 "n" next-line
167 "p" previous-line
a1506d29 168
6748645f 169 "g" gnus-server-regenerate-server
eec82323 170
389b76fa
G
171 "G" gnus-group-make-nnir-group
172
01c52d31
MB
173 "z" gnus-server-compact-server
174
eec82323
LMI
175 "\C-c\C-i" gnus-info-find-node
176 "\C-c\C-b" gnus-bug))
177
0f49874b 178(defface gnus-server-agent
23f87bed
MB
179 '((((class color) (background light)) (:foreground "PaleTurquoise" :bold t))
180 (((class color) (background dark)) (:foreground "PaleTurquoise" :bold t))
181 (t (:bold t)))
182 "Face used for displaying AGENTIZED servers"
183 :group 'gnus-server-visual)
0f49874b
MB
184;; backward-compatibility alias
185(put 'gnus-server-agent-face 'face-alias 'gnus-server-agent)
3d493bef 186(put 'gnus-server-agent-face 'obsolete-face "22.1")
23f87bed 187
0f49874b 188(defface gnus-server-opened
23f87bed
MB
189 '((((class color) (background light)) (:foreground "Green3" :bold t))
190 (((class color) (background dark)) (:foreground "Green1" :bold t))
191 (t (:bold t)))
192 "Face used for displaying OPENED servers"
193 :group 'gnus-server-visual)
0f49874b
MB
194;; backward-compatibility alias
195(put 'gnus-server-opened-face 'face-alias 'gnus-server-opened)
3d493bef 196(put 'gnus-server-opened-face 'obsolete-face "22.1")
23f87bed 197
0f49874b 198(defface gnus-server-closed
23f87bed
MB
199 '((((class color) (background light)) (:foreground "Steel Blue" :italic t))
200 (((class color) (background dark))
01c52d31 201 (:foreground "LightBlue" :italic t))
23f87bed
MB
202 (t (:italic t)))
203 "Face used for displaying CLOSED servers"
204 :group 'gnus-server-visual)
0f49874b
MB
205;; backward-compatibility alias
206(put 'gnus-server-closed-face 'face-alias 'gnus-server-closed)
3d493bef 207(put 'gnus-server-closed-face 'obsolete-face "22.1")
23f87bed 208
0f49874b 209(defface gnus-server-denied
23f87bed
MB
210 '((((class color) (background light)) (:foreground "Red" :bold t))
211 (((class color) (background dark)) (:foreground "Pink" :bold t))
212 (t (:inverse-video t :bold t)))
213 "Face used for displaying DENIED servers"
214 :group 'gnus-server-visual)
0f49874b
MB
215;; backward-compatibility alias
216(put 'gnus-server-denied-face 'face-alias 'gnus-server-denied)
3d493bef 217(put 'gnus-server-denied-face 'obsolete-face "22.1")
23f87bed 218
0f49874b 219(defface gnus-server-offline
23f87bed
MB
220 '((((class color) (background light)) (:foreground "Orange" :bold t))
221 (((class color) (background dark)) (:foreground "Yellow" :bold t))
222 (t (:inverse-video t :bold t)))
223 "Face used for displaying OFFLINE servers"
224 :group 'gnus-server-visual)
0f49874b
MB
225;; backward-compatibility alias
226(put 'gnus-server-offline-face 'face-alias 'gnus-server-offline)
3d493bef 227(put 'gnus-server-offline-face 'obsolete-face "22.1")
23f87bed 228
23f87bed 229(defvar gnus-server-font-lock-keywords
f931ce84
MB
230 '(("(\\(agent\\))" 1 'gnus-server-agent)
231 ("(\\(opened\\))" 1 'gnus-server-opened)
232 ("(\\(closed\\))" 1 'gnus-server-closed)
233 ("(\\(offline\\))" 1 'gnus-server-offline)
234 ("(\\(denied\\))" 1 'gnus-server-denied)))
23f87bed 235
eec82323
LMI
236(defun gnus-server-mode ()
237 "Major mode for listing and editing servers.
238
239All normal editing commands are switched off.
240\\<gnus-server-mode-map>
241For more in-depth information on this mode, read the manual
acc15099 242\(`\\[gnus-info-find-node]').
eec82323
LMI
243
244The following commands are available:
245
246\\{gnus-server-mode-map}"
1b3b87df 247 ;; FIXME: Use define-derived-mode.
eec82323
LMI
248 (interactive)
249 (when (gnus-visual-p 'server-menu 'menu)
250 (gnus-server-make-menu-bar))
251 (kill-all-local-variables)
252 (gnus-simplify-mode-line)
253 (setq major-mode 'gnus-server-mode)
254 (setq mode-name "Server")
255 (gnus-set-default-directory)
256 (setq mode-line-process nil)
257 (use-local-map gnus-server-mode-map)
16409b0b 258 (buffer-disable-undo)
eec82323
LMI
259 (setq truncate-lines t)
260 (setq buffer-read-only t)
23f87bed
MB
261 (if (featurep 'xemacs)
262 (put 'gnus-server-mode 'font-lock-defaults '(gnus-server-font-lock-keywords t))
263 (set (make-local-variable 'font-lock-defaults)
264 '(gnus-server-font-lock-keywords t)))
cfcd5c91 265 (gnus-run-mode-hooks 'gnus-server-mode-hook))
eec82323 266
6748645f
LMI
267(defun gnus-server-insert-server-line (gnus-tmp-name method)
268 (let* ((gnus-tmp-how (car method))
269 (gnus-tmp-where (nth 1 method))
eec82323 270 (elem (assoc method gnus-opened-servers))
13efce40 271 (gnus-tmp-status
23f87bed
MB
272 (cond
273 ((eq (nth 1 elem) 'denied) "(denied)")
274 ((eq (nth 1 elem) 'offline) "(offline)")
275 (t
13efce40
MB
276 (condition-case nil
277 (if (or (gnus-server-opened method)
278 (eq (nth 1 elem) 'ok))
279 "(opened)"
280 "(closed)")
281 ((error) "(error)")))))
23f87bed
MB
282 (gnus-tmp-agent (if (and gnus-agent
283 (gnus-agent-method-p method))
284 " (agent)"
285 "")))
eec82323
LMI
286 (beginning-of-line)
287 (gnus-add-text-properties
288 (point)
289 (prog1 (1+ (point))
290 ;; Insert the text.
291 (eval gnus-server-line-format-spec))
23f87bed 292 (list 'gnus-server (intern gnus-tmp-name)
b890d447 293 'gnus-named-server (intern (gnus-method-to-server method t))))))
eec82323
LMI
294
295(defun gnus-enter-server-buffer ()
296 "Set up the server buffer."
297 (gnus-server-setup-buffer)
298 (gnus-configure-windows 'server)
42a83f4b
SM
299 ;; Usually `gnus-configure-windows' will finish with the
300 ;; `gnus-server-buffer' selected as the current buffer, but not always (I
301 ;; bumped into it when starting from a dedicated *Group* frame, and
302 ;; gnus-configure-windows opened *Server* into its own dedicated frame).
303 (with-current-buffer (get-buffer gnus-server-buffer)
304 (gnus-server-prepare)))
eec82323
LMI
305
306(defun gnus-server-setup-buffer ()
307 "Initialize the server buffer."
308 (unless (get-buffer gnus-server-buffer)
42a83f4b 309 (with-current-buffer (gnus-get-buffer-create gnus-server-buffer)
71e691a5 310 (gnus-server-mode))))
eec82323
LMI
311
312(defun gnus-server-prepare ()
6748645f
LMI
313 (gnus-set-format 'server-mode)
314 (gnus-set-format 'server t)
eec82323
LMI
315 (let ((alist gnus-server-alist)
316 (buffer-read-only nil)
eec82323
LMI
317 done server op-ser)
318 (erase-buffer)
319 (setq gnus-inserted-opened-servers nil)
320 ;; First we do the real list of servers.
321 (while alist
322 (unless (member (cdar alist) done)
323 (push (cdar alist) done)
df9a602c 324 (setq server (pop alist))
eec82323
LMI
325 (when (and server (car server) (cdr server))
326 (gnus-server-insert-server-line (car server) (cdr server))))
327 (when (member (cdar alist) done)
328 (pop alist)))
329 ;; Then we insert the list of servers that have been opened in
330 ;; this session.
01c52d31
MB
331 (dolist (open gnus-opened-servers)
332 (when (and (not (member (car open) done))
6748645f 333 ;; Just ignore ephemeral servers.
f4887f8b 334 (not (gnus-method-ephemeral-p (car open))))
01c52d31 335 (push (car open) done)
eec82323 336 (gnus-server-insert-server-line
01c52d31
MB
337 (setq op-ser (format "%s:%s" (caar open) (nth 1 (car open))))
338 (car open))
339 (push (list op-ser (car open)) gnus-inserted-opened-servers))))
eec82323
LMI
340 (goto-char (point-min))
341 (gnus-server-position-point))
342
343(defun gnus-server-server-name ()
01c52d31 344 (let ((server (get-text-property (point-at-bol) 'gnus-server)))
eec82323
LMI
345 (and server (symbol-name server))))
346
23f87bed 347(defun gnus-server-named-server ()
01c52d31
MB
348 "Return a server name that matches one of the names returned by
349`gnus-method-to-server'."
350 (let ((server (get-text-property (point-at-bol) 'gnus-named-server)))
23f87bed
MB
351 (and server (symbol-name server))))
352
eec82323
LMI
353(defalias 'gnus-server-position-point 'gnus-goto-colon)
354
355(defconst gnus-server-edit-buffer "*Gnus edit server*")
356
357(defun gnus-server-update-server (server)
42a83f4b 358 (with-current-buffer gnus-server-buffer
eec82323
LMI
359 (let* ((buffer-read-only nil)
360 (entry (assoc server gnus-server-alist))
361 (oentry (assoc (gnus-server-to-method server)
362 gnus-opened-servers)))
363 (when entry
364 (gnus-dribble-enter
365 (concat "(gnus-server-set-info \"" server "\" '"
b229f37d
KY
366 (gnus-prin1-to-string (cdr entry)) ")\n")
367 (concat "^(gnus-server-set-info \"" (regexp-quote server) "\"")))
eec82323
LMI
368 (when (or entry oentry)
369 ;; Buffer may be narrowed.
370 (save-restriction
371 (widen)
372 (when (gnus-server-goto-server server)
373 (gnus-delete-line))
374 (if entry
375 (gnus-server-insert-server-line (car entry) (cdr entry))
376 (gnus-server-insert-server-line
377 (format "%s:%s" (caar oentry) (nth 1 (car oentry)))
378 (car oentry)))
379 (gnus-server-position-point))))))
380
381(defun gnus-server-set-info (server info)
382 ;; Enter a select method into the virtual server alist.
383 (when (and server info)
384 (gnus-dribble-enter
385 (concat "(gnus-server-set-info \"" server "\" '"
b229f37d
KY
386 (gnus-prin1-to-string info) ")")
387 (concat "^(gnus-server-set-info \"" (regexp-quote server) "\""))
eec82323 388 (let* ((server (nth 1 info))
23f87bed
MB
389 (entry (assoc server gnus-server-alist))
390 (cached (assoc server gnus-server-method-cache)))
391 (if cached
392 (setq gnus-server-method-cache
393 (delq cached gnus-server-method-cache)))
01c52d31
MB
394 (if entry
395 (progn
396 ;; Remove the server from `gnus-opened-servers' since
397 ;; it has never been opened with the new `info' yet.
398 (gnus-opened-servers-remove (cdr entry))
399 ;; Don't make a new Lisp object.
400 (setcar (cdr entry) (car info))
401 (setcdr (cdr entry) (cdr info)))
eec82323
LMI
402 (setq gnus-server-alist
403 (nconc gnus-server-alist (list (cons server info))))))))
404
405;;; Interactive server functions.
406
407(defun gnus-server-kill-server (server)
408 "Kill the server on the current line."
409 (interactive (list (gnus-server-server-name)))
410 (unless (gnus-server-goto-server server)
411 (if server (error "No such server: %s" server)
412 (error "No server on the current line")))
413 (unless (assoc server gnus-server-alist)
414 (error "Read-only server %s" server))
6748645f 415 (gnus-dribble-touch)
eec82323
LMI
416 (let ((buffer-read-only nil))
417 (gnus-delete-line))
418 (push (assoc server gnus-server-alist) gnus-server-killed-servers)
419 (setq gnus-server-alist (delq (car gnus-server-killed-servers)
420 gnus-server-alist))
16409b0b
GM
421 (let ((groups (gnus-groups-from-server server)))
422 (when (and groups
423 (gnus-yes-or-no-p
424 (format "Kill all %s groups from this server? "
425 (length groups))))
426 (dolist (group groups)
427 (setq gnus-newsrc-alist
428 (delq (assoc group gnus-newsrc-alist)
429 gnus-newsrc-alist))
430 (when gnus-group-change-level-function
431 (funcall gnus-group-change-level-function
432 group gnus-level-killed 3)))))
eec82323
LMI
433 (gnus-server-position-point))
434
435(defun gnus-server-yank-server ()
436 "Yank the previously killed server."
437 (interactive)
438 (unless gnus-server-killed-servers
439 (error "No killed servers to be yanked"))
440 (let ((alist gnus-server-alist)
441 (server (gnus-server-server-name))
442 (killed (car gnus-server-killed-servers)))
443 (if (not server)
444 (setq gnus-server-alist (nconc gnus-server-alist (list killed)))
445 (if (string= server (caar gnus-server-alist))
446 (push killed gnus-server-alist)
447 (while (and (cdr alist)
448 (not (string= server (caadr alist))))
449 (setq alist (cdr alist)))
450 (if alist
451 (setcdr alist (cons killed (cdr alist)))
23f87bed 452 (setq gnus-server-alist (list killed)))))
eec82323
LMI
453 (gnus-server-update-server (car killed))
454 (setq gnus-server-killed-servers (cdr gnus-server-killed-servers))
455 (gnus-server-position-point)))
456
457(defun gnus-server-exit ()
458 "Return to the group buffer."
459 (interactive)
6748645f 460 (gnus-run-hooks 'gnus-server-exit-hook)
23f87bed 461 (gnus-kill-buffer (current-buffer))
eec82323
LMI
462 (gnus-configure-windows 'group t))
463
464(defun gnus-server-list-servers ()
465 "List all available servers."
466 (interactive)
467 (let ((cur (gnus-server-server-name)))
468 (gnus-server-prepare)
469 (if cur (gnus-server-goto-server cur)
470 (goto-char (point-max))
471 (forward-line -1))
472 (gnus-server-position-point)))
473
474(defun gnus-server-set-status (method status)
475 "Make METHOD have STATUS."
476 (let ((entry (assoc method gnus-opened-servers)))
477 (if entry
478 (setcar (cdr entry) status)
479 (push (list method status) gnus-opened-servers))))
480
481(defun gnus-opened-servers-remove (method)
482 "Remove METHOD from the list of opened servers."
483 (setq gnus-opened-servers (delq (assoc method gnus-opened-servers)
484 gnus-opened-servers)))
485
486(defun gnus-server-open-server (server)
487 "Force an open of SERVER."
488 (interactive (list (gnus-server-server-name)))
489 (let ((method (gnus-server-to-method server)))
490 (unless method
491 (error "No such server: %s" server))
492 (gnus-server-set-status method 'ok)
493 (prog1
350a1888 494 (gnus-open-server method)
eec82323
LMI
495 (gnus-server-update-server server)
496 (gnus-server-position-point))))
497
498(defun gnus-server-open-all-servers ()
499 "Open all servers."
500 (interactive)
01c52d31
MB
501 (dolist (server gnus-inserted-opened-servers)
502 (gnus-server-open-server (car server))))
eec82323
LMI
503
504(defun gnus-server-close-server (server)
505 "Close SERVER."
506 (interactive (list (gnus-server-server-name)))
507 (let ((method (gnus-server-to-method server)))
508 (unless method
509 (error "No such server: %s" server))
510 (gnus-server-set-status method 'closed)
511 (prog1
512 (gnus-close-server method)
513 (gnus-server-update-server server)
514 (gnus-server-position-point))))
515
23f87bed
MB
516(defun gnus-server-offline-server (server)
517 "Set SERVER to offline."
518 (interactive (list (gnus-server-server-name)))
519 (let ((method (gnus-server-to-method server)))
520 (unless method
521 (error "No such server: %s" server))
522 (prog1
523 (gnus-close-server method)
524 (gnus-server-set-status method 'offline)
525 (gnus-server-update-server server)
526 (gnus-server-position-point))))
527
eec82323
LMI
528(defun gnus-server-close-all-servers ()
529 "Close all servers."
530 (interactive)
23f87bed 531 (dolist (server gnus-inserted-opened-servers)
01c52d31
MB
532 (gnus-server-close-server (car server)))
533 (dolist (server gnus-server-alist)
23f87bed 534 (gnus-server-close-server (car server))))
eec82323
LMI
535
536(defun gnus-server-deny-server (server)
537 "Make sure SERVER will never be attempted opened."
538 (interactive (list (gnus-server-server-name)))
539 (let ((method (gnus-server-to-method server)))
540 (unless method
541 (error "No such server: %s" server))
542 (gnus-server-set-status method 'denied))
543 (gnus-server-update-server server)
544 (gnus-server-position-point)
545 t)
546
547(defun gnus-server-remove-denials ()
548 "Make all denied servers into closed servers."
549 (interactive)
23f87bed
MB
550 (dolist (server gnus-opened-servers)
551 (when (eq (nth 1 server) 'denied)
552 (setcar (nthcdr 1 server) 'closed)))
eec82323
LMI
553 (gnus-server-list-servers))
554
555(defun gnus-server-copy-server (from to)
4c36be58 556 "Copy a server definition to a new name."
eec82323
LMI
557 (interactive
558 (list
559 (or (gnus-server-server-name)
560 (error "No server on the current line"))
561 (read-string "Copy to: ")))
562 (unless from
563 (error "No server on current line"))
564 (unless (and to (not (string= to "")))
565 (error "No name to copy to"))
566 (when (assoc to gnus-server-alist)
567 (error "%s already exists" to))
568 (unless (gnus-server-to-method from)
569 (error "%s: no such server" from))
570 (let ((to-entry (cons from (gnus-copy-sequence
571 (gnus-server-to-method from)))))
572 (setcar to-entry to)
573 (setcar (nthcdr 2 to-entry) to)
574 (push to-entry gnus-server-killed-servers)
575 (gnus-server-yank-server)))
576
577(defun gnus-server-add-server (how where)
578 (interactive
229b59da
G
579 (list (intern (gnus-completing-read "Server method"
580 (mapcar 'car gnus-valid-select-methods)
581 t))
eec82323
LMI
582 (read-string "Server name: ")))
583 (when (assq where gnus-server-alist)
584 (error "Server with that name already defined"))
585 (push (list where how where) gnus-server-killed-servers)
586 (gnus-server-yank-server))
587
588(defun gnus-server-goto-server (server)
589 "Jump to a server line."
590 (interactive
229b59da 591 (list (gnus-completing-read "Goto server" (mapcar 'car gnus-server-alist) t)))
eec82323
LMI
592 (let ((to (text-property-any (point-min) (point-max)
593 'gnus-server (intern server))))
594 (when to
595 (goto-char to)
596 (gnus-server-position-point))))
597
598(defun gnus-server-edit-server (server)
599 "Edit the server on the current line."
600 (interactive (list (gnus-server-server-name)))
601 (unless server
602 (error "No server on current line"))
603 (unless (assoc server gnus-server-alist)
604 (error "This server can't be edited"))
605 (let ((info (cdr (assoc server gnus-server-alist))))
606 (gnus-close-server info)
607 (gnus-edit-form
608 info "Editing the server."
609 `(lambda (form)
610 (gnus-server-set-info ,server form)
611 (gnus-server-list-servers)
01c52d31
MB
612 (gnus-server-position-point))
613 'edit-server)))
eec82323 614
59e75882
G
615(defun gnus-server-show-server (server)
616 "Show the definition of the server on the current line."
617 (interactive (list (gnus-server-server-name)))
618 (unless server
619 (error "No server on current line"))
620 (let ((info (gnus-server-to-method server)))
621 (gnus-edit-form
622 info "Showing the server."
623 `(lambda (form)
624 (gnus-server-position-point))
625 'edit-server)))
626
eec82323
LMI
627(defun gnus-server-scan-server (server)
628 "Request a scan from the current server."
629 (interactive (list (gnus-server-server-name)))
6748645f
LMI
630 (let ((method (gnus-server-to-method server)))
631 (if (not (gnus-get-function method 'request-scan))
632 (error "Server %s can't scan" (car method))
633 (gnus-message 3 "Scanning %s..." server)
634 (gnus-request-scan nil method)
635 (gnus-message 3 "Scanning %s...done" server))))
eec82323 636
23f87bed
MB
637(defun gnus-server-read-server-in-server-buffer (server)
638 "Browse a server in server buffer."
639 (interactive (list (gnus-server-server-name)))
640 (let (gnus-server-browse-in-group-buffer)
641 (gnus-server-read-server server)))
642
eec82323
LMI
643(defun gnus-server-read-server (server)
644 "Browse a server."
645 (interactive (list (gnus-server-server-name)))
646 (let ((buf (current-buffer)))
647 (prog1
6748645f 648 (gnus-browse-foreign-server server buf)
42a83f4b 649 (with-current-buffer buf
eec82323
LMI
650 (gnus-server-update-server (gnus-server-server-name))
651 (gnus-server-position-point)))))
652
653(defun gnus-server-pick-server (e)
654 (interactive "e")
655 (mouse-set-point e)
656 (gnus-server-read-server (gnus-server-server-name)))
657
658\f
659;;;
660;;; Browse Server Mode
661;;;
662
663(defvar gnus-browse-menu-hook nil
664 "*Hook run after the creation of the browse mode menu.")
665
8ccbef23
G
666(defcustom gnus-browse-subscribe-newsgroup-method
667 'gnus-subscribe-alphabetically
668 "Function(s) called when subscribing groups in the Browse Server Buffer
669A few pre-made functions are supplied: `gnus-subscribe-randomly'
670inserts new groups at the beginning of the list of groups;
671`gnus-subscribe-alphabetically' inserts new groups in strict
672alphabetic order; `gnus-subscribe-hierarchically' inserts new groups
673in hierarchical newsgroup order; `gnus-subscribe-interactively' asks
674for your decision; `gnus-subscribe-killed' kills all new groups;
675`gnus-subscribe-zombies' will make all new groups into zombies;
676`gnus-subscribe-topics' will enter groups into the topics that
677claim them."
678 :version "24.1"
679 :group 'gnus-server
680 :type '(radio (function-item gnus-subscribe-randomly)
681 (function-item gnus-subscribe-alphabetically)
682 (function-item gnus-subscribe-hierarchically)
683 (function-item gnus-subscribe-interactively)
684 (function-item gnus-subscribe-killed)
685 (function-item gnus-subscribe-zombies)
686 (function-item gnus-subscribe-topics)
687 function
688 (repeat function)))
689
eec82323
LMI
690(defvar gnus-browse-mode-hook nil)
691(defvar gnus-browse-mode-map nil)
692(put 'gnus-browse-mode 'mode-class 'special)
693
694(unless gnus-browse-mode-map
695 (setq gnus-browse-mode-map (make-keymap))
696 (suppress-keymap gnus-browse-mode-map)
697
698 (gnus-define-keys
16409b0b
GM
699 gnus-browse-mode-map
700 " " gnus-browse-read-group
701 "=" gnus-browse-select-group
702 "n" gnus-browse-next-group
703 "p" gnus-browse-prev-group
704 "\177" gnus-browse-prev-group
705 [delete] gnus-browse-prev-group
706 "N" gnus-browse-next-group
707 "P" gnus-browse-prev-group
708 "\M-n" gnus-browse-next-group
709 "\M-p" gnus-browse-prev-group
710 "\r" gnus-browse-select-group
711 "u" gnus-browse-unsubscribe-current-group
712 "l" gnus-browse-exit
713 "L" gnus-browse-exit
714 "q" gnus-browse-exit
715 "Q" gnus-browse-exit
23f87bed 716 "d" gnus-browse-describe-group
61bbede6 717 [delete] gnus-browse-delete-group
16409b0b
GM
718 "\C-c\C-c" gnus-browse-exit
719 "?" gnus-browse-describe-briefly
720
721 "\C-c\C-i" gnus-info-find-node
722 "\C-c\C-b" gnus-bug))
eec82323
LMI
723
724(defun gnus-browse-make-menu-bar ()
725 (gnus-turn-off-edit-menu 'browse)
726 (unless (boundp 'gnus-browse-menu)
727 (easy-menu-define
728 gnus-browse-menu gnus-browse-mode-map ""
729 '("Browse"
730 ["Subscribe" gnus-browse-unsubscribe-current-group t]
731 ["Read" gnus-browse-read-group t]
6748645f 732 ["Select" gnus-browse-select-group t]
23f87bed 733 ["Describe" gnus-browse-describe-group t]
eec82323 734 ["Next" gnus-browse-next-group t]
158f4aea 735 ["Prev" gnus-browse-prev-group t]
eec82323 736 ["Exit" gnus-browse-exit t]))
6748645f 737 (gnus-run-hooks 'gnus-browse-menu-hook)))
eec82323
LMI
738
739(defvar gnus-browse-current-method nil)
740(defvar gnus-browse-return-buffer nil)
741
742(defvar gnus-browse-buffer "*Gnus Browse Server*")
743
6748645f
LMI
744(defun gnus-browse-foreign-server (server &optional return-buffer)
745 "Browse the server SERVER."
16409b0b 746 (setq gnus-browse-current-method (gnus-server-to-method server))
eec82323 747 (setq gnus-browse-return-buffer return-buffer)
16409b0b 748 (let* ((method gnus-browse-current-method)
23f87bed 749 (orig-select-method gnus-select-method)
6748645f
LMI
750 (gnus-select-method method)
751 groups group)
eec82323
LMI
752 (gnus-message 5 "Connecting to %s..." (nth 1 method))
753 (cond
754 ((not (gnus-check-server method))
755 (gnus-message
a8151ef7
LMI
756 1 "Unable to contact server %s: %s" (nth 1 method)
757 (gnus-status-message method))
eec82323
LMI
758 nil)
759 ((not
760 (prog2
761 (gnus-message 6 "Reading active file...")
762 (gnus-request-list method)
763 (gnus-message 6 "Reading active file...done")))
764 (gnus-message
765 1 "Couldn't request list: %s" (gnus-status-message method))
766 nil)
767 (t
23f87bed 768 (with-current-buffer nntp-server-buffer
eec82323
LMI
769 (let ((cur (current-buffer)))
770 (goto-char (point-min))
549c9aed
G
771 (unless (or (null gnus-ignored-newsgroups)
772 (string= gnus-ignored-newsgroups ""))
eec82323 773 (delete-matching-lines gnus-ignored-newsgroups))
23f87bed
MB
774 ;; We treat NNTP as a special case to avoid problems with
775 ;; garbage group names like `"foo' that appear in some badly
776 ;; managed active files. -jh.
777 (if (eq (car method) 'nntp)
778 (while (not (eobp))
779 (ignore-errors
bf247b6e 780 (push (cons
01c52d31
MB
781 (mm-string-as-unibyte
782 (buffer-substring
783 (point)
784 (progn
785 (skip-chars-forward "^ \t")
786 (point))))
23f87bed
MB
787 (let ((last (read cur)))
788 (cons (read cur) last)))
789 groups))
790 (forward-line))
791 (while (not (eobp))
792 (ignore-errors
793 (push (cons
01c52d31
MB
794 (mm-string-as-unibyte
795 (if (eq (char-after) ?\")
796 (read cur)
797 (let ((p (point)) (name ""))
798 (skip-chars-forward "^ \t\\\\")
799 (setq name (buffer-substring p (point)))
800 (while (eq (char-after) ?\\)
801 (setq p (1+ (point)))
802 (forward-char 2)
803 (skip-chars-forward "^ \t\\\\")
804 (setq name (concat name (buffer-substring
805 p (point)))))
806 name)))
23f87bed
MB
807 (let ((last (read cur)))
808 (cons (read cur) last)))
809 groups))
810 (forward-line)))))
eec82323
LMI
811 (setq groups (sort groups
812 (lambda (l1 l2)
813 (string< (car l1) (car l2)))))
23f87bed
MB
814 (if gnus-server-browse-in-group-buffer
815 (let* ((gnus-select-method orig-select-method)
816 (gnus-group-listed-groups
817 (mapcar (lambda (group)
818 (let ((name
819 (gnus-group-prefixed-name
820 (car group) method)))
821 (gnus-set-active name (cdr group))
822 name))
823 groups)))
824 (gnus-configure-windows 'group)
825 (funcall gnus-group-prepare-function
826 gnus-level-killed 'ignore 1 'ignore))
827 (gnus-get-buffer-create gnus-browse-buffer)
23f87bed
MB
828 (gnus-configure-windows 'browse)
829 (buffer-disable-undo)
830 (let ((buffer-read-only nil))
831 (erase-buffer))
832 (gnus-browse-mode)
833 (setq mode-line-buffer-identification
834 (list
835 (format
836 "Gnus: %%b {%s:%s}" (car method) (cadr method))))
837 (let ((buffer-read-only nil)
838 name
839 (prefix (let ((gnus-select-method orig-select-method))
840 (gnus-group-prefixed-name "" method))))
841 (while (setq group (pop groups))
842 (gnus-add-text-properties
843 (point)
844 (prog1 (1+ (point))
845 (insert
846 (format "%c%7d: %s\n"
01c52d31
MB
847 (let ((level
848 (if (string= prefix "")
849 (gnus-group-level (setq name (car group)))
850 (gnus-group-level
851 (concat prefix (setq name (car group)))))))
852 (cond
853 ((<= level gnus-level-subscribed) ? )
854 ((<= level gnus-level-unsubscribed) ?U)
855 ((= level gnus-level-zombie) ?Z)
856 (t ?K)))
23f87bed 857 (max 0 (- (1+ (cddr group)) (cadr group)))
01c52d31
MB
858 ;; Don't decode if name is ASCII
859 (if (and (fboundp 'detect-coding-string)
860 (eq (detect-coding-string name t) 'undecided))
861 name
862 (mm-decode-coding-string
863 name
864 (inline (gnus-group-name-charset method name)))))))
865 (list 'gnus-group name)
866 )))
23f87bed 867 (switch-to-buffer (current-buffer)))
eec82323
LMI
868 (goto-char (point-min))
869 (gnus-group-position-point)
870 (gnus-message 5 "Connecting to %s...done" (nth 1 method))
871 t))))
872
1b3b87df 873(define-derived-mode gnus-browse-mode fundamental-mode "Browse Server"
eec82323
LMI
874 "Major mode for browsing a foreign server.
875
876All normal editing commands are switched off.
877
878\\<gnus-browse-mode-map>
879The only things you can do in this buffer is
880
8811) `\\[gnus-browse-unsubscribe-current-group]' to subscribe to a group.
882The group will be inserted into the group buffer upon exit from this
883buffer.
884
8852) `\\[gnus-browse-read-group]' to read a group ephemerally.
886
8873) `\\[gnus-browse-exit]' to return to the group buffer."
eec82323
LMI
888 (when (gnus-visual-p 'browse-menu 'menu)
889 (gnus-browse-make-menu-bar))
890 (gnus-simplify-mode-line)
eec82323 891 (setq mode-line-process nil)
16409b0b 892 (buffer-disable-undo)
eec82323
LMI
893 (setq truncate-lines t)
894 (gnus-set-default-directory)
1b3b87df 895 (setq buffer-read-only t))
eec82323 896
10ace8ea
MB
897(defun gnus-browse-read-group (&optional no-article number)
898 "Enter the group at the current line.
899If NUMBER, fetch this number of articles."
900 (interactive "P")
6748645f
LMI
901 (let ((group (gnus-browse-group-name)))
902 (if (or (not (gnus-get-info group))
903 (gnus-ephemeral-group-p group))
904 (unless (gnus-group-read-ephemeral-group
23f87bed 905 group gnus-browse-current-method nil
10ace8ea
MB
906 (cons (current-buffer) 'browse)
907 nil nil nil number)
6748645f
LMI
908 (error "Couldn't enter %s" group))
909 (unless (gnus-group-read-group nil no-article group)
910 (error "Couldn't enter %s" group)))))
16409b0b 911
10ace8ea
MB
912(defun gnus-browse-select-group (&optional number)
913 "Select the current group.
914If NUMBER, fetch this number of articles."
915 (interactive "P")
916 (gnus-browse-read-group 'no number))
eec82323
LMI
917
918(defun gnus-browse-next-group (n)
919 "Go to the next group."
920 (interactive "p")
921 (prog1
922 (forward-line n)
923 (gnus-group-position-point)))
924
925(defun gnus-browse-prev-group (n)
926 "Go to the next group."
927 (interactive "p")
928 (gnus-browse-next-group (- n)))
929
930(defun gnus-browse-unsubscribe-current-group (arg)
8ccbef23
G
931 "(Un)subscribe to the next ARG groups.
932The variable `gnus-browse-subscribe-newsgroup-method' determines
933how new groups will be entered into the group buffer."
eec82323
LMI
934 (interactive "p")
935 (when (eobp)
a8151ef7 936 (error "No group at current line"))
eec82323
LMI
937 (let ((ward (if (< arg 0) -1 1))
938 (arg (abs arg)))
939 (while (and (> arg 0)
940 (not (eobp))
941 (gnus-browse-unsubscribe-group)
942 (zerop (gnus-browse-next-group ward)))
943 (decf arg))
944 (gnus-group-position-point)
945 (when (/= 0 arg)
946 (gnus-message 7 "No more newsgroups"))
947 arg))
948
949(defun gnus-browse-group-name ()
950 (save-excursion
951 (beginning-of-line)
16409b0b 952 (let ((name (get-text-property (point) 'gnus-group)))
01c52d31 953 (when (re-search-forward ": \\(.*\\)$" (point-at-eol) t)
bf247b6e 954 (concat (gnus-method-to-server-name gnus-browse-current-method) ":"
23f87bed
MB
955 (or name
956 (match-string-no-properties 1)))))))
957
958(defun gnus-browse-describe-group (group)
959 "Describe the current group."
960 (interactive (list (gnus-browse-group-name)))
961 (gnus-group-describe-group nil group))
eec82323 962
61bbede6
LI
963(defun gnus-browse-delete-group (group force)
964 "Delete the current group. Only meaningful with editable groups.
965If FORCE (the prefix) is non-nil, all the articles in the group will
966be deleted. This is \"deleted\" as in \"removed forever from the face
967of the Earth\". There is no undo. The user will be prompted before
968doing the deletion."
969 (interactive (list (gnus-browse-group-name)
970 current-prefix-arg))
971 (gnus-group-delete-group group force))
972
eec82323
LMI
973(defun gnus-browse-unsubscribe-group ()
974 "Toggle subscription of the current group in the browse buffer."
975 (let ((sub nil)
976 (buffer-read-only nil)
977 group)
978 (save-excursion
979 (beginning-of-line)
980 ;; If this group it killed, then we want to subscribe it.
23f87bed 981 (unless (eq (char-after) ? )
eec82323 982 (setq sub t))
6748645f 983 (setq group (gnus-browse-group-name))
23f87bed
MB
984 (when (gnus-server-equal gnus-browse-current-method "native")
985 (setq group (gnus-group-real-name group)))
eec82323
LMI
986 (if sub
987 (progn
6748645f
LMI
988 ;; Make sure the group has been properly removed before we
989 ;; subscribe to it.
7ce31649
MB
990 (if (gnus-ephemeral-group-p group)
991 (gnus-kill-ephemeral-group group))
8ccbef23
G
992 (let ((entry (gnus-group-entry group)))
993 (if entry
994 ;; Just change the subscription level if it is an
995 ;; unsubscribed group.
996 (gnus-group-change-level entry
997 gnus-level-default-subscribed)
998 ;; If it is a killed group or a zombie, feed it to the
999 ;; mechanism for new group subscription.
1000 (gnus-call-subscribe-functions
1001 gnus-browse-subscribe-newsgroup-method
549c9aed
G
1002 group)
1003 (gnus-request-update-group-status group 'subscribe)))
23f87bed 1004 (delete-char 1)
8ccbef23
G
1005 (insert (let ((lvl (gnus-group-level group)))
1006 (cond
1007 ((< lvl gnus-level-unsubscribed) ? )
1008 ((< lvl gnus-level-zombie) ?U)
1009 ((< lvl gnus-level-killed) ?Z)
1010 (t ?K)))))
eec82323 1011 (gnus-group-change-level
23f87bed
MB
1012 group gnus-level-unsubscribed gnus-level-default-subscribed)
1013 (delete-char 1)
1014 (insert ?U)))
eec82323
LMI
1015 t))
1016
1017(defun gnus-browse-exit ()
1018 "Quit browsing and return to the group buffer."
1019 (interactive)
1b3b87df 1020 (when (derived-mode-p 'gnus-browse-mode)
23f87bed 1021 (gnus-kill-buffer (current-buffer)))
eec82323 1022 ;; Insert the newly subscribed groups in the group buffer.
42a83f4b 1023 (with-current-buffer gnus-group-buffer
eec82323
LMI
1024 (gnus-group-list-groups nil))
1025 (if gnus-browse-return-buffer
1026 (gnus-configure-windows 'server 'force)
1027 (gnus-configure-windows 'group 'force)))
1028
1029(defun gnus-browse-describe-briefly ()
1030 "Give a one line description of the group mode commands."
1031 (interactive)
bdaa75c7 1032 (gnus-message 6 "%s"
eec82323
LMI
1033 (substitute-command-keys "\\<gnus-browse-mode-map>\\[gnus-group-next-group]:Forward \\[gnus-group-prev-group]:Backward \\[gnus-browse-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-browse-describe-briefly]:This help")))
1034
1035(defun gnus-server-regenerate-server ()
1036 "Issue a command to the server to regenerate all its data structures."
1037 (interactive)
1038 (let ((server (gnus-server-server-name)))
1039 (unless server
1040 (error "No server on the current line"))
23f87bed
MB
1041 (condition-case ()
1042 (gnus-get-function (gnus-server-to-method server)
1043 'request-regenerate)
1044 (error
01c52d31 1045 (error "This back end doesn't support regeneration")))
23f87bed
MB
1046 (gnus-message 5 "Requesting regeneration of %s..." server)
1047 (unless (gnus-open-server server)
1048 (error "Couldn't open server"))
1049 (if (gnus-request-regenerate server)
1050 (gnus-message 5 "Requesting regeneration of %s...done" server)
1051 (gnus-message 5 "Couldn't regenerate %s" server))))
eec82323 1052
01c52d31
MB
1053
1054;;;
1055;;; Server compaction. -- dvl
1056;;;
1057
1058;; #### FIXME: this function currently fails to update the Group buffer's
1059;; #### appearance.
1060(defun gnus-server-compact-server ()
1061 "Issue a command to the server to compact all its groups.
1062
1063Note: currently only implemented in nnml."
1064 (interactive)
1065 (let ((server (gnus-server-server-name)))
1066 (unless server
1067 (error "No server on the current line"))
1068 (condition-case ()
1069 (gnus-get-function (gnus-server-to-method server)
1070 'request-compact)
1071 (error
1072 (error "This back end doesn't support compaction")))
1073 (gnus-message 5 "\
1074Requesting compaction of %s... (this may take a long time)"
1075 server)
1076 (unless (gnus-open-server server)
1077 (error "Couldn't open server"))
1078 (if (not (gnus-request-compact server))
1079 (gnus-message 5 "Couldn't compact %s" server)
1080 (gnus-message 5 "Requesting compaction of %s...done" server)
1081 ;; Invalidate the original article buffer which might be out of date.
1082 ;; #### NOTE: Yes, this might be a bit rude, but since compaction
1083 ;; #### will not happen very often, I think this is acceptable.
1084 (let ((original (get-buffer gnus-original-article-buffer)))
1085 (and original (gnus-kill-buffer original))))))
1086
eec82323
LMI
1087(provide 'gnus-srvr)
1088
715a2ca2 1089;;; gnus-srvr.el ends here