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