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