Importing Oort Gnus v0.04.
[elisp/gnus.git-] / lisp / gnus-srvr.el
1 ;;; gnus-srvr.el --- virtual server support for Gnus
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
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 the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-spec)
33 (require 'gnus-group)
34 (require 'gnus-int)
35 (require 'gnus-range)
36
37 (defcustom gnus-server-mode-hook nil
38   "Hook run in `gnus-server-mode' buffers."
39   :group 'gnus-server
40   :type 'hook)
41
42 (defcustom gnus-server-exit-hook nil
43   "Hook run when exiting the server buffer."
44   :group 'gnus-server
45   :type 'hook)
46
47 (defcustom gnus-server-line-format "     {%(%h:%w%)} %s%a\n"
48   "Format of server lines.
49 It works along the same lines as a normal formatting string,
50 with some simple extensions.
51
52 The following specs are understood:
53
54 %h backend
55 %n name
56 %w address
57 %s status
58 %a agent covered"
59   :group 'gnus-server-visual
60   :type 'string)
61
62 (defcustom gnus-server-mode-line-format "Gnus: %%b"
63   "The format specification for the server mode line."
64   :group 'gnus-server-visual
65   :type 'string)
66
67 (defcustom gnus-server-browse-in-group-buffer nil
68   "Whether server browsing should take place in the group buffer.
69 If nil, a faster, but more primitive, buffer is used instead."
70   :group 'gnus-server-visual
71   :type 'string)
72
73 ;;; Internal variables.
74
75 (defvar gnus-inserted-opened-servers nil)
76
77 (defvar gnus-server-line-format-alist
78   `((?h gnus-tmp-how ?s)
79     (?n gnus-tmp-name ?s)
80     (?w gnus-tmp-where ?s)
81     (?s gnus-tmp-status ?s)
82     (?a gnus-tmp-agent ?s)))
83
84 (defvar gnus-server-mode-line-format-alist
85   `((?S gnus-tmp-news-server ?s)
86     (?M gnus-tmp-news-method ?s)
87     (?u gnus-tmp-user-defined ?s)))
88
89 (defvar gnus-server-line-format-spec nil)
90 (defvar gnus-server-mode-line-format-spec nil)
91 (defvar gnus-server-killed-servers nil)
92
93 (defvar gnus-server-mode-map)
94
95 (defvar gnus-server-menu-hook nil
96   "*Hook run after the creation of the server mode menu.")
97
98 (defun gnus-server-make-menu-bar ()
99   (gnus-turn-off-edit-menu 'server)
100   (unless (boundp 'gnus-server-server-menu)
101     (easy-menu-define
102      gnus-server-server-menu gnus-server-mode-map ""
103      '("Server"
104        ["Add" gnus-server-add-server t]
105        ["Browse" gnus-server-read-server t]
106        ["Scan" gnus-server-scan-server t]
107        ["List" gnus-server-list-servers t]
108        ["Kill" gnus-server-kill-server t]
109        ["Yank" gnus-server-yank-server t]
110        ["Copy" gnus-server-copy-server t]
111        ["Edit" gnus-server-edit-server t]
112        ["Regenerate" gnus-server-regenerate-server t]
113        ["Exit" gnus-server-exit t]))
114
115     (easy-menu-define
116      gnus-server-connections-menu gnus-server-mode-map ""
117      '("Connections"
118        ["Open" gnus-server-open-server t]
119        ["Close" gnus-server-close-server t]
120        ["Deny" gnus-server-deny-server t]
121        "---"
122        ["Open All" gnus-server-open-all-servers t]
123        ["Close All" gnus-server-close-all-servers t]
124        ["Reset All" gnus-server-remove-denials t]))
125
126     (gnus-run-hooks 'gnus-server-menu-hook)))
127
128 (defvar gnus-server-mode-map nil)
129 (put 'gnus-server-mode 'mode-class 'special)
130
131 (unless gnus-server-mode-map
132   (setq gnus-server-mode-map (make-sparse-keymap))
133   (suppress-keymap gnus-server-mode-map)
134
135   (gnus-define-keys gnus-server-mode-map
136     " " gnus-server-read-server-in-server-buffer
137     "\r" gnus-server-read-server
138     gnus-mouse-2 gnus-server-pick-server
139     "q" gnus-server-exit
140     "l" gnus-server-list-servers
141     "k" gnus-server-kill-server
142     "y" gnus-server-yank-server
143     "c" gnus-server-copy-server
144     "a" gnus-server-add-server
145     "e" gnus-server-edit-server
146     "s" gnus-server-scan-server
147
148     "O" gnus-server-open-server
149     "\M-o" gnus-server-open-all-servers
150     "C" gnus-server-close-server
151     "\M-c" gnus-server-close-all-servers
152     "D" gnus-server-deny-server
153     "R" gnus-server-remove-denials
154
155     "n" next-line
156     "p" previous-line
157
158     "g" gnus-server-regenerate-server
159
160     "\C-c\C-i" gnus-info-find-node
161     "\C-c\C-b" gnus-bug))
162
163 (defface gnus-server-agent-face
164   '((((class color) (background light)) (:foreground "PaleTurquoise" :bold t))
165     (((class color) (background dark)) (:foreground "PaleTurquoise" :bold t))
166     (t (:bold t)))
167   "Face used for displaying AGENTIZED servers"
168   :group 'gnus-server-visual)
169
170 (defface gnus-server-opened-face
171   '((((class color) (background light)) (:foreground "Green3" :bold t))
172     (((class color) (background dark)) (:foreground "Green1" :bold t))
173     (t (:bold t)))
174   "Face used for displaying OPENED servers"
175   :group 'gnus-server-visual)
176
177 (defface gnus-server-closed-face
178   '((((class color) (background light)) (:foreground "Steel Blue" :italic t))
179     (((class color) (background dark))
180      (:foreground "Light Steel Blue" :italic t))
181     (t (:italic t)))
182   "Face used for displaying CLOSED servers"
183   :group 'gnus-server-visual)
184
185 (defface gnus-server-denied-face
186   '((((class color) (background light)) (:foreground "Red" :bold t))
187     (((class color) (background dark)) (:foreground "Pink" :bold t))
188     (t (:inverse-video t :bold t)))
189   "Face used for displaying DENIED servers"
190   :group 'gnus-server-visual)
191
192 (defcustom gnus-server-agent-face 'gnus-server-agent-face
193   "Face name to use on AGENTIZED servers."
194   :group 'gnus-server-visual
195   :type 'face)
196
197 (defcustom gnus-server-opened-face 'gnus-server-opened-face
198   "Face name to use on OPENED servers."
199   :group 'gnus-server-visual
200   :type 'face)
201
202 (defcustom gnus-server-closed-face 'gnus-server-closed-face
203   "Face name to use on CLOSED servers."
204   :group 'gnus-server-visual
205   :type 'face)
206
207 (defcustom gnus-server-denied-face 'gnus-server-denied-face
208   "Face name to use on DENIED servers."
209   :group 'gnus-server-visual
210   :type 'face)
211
212 (defvar gnus-server-font-lock-keywords
213   (list
214    '("(\\(agent\\))" 1 gnus-server-agent-face)
215    '("(\\(opened\\))" 1 gnus-server-opened-face)
216    '("(\\(closed\\))" 1 gnus-server-closed-face)
217    '("(\\(denied\\))" 1 gnus-server-denied-face)))
218
219 (defun gnus-server-mode ()
220   "Major mode for listing and editing servers.
221
222 All normal editing commands are switched off.
223 \\<gnus-server-mode-map>
224 For more in-depth information on this mode, read the manual
225 \(`\\[gnus-info-find-node]').
226
227 The following commands are available:
228
229 \\{gnus-server-mode-map}"
230   (interactive)
231   (when (gnus-visual-p 'server-menu 'menu)
232     (gnus-server-make-menu-bar))
233   (kill-all-local-variables)
234   (gnus-simplify-mode-line)
235   (setq major-mode 'gnus-server-mode)
236   (setq mode-name "Server")
237   (gnus-set-default-directory)
238   (setq mode-line-process nil)
239   (use-local-map gnus-server-mode-map)
240   (buffer-disable-undo)
241   (setq truncate-lines t)
242   (setq buffer-read-only t)
243   (if (featurep 'xemacs)
244       (put 'gnus-server-mode 'font-lock-defaults '(gnus-server-font-lock-keywords t))
245     (set (make-local-variable 'font-lock-defaults)
246          '(gnus-server-font-lock-keywords t)))
247   (gnus-run-hooks 'gnus-server-mode-hook))
248
249 (defun gnus-server-insert-server-line (gnus-tmp-name method)
250   (let* ((gnus-tmp-how (car method))
251          (gnus-tmp-where (nth 1 method))
252          (elem (assoc method gnus-opened-servers))
253          (gnus-tmp-status
254           (if (eq (nth 1 elem) 'denied)
255               "(denied)"
256             (condition-case nil
257                 (if (or (gnus-server-opened method)
258                         (eq (nth 1 elem) 'ok))
259                     "(opened)"
260                   "(closed)")
261               ((error) "(error)"))))
262          (gnus-tmp-agent (if (and gnus-agent
263                                   (member method
264                                           gnus-agent-covered-methods))
265                              " (agent)"
266                            "")))
267     (beginning-of-line)
268     (gnus-add-text-properties
269      (point)
270      (prog1 (1+ (point))
271        ;; Insert the text.
272        (eval gnus-server-line-format-spec))
273      (list 'gnus-server (intern gnus-tmp-name)))))
274
275 (defun gnus-enter-server-buffer ()
276   "Set up the server buffer."
277   (gnus-server-setup-buffer)
278   (gnus-configure-windows 'server)
279   (gnus-server-prepare))
280
281 (defun gnus-server-setup-buffer ()
282   "Initialize the server buffer."
283   (unless (get-buffer gnus-server-buffer)
284     (save-excursion
285       (set-buffer (gnus-get-buffer-create gnus-server-buffer))
286       (gnus-server-mode)
287       (when gnus-carpal
288         (gnus-carpal-setup-buffer 'server)))))
289
290 (defun gnus-server-prepare ()
291   (gnus-set-format 'server-mode)
292   (gnus-set-format 'server t)
293   (let ((alist gnus-server-alist)
294         (buffer-read-only nil)
295         (opened gnus-opened-servers)
296         done server op-ser)
297     (erase-buffer)
298     (setq gnus-inserted-opened-servers nil)
299     ;; First we do the real list of servers.
300     (while alist
301       (unless (member (cdar alist) done)
302         (push (cdar alist) done)
303         (cdr (setq server (pop alist)))
304         (when (and server (car server) (cdr server))
305           (gnus-server-insert-server-line (car server) (cdr server))))
306       (when (member (cdar alist) done)
307         (pop alist)))
308     ;; Then we insert the list of servers that have been opened in
309     ;; this session.
310     (while opened
311       (when (and (not (member (caar opened) done))
312                  ;; Just ignore ephemeral servers.
313                  (not (member (caar opened) gnus-ephemeral-servers)))
314         (push (caar opened) done)
315         (gnus-server-insert-server-line
316          (setq op-ser (format "%s:%s" (caaar opened) (nth 1 (caar opened))))
317          (caar opened))
318         (push (list op-ser (caar opened)) gnus-inserted-opened-servers))
319       (setq opened (cdr opened))))
320   (goto-char (point-min))
321   (gnus-server-position-point))
322
323 (defun gnus-server-server-name ()
324   (let ((server (get-text-property (gnus-point-at-bol) 'gnus-server)))
325     (and server (symbol-name server))))
326
327 (defalias 'gnus-server-position-point 'gnus-goto-colon)
328
329 (defconst gnus-server-edit-buffer "*Gnus edit server*")
330
331 (defun gnus-server-update-server (server)
332   (save-excursion
333     (set-buffer gnus-server-buffer)
334     (let* ((buffer-read-only nil)
335            (entry (assoc server gnus-server-alist))
336            (oentry (assoc (gnus-server-to-method server)
337                           gnus-opened-servers)))
338       (when entry
339         (gnus-dribble-enter
340          (concat "(gnus-server-set-info \"" server "\" '"
341                  (prin1-to-string (cdr entry)) ")\n")))
342       (when (or entry oentry)
343         ;; Buffer may be narrowed.
344         (save-restriction
345           (widen)
346           (when (gnus-server-goto-server server)
347             (gnus-delete-line))
348           (if entry
349               (gnus-server-insert-server-line (car entry) (cdr entry))
350             (gnus-server-insert-server-line
351              (format "%s:%s" (caar oentry) (nth 1 (car oentry)))
352              (car oentry)))
353           (gnus-server-position-point))))))
354
355 (defun gnus-server-set-info (server info)
356   ;; Enter a select method into the virtual server alist.
357   (when (and server info)
358     (gnus-dribble-enter
359      (concat "(gnus-server-set-info \"" server "\" '"
360              (prin1-to-string info) ")"))
361     (let* ((server (nth 1 info))
362            (entry (assoc server gnus-server-alist)))
363       (if entry (setcdr entry info)
364         (setq gnus-server-alist
365               (nconc gnus-server-alist (list (cons server info))))))))
366
367 ;;; Interactive server functions.
368
369 (defun gnus-server-kill-server (server)
370   "Kill the server on the current line."
371   (interactive (list (gnus-server-server-name)))
372   (unless (gnus-server-goto-server server)
373     (if server (error "No such server: %s" server)
374       (error "No server on the current line")))
375   (unless (assoc server gnus-server-alist)
376     (error "Read-only server %s" server))
377   (gnus-dribble-touch)
378   (let ((buffer-read-only nil))
379     (gnus-delete-line))
380   (push (assoc server gnus-server-alist) gnus-server-killed-servers)
381   (setq gnus-server-alist (delq (car gnus-server-killed-servers)
382                                 gnus-server-alist))
383   (let ((groups (gnus-groups-from-server server)))
384     (when (and groups
385                (gnus-yes-or-no-p
386                 (format "Kill all %s groups from this server? "
387                         (length groups))))
388       (dolist (group groups)
389         (setq gnus-newsrc-alist
390               (delq (assoc group gnus-newsrc-alist)
391                     gnus-newsrc-alist))
392         (when gnus-group-change-level-function
393           (funcall gnus-group-change-level-function
394                    group gnus-level-killed 3)))))
395   (gnus-server-position-point))
396
397 (defun gnus-server-yank-server ()
398   "Yank the previously killed server."
399   (interactive)
400   (unless gnus-server-killed-servers
401     (error "No killed servers to be yanked"))
402   (let ((alist gnus-server-alist)
403         (server (gnus-server-server-name))
404         (killed (car gnus-server-killed-servers)))
405     (if (not server)
406         (setq gnus-server-alist (nconc gnus-server-alist (list killed)))
407       (if (string= server (caar gnus-server-alist))
408           (push killed gnus-server-alist)
409         (while (and (cdr alist)
410                     (not (string= server (caadr alist))))
411           (setq alist (cdr alist)))
412         (if alist
413             (setcdr alist (cons killed (cdr alist)))
414           (setq gnus-server-alist (list killed)))))
415     (gnus-server-update-server (car killed))
416     (setq gnus-server-killed-servers (cdr gnus-server-killed-servers))
417     (gnus-server-position-point)))
418
419 (defun gnus-server-exit ()
420   "Return to the group buffer."
421   (interactive)
422   (gnus-run-hooks 'gnus-server-exit-hook)
423   (kill-buffer (current-buffer))
424   (gnus-configure-windows 'group t))
425
426 (defun gnus-server-list-servers ()
427   "List all available servers."
428   (interactive)
429   (let ((cur (gnus-server-server-name)))
430     (gnus-server-prepare)
431     (if cur (gnus-server-goto-server cur)
432       (goto-char (point-max))
433       (forward-line -1))
434     (gnus-server-position-point)))
435
436 (defun gnus-server-set-status (method status)
437   "Make METHOD have STATUS."
438   (let ((entry (assoc method gnus-opened-servers)))
439     (if entry
440         (setcar (cdr entry) status)
441       (push (list method status) gnus-opened-servers))))
442
443 (defun gnus-opened-servers-remove (method)
444   "Remove METHOD from the list of opened servers."
445   (setq gnus-opened-servers (delq (assoc method gnus-opened-servers)
446                                   gnus-opened-servers)))
447
448 (defun gnus-server-open-server (server)
449   "Force an open of SERVER."
450   (interactive (list (gnus-server-server-name)))
451   (let ((method (gnus-server-to-method server)))
452     (unless method
453       (error "No such server: %s" server))
454     (gnus-server-set-status method 'ok)
455     (prog1
456         (or (gnus-open-server method)
457             (progn (message "Couldn't open %s" server) nil))
458       (gnus-server-update-server server)
459       (gnus-server-position-point))))
460
461 (defun gnus-server-open-all-servers ()
462   "Open all servers."
463   (interactive)
464   (let ((servers gnus-inserted-opened-servers))
465     (while servers
466       (gnus-server-open-server (car (pop servers))))))
467
468 (defun gnus-server-close-server (server)
469   "Close SERVER."
470   (interactive (list (gnus-server-server-name)))
471   (let ((method (gnus-server-to-method server)))
472     (unless method
473       (error "No such server: %s" server))
474     (gnus-server-set-status method 'closed)
475     (prog1
476         (gnus-close-server method)
477       (gnus-server-update-server server)
478       (gnus-server-position-point))))
479
480 (defun gnus-server-close-all-servers ()
481   "Close all servers."
482   (interactive)
483   (dolist (server gnus-inserted-opened-servers)
484     (gnus-server-close-server (car server))))
485
486 (defun gnus-server-deny-server (server)
487   "Make sure SERVER will never be attempted opened."
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 'denied))
493   (gnus-server-update-server server)
494   (gnus-server-position-point)
495   t)
496
497 (defun gnus-server-remove-denials ()
498   "Make all denied servers into closed servers."
499   (interactive)
500   (dolist (server gnus-opened-servers)
501     (when (eq (nth 1 server) 'denied)
502       (setcar (nthcdr 1 server) 'closed)))
503   (gnus-server-list-servers))
504
505 (defun gnus-server-copy-server (from to)
506   (interactive
507    (list
508     (or (gnus-server-server-name)
509         (error "No server on the current line"))
510     (read-string "Copy to: ")))
511   (unless from
512     (error "No server on current line"))
513   (unless (and to (not (string= to "")))
514     (error "No name to copy to"))
515   (when (assoc to gnus-server-alist)
516     (error "%s already exists" to))
517   (unless (gnus-server-to-method from)
518     (error "%s: no such server" from))
519   (let ((to-entry (cons from (gnus-copy-sequence
520                               (gnus-server-to-method from)))))
521     (setcar to-entry to)
522     (setcar (nthcdr 2 to-entry) to)
523     (push to-entry gnus-server-killed-servers)
524     (gnus-server-yank-server)))
525
526 (defun gnus-server-add-server (how where)
527   (interactive
528    (list (intern (completing-read "Server method: "
529                                   gnus-valid-select-methods nil t))
530          (read-string "Server name: ")))
531   (when (assq where gnus-server-alist)
532     (error "Server with that name already defined"))
533   (push (list where how where) gnus-server-killed-servers)
534   (gnus-server-yank-server))
535
536 (defun gnus-server-goto-server (server)
537   "Jump to a server line."
538   (interactive
539    (list (completing-read "Goto server: " gnus-server-alist nil t)))
540   (let ((to (text-property-any (point-min) (point-max)
541                                'gnus-server (intern server))))
542     (when to
543       (goto-char to)
544       (gnus-server-position-point))))
545
546 (defun gnus-server-edit-server (server)
547   "Edit the server on the current line."
548   (interactive (list (gnus-server-server-name)))
549   (unless server
550     (error "No server on current line"))
551   (unless (assoc server gnus-server-alist)
552     (error "This server can't be edited"))
553   (let ((info (cdr (assoc server gnus-server-alist))))
554     (gnus-close-server info)
555     (gnus-edit-form
556      info "Editing the server."
557      `(lambda (form)
558         (gnus-server-set-info ,server form)
559         (gnus-server-list-servers)
560         (gnus-server-position-point)))))
561
562 (defun gnus-server-scan-server (server)
563   "Request a scan from the current server."
564   (interactive (list (gnus-server-server-name)))
565   (let ((method (gnus-server-to-method server)))
566     (if (not (gnus-get-function method 'request-scan))
567         (error "Server %s can't scan" (car method))
568       (gnus-message 3 "Scanning %s..." server)
569       (gnus-request-scan nil method)
570       (gnus-message 3 "Scanning %s...done" server))))
571
572 (defun gnus-server-read-server-in-server-buffer (server)
573   "Browse a server in server buffer."
574   (interactive (list (gnus-server-server-name)))
575   (let (gnus-server-browse-in-group-buffer)
576     (gnus-server-read-server server)))
577
578 (defun gnus-server-read-server (server)
579   "Browse a server."
580   (interactive (list (gnus-server-server-name)))
581   (let ((buf (current-buffer)))
582     (prog1
583         (gnus-browse-foreign-server server buf)
584       (save-excursion
585         (set-buffer buf)
586         (gnus-server-update-server (gnus-server-server-name))
587         (gnus-server-position-point)))))
588
589 (defun gnus-server-pick-server (e)
590   (interactive "e")
591   (mouse-set-point e)
592   (gnus-server-read-server (gnus-server-server-name)))
593
594 \f
595 ;;;
596 ;;; Browse Server Mode
597 ;;;
598
599 (defvar gnus-browse-menu-hook nil
600   "*Hook run after the creation of the browse mode menu.")
601
602 (defvar gnus-browse-mode-hook nil)
603 (defvar gnus-browse-mode-map nil)
604 (put 'gnus-browse-mode 'mode-class 'special)
605
606 (unless gnus-browse-mode-map
607   (setq gnus-browse-mode-map (make-keymap))
608   (suppress-keymap gnus-browse-mode-map)
609
610   (gnus-define-keys
611       gnus-browse-mode-map
612     " " gnus-browse-read-group
613     "=" gnus-browse-select-group
614     "n" gnus-browse-next-group
615     "p" gnus-browse-prev-group
616     "\177" gnus-browse-prev-group
617     [delete] gnus-browse-prev-group
618     "N" gnus-browse-next-group
619     "P" gnus-browse-prev-group
620     "\M-n" gnus-browse-next-group
621     "\M-p" gnus-browse-prev-group
622     "\r" gnus-browse-select-group
623     "u" gnus-browse-unsubscribe-current-group
624     "l" gnus-browse-exit
625     "L" gnus-browse-exit
626     "q" gnus-browse-exit
627     "Q" gnus-browse-exit
628     "\C-c\C-c" gnus-browse-exit
629     "?" gnus-browse-describe-briefly
630
631     "\C-c\C-i" gnus-info-find-node
632     "\C-c\C-b" gnus-bug))
633
634 (defun gnus-browse-make-menu-bar ()
635   (gnus-turn-off-edit-menu 'browse)
636   (unless (boundp 'gnus-browse-menu)
637     (easy-menu-define
638      gnus-browse-menu gnus-browse-mode-map ""
639      '("Browse"
640        ["Subscribe" gnus-browse-unsubscribe-current-group t]
641        ["Read" gnus-browse-read-group t]
642        ["Select" gnus-browse-select-group t]
643        ["Next" gnus-browse-next-group t]
644        ["Prev" gnus-browse-prev-group t]
645        ["Exit" gnus-browse-exit t]))
646     (gnus-run-hooks 'gnus-browse-menu-hook)))
647
648 (defvar gnus-browse-current-method nil)
649 (defvar gnus-browse-return-buffer nil)
650
651 (defvar gnus-browse-buffer "*Gnus Browse Server*")
652
653 (defun gnus-browse-foreign-server (server &optional return-buffer)
654   "Browse the server SERVER."
655   (setq gnus-browse-current-method (gnus-server-to-method server))
656   (setq gnus-browse-return-buffer return-buffer)
657   (let* ((method gnus-browse-current-method)
658          (orig-select-method gnus-select-method)
659          (gnus-select-method method)
660          groups group)
661     (gnus-message 5 "Connecting to %s..." (nth 1 method))
662     (cond
663      ((not (gnus-check-server method))
664       (gnus-message
665        1 "Unable to contact server %s: %s" (nth 1 method)
666        (gnus-status-message method))
667       nil)
668      ((not
669        (prog2
670            (gnus-message 6 "Reading active file...")
671            (gnus-request-list method)
672          (gnus-message 6 "Reading active file...done")))
673       (gnus-message
674        1 "Couldn't request list: %s" (gnus-status-message method))
675       nil)
676      (t
677       (save-excursion
678         (set-buffer nntp-server-buffer)
679         (let ((cur (current-buffer)))
680           (goto-char (point-min))
681           (unless (string= gnus-ignored-newsgroups "")
682             (delete-matching-lines gnus-ignored-newsgroups))
683           (while (not (eobp))
684             (ignore-errors
685               (push (cons
686                      (if (eq (char-after) ?\")
687                          (read cur)
688                        (let ((p (point)) (name ""))
689                          (skip-chars-forward "^ \t\\\\")
690                          (setq name (buffer-substring p (point)))
691                          (while (eq (char-after) ?\\)
692                            (setq p (1+ (point)))
693                            (forward-char 2)
694                            (skip-chars-forward "^ \t\\\\")
695                            (setq name (concat name (buffer-substring
696                                                     p (point)))))
697                          name))
698                      (let ((last (read cur)))
699                        (cons (read cur) last)))
700                     groups))
701             (forward-line))))
702       (setq groups (sort groups
703                          (lambda (l1 l2)
704                            (string< (car l1) (car l2)))))
705       (if gnus-server-browse-in-group-buffer
706           (let* ((gnus-select-method orig-select-method)
707                  (gnus-group-listed-groups
708                   (mapcar (lambda (group)
709                             (let ((name
710                                    (gnus-group-prefixed-name
711                                     (car group) method)))
712                               (gnus-set-active name (cdr group))
713                               name))
714                           groups)))
715             (gnus-configure-windows 'group)
716             (funcall gnus-group-prepare-function
717                      gnus-level-killed 'ignore 1 'ignore))
718         (gnus-get-buffer-create gnus-browse-buffer)
719         (when gnus-carpal
720           (gnus-carpal-setup-buffer 'browse))
721         (gnus-configure-windows 'browse)
722         (buffer-disable-undo)
723         (let ((buffer-read-only nil))
724           (erase-buffer))
725         (gnus-browse-mode)
726         (setq mode-line-buffer-identification
727               (list
728                (format
729                 "Gnus: %%b {%s:%s}" (car method) (cadr method))))
730         (let ((buffer-read-only nil) charset
731               (prefix (let ((gnus-select-method orig-select-method))
732                         (gnus-group-prefixed-name "" method))))
733           (while groups
734             (setq group (car groups))
735             (setq charset (gnus-group-name-charset method (car group)))
736             (gnus-add-text-properties
737              (point)
738              (prog1 (1+ (point))
739                (insert
740                 (format "%c%7d: %s\n"
741                         (let ((level (gnus-group-level (concat prefix (car group)))))
742                               (cond
743                                ((<= level gnus-level-subscribed) ? )
744                                ((<= level gnus-level-unsubscribed) ?U)
745                                ((= level gnus-level-zombie) ?Z)
746                                (t ?K)))
747                         (max 0 (- (1+ (cddr group)) (cadr group)))
748                         (gnus-group-name-decode (car group) charset))))
749              (list 'gnus-group (car group)))
750             (setq groups (cdr groups))))
751         (switch-to-buffer (current-buffer)))
752       (goto-char (point-min))
753       (gnus-group-position-point)
754       (gnus-message 5 "Connecting to %s...done" (nth 1 method))
755       t))))
756
757 (defun gnus-browse-mode ()
758   "Major mode for browsing a foreign server.
759
760 All normal editing commands are switched off.
761
762 \\<gnus-browse-mode-map>
763 The only things you can do in this buffer is
764
765 1) `\\[gnus-browse-unsubscribe-current-group]' to subscribe to a group.
766 The group will be inserted into the group buffer upon exit from this
767 buffer.
768
769 2) `\\[gnus-browse-read-group]' to read a group ephemerally.
770
771 3) `\\[gnus-browse-exit]' to return to the group buffer."
772   (interactive)
773   (kill-all-local-variables)
774   (when (gnus-visual-p 'browse-menu 'menu)
775     (gnus-browse-make-menu-bar))
776   (gnus-simplify-mode-line)
777   (setq major-mode 'gnus-browse-mode)
778   (setq mode-name "Browse Server")
779   (setq mode-line-process nil)
780   (use-local-map gnus-browse-mode-map)
781   (buffer-disable-undo)
782   (setq truncate-lines t)
783   (gnus-set-default-directory)
784   (setq buffer-read-only t)
785   (gnus-run-hooks 'gnus-browse-mode-hook))
786
787 (defun gnus-browse-read-group (&optional no-article)
788   "Enter the group at the current line."
789   (interactive)
790   (let ((group (gnus-browse-group-name)))
791     (if (or (not (gnus-get-info group))
792             (gnus-ephemeral-group-p group))
793         (unless (gnus-group-read-ephemeral-group
794                  (gnus-group-real-name group) gnus-browse-current-method nil
795                  (cons (current-buffer) 'browse))
796           (error "Couldn't enter %s" group))
797       (unless (gnus-group-read-group nil no-article group)
798         (error "Couldn't enter %s" group)))))
799
800 (defun gnus-browse-select-group ()
801   "Select the current group."
802   (interactive)
803   (gnus-browse-read-group 'no))
804
805 (defun gnus-browse-next-group (n)
806   "Go to the next group."
807   (interactive "p")
808   (prog1
809       (forward-line n)
810     (gnus-group-position-point)))
811
812 (defun gnus-browse-prev-group (n)
813   "Go to the next group."
814   (interactive "p")
815   (gnus-browse-next-group (- n)))
816
817 (defun gnus-browse-unsubscribe-current-group (arg)
818   "(Un)subscribe to the next ARG groups."
819   (interactive "p")
820   (when (eobp)
821     (error "No group at current line"))
822   (let ((ward (if (< arg 0) -1 1))
823         (arg (abs arg)))
824     (while (and (> arg 0)
825                 (not (eobp))
826                 (gnus-browse-unsubscribe-group)
827                 (zerop (gnus-browse-next-group ward)))
828       (decf arg))
829     (gnus-group-position-point)
830     (when (/= 0 arg)
831       (gnus-message 7 "No more newsgroups"))
832     arg))
833
834 (defun gnus-browse-group-name ()
835   (save-excursion
836     (beginning-of-line)
837     (let ((name (get-text-property (point) 'gnus-group)))
838       (when (re-search-forward ": \\(.*\\)$" (gnus-point-at-eol) t)
839         (gnus-group-prefixed-name
840          (or name
841              (match-string-no-properties 1))
842          gnus-browse-current-method)))))
843
844 (defun gnus-browse-unsubscribe-group ()
845   "Toggle subscription of the current group in the browse buffer."
846   (let ((sub nil)
847         (buffer-read-only nil)
848         group)
849     (save-excursion
850       (beginning-of-line)
851       ;; If this group it killed, then we want to subscribe it.
852       (unless (eq (char-after) ? )
853         (setq sub t))
854       (setq group (gnus-browse-group-name))
855       ;;;;
856       ;;(when (and sub
857       ;;                 (cadr (gnus-gethash group gnus-newsrc-hashtb)))
858       ;;(error "Group already subscribed"))
859       (if sub
860           (progn
861             ;; Make sure the group has been properly removed before we
862             ;; subscribe to it.
863             (gnus-kill-ephemeral-group group)
864             (gnus-group-change-level
865              (list t group gnus-level-default-subscribed
866                    nil nil (if (gnus-server-equal
867                                 gnus-browse-current-method "native")
868                                nil
869                              (gnus-method-simplify
870                               gnus-browse-current-method)))
871              gnus-level-default-subscribed (gnus-group-level group)
872              (and (car (nth 1 gnus-newsrc-alist))
873                   (gnus-gethash (car (nth 1 gnus-newsrc-alist))
874                                 gnus-newsrc-hashtb))
875              t)
876             (delete-char 1)
877             (insert ? ))
878         (gnus-group-change-level
879          group gnus-level-unsubscribed gnus-level-default-subscribed)
880         (delete-char 1)
881         (insert ?U)))
882     t))
883
884 (defun gnus-browse-exit ()
885   "Quit browsing and return to the group buffer."
886   (interactive)
887   (when (eq major-mode 'gnus-browse-mode)
888     (kill-buffer (current-buffer)))
889   ;; Insert the newly subscribed groups in the group buffer.
890   (save-excursion
891     (set-buffer gnus-group-buffer)
892     (gnus-group-list-groups nil))
893   (if gnus-browse-return-buffer
894       (gnus-configure-windows 'server 'force)
895     (gnus-configure-windows 'group 'force)))
896
897 (defun gnus-browse-describe-briefly ()
898   "Give a one line description of the group mode commands."
899   (interactive)
900   (gnus-message 6
901                 (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")))
902
903 (defun gnus-server-regenerate-server ()
904   "Issue a command to the server to regenerate all its data structures."
905   (interactive)
906   (let ((server (gnus-server-server-name)))
907     (unless server
908       (error "No server on the current line"))
909     (condition-case ()
910         (gnus-get-function (gnus-server-to-method server)
911                            'request-regenerate)
912       (error
913         (error "This backend doesn't support regeneration")))
914     (gnus-message 5 "Requesting regeneration of %s..." server)
915     (unless (gnus-open-server server)
916       (error "Couldn't open server"))
917     (if (gnus-request-regenerate server)
918         (gnus-message 5 "Requesting regeneration of %s...done" server)
919       (gnus-message 5 "Couldn't regenerate %s" server))))
920
921 (provide 'gnus-srvr)
922
923 ;;; gnus-srvr.el ends here