diff options
49 files changed, 1289 insertions, 2264 deletions
diff --git a/lisp/gnus/auth-source.el b/lisp/gnus/auth-source.el index 5b44c0b9937..a0be0ca8ba4 100644 --- a/lisp/gnus/auth-source.el +++ b/lisp/gnus/auth-source.el @@ -107,7 +107,8 @@ Only relevant if `auth-source-debug' is not nil." :version "23.2" ;; No Gnus :type `boolean) -(defcustom auth-sources '((:source "~/.authinfo.gpg")) +(defcustom auth-sources '((:source "~/.authinfo.gpg") + (:source "~/.authinfo")) "List of authentication sources. The default will get login and password information from a .gpg @@ -311,20 +312,23 @@ Return structure as specified by MODE." (setq result (mapcar (lambda (m) - (if (equal "password" m) - (let ((passwd (read-passwd "Password: "))) - (cond - ;; Secret Service API. - ((consp source) - (apply - 'secrets-create-item - (auth-get-source entry) name passwd spec)) - (t)) ;; netrc not implemented yes. - passwd) - (or - ;; the originally requested :user - user - "unknown-user"))) + (cond + ((equal "password" m) + (let ((passwd (read-passwd + (format "Password for %s on %s: " prot host)))) + (cond + ;; Secret Service API. + ((consp source) + (apply + 'secrets-create-item + (auth-get-source entry) name passwd spec)) + (t)) ;; netrc not implemented yes. + passwd)) + ((equal "login" m) + (or user + (read-string (format "User name for %s on %s: " prot host)))) + (t + "unknownuser"))) (if (consp mode) mode (list mode)))) (if (consp mode) result (car result)))) diff --git a/lisp/gnus/earcon.el b/lisp/gnus/earcon.el index 2578abc073d..2086f86c417 100644 --- a/lisp/gnus/earcon.el +++ b/lisp/gnus/earcon.el @@ -151,8 +151,7 @@ If N is negative, move backward instead." (defun earcon-button-push (marker) ;; Push button starting at MARKER. - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (goto-char marker) (let* ((entry (earcon-button-entry)) (inhibit-point-motion-hooks t) @@ -214,8 +213,7 @@ If N is negative, move backward instead." (defun gnus-earcon-display () "Play sounds in message buffers." (interactive) - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (goto-char (point-min)) ;; Skip headers (unless (search-forward "\n\n" nil t) diff --git a/lisp/gnus/flow-fill.el b/lisp/gnus/flow-fill.el index c4c64db7ed1..2420577ea45 100644 --- a/lisp/gnus/flow-fill.el +++ b/lisp/gnus/flow-fill.el @@ -97,8 +97,7 @@ RFC 2646 suggests 66 characters for readability." ;;;###autoload (defun fill-flowed (&optional buffer delete-space) - (save-excursion - (set-buffer (or (current-buffer) buffer)) + (with-current-buffer (or (current-buffer) buffer) (goto-char (point-min)) ;; Remove space stuffing. (while (re-search-forward "^\\( \\|>+ $\\)" nil t) diff --git a/lisp/gnus/gnus-agent.el b/lisp/gnus/gnus-agent.el index bbfdc66af99..6dcc77cdfb9 100644 --- a/lisp/gnus/gnus-agent.el +++ b/lisp/gnus/gnus-agent.el @@ -305,8 +305,7 @@ buffer. Automatically blocks multiple updates due to recursion." `(prog1 (let ((gnus-agent-inhibit-update-total-fetched-for t)) ,@body) (when (and gnus-agent-need-update-total-fetched-for (not gnus-agent-inhibit-update-total-fetched-for)) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (setq gnus-agent-need-update-total-fetched-for nil) (gnus-group-update-group ,group t))))) @@ -474,8 +473,7 @@ manipulated as follows: (defun gnus-agent-stop-fetch () "Save all data structures and clean up." (setq gnus-agent-spam-hashtb nil) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (widen))) (defmacro gnus-agent-with-fetch (&rest forms) @@ -1608,8 +1606,7 @@ downloaded into the agent." nntp-server-buffer (point-min) (point-max)) (setq pos (nreverse pos))))) ;; Then save these articles into the Agent. - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (while pos (narrow-to-region (cdar pos) (or (cdadr pos) (point-max))) (goto-char (point-min)) @@ -1693,8 +1690,7 @@ downloaded into the agent." (setq date (or date t)) (let (gnus-agent-article-alist group alist beg end) - (save-excursion - (set-buffer gnus-agent-overview-buffer) + (with-current-buffer gnus-agent-overview-buffer (when (nnheader-find-nov-line article) (forward-word 1) (setq beg (point)) @@ -1705,9 +1701,8 @@ downloaded into the agent." (push (setq alist (list group (gnus-agent-load-alist (caar crosses)))) gnus-agent-group-alist)) (setcdr alist (cons (cons (cdar crosses) date) (cdr alist))) - (save-excursion - (set-buffer (gnus-get-buffer-create (format " *Gnus agent overview %s*" - group))) + (with-current-buffer (gnus-get-buffer-create + (format " *Gnus agent overview %s*"group)) (when (= (point-max) (point-min)) (push (cons group (current-buffer)) gnus-agent-buffer-alist) (ignore-errors @@ -1939,9 +1934,7 @@ article numbers will be returned." 10 "gnus-agent-fetch-headers: undownloaded articles are '%s'" (gnus-compress-sequence articles t)) - (save-excursion - (set-buffer nntp-server-buffer) - + (with-current-buffer nntp-server-buffer (if articles (progn (gnus-message 7 "Fetching headers for %s..." @@ -2767,8 +2760,7 @@ The following commands are available: (defun gnus-category-setup-buffer () (unless (get-buffer gnus-category-buffer) - (save-excursion - (set-buffer (gnus-get-buffer-create gnus-category-buffer)) + (with-current-buffer (gnus-get-buffer-create gnus-category-buffer) (gnus-category-mode)))) (defun gnus-category-prepare () diff --git a/lisp/gnus/gnus-async.el b/lisp/gnus/gnus-async.el index 979e67120d1..a2ab54bea8b 100644 --- a/lisp/gnus/gnus-async.el +++ b/lisp/gnus/gnus-async.el @@ -145,8 +145,7 @@ that was fetched." (when (and (gnus-buffer-live-p summary) gnus-asynchronous (gnus-group-asynchronous-p group)) - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (let ((next (caadr (gnus-data-find-list article)))) (when next (if (not (fboundp 'run-with-idle-timer)) @@ -205,8 +204,7 @@ that was fetched." (when (and do-fetch article) ;; We want to fetch some more articles. - (save-excursion - (set-buffer summary) + (with-current-buffer summary (let (mark) (gnus-async-set-buffer) (goto-char (point-max)) diff --git a/lisp/gnus/gnus-bcklg.el b/lisp/gnus/gnus-bcklg.el index b3851858513..68233328802 100644 --- a/lisp/gnus/gnus-bcklg.el +++ b/lisp/gnus/gnus-bcklg.el @@ -40,8 +40,7 @@ (defun gnus-backlog-buffer () "Return the backlog buffer." (or (get-buffer gnus-backlog-buffer) - (save-excursion - (set-buffer (gnus-get-buffer-create gnus-backlog-buffer)) + (with-current-buffer (gnus-get-buffer-create gnus-backlog-buffer) (buffer-disable-undo) (setq buffer-read-only t) (get-buffer gnus-backlog-buffer)))) @@ -76,8 +75,7 @@ (gnus-backlog-remove-oldest-article)) (push ident gnus-backlog-articles) ;; Insert the new article. - (save-excursion - (set-buffer (gnus-backlog-buffer)) + (with-current-buffer (gnus-backlog-buffer) (let (buffer-read-only) (goto-char (point-max)) (unless (bolp) @@ -90,8 +88,7 @@ (gnus-error 3 "Article %d is blank" number)))))))) (defun gnus-backlog-remove-oldest-article () - (save-excursion - (set-buffer (gnus-backlog-buffer)) + (with-current-buffer (gnus-backlog-buffer) (goto-char (point-min)) (if (zerop (buffer-size)) () ; The buffer is empty. @@ -114,8 +111,7 @@ beg end) (when (memq ident gnus-backlog-articles) ;; It was in the backlog. - (save-excursion - (set-buffer (gnus-backlog-buffer)) + (with-current-buffer (gnus-backlog-buffer) (let (buffer-read-only) (when (setq beg (text-property-any (point-min) (point-max) 'gnus-backlog @@ -138,8 +134,7 @@ beg end) (when (memq ident gnus-backlog-articles) ;; It was in the backlog. - (save-excursion - (set-buffer (gnus-backlog-buffer)) + (with-current-buffer (gnus-backlog-buffer) (if (not (setq beg (text-property-any (point-min) (point-max) 'gnus-backlog ident))) @@ -150,8 +145,7 @@ (setq end (next-single-property-change (1+ beg) 'gnus-backlog (current-buffer) (point-max))))) - (save-excursion - (and buffer (set-buffer buffer)) + (with-current-buffer (or (current-buffer) buffer) (let ((buffer-read-only nil)) (erase-buffer) (insert-buffer-substring gnus-backlog-buffer beg end))) diff --git a/lisp/gnus/gnus-cache.el b/lisp/gnus/gnus-cache.el index e3f33be8819..4b2d6705707 100644 --- a/lisp/gnus/gnus-cache.el +++ b/lisp/gnus/gnus-cache.el @@ -180,8 +180,7 @@ it's not cached." ;; Save the article in the cache. (if (file-exists-p file) t ; The article already is saved. - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (require 'gnus-art) (let ((gnus-use-cache nil) (gnus-article-decode-hook nil)) @@ -554,8 +553,7 @@ system for example was used.") (let ((cache-buf (gnus-get-buffer-create " *gnus-cache*")) beg end) (gnus-cache-save-buffers) - (save-excursion - (set-buffer cache-buf) + (with-current-buffer cache-buf (erase-buffer) (let ((coding-system-for-read gnus-cache-overview-coding-system) (file-name-coding-system nnmail-pathname-coding-system)) @@ -844,8 +842,7 @@ supported." ,@body) (when (and gnus-cache-need-update-total-fetched-for (not gnus-cache-inhibit-update-total-fetched-for)) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (setq gnus-cache-need-update-total-fetched-for nil) (gnus-group-update-group ,group t))))) diff --git a/lisp/gnus/gnus-demon.el b/lisp/gnus/gnus-demon.el index caf9f8784b9..67c1c8ba3bc 100644 --- a/lisp/gnus/gnus-demon.el +++ b/lisp/gnus/gnus-demon.el @@ -291,11 +291,9 @@ minutes, the connection is closed." (let ((win (current-window-configuration))) (unwind-protect (save-window-excursion - (save-excursion - (when (gnus-alive-p) - (save-excursion - (set-buffer gnus-group-buffer) - (gnus-group-get-new-news))))) + (when (gnus-alive-p) + (with-current-buffer gnus-group-buffer + (gnus-group-get-new-news)))) (set-window-configuration win)))) (defun gnus-demon-add-scan-timestamps () diff --git a/lisp/gnus/gnus-int.el b/lisp/gnus/gnus-int.el index d805f3104d2..389b1a22a8b 100644 --- a/lisp/gnus/gnus-int.el +++ b/lisp/gnus/gnus-int.el @@ -179,10 +179,7 @@ If it is down, start it up (again)." (format " on %s" (nth 1 method))))) (gnus-run-hooks 'gnus-open-server-hook) (prog1 - (condition-case () - (setq result (gnus-open-server method)) - (quit (message "Quit gnus-check-server") - nil)) + (setq result (gnus-open-server method)) (unless silent (gnus-message 5 "Opening %s server%s...%s" (car method) (if (equal (nth 1 method) "") "" @@ -225,6 +222,10 @@ If it is down, start it up (again)." ;;; Interface functions to the backends. ;;; +(defun gnus-method-denied-p (method) + (eq (nth 1 (assoc method gnus-opened-servers)) + 'denied)) + (defun gnus-open-server (gnus-command-method) "Open a connection to GNUS-COMMAND-METHOD." (when (stringp gnus-command-method) @@ -319,6 +320,22 @@ If it is down, start it up (again)." (funcall (gnus-get-function gnus-command-method 'request-list) (nth 1 gnus-command-method))) +(defun gnus-finish-retrieve-group-infos (gnus-command-method infos data) + "Read and update infos from GNUS-COMMAND-METHOD." + (when (stringp gnus-command-method) + (setq gnus-command-method (gnus-server-to-method gnus-command-method))) + (funcall (gnus-get-function gnus-command-method 'finish-retrieve-group-infos) + (nth 1 gnus-command-method) + infos data)) + +(defun gnus-retrieve-group-data-early (gnus-command-method infos) + "Start early async retrival of data from GNUS-COMMAND-METHOD." + (when (stringp gnus-command-method) + (setq gnus-command-method (gnus-server-to-method gnus-command-method))) + (funcall (gnus-get-function gnus-command-method 'retrieve-group-data-early) + (nth 1 gnus-command-method) + infos)) + (defun gnus-request-list-newsgroups (gnus-command-method) "Request the newsgroups file from GNUS-COMMAND-METHOD." (when (stringp gnus-command-method) @@ -490,8 +507,7 @@ If BUFFER, insert the article in that group." (setq res (gnus-request-article article group) clean-up t))) (when clean-up - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (when (search-forward "\n\n" nil t) (delete-region (1- (point)) (point-max))) @@ -523,8 +539,7 @@ If BUFFER, insert the article in that group." (setq res (gnus-request-article article group) clean-up t))) (when clean-up - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (when (search-forward "\n\n" nil t) (delete-region (point-min) (1- (point)))))) diff --git a/lisp/gnus/gnus-kill.el b/lisp/gnus/gnus-kill.el index fc564490fc9..5483a741f2f 100644 --- a/lisp/gnus/gnus-kill.el +++ b/lisp/gnus/gnus-kill.el @@ -349,8 +349,7 @@ If NEWSGROUP is nil, return the global kill file instead." (defun gnus-expunge (marks) "Remove lines marked with MARKS." - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-summary-limit-to-marks marks 'reverse))) (defun gnus-apply-kill-file-unless-scored () @@ -442,8 +441,7 @@ Returns the number of articles marked as read." (progn (delete-region beg (point)) (insert (or (eval form) ""))) - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (ignore-errors (eval form))))) (and (buffer-modified-p) gnus-kill-save-kill-file @@ -555,8 +553,7 @@ COMMAND must be a Lisp expression or a string representing a key sequence." (and (eq 'quote (car (nth 2 object))) (not (consp (cdadr (nth 2 object)))))) (concat "\n" (gnus-prin1-to-string object)) - (save-excursion - (set-buffer (gnus-get-buffer-create "*Gnus PP*")) + (with-current-buffer (gnus-get-buffer-create "*Gnus PP*") (buffer-disable-undo) (erase-buffer) (insert (format "\n(%S %S\n '(" (nth 0 object) (nth 1 object))) @@ -610,8 +607,7 @@ COMMAND must be a Lisp expression or a string representing a key sequence." 6 "Searching for article: %d..." (mail-header-number header)) (gnus-article-setup-buffer) (gnus-article-prepare (mail-header-number header) t) - (when (save-excursion - (set-buffer gnus-article-buffer) + (when (with-current-buffer gnus-article-buffer (goto-char (point-min)) (setq did-kill (re-search-forward regexp nil t))) (cond ((stringp form) ;Keyboard macro. diff --git a/lisp/gnus/gnus-logic.el b/lisp/gnus/gnus-logic.el index e6d28ae26aa..9637ebfb387 100644 --- a/lisp/gnus/gnus-logic.el +++ b/lisp/gnus/gnus-logic.el @@ -179,8 +179,7 @@ (defun gnus-advanced-body (header match type) (when (string= header "all") (setq header "article")) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (let* ((request-func (cond ((string= "head" header) 'gnus-request-head) ((string= "body" header) diff --git a/lisp/gnus/gnus-range.el b/lisp/gnus/gnus-range.el index 5eb8080ac0a..a4262df5328 100644 --- a/lisp/gnus/gnus-range.el +++ b/lisp/gnus/gnus-range.el @@ -59,6 +59,36 @@ If RANGE is a single range, return (RANGE). Otherwise, return RANGE." (setq list2 (cdr list2))) list1)) +(defun gnus-range-nconcat (&rest ranges) + "Return a range comprising all the RANGES, which are pre-sorted. +RANGES will be destructively altered." + (setq ranges (delete nil ranges)) + (let* ((result (gnus-range-normalize (pop ranges))) + (last (last result))) + (dolist (range ranges) + (setq range (gnus-range-normalize range)) + ;; Normalize the single-number case, so that we don't need to + ;; special-case that so much. + (when (numberp (car last)) + (setcar last (cons (car last) (car last)))) + (when (numberp (car range)) + (setcar range (cons (car range) (car range)))) + (if (= (1+ (cdar last)) (caar range)) + (progn + (setcdr (car last) (cdar range)) + (setcdr last (cdr range))) + (setcdr last range) + ;; Denormalize back, since we couldn't join the ranges up. + (when (= (caar range) (cdar range)) + (setcar range (caar range))) + (when (= (caar last) (cdar last)) + (setcar last (caar last)))) + (setq last (last last))) + (if (and (consp (car result)) + (= (length result) 1)) + (car result) + result))) + (defun gnus-range-difference (range1 range2) "Return the range of elements in RANGE1 that do not appear in RANGE2. Both ranges must be in ascending order." diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el index 8ba6c169bc4..a30847b0e2b 100644 --- a/lisp/gnus/gnus-registry.el +++ b/lisp/gnus/gnus-registry.el @@ -241,8 +241,7 @@ considered precious) will not be trimmed." "Save the registry cache file." (interactive) (let ((file gnus-registry-cache-file)) - (save-excursion - (set-buffer (gnus-get-buffer-create " *Gnus-registry-cache*")) + (with-current-buffer (gnus-get-buffer-create " *Gnus-registry-cache*") (make-local-variable 'version-control) (setq version-control gnus-backup-startup-file) (setq buffer-file-name file) @@ -674,8 +673,7 @@ Consults `gnus-registry-unfollowed-groups' and word words) (if (or (not (gnus-registry-fetch-extra id 'keywords)) force) - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (article-goto-body) (save-window-excursion (save-restriction diff --git a/lisp/gnus/gnus-score.el b/lisp/gnus/gnus-score.el index bd4a39eb7b1..5cd60ddaabf 100644 --- a/lisp/gnus/gnus-score.el +++ b/lisp/gnus/gnus-score.el @@ -708,8 +708,7 @@ file for the command instead of the current score file." ;; Change score file to the "all.SCORE" file. (when (eq symp 'a) - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-score-load-file ;; This is a kludge; yes... (cond @@ -735,14 +734,12 @@ file for the command instead of the current score file." (when (eq symp 'a) ;; We change the score file back to the previous one. - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-score-load-file current-score-file))))) (defun gnus-score-insert-help (string alist idx) (setq gnus-score-help-winconf (current-window-configuration)) - (save-excursion - (set-buffer (gnus-get-buffer-create "*Score Help*")) + (with-current-buffer (gnus-get-buffer-create "*Score Help*") (buffer-disable-undo) (delete-windows-on (current-buffer)) (erase-buffer) @@ -1270,8 +1267,7 @@ If FORMAT, also format the current score file." exclude-files)) gnus-scores-exclude-files)) (when local - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (while local (and (consp (car local)) (symbolp (caar local)) @@ -1528,8 +1524,7 @@ If FORMAT, also format the current score file." (cons (cons header (or gnus-summary-default-score 0)) gnus-scores-articles)))) - (save-excursion - (set-buffer (gnus-get-buffer-create "*Headers*")) + (with-current-buffer (gnus-get-buffer-create "*Headers*") (buffer-disable-undo) (when (gnus-buffer-live-p gnus-summary-buffer) (message-clone-locals gnus-summary-buffer)) @@ -1854,8 +1849,7 @@ score in `gnus-newsgroup-scored' by SCORE." ;; Change score file to the adaptive score file. All entries that ;; this function makes will be put into this file. - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-score-load-file (or gnus-newsgroup-adaptive-score-file (gnus-score-file-name @@ -1946,15 +1940,13 @@ score in `gnus-newsgroup-scored' by SCORE." (setq rest entries))) (setq entries rest)))) ;; We change the score file back to the previous one. - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-score-load-file current-score-file)) (list (cons "references" news))))) (defun gnus-score-add-followups (header score scores &optional thread) "Add a score entry to the adapt file." - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (let* ((id (mail-header-id header)) (scores (car scores)) entry dont) @@ -2282,8 +2274,7 @@ score in `gnus-newsgroup-scored' by SCORE." "Create adaptive score rules for this newsgroup." (when gnus-newsgroup-adaptive ;; We change the score file to the adaptive score file. - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-score-load-file (or gnus-newsgroup-adaptive-score-file (gnus-home-score-file gnus-newsgroup-name t) @@ -2697,8 +2688,7 @@ GROUP using BNews sys file syntax." (trans (cdr (assq ?: nnheader-file-name-translation-alist))) (group-trans (nnheader-translate-file-chars group t)) ofiles not-match regexp) - (save-excursion - (set-buffer (gnus-get-buffer-create "*gnus score files*")) + (with-current-buffer (gnus-get-buffer-create "*gnus score files*") (buffer-disable-undo) ;; Go through all score file names and create regexp with them ;; as the source. diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el index 1c06a774203..e25d31ec87e 100644 --- a/lisp/gnus/gnus-start.el +++ b/lisp/gnus/gnus-start.el @@ -594,8 +594,7 @@ Can be used to turn version control on or off." (defun gnus-subscribe-hierarchically (newgroup) "Subscribe new NEWGROUP and insert it in hierarchical newsgroup order." ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams) - (save-excursion - (set-buffer (nnheader-find-file-noselect gnus-current-startup-file)) + (with-current-buffer (nnheader-find-file-noselect gnus-current-startup-file) (prog1 (let ((groupkey newgroup) before) (while (and (not before) groupkey) @@ -857,8 +856,7 @@ prompt the user for the name of an NNTP server to use." ;; it's not needed). ;; (set-window-point (get-buffer-window (current-buffer)) (point-max)) (bury-buffer gnus-dribble-buffer) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (gnus-group-set-mode-line)) (set-buffer obuf)))) @@ -871,10 +869,9 @@ prompt the user for the name of an NNTP server to use." (let ((dribble-file (gnus-dribble-file-name))) (unless (file-exists-p (file-name-directory dribble-file)) (make-directory (file-name-directory dribble-file) t)) - (save-excursion - (set-buffer (setq gnus-dribble-buffer - (gnus-get-buffer-create - (file-name-nondirectory dribble-file)))) + (with-current-buffer (setq gnus-dribble-buffer + (gnus-get-buffer-create + (file-name-nondirectory dribble-file))) (set (make-local-variable 'file-precious-flag) t) (erase-buffer) (setq buffer-file-name dribble-file) @@ -923,8 +920,7 @@ prompt the user for the name of an NNTP server to use." (when (file-exists-p (gnus-dribble-file-name)) (delete-file (gnus-dribble-file-name))) (when gnus-dribble-buffer - (save-excursion - (set-buffer gnus-dribble-buffer) + (with-current-buffer gnus-dribble-buffer (let ((auto (make-auto-save-file-name))) (when (file-exists-p auto) (delete-file auto)) @@ -934,14 +930,12 @@ prompt the user for the name of an NNTP server to use." (defun gnus-dribble-save () (when (and gnus-dribble-buffer (buffer-name gnus-dribble-buffer)) - (save-excursion - (set-buffer gnus-dribble-buffer) + (with-current-buffer gnus-dribble-buffer (save-buffer)))) (defun gnus-dribble-clear () (when (gnus-buffer-exists-p gnus-dribble-buffer) - (save-excursion - (set-buffer gnus-dribble-buffer) + (with-current-buffer gnus-dribble-buffer (erase-buffer) (set-buffer-modified-p nil) (setq buffer-saved-size (buffer-size))))) @@ -1302,8 +1296,7 @@ for new groups, and subscribe the new groups as zombies." (when (gnus-active group) (gnus-group-change-level group gnus-level-default-subscribed gnus-level-killed))) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer ;; Don't error if the group already exists. This happens when a ;; first-time user types 'F'. -- didier (gnus-group-make-help-group t)) @@ -1734,7 +1727,7 @@ If SCAN, request a scan of that group as well." 'primary) (t 'foreign))) - (push (setq method-group-list (list method method-type nil)) + (push (setq method-group-list (list method method-type nil nil)) type-cache)) ;; Only add groups that need updating. (if (<= (gnus-info-level info) @@ -1760,19 +1753,28 @@ If SCAN, request a scan of that group as well." (< (gnus-method-rank (cadr c1) (car c1)) (gnus-method-rank (cadr c2) (car c2)))))) - (while type-cache - (setq method (nth 0 (car type-cache)) - method-type (nth 1 (car type-cache)) - infos (nth 2 (car type-cache))) - (pop type-cache) - - (when (and method - infos) - ;; See if any of the groups from this method require updating. - (gnus-read-active-for-groups method infos) - (dolist (info infos) - (inline (gnus-get-unread-articles-in-group - info (gnus-active (gnus-info-group info))))))) + ;; Start early async retrieval of data. + (dolist (elem type-cache) + (destructuring-bind (method method-type infos dummy) elem + (when (and method infos + (not (gnus-method-denied-p method)) + (gnus-check-backend-function + 'retrieve-group-data-early (car method))) + (when (gnus-check-backend-function 'request-scan (car method)) + (dolist (info infos) + (gnus-request-scan (gnus-info-group info) method))) + (setcar (nthcdr 3 elem) + (gnus-retrieve-group-data-early method infos))))) + + ;; Do the rest of the retrieval. + (dolist (elem type-cache) + (destructuring-bind (method method-type infos early-data) elem + (when (and method infos) + ;; See if any of the groups from this method require updating. + (gnus-read-active-for-groups method infos early-data) + (dolist (info infos) + (inline (gnus-get-unread-articles-in-group + info (gnus-active (gnus-info-group info)))))))) (gnus-message 6 "Checking new news...done"))) (defun gnus-method-rank (type method) @@ -1796,9 +1798,14 @@ If SCAN, request a scan of that group as well." (t 100))) -(defun gnus-read-active-for-groups (method infos) +(defun gnus-read-active-for-groups (method infos early-data) (with-current-buffer nntp-server-buffer (cond + ((and + (gnus-check-backend-function 'finish-retrieve-group-infos (car method)) + (or (not (gnus-agent-method-p method)) + (gnus-online method))) + (gnus-finish-retrieve-group-infos method infos early-data)) ((gnus-check-backend-function 'retrieve-groups (car method)) (when (gnus-check-backend-function 'request-scan (car method)) (dolist (info infos) @@ -1867,8 +1874,7 @@ If SCAN, request a scan of that group as well." (defun gnus-parse-active () "Parse active info in the nntp server buffer." - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) ;; Parse the result we got from `gnus-request-group'. (when (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) [0-9]+") @@ -2022,8 +2028,7 @@ If SCAN, request a scan of that group as well." (list "archive"))))) method) (setq gnus-have-read-active-file nil) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (while (setq method (pop methods)) ;; Only do each method once, in case the methods appear more ;; than once in this list. @@ -2089,8 +2094,7 @@ If SCAN, request a scan of that group as well." (defun gnus-read-active-file-2 (groups method) "Read an active file for GROUPS in METHOD using `gnus-retrieve-groups'." (when groups - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (gnus-check-server method) (let ((list-type (gnus-retrieve-groups groups method))) (cond ((not list-type) @@ -2771,8 +2775,7 @@ If FORCE is non-nil, the .newsrc file is read." (not force) (or (not gnus-dribble-buffer) (not (buffer-name gnus-dribble-buffer)) - (zerop (save-excursion - (set-buffer gnus-dribble-buffer) + (zerop (with-current-buffer gnus-dribble-buffer (buffer-size))))) (gnus-message 4 "(No changes need to be saved)") (gnus-run-hooks 'gnus-save-newsrc-hook) @@ -2906,8 +2909,7 @@ If FORCE is non-nil, the .newsrc file is read." (defun gnus-gnus-to-newsrc-format () ;; Generate and save the .newsrc file. - (save-excursion - (set-buffer (create-file-buffer gnus-current-startup-file)) + (with-current-buffer (create-file-buffer gnus-current-startup-file) (let ((newsrc (cdr gnus-newsrc-alist)) (standard-output (current-buffer)) info ranges range method) @@ -2980,8 +2982,7 @@ If FORCE is non-nil, the .newsrc file is read." (gnus-run-hooks 'gnus-slave-mode-hook)) (defun gnus-slave-save-newsrc () - (save-excursion - (set-buffer gnus-dribble-buffer) + (with-current-buffer gnus-dribble-buffer (let ((slave-name (mm-make-temp-file (concat gnus-current-startup-file "-slave-"))) (modes (ignore-errors @@ -3005,8 +3006,7 @@ If FORCE is non-nil, the .newsrc file is read." (if (not slave-files) () ; There are no slave files to read. (gnus-message 7 "Reading slave newsrcs...") - (save-excursion - (set-buffer (gnus-get-buffer-create " *gnus slave*")) + (with-current-buffer (gnus-get-buffer-create " *gnus slave*") (setq slave-files (sort (mapcar (lambda (file) (list (nth 5 (file-attributes file)) file)) @@ -3126,8 +3126,7 @@ If FORCE is non-nil, the .newsrc file is read." (defun gnus-group-get-description (group) "Get the description of a group by sending XGTITLE to the server." (when (gnus-request-group-description group) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (when (looking-at "[^ \t]+[ \t]+\\(.*\\)") (match-string 1))))) diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el index df20456b278..3c3a0590536 100644 --- a/lisp/gnus/gnus-sum.el +++ b/lisp/gnus/gnus-sum.el @@ -5504,11 +5504,11 @@ If SELECT-ARTICLES, only select those articles from GROUP." (mm-decode-coding-string (gnus-status-message group) charset)))) (unless (gnus-request-group group t) - (when (equal major-mode 'gnus-summary-mode) - (gnus-kill-buffer (current-buffer))) - (error "Couldn't request group %s: %s" - (mm-decode-coding-string group charset) - (mm-decode-coding-string (gnus-status-message group) charset))) + (when (equal major-mode 'gnus-summary-mode) + (gnus-kill-buffer (current-buffer))) + (error "Couldn't request group %s: %s" + (mm-decode-coding-string group charset) + (mm-decode-coding-string (gnus-status-message group) charset))) (when gnus-agent (gnus-agent-possibly-alter-active group (gnus-active group) info) @@ -7394,7 +7394,7 @@ If prefix argument NO-ARTICLE is non-nil, no article is selected initially." "Go to the first subject satisfying any non-nil constraint. If UNREAD is non-nil, the article should be unread. If UNDOWNLOADED is non-nil, the article should be undownloaded. -If UNSEEN is non-nil, the article should be unseen. +If UNSEEN is non-nil, the article should be unseen as well as unread. Returns the article selected or nil if there are no matching articles." (interactive "P") (cond @@ -7417,7 +7417,8 @@ Returns the article selected or nil if there are no matching articles." (and undownloaded (memq num gnus-newsgroup-undownloaded)) (and unseen - (memq num gnus-newsgroup-unseen))))))) + (memq num gnus-newsgroup-unseen) + (memq num gnus-newsgroup-unreads))))))) (setq data (cdr data))) (prog1 (if data @@ -7908,8 +7909,8 @@ Return nil if there are no unseen articles." (gnus-summary-position-point))) (defun gnus-summary-first-unseen-or-unread-subject () - "Place the point on the subject line of the first unseen article or, -if all article have been seen, on the subject line of the first unread + "Place the point on the subject line of the first unseen and unread article. +If all article have been seen, on the subject line of the first unread article." (interactive) (prog1 @@ -9690,7 +9691,8 @@ ACTION can be either `move' (the default), `crosspost' or `copy'." to-newsgroup (list 'quote select-method) (not articles) t) ; Accept form (not articles) ; Only save nov last time - move-is-internal))) ; is this move internal? + (and move-is-internal + (gnus-group-real-name to-newsgroup))))) ; is this move internal? ;; Copy the article. ((eq action 'copy) (with-current-buffer copy-buf @@ -9821,8 +9823,9 @@ ACTION can be either `move' (the default), `crosspost' or `copy'." (gnus-add-marked-articles to-group 'expire (list to-article) info)) - (gnus-request-set-mark - to-group (list (list (list to-article) 'add to-marks)))) + (when to-marks + (gnus-request-set-mark + to-group (list (list (list to-article) 'add to-marks))))) (gnus-dribble-enter (concat "(gnus-group-set-info '" diff --git a/lisp/gnus/gnus-topic.el b/lisp/gnus/gnus-topic.el index 89e61bcb598..7c710357b9d 100644 --- a/lisp/gnus/gnus-topic.el +++ b/lisp/gnus/gnus-topic.el @@ -148,8 +148,7 @@ See Info node `(gnus)Formatting Variables'." (defun gnus-group-parent-topic (group) "Return the topic GROUP is member of by looking at the group buffer." - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (if (gnus-group-goto-group group) (gnus-current-topic) (gnus-group-topic group)))) @@ -912,8 +911,7 @@ articles in the topic and its subtopics." (defun gnus-topic-change-level (group level oldlevel &optional previous) "Run when changing levels to enter/remove groups from topics." - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (let ((buffer-read-only nil)) (unless gnus-topic-inhibit-change-level (gnus-group-goto-group (or (car (nth 2 previous)) group)) diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el index 7cdb70a3580..334f0eea7db 100644 --- a/lisp/gnus/gnus-util.el +++ b/lisp/gnus/gnus-util.el @@ -1115,8 +1115,7 @@ FILENAME exists and is Babyl format." (gnus-yes-or-no-p (concat "\"" filename "\" does not exist, create it? "))) (let ((file-buffer (create-file-buffer filename))) - (save-excursion - (set-buffer file-buffer) + (with-current-buffer file-buffer (if (fboundp 'rmail-insert-rmail-file-header) (rmail-insert-rmail-file-header)) (let ((require-final-newline nil) @@ -1194,8 +1193,7 @@ FILENAME exists and is Babyl format." (gnus-y-or-n-p (concat "\"" filename "\" does not exist, create it? "))) (let ((file-buffer (create-file-buffer filename))) - (save-excursion - (set-buffer file-buffer) + (with-current-buffer file-buffer (let ((require-final-newline nil) (coding-system-for-write mm-text-coding-system)) (gnus-write-buffer filename))) @@ -1274,8 +1272,7 @@ This function saves the current buffer." "Say whether Gnus is running or not." (and (boundp 'gnus-group-buffer) (get-buffer gnus-group-buffer) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (eq major-mode 'gnus-group-mode)))) (defun gnus-remove-if (predicate list) diff --git a/lisp/gnus/gnus-uu.el b/lisp/gnus/gnus-uu.el index 35120eae767..614a52c176c 100644 --- a/lisp/gnus/gnus-uu.el +++ b/lisp/gnus/gnus-uu.el @@ -827,8 +827,7 @@ When called interactively, prompt for REGEXP." (defun gnus-uu-save-article (buffer in-state) (cond (gnus-uu-save-separate-articles - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (let ((coding-system-for-write mm-text-coding-system)) (gnus-write-buffer (concat gnus-uu-saved-article-name gnus-current-article))) @@ -838,8 +837,7 @@ When called interactively, prompt for REGEXP." ((eq in-state 'last) (list 'end)) (t (list 'middle))))) ((not gnus-uu-save-in-digest) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (write-region (point-min) (point-max) gnus-uu-saved-article-name t) (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin)) ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name @@ -857,11 +855,9 @@ When called interactively, prompt for REGEXP." (eq in-state 'first-and-last)) (progn (setq state (list 'begin)) - (save-excursion - (set-buffer (gnus-get-buffer-create "*gnus-uu-body*")) + (with-current-buffer (gnus-get-buffer-create "*gnus-uu-body*") (erase-buffer)) - (save-excursion - (set-buffer (gnus-get-buffer-create "*gnus-uu-pre*")) + (with-current-buffer (gnus-get-buffer-create "*gnus-uu-pre*") (erase-buffer) (insert (format "Date: %s\nFrom: %s\nSubject: %s Digest\n\n" @@ -873,8 +869,7 @@ When called interactively, prompt for REGEXP." (insert "Topics:\n"))) (when (not (eq in-state 'end)) (setq state (list 'middle)))) - (save-excursion - (set-buffer "*gnus-uu-body*") + (with-current-buffer "*gnus-uu-body*" (goto-char (setq beg (point-max))) (save-excursion (save-restriction @@ -940,8 +935,7 @@ When called interactively, prompt for REGEXP." (when (re-search-forward "^Subject: \\(.*\\)$" nil t) (setq subj (buffer-substring (match-beginning 1) (match-end 1)))) (when subj - (save-excursion - (set-buffer "*gnus-uu-pre*") + (with-current-buffer "*gnus-uu-pre*" (insert (format " %s\n" subj))))) (when (or (eq in-state 'last) (eq in-state 'first-and-last)) @@ -951,8 +945,7 @@ When called interactively, prompt for REGEXP." (insert-buffer-substring "*gnus-uu-pre*") (goto-char (point-max)) (insert-buffer-substring "*gnus-uu-body*")) - (save-excursion - (set-buffer "*gnus-uu-pre*") + (with-current-buffer "*gnus-uu-pre*" (insert (format "\n\n%s\n\n" (make-string 70 ?-))) (if gnus-uu-digest-buffer (with-current-buffer gnus-uu-digest-buffer @@ -960,8 +953,7 @@ When called interactively, prompt for REGEXP." (insert-buffer-substring "*gnus-uu-pre*")) (let ((coding-system-for-write mm-text-coding-system)) (gnus-write-buffer gnus-uu-saved-article-name)))) - (save-excursion - (set-buffer "*gnus-uu-body*") + (with-current-buffer "*gnus-uu-body*" (goto-char (point-max)) (insert (concat (setq end-string (format "End of %s Digest" name)) @@ -993,8 +985,7 @@ When called interactively, prompt for REGEXP." (defun gnus-uu-binhex-article (buffer in-state) (let (state start-char) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (widen) (goto-char (point-min)) (when (not (re-search-forward gnus-uu-binhex-begin-line nil t)) @@ -1030,8 +1021,7 @@ When called interactively, prompt for REGEXP." ;; yEnc (defun gnus-uu-yenc-article (buffer in-state) - (save-excursion - (set-buffer gnus-original-article-buffer) + (with-current-buffer gnus-original-article-buffer (widen) (let ((file-name (yenc-extract-filename)) state start-char) @@ -1065,8 +1055,7 @@ When called interactively, prompt for REGEXP." (defun gnus-uu-decode-postscript-article (process-buffer in-state) (let ((state (list 'ok)) start-char end-char file-name) - (save-excursion - (set-buffer process-buffer) + (with-current-buffer process-buffer (goto-char (point-min)) (if (not (re-search-forward gnus-uu-postscript-begin-string nil t)) (setq state (list 'wrong-type)) @@ -1128,8 +1117,7 @@ When called interactively, prompt for REGEXP." ;; replaces the last thing that looks like "2/3" with "[0-9]+/3" ;; or, if it can't find something like that, tries "2 of 3", then ;; finally just replaces the next to last number with "[0-9]+". - (save-excursion - (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) + (with-current-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name) (buffer-disable-undo) (erase-buffer) (insert (regexp-quote string)) @@ -1228,8 +1216,7 @@ When called interactively, prompt for REGEXP." ;; decoded in. Returns the list of expanded strings. (let ((out-list string-list) string) - (save-excursion - (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) + (with-current-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name) (buffer-disable-undo) (while string-list (erase-buffer) @@ -1332,11 +1319,9 @@ When called interactively, prompt for REGEXP." (gnus-summary-display-article article) ;; Push the article to the processing function. - (save-excursion - (set-buffer gnus-original-article-buffer) + (with-current-buffer gnus-original-article-buffer (let ((buffer-read-only nil)) - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (setq process-state (funcall process-function gnus-original-article-buffer state))))) @@ -1477,8 +1462,7 @@ When called interactively, prompt for REGEXP." (defun gnus-uu-uustrip-article (process-buffer in-state) ;; Uudecodes a file asynchronously. - (save-excursion - (set-buffer process-buffer) + (with-current-buffer process-buffer (let ((state (list 'wrong-type)) process-connection-type case-fold-search buffer-read-only files start-char) @@ -1600,8 +1584,7 @@ Gnus might fail to display all of it.") (defun gnus-uu-unshar-article (process-buffer in-state) (let ((state (list 'ok)) start-char) - (save-excursion - (set-buffer process-buffer) + (with-current-buffer process-buffer (goto-char (point-min)) (if (not (re-search-forward gnus-uu-shar-begin-string nil t)) (setq state (list 'wrong-type)) @@ -1688,8 +1671,7 @@ Gnus might fail to display all of it.") (setq command (format "cd %s ; %s" dir (gnus-uu-command action file-path))) - (save-excursion - (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) + (with-current-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name) (erase-buffer)) (gnus-message 5 "Unpacking: %s..." (gnus-uu-command action file-path)) @@ -2039,9 +2021,8 @@ If no file has been included, the user will be asked for a file." (setq file-name file-path)) (unwind-protect - (if (save-excursion - (set-buffer (setq uubuf - (gnus-get-buffer-create uuencode-buffer-name))) + (if (with-current-buffer + (setq uubuf (gnus-get-buffer-create uuencode-buffer-name)) (erase-buffer) (funcall gnus-uu-post-encode-method file-path file-name)) (insert-buffer-substring uubuf) @@ -2073,8 +2054,8 @@ If no file has been included, the user will be asked for a file." (setq beg-binary (point)) (setq end-binary (point-max)) - (save-excursion - (set-buffer (setq uubuf (gnus-get-buffer-create encoded-buffer-name))) + (with-current-buffer + (setq uubuf (gnus-get-buffer-create encoded-buffer-name)) (erase-buffer) (insert-buffer-substring post-buf beg-binary end-binary) (goto-char (point-min)) @@ -2129,8 +2110,7 @@ If no file has been included, the user will be asked for a file." (insert (format " (%d/%d)" i parts))) (goto-char (point-max)) - (save-excursion - (set-buffer uubuf) + (with-current-buffer uubuf (goto-char beg) (if (= i parts) (goto-char (point-max)) diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el index 797f8a44bd1..2173d713d11 100644 --- a/lisp/gnus/gnus.el +++ b/lisp/gnus/gnus.el @@ -2743,6 +2743,8 @@ a string, be sure to use a valid format, see RFC 2616." '((seen range) (killed range) (bookmark tuple) + (uid tuple) + (active tuple) (score tuple))) ;; Propagate flags to server, with the following exceptions: diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el index c4cbce4abaf..948fc08135d 100644 --- a/lisp/gnus/mm-decode.el +++ b/lisp/gnus/mm-decode.el @@ -114,6 +114,7 @@ "Render of HTML contents. It is one of defined renderer types, or a rendering function. The defined renderer types are: +`gnus-article-html' : use Gnus renderer based on w3m; `w3m' : use emacs-w3m; `w3m-standalone': use w3m; `links': use links; @@ -122,8 +123,9 @@ The defined renderer types are: `html2text' : use html2text; nil : use external viewer (default web browser)." :version "24.1" - :type '(choice (const w3) - (const w3m :tag "emacs-w3m") + :type '(choice (const gnus-article-html) + (const w3) + (const w3m :tag "emacs-w3m") (const w3m-standalone :tag "standalone w3m" ) (const links) (const lynx) diff --git a/lisp/gnus/mm-partial.el b/lisp/gnus/mm-partial.el index 3fec4a2a975..6509b648fe7 100644 --- a/lisp/gnus/mm-partial.el +++ b/lisp/gnus/mm-partial.el @@ -70,8 +70,7 @@ If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing." (sort (cons handle (mm-partial-find-parts id - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-summary-article-number)))) #'(lambda (a b) (let ((anumber (string-to-number @@ -83,8 +82,7 @@ If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing." (< anumber bnumber))))) (setq gnus-article-mime-handles (mm-merge-handles gnus-article-mime-handles phandles)) - (save-excursion - (set-buffer (generate-new-buffer " *mm*")) + (with-current-buffer (generate-new-buffer " *mm*") (while (setq phandle (pop phandles)) (setq nn (string-to-number (cdr (assq 'number diff --git a/lisp/gnus/nnagent.el b/lisp/gnus/nnagent.el index 263d721dad2..ccd4e890da7 100644 --- a/lisp/gnus/nnagent.el +++ b/lisp/gnus/nnagent.el @@ -252,6 +252,9 @@ (nnoo-parent-function 'nnagent 'nnml-request-regenerate (list (nnagent-server server)))) +(deffoo nnagent-retrieve-group-data-early (server infos) + nil) + ;; Use nnml functions for just about everything. (nnoo-import nnagent (nnml)) diff --git a/lisp/gnus/nnbabyl.el b/lisp/gnus/nnbabyl.el index 58e848bcb5c..512de38559d 100644 --- a/lisp/gnus/nnbabyl.el +++ b/lisp/gnus/nnbabyl.el @@ -75,8 +75,7 @@ (nnoo-define-basics nnbabyl) (deffoo nnbabyl-retrieve-headers (articles &optional group server fetch-old) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let ((number (length articles)) (count 0) @@ -136,8 +135,7 @@ ;; Restore buffer mode. (when (and (nnbabyl-server-opened) nnbabyl-previous-buffer-mode) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (narrow-to-region (caar nnbabyl-previous-buffer-mode) (cdar nnbabyl-previous-buffer-mode)) @@ -155,8 +153,7 @@ (deffoo nnbabyl-request-article (article &optional newsgroup server buffer) (nnbabyl-possibly-change-newsgroup newsgroup server) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (goto-char (point-min)) (when (search-forward (nnbabyl-article-string article) nil t) (let (start stop summary-line) @@ -216,8 +213,7 @@ (nnmail-get-new-mail 'nnbabyl (lambda () - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (save-buffer))) (file-name-directory nnbabyl-mbox-file) group @@ -264,8 +260,7 @@ rest) (nnmail-activate 'nnbabyl) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (set-text-properties (point-min) (point-max) nil) (while (and articles is-old) (goto-char (point-min)) @@ -308,8 +303,7 @@ result) (and (nnbabyl-request-article article group server) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (insert-buffer-substring nntp-server-buffer) (goto-char (point-min)) (while (re-search-forward @@ -373,8 +367,7 @@ (deffoo nnbabyl-request-replace-article (article group buffer) (nnbabyl-possibly-change-newsgroup group) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (goto-char (point-min)) (if (not (search-forward (nnbabyl-article-string article) nil t)) nil @@ -388,8 +381,7 @@ ;; Delete all articles in GROUP. (if (not force) () ; Don't delete the articles. - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (goto-char (point-min)) ;; Delete all articles in this group. (let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":")) @@ -409,8 +401,7 @@ (deffoo nnbabyl-request-rename-group (group new-name &optional server) (nnbabyl-possibly-change-newsgroup group server) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (goto-char (point-min)) (let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":")) (new-ident (concat "\nX-Gnus-Newsgroup: " new-name ":")) @@ -558,9 +549,8 @@ (defun nnbabyl-create-mbox () (unless (file-exists-p nnbabyl-mbox-file) ;; Create a new, empty RMAIL mbox file. - (save-excursion - (set-buffer (setq nnbabyl-mbox-buffer - (create-file-buffer nnbabyl-mbox-file))) + (with-current-buffer (setq nnbabyl-mbox-buffer + (create-file-buffer nnbabyl-mbox-file)) (setq buffer-file-name nnbabyl-mbox-file) (insert "BABYL OPTIONS:\n\n\^_") (nnmail-write-region @@ -572,8 +562,7 @@ (unless (and nnbabyl-mbox-buffer (buffer-name nnbabyl-mbox-buffer) - (save-excursion - (set-buffer nnbabyl-mbox-buffer) + (with-current-buffer nnbabyl-mbox-buffer (= (buffer-size) (nnheader-file-size nnbabyl-mbox-file)))) ;; This buffer has changed since we read it last. Possibly. (save-excursion diff --git a/lisp/gnus/nndiary.el b/lisp/gnus/nndiary.el index 3189d33dd5a..790e390424e 100644 --- a/lisp/gnus/nndiary.el +++ b/lisp/gnus/nndiary.el @@ -380,8 +380,7 @@ all. This may very well take some time.") (deffoo nndiary-retrieve-headers (sequence &optional group server fetch-old) (when (nndiary-possibly-change-directory group server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let* ((file nil) (number (length sequence)) @@ -615,8 +614,7 @@ all. This may very well take some time.") (let (nndiary-current-directory nndiary-current-group nndiary-article-file-alist) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (insert-buffer-substring nntp-server-buffer) (setq result (eval accept-form)) (kill-buffer (current-buffer)) @@ -672,8 +670,7 @@ all. This may very well take some time.") (deffoo nndiary-request-replace-article (article group buffer) (nndiary-possibly-change-directory group) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (nndiary-possibly-create-directory group) (let ((chars (nnmail-insert-lines)) (art (concat (int-to-string article) "\t")) @@ -688,8 +685,7 @@ all. This may very well take some time.") t) (setq headers (nndiary-parse-head chars article)) ;; Replace the NOV line in the NOV file. - (save-excursion - (set-buffer (nndiary-open-nov group)) + (with-current-buffer (nndiary-open-nov group) (goto-char (point-min)) (if (or (looking-at art) (search-forward (concat "\n" art) nil t)) @@ -842,8 +838,7 @@ all. This may very well take some time.") ;; Find an article number in the current group given the Message-ID. (defun nndiary-find-group-number (id) - (save-excursion - (set-buffer (get-buffer-create " *nndiary id*")) + (with-current-buffer (get-buffer-create " *nndiary id*") (let ((alist nndiary-group-alist) number) ;; We want to look through all .overview files, but we want to @@ -888,8 +883,7 @@ all. This may very well take some time.") (let ((nov (expand-file-name nndiary-nov-file-name nndiary-current-directory))) (when (file-exists-p nov) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (nnheader-insert-file-contents nov) (if (and fetch-old @@ -989,8 +983,7 @@ all. This may very well take some time.") (defun nndiary-add-nov (group article headers) "Add a nov line for the GROUP base." - (save-excursion - (set-buffer (nndiary-open-nov group)) + (with-current-buffer (nndiary-open-nov group) (goto-char (point-max)) (mail-header-set-number headers article) (nnheader-insert-nov headers))) @@ -1015,8 +1008,7 @@ all. This may very well take some time.") (or (cdr (assoc group nndiary-nov-buffer-alist)) (let ((buffer (get-buffer-create (format " *nndiary overview %s*" group)))) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (set (make-local-variable 'nndiary-nov-buffer-file-name) (expand-file-name nndiary-nov-file-name @@ -1103,9 +1095,8 @@ all. This may very well take some time.") (nov (concat dir nndiary-nov-file-name)) (nov-buffer (get-buffer-create " *nov*")) chars file headers) - (save-excursion - ;; Init the nov buffer. - (set-buffer nov-buffer) + ;; Init the nov buffer. + (with-current-buffer nov-buffer (buffer-disable-undo) (erase-buffer) (set-buffer nntp-server-buffer) @@ -1125,20 +1116,17 @@ all. This may very well take some time.") (unless (zerop (buffer-size)) (goto-char (point-min)) (setq headers (nndiary-parse-head chars (caar files))) - (save-excursion - (set-buffer nov-buffer) + (with-current-buffer nov-buffer (goto-char (point-max)) (nnheader-insert-nov headers))) (widen)) (setq files (cdr files))) - (save-excursion - (set-buffer nov-buffer) + (with-current-buffer nov-buffer (nnmail-write-region 1 (point-max) nov nil 'nomesg) (kill-buffer (current-buffer)))))) (defun nndiary-nov-delete-article (group article) - (save-excursion - (set-buffer (nndiary-open-nov group)) + (with-current-buffer (nndiary-open-nov group) (when (nnheader-find-nov-line article) (delete-region (point) (progn (forward-line 1) (point))) (when (bobp) diff --git a/lisp/gnus/nndoc.el b/lisp/gnus/nndoc.el index ddeac7f9523..2e492057003 100644 --- a/lisp/gnus/nndoc.el +++ b/lisp/gnus/nndoc.el @@ -215,8 +215,7 @@ from the document.") (deffoo nndoc-retrieve-headers (articles &optional newsgroup server fetch-old) (when (nndoc-possibly-change-buffer newsgroup server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let (article entry) (if (stringp (car articles)) @@ -333,8 +332,7 @@ from the document.") (concat " *nndoc " group "*")))) nndoc-group-alist) (setq nndoc-dissection-alist nil) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (erase-buffer) (if (and (stringp nndoc-address) (string-match nndoc-binary-file-names nndoc-address)) @@ -347,8 +345,7 @@ from the document.") ;; Initialize the nndoc structures according to this new document. (when (and nndoc-current-buffer (not nndoc-dissection-alist)) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (nndoc-set-delims) (if (eq nndoc-article-type 'mime-parts) (nndoc-dissect-mime-parts) @@ -588,8 +585,7 @@ from the document.") (defun nndoc-generate-clari-briefs-head (article) (let ((entry (cdr (assq article nndoc-dissection-alist))) subject from) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (save-restriction (narrow-to-region (car entry) (nth 3 entry)) (goto-char (point-min)) @@ -677,8 +673,7 @@ from the document.") (let ((entry (cdr (assq article nndoc-dissection-alist))) (from "<no address given>") subject date) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (save-restriction (narrow-to-region (car entry) (nth 1 entry)) (goto-char (point-min)) @@ -829,8 +824,7 @@ from the document.") (first t) art-begin head-begin head-end body-begin body-end) (setq nndoc-dissection-alist nil) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (goto-char (point-min)) ;; Remove blank lines. (while (eq (following-char) ?\n) @@ -902,8 +896,7 @@ When a MIME entity contains sub-entities, dissection produces one article for the header of this entity, and one article per sub-entity." (setq nndoc-dissection-alist nil nndoc-mime-split-ordinal 0) - (save-excursion - (set-buffer nndoc-current-buffer) + (with-current-buffer nndoc-current-buffer (nndoc-dissect-mime-parts-sub (point-min) (point-max) nil nil nil))) (defun nndoc-dissect-mime-parts-sub (head-begin body-end article-insert diff --git a/lisp/gnus/nndraft.el b/lisp/gnus/nndraft.el index dd2b8a6b48d..e92e00efe6f 100644 --- a/lisp/gnus/nndraft.el +++ b/lisp/gnus/nndraft.el @@ -77,8 +77,7 @@ are generated if and only if they are also in `message-draft-headers'.") (deffoo nndraft-retrieve-headers (articles &optional group server fetch-old) (nndraft-possibly-change-group group) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let* (article) ;; We don't support fetching by Message-ID. @@ -119,8 +118,7 @@ are generated if and only if they are also in `message-draft-headers'.") mm-text-coding-system) mm-auto-save-coding-system))) (nnmail-find-file newest))) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) ;; If there's a mail header separator in this file, ;; we remove it. @@ -209,8 +207,7 @@ are generated if and only if they are also in `message-draft-headers'.") result) (and (nndraft-request-article article group server) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (erase-buffer) (insert-buffer-substring nntp-server-buffer) (setq result (eval accept-form)) diff --git a/lisp/gnus/nneething.el b/lisp/gnus/nneething.el index 2f05c7e7900..bd5bfba0468 100644 --- a/lisp/gnus/nneething.el +++ b/lisp/gnus/nneething.el @@ -81,8 +81,7 @@ included.") (deffoo nneething-retrieve-headers (articles &optional group server fetch-old) (nneething-possibly-change-directory group) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let* ((number (length articles)) (count 0) @@ -323,8 +322,7 @@ included.") (if (equal '(0 0) (nth 5 atts)) "" (concat "Date: " (current-time-string (nth 5 atts)) "\n")) (or (when buffer - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (when (re-search-forward "<[a-zA-Z0-9_]@[-a-zA-Z0-9_]>" 1000 t) (concat "From: " (match-string 0) "\n")))) (nneething-from-line (nth 2 atts) file)) @@ -332,8 +330,7 @@ included.") (concat "Chars: " (int-to-string (nth 7 atts)) "\n") "") (if buffer - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (concat "Lines: " (int-to-string (count-lines (point-min) (point-max))) "\n")) @@ -382,8 +379,7 @@ included.") (defun nneething-get-head (file) "Either find the head in FILE or make a head for FILE." - (save-excursion - (set-buffer (get-buffer-create nneething-work-buffer)) + (with-current-buffer (get-buffer-create nneething-work-buffer) (setq case-fold-search nil) (buffer-disable-undo) (erase-buffer) diff --git a/lisp/gnus/nnfolder.el b/lisp/gnus/nnfolder.el index 6413e98cc1e..5cebcb0e5fc 100644 --- a/lisp/gnus/nnfolder.el +++ b/lisp/gnus/nnfolder.el @@ -157,8 +157,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (nnoo-define-basics nnfolder) (deffoo nnfolder-retrieve-headers (articles &optional group server fetch-old) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let (article start stop num) (nnfolder-possibly-change-group group server) @@ -261,8 +260,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (deffoo nnfolder-request-article (article &optional group server buffer) (nnfolder-possibly-change-group group server) - (save-excursion - (set-buffer nnfolder-current-buffer) + (with-current-buffer nnfolder-current-buffer (goto-char (point-min)) (when (nnfolder-goto-article article) (let (start stop) @@ -360,8 +358,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") nnfolder-current-group (car inf)))) (when (and nnfolder-current-buffer (buffer-name nnfolder-current-buffer)) - (save-excursion - (set-buffer nnfolder-current-buffer) + (with-current-buffer nnfolder-current-buffer ;; If the buffer was modified, write the file out now. (nnfolder-save-buffer) ;; If we're shutting the server down, we need to kill the @@ -447,8 +444,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") target) (nnmail-activate 'nnfolder) - (save-excursion - (set-buffer nnfolder-current-buffer) + (with-current-buffer nnfolder-current-buffer ;; Since messages are sorted in arrival order and expired in the ;; same order, we can stop as soon as we find a message that is ;; too old. @@ -501,8 +497,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") result) (and (nnfolder-request-article article group server) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (erase-buffer) (insert-buffer-substring nntp-server-buffer) (goto-char (point-min)) @@ -578,8 +573,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (deffoo nnfolder-request-replace-article (article group buffer) (nnfolder-possibly-change-group group) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char (point-min)) (if (not (looking-at "X-From-Line: ")) (insert "From nobody " (current-time-string) "\n") @@ -596,8 +590,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (nnfolder-delete-mail) (insert-buffer-substring buffer) (unless (or gnus-nov-is-evil nnfolder-nov-is-evil) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (let ((headers (nnfolder-parse-head article (point-min) (point-max)))) (with-current-buffer (nnfolder-open-nov group) @@ -630,8 +623,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (deffoo nnfolder-request-rename-group (group new-name &optional server) (nnfolder-possibly-change-group group server) - (save-excursion - (set-buffer nnfolder-current-buffer) + (with-current-buffer nnfolder-current-buffer (and (file-writable-p buffer-file-name) (ignore-errors (let ((new-file (nnfolder-group-pathname new-name))) @@ -671,8 +663,7 @@ the group. Then the marks file will be regenerated properly by Gnus.") (marker (concat "\n" nnfolder-article-marker)) (number "[0-9]+") (activemin (cdr active))) - (save-excursion - (set-buffer nnfolder-current-buffer) + (with-current-buffer nnfolder-current-buffer (goto-char (point-min)) (while (and (search-forward marker nil t) (re-search-forward number nil t)) @@ -1114,8 +1105,7 @@ This command does not work if you use short group names." (defun nnfolder-open-nov (group) (or (cdr (assoc group nnfolder-nov-buffer-alist)) (let ((buffer (get-buffer-create (format " *nnfolder overview %s*" group)))) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (set (make-local-variable 'nnfolder-nov-buffer-file-name) (nnfolder-group-nov-pathname group)) (erase-buffer) @@ -1139,8 +1129,7 @@ This command does not work if you use short group names." (setq nnfolder-nov-buffer-alist (cdr nnfolder-nov-buffer-alist))))) (defun nnfolder-nov-delete-article (group article) - (save-excursion - (set-buffer (nnfolder-open-nov group)) + (with-current-buffer (nnfolder-open-nov group) (when (nnheader-find-nov-line article) (delete-region (point) (progn (forward-line 1) (point)))) t)) @@ -1150,8 +1139,7 @@ This command does not work if you use short group names." nil (let ((nov (nnfolder-group-nov-pathname nnfolder-current-group))) (when (file-exists-p nov) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (nnheader-insert-file-contents nov) (if (and fetch-old @@ -1187,8 +1175,7 @@ This command does not work if you use short group names." (defun nnfolder-add-nov (group article headers) "Add a nov line for the GROUP base." - (save-excursion - (set-buffer (nnfolder-open-nov group)) + (with-current-buffer (nnfolder-open-nov group) (goto-char (point-max)) (mail-header-set-number headers article) (nnheader-insert-nov headers))) diff --git a/lisp/gnus/nnheader.el b/lisp/gnus/nnheader.el index 9a90a76f7af..1bfdbeab9c4 100644 --- a/lisp/gnus/nnheader.el +++ b/lisp/gnus/nnheader.el @@ -835,8 +835,7 @@ The first string in ARGS can be a format string." "Clear the communication buffer and insert FORMAT and ARGS into the buffer. If FORMAT isn't a format string, it and all ARGS will be inserted without formatting." - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (if (string-match "%" format) (insert (apply 'format format args)) diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el index d412af46d0c..e7bf0f376a8 100644 --- a/lisp/gnus/nnimap.el +++ b/lisp/gnus/nnimap.el @@ -1,11 +1,9 @@ -;;; nnimap.el --- imap backend for Gnus +;;; nnimap.el --- IMAP interface for Gnus -;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, -;; 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +;; Copyright (C) 2010 Free Software Foundation, Inc. -;; Author: Simon Josefsson <simon@josefsson.org> -;; Jim Radford <radford@robby.caltech.edu> -;; Keywords: mail +;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> +;; Simon Josefsson <simon@josefsson.org> ;; This file is part of GNU Emacs. @@ -24,1791 +22,942 @@ ;;; Commentary: -;; Todo, major things: -;; -;; o Fix Gnus to view correct number of unread/total articles in group buffer -;; o Fix Gnus to handle leading '.' in group names (fixed?) -;; o Finish disconnected mode (moving articles between mailboxes unplugged) -;; o Sieve -;; o MIME (partial article fetches) -;; o Split to other backends, different split rules for different -;; servers/inboxes -;; -;; Todo, minor things: -;; -;; o Don't require half of Gnus -- backends should be standalone -;; o Verify that we don't use IMAP4rev1 specific things (RFC2060 App B) -;; o Dont uid fetch 1,* in nnimap-retrive-groups (slow) -;; o Split up big fetches (1,* header especially) in smaller chunks -;; o What do I do with gnus-newsgroup-*? -;; o Tell Gnus about new groups (how can we tell?) -;; o Respooling (fix Gnus?) (unnecessary?) -;; o Add support for the following: (if applicable) -;; request-list-newsgroups, request-regenerate -;; list-active-group, -;; request-associate-buffer, request-restore-buffer, -;; o Do The Right Thing when UIDVALIDITY changes (what's the right thing?) -;; o Support RFC2221 (Login referrals) -;; o IMAP2BIS compatibility? (RFC2061) -;; o ACAP stuff (perhaps a different project, would be nice to ACAPify -;; .newsrc.eld) -;; o What about Gnus's article editing, can we support it? NO! -;; o Use \Draft to support the draft group?? -;; o Duplicate suppression -;; o Rewrite UID SEARCH UID X as UID FETCH X (UID) for those with slow servers +;; nnimap interfaces Gnus with IMAP servers. ;;; Code: -;; For Emacs < 22.2. (eval-and-compile - (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) + (require 'nnheader)) -(require 'imap) -(require 'nnoo) -(require 'nnmail) -(require 'nnheader) -(require 'mm-util) -(require 'gnus) -(require 'gnus-range) -(require 'gnus-start) -(require 'gnus-int) +(eval-when-compile + (require 'cl)) -(eval-when-compile (require 'cl)) - -(autoload 'auth-source-user-or-password "auth-source") +(require 'netrc) (nnoo-declare nnimap) -(defconst nnimap-version "nnimap 1.0") - -(defgroup nnimap nil - "Reading IMAP mail with Gnus." - :group 'gnus) - (defvoo nnimap-address nil - "Address of physical IMAP server. If nil, use the virtual server's name.") + "The address of the IMAP server.") (defvoo nnimap-server-port nil - "Port number on physical IMAP server. -If nil, defaults to 993 for TLS/SSL connections and 143 otherwise.") - -;; Splitting variables - -(defcustom nnimap-split-crosspost t - "If non-nil, do crossposting if several split methods match the mail. -If nil, the first match found will be used." - :group 'nnimap - :type 'boolean) - -(defcustom nnimap-split-inbox nil - "Name of mailbox to split mail from. - -Mail is read from this mailbox and split according to rules in -`nnimap-split-rule'. - -This can be a string or a list of strings." - :group 'nnimap - :type '(choice (string) - (repeat string))) - -(define-widget 'nnimap-strict-function 'function - "This widget only matches values that are functionp. - -Warning: This means that a value that is the symbol of a not yet -loaded function will not match. Use with care." - :match 'nnimap-strict-function-match) - -(defun nnimap-strict-function-match (widget value) - "Ignoring WIDGET, match if VALUE is a function." - (functionp value)) - -(defcustom nnimap-split-rule nil - "Mail will be split according to these rules. - -Mail is read from mailbox(es) specified in `nnimap-split-inbox'. - -If you'd like, for instance, one mail group for mail from the -\"gnus-imap\" mailing list, one group for junk mail and leave -everything else in the incoming mailbox, you could do something like -this: - -\(setq nnimap-split-rule '((\"INBOX.gnus-imap\" \"From:.*gnus-imap\") - (\"INBOX.junk\" \"Subject:.*buy\"))) - -As you can see, `nnimap-split-rule' is a list of lists, where the -first element in each \"rule\" is the name of the IMAP mailbox (or the -symbol `junk' if you want to remove the mail), and the second is a -regexp that nnimap will try to match on the header to find a fit. - -The second element can also be a function. In that case, it will be -called narrowed to the headers with the first element of the rule as -the argument. It should return a non-nil value if it thinks that the -mail belongs in that group. - -This variable can also have a function as its value, the function will -be called with the headers narrowed and should return a group where it -thinks the article should be splitted to. See `nnimap-split-fancy'. - -To allow for different split rules on different virtual servers, and -even different split rules in different inboxes on the same server, -the syntax of this variable have been extended along the lines of: - -\(setq nnimap-split-rule - '((\"my1server\" (\".*\" ((\"ding\" \"ding@gnus.org\") - (\"junk\" \"From:.*Simon\"))) - (\"my2server\" (\"INBOX\" nnimap-split-fancy)) - (\"my[34]server\" (\".*\" ((\"private\" \"To:.*Simon\") - (\"junk\" my-junk-func))))) - -The virtual server name is in fact a regexp, so that the same rules -may apply to several servers. In the example, the servers -\"my3server\" and \"my4server\" both use the same rules. Similarly, -the inbox string is also a regexp. The actual splitting rules are as -before, either a function, or a list with group/regexp or -group/function elements." - :group 'nnimap - ;; FIXME: Doesn't allow `("my2server" ("INBOX" nnimap-split-fancy))' - ;; per example above. -- fx - :type '(choice :tag "Rule type" - (repeat :menu-tag "Single-server" - :tag "Single-server list" - (list (string :tag "Mailbox") - (choice :tag "Predicate" - (regexp :tag "A regexp") - (nnimap-strict-function :tag "A function")))) - (choice :menu-tag "A function" - :tag "A function" - (function-item nnimap-split-fancy) - (function-item nnmail-split-fancy) - (nnimap-strict-function :tag "User-defined function")) - (repeat :menu-tag "Multi-server (extended)" - :tag "Multi-server list" - (list (regexp :tag "Server regexp") - (list (regexp :tag "Incoming Mailbox regexp") - (repeat :tag "Rules for matching server(s) and mailbox(es)" - (list (string :tag "Destination mailbox") - (choice :tag "Predicate" - (regexp :tag "A Regexp") - (nnimap-strict-function :tag "A Function"))))))))) - -(defcustom nnimap-split-predicate "UNSEEN UNDELETED" - "The predicate used to find articles to split. -If you use another IMAP client to peek on articles but always would -like nnimap to split them once it's started, you could change this to -\"UNDELETED\". Other available predicates are available in -RFC2060 section 6.4.4." - :group 'nnimap - :type 'string) - -(defcustom nnimap-split-fancy nil - "Like the variable `nnmail-split-fancy'." - :group 'nnimap - :type 'sexp) + "The IMAP port used. +If nnimap-stream is `ssl', this will default to `imaps'. If not, +it will default to `imap'.") + +(defvoo nnimap-stream 'ssl + "How nnimap will talk to the IMAP server. +Values are `ssl' and `network'.") + +(defvoo nnimap-shell-program (if (boundp 'imap-shell-program) + (if (listp imap-shell-program) + (car imap-shell-program) + imap-shell-program) + "ssh %s imapd")) + +(defvoo nnimap-inbox nil + "The mail box where incoming mail arrives and should be split out of.") + +(defvoo nnimap-expunge-inbox nil + "If non-nil, expunge the inbox after fetching mail. +This is always done if the server supports UID EXPUNGE, but it's +not done by default on servers that doesn't support that command.") + +(defvoo nnimap-connection-alist nil) +(defvar nnimap-process nil) + +(defvar nnimap-status-string "") (defvar nnimap-split-download-body-default nil "Internal variable with default value for `nnimap-split-download-body'.") -(defcustom nnimap-split-download-body 'default - "Whether to download entire articles during splitting. -This is generally not required, and will slow things down considerably. -You may need it if you want to use an advanced splitting function that -analyzes the body before splitting the article. -If this variable is nil, bodies will not be downloaded; if this -variable is the symbol `default' the default behavior is -used (which currently is nil, unless you use a statistical -spam.el test); if this variable is another non-nil value bodies -will be downloaded." - :version "22.1" - :group 'nnimap - :type '(choice (const :tag "Let system decide" deault) - boolean)) - -;; Performance / bug workaround variables - -(defcustom nnimap-close-asynchronous t - "Close mailboxes asynchronously in `nnimap-close-group'. -This means that errors caught by nnimap when closing the mailbox will -not prevent Gnus from updating the group status, which may be harmful. -However, it increases speed." - :version "22.1" - :type 'boolean - :group 'nnimap) - -(defcustom nnimap-dont-close t - "Never close mailboxes. -This increases the speed of closing mailboxes (quiting group) but may -decrease the speed of selecting another mailbox later. Re-selecting -the same mailbox will be faster though." - :version "22.1" - :type 'boolean - :group 'nnimap) - -(defcustom nnimap-retrieve-groups-asynchronous t - "Send asynchronous STATUS commands for each mailbox before checking mail. -If you have mailboxes that rarely receives mail, this speeds up new -mail checking. It works by first sending STATUS commands for each -mailbox, and then only checking groups which has a modified UIDNEXT -more carefully for new mail. - -In summary, the default is O((1-p)*k+p*n) and changing it to nil makes -it O(n). If p is small, then the default is probably faster." - :version "22.1" - :type 'boolean - :group 'nnimap) - -(defvoo nnimap-need-unselect-to-notice-new-mail t - "Unselect mailboxes before looking for new mail in them. -Some servers seem to need this under some circumstances.") - -(defvoo nnimap-logout-timeout nil - "Close server immediately if it can't logout in this number of seconds. -If it is nil, never close server until logout completes. This variable -overrides `imap-logout-timeout' on a per-server basis.") - -;; Authorization / Privacy variables - -(defvoo nnimap-auth-method nil - "Obsolete.") - -(defvoo nnimap-stream nil - "How nnimap will connect to the server. - -The default, nil, will try to use the \"best\" method the server can -handle. - -Change this if - -1) you want to connect with TLS/SSL. The TLS/SSL integration - with IMAP is suboptimal so you'll have to tell it - specifically. - -2) your server is more capable than your environment -- i.e. your - server accept Kerberos login's but you haven't installed the - `imtest' program or your machine isn't configured for Kerberos. - -Possible choices: gssapi, kerberos4, starttls, tls, ssl, network, shell. -See also `imap-streams' and `imap-stream-alist'.") - -(defvoo nnimap-authenticator nil - "How nnimap authenticate itself to the server. - -The default, nil, will try to use the \"best\" method the server can -handle. - -There is only one reason for fiddling with this variable, and that is -if your server is more capable than your environment -- i.e. you -connect to a server that accept Kerberos login's but you haven't -installed the `imtest' program or your machine isn't configured for -Kerberos. - -Possible choices: gssapi, kerberos4, digest-md5, cram-md5, login, anonymous. -See also `imap-authenticators' and `imap-authenticator-alist'") - -(defvoo nnimap-directory (nnheader-concat gnus-directory "overview/") - "Directory to keep NOV cache files for nnimap groups. -See also `nnimap-nov-file-name'.") - -(defvoo nnimap-nov-file-name "nnimap." - "NOV cache base filename. -The group name and `nnimap-nov-file-name-suffix' will be appended. A -typical complete file name would be -~/News/overview/nnimap.pdc.INBOX.ding.nov, or -~/News/overview/nnimap/pdc/INBOX/ding/nov if -`nnmail-use-long-file-names' is nil") - -(defvoo nnimap-nov-file-name-suffix ".novcache" - "Suffix for NOV cache base filename.") - -(defvoo nnimap-nov-is-evil gnus-agent - "If non-nil, never generate or use a local nov database for this backend. -Using nov databases should speed up header fetching considerably. -However, it will invoke a UID SEARCH UID command on the server, and -some servers implement this command inefficiently by opening each and -every message in the group, thus making it quite slow. -Unlike other backends, you do not need to take special care if you -flip this variable.") - -(defvoo nnimap-search-uids-not-since-is-evil nil - "If non-nil, avoid \"UID SEARCH UID ... NOT SINCE\" queries when expiring. -Instead, use \"UID SEARCH SINCE\" to prune the list of expirable -articles within Gnus. This seems to be faster on Courier in some cases.") - -(defvoo nnimap-expunge-on-close 'always ; 'ask, 'never - "Whether to expunge a group when it is closed. -When a IMAP group with articles marked for deletion is closed, this -variable determine if nnimap should actually remove the articles or -not. - -If always, nnimap always perform a expunge when closing the group. -If never, nnimap never expunges articles marked for deletion. -If ask, nnimap will ask you if you wish to expunge marked articles. - -When setting this variable to `never', you can only expunge articles -by using `G x' (gnus-group-nnimap-expunge) from the Group buffer.") - -(defvoo nnimap-list-pattern "*" - "A string LIMIT or list of strings with mailbox wildcards used to limit available groups. -See below for available wildcards. - -The LIMIT string can be a cons cell (REFERENCE . LIMIT), where -REFERENCE will be passed as the first parameter to LIST/LSUB. The -semantics of this are server specific, on the University of Washington -server you can specify a directory. - -Example: - '(\"INBOX\" \"mail/*\" (\"~friend/mail/\" . \"list/*\")) - -There are two wildcards * and %. * matches everything, % matches -everything in the current hierarchy.") - -(defvoo nnimap-news-groups nil - "IMAP support a news-like mode, also known as bulletin board mode, -where replies is sent via IMAP instead of SMTP. - -This variable should contain a regexp matching groups where you wish -replies to be stored to the mailbox directly. - -Example: - '(\"^[^I][^N][^B][^O][^X].*$\") - -This will match all groups not beginning with \"INBOX\". - -Note that there is nothing technically different between mail-like and -news-like mailboxes. If you wish to have a group with todo items or -similar which you wouldn't want to set up a mailing list for, you can -use this to make replies go directly to the group.") - -(defvoo nnimap-expunge-search-string "UID %s NOT SINCE %s" - "IMAP search command to use for articles that are to be expired. -The first %s is replaced by a UID set of articles to search on, -and the second %s is replaced by a date criterium. - -One useful (and perhaps the only useful) value to change this to would -be `UID %s NOT SENTSINCE %s' to make nnimap use the Date: header -instead of the internal date of messages. See section 6.4.4 of RFC -2060 for more information on valid strings. - -However, if `nnimap-search-uids-not-since-is-evil' is true, this -variable has no effect since the search logic is reversed.") - -(defvoo nnimap-importantize-dormant t - "If non-nil, mark \"dormant\" articles as \"ticked\" for other IMAP clients. -Note that within Gnus, dormant articles will still (only) be -marked as ticked. This is to make \"dormant\" articles stand out, -just like \"ticked\" articles, in other IMAP clients.") - -(defvoo nnimap-server-address nil - "Obsolete. Use `nnimap-address'.") - -(defcustom nnimap-authinfo-file "~/.authinfo" - "Authorization information for IMAP servers. In .netrc format." - :type - '(choice file - (repeat :tag "Entries" - :menu-tag "Inline" - (list :format "%v" - :value ("" ("login" . "") ("password" . "")) - (string :tag "Host") - (checklist :inline t - (cons :format "%v" - (const :format "" "login") - (string :format "Login: %v")) - (cons :format "%v" - (const :format "" "password") - (string :format "Password: %v")))))) - :group 'nnimap) - -(defcustom nnimap-prune-cache t - "If non-nil, nnimap check whether articles still exist on server before using data stored in NOV cache." - :type 'boolean - :group 'nnimap) - -(defvar nnimap-request-list-method 'imap-mailbox-list - "Method to use to request a list of all folders from the server. -If this is 'imap-mailbox-lsub, then use a server-side subscription list to -restrict visible folders.") - -(defcustom nnimap-id nil - "Plist with client identity to send to server upon login. -A nil value means no information is sent, symbol `no' to disable ID query -altogether, or plist with identifier-value pairs to send to -server. RFC 2971 describes the list as follows: - - Any string may be sent as a field, but the following are defined to - describe certain values that might be sent. Implementations are free - to send none, any, or all of these. Strings are not case-sensitive. - Field strings MUST NOT be longer than 30 octets. Value strings MUST - NOT be longer than 1024 octets. Implementations MUST NOT send more - than 30 field-value pairs. - - name Name of the program - version Version number of the program - os Name of the operating system - os-version Version of the operating system - vendor Vendor of the client/server - support-url URL to contact for support - address Postal address of contact/vendor - date Date program was released, specified as a date-time - in IMAP4rev1 - command Command used to start the program - arguments Arguments supplied on the command line, if any - if any - environment Description of environment, i.e., UNIX environment - variables or Windows registry settings - - Implementations MUST NOT send the same field name more than once. - -An example plist would be '(\"name\" \"Gnus\" \"version\" gnus-version-number -\"os\" system-configuration \"vendor\" \"GNU\")." - :group 'nnimap - :type '(choice (const :tag "No information" nil) - (const :tag "Disable ID query" no) - (plist :key-type string :value-type string))) - -(defcustom nnimap-debug nil - "If non-nil, trace nnimap- functions into `nnimap-debug-buffer'. -Uses `trace-function-background', so you can turn it off with, -say, `untrace-all'. - -Note that username, passwords and other privacy sensitive -information (such as e-mail) may be stored in the buffer. -It is not written to disk, however. Do not enable this -variable unless you are comfortable with that. - -This variable only takes effect when loading the `nnimap' library. -See also `nnimap-log'." - :group 'nnimap - :type 'boolean) - -;; Internal variables: - -(defvar nnimap-debug-buffer "*nnimap-debug*") -(defvar nnimap-mailbox-info (gnus-make-hashtable 997)) -(defvar nnimap-current-move-server nil) -(defvar nnimap-current-move-group nil) -(defvar nnimap-current-move-article nil) -(defvar nnimap-length) -(defvar nnimap-progress-chars '(?| ?/ ?- ?\\)) -(defvar nnimap-progress-how-often 20) -(defvar nnimap-counter) -(defvar nnimap-server-buffer-alist nil) ;; Map server name to buffers. -(defvar nnimap-current-server nil) ;; Current server -(defvar nnimap-server-buffer nil) ;; Current servers' buffer - - - -(nnoo-define-basics nnimap) - -;; Utility functions: - -(defsubst nnimap-decode-group-name (group) - (and group - (gnus-group-decoded-name group))) - -(defsubst nnimap-encode-group-name (group) - (and group - (mm-encode-coding-string group (gnus-group-name-charset nil group)))) - -(defun nnimap-group-prefixed-name (group &optional server) - (gnus-group-prefixed-name group - (gnus-server-to-method - (format "nnimap:%s" - (or server nnimap-current-server))))) - -(defsubst nnimap-get-server-buffer (server) - "Return buffer for SERVER, if nil use current server." - (cadr (assoc (or server nnimap-current-server) nnimap-server-buffer-alist))) - -(defun nnimap-remove-server-from-buffer-alist (server list) - "Remove SERVER from LIST." - (let (l) - (dolist (e list) - (unless (equal server (car-safe e)) - (push e l))) - l)) - -(defun nnimap-possibly-change-server (server) - "Return buffer for SERVER, changing the current server as a side-effect. -If SERVER is nil, uses the current server." - (setq nnimap-current-server (or server nnimap-current-server) - nnimap-server-buffer (nnimap-get-server-buffer nnimap-current-server))) - -(defun nnimap-verify-uidvalidity (group server) - "Verify stored uidvalidity match current one in GROUP on SERVER." - (let* ((gnusgroup (nnimap-group-prefixed-name group server)) - (new-uidvalidity (imap-mailbox-get 'uidvalidity)) - (old-uidvalidity (gnus-group-get-parameter gnusgroup 'uidvalidity)) - (dir (file-name-as-directory (expand-file-name nnimap-directory))) - (nameuid (nnheader-translate-file-chars - (concat nnimap-nov-file-name - (if (equal server "") - "unnamed" - server) "." group "." old-uidvalidity - nnimap-nov-file-name-suffix) t)) - (file (if (or nnmail-use-long-file-names - (file-exists-p (expand-file-name nameuid dir))) - (expand-file-name nameuid dir) - (expand-file-name - (mm-encode-coding-string - (nnheader-replace-chars-in-string nameuid ?. ?/) - nnmail-pathname-coding-system) - dir)))) - (if old-uidvalidity - (if (not (equal old-uidvalidity new-uidvalidity)) - ;; uidvalidity clash - (progn - (gnus-group-set-parameter gnusgroup 'uidvalidity new-uidvalidity) - (gnus-group-remove-parameter gnusgroup 'imap-status) - (gnus-sethash (gnus-group-prefixed-name group server) - nil nnimap-mailbox-info) - (gnus-delete-file file)) - t) - (gnus-group-add-parameter gnusgroup (cons 'uidvalidity new-uidvalidity)) - (gnus-group-remove-parameter gnusgroup 'imap-status) - (gnus-sethash ; Maybe not necessary here. - (gnus-group-prefixed-name group server) - nil nnimap-mailbox-info) - t))) +(defstruct nnimap + group process commands capabilities) -(defun nnimap-before-find-minmax-bugworkaround () - "Function called before iterating through mailboxes with -`nnimap-find-minmax-uid'." - (when nnimap-need-unselect-to-notice-new-mail - ;; XXX this is for UoW imapd problem, it doesn't notice new mail in - ;; currently selected mailbox without a re-select/examine. - (or (null (imap-current-mailbox nnimap-server-buffer)) - (imap-mailbox-unselect nnimap-server-buffer)))) - -(defun nnimap-find-minmax-uid (group &optional examine) - "Find lowest and highest active article number in GROUP. -If EXAMINE is non-nil the group is selected read-only." - (with-current-buffer nnimap-server-buffer - (let ((decoded-group (nnimap-decode-group-name group))) - (when (or (string= decoded-group (imap-current-mailbox)) - (imap-mailbox-select decoded-group examine)) - (let (minuid maxuid) - (when (> (imap-mailbox-get 'exists) 0) - (imap-fetch "1:*" "UID" nil 'nouidfetch) - (imap-message-map - (lambda (uid Uid) - (setq minuid (if minuid (min minuid uid) uid) - maxuid (if maxuid (max maxuid uid) uid))) - 'UID)) - (list (imap-mailbox-get 'exists) minuid maxuid)))))) - -(defun nnimap-possibly-change-group (group &optional server) - "Make GROUP the current group, and SERVER the current server." - (when (nnimap-possibly-change-server server) - (let ((decoded-group (nnimap-decode-group-name group))) - (with-current-buffer nnimap-server-buffer - (if (or (null group) (imap-current-mailbox-p decoded-group)) - imap-current-mailbox ; Note: utf-7 encoded. - (if (imap-mailbox-select decoded-group) - (if (or (nnimap-verify-uidvalidity - group (or server nnimap-current-server)) - (zerop (imap-mailbox-get 'exists decoded-group)) - t ;; for OGnus to see if ignoring uidvalidity - ;; changes has any bad effects. - (yes-or-no-p - (format - "nnimap: Group %s is not uidvalid. Continue? " - decoded-group))) - imap-current-mailbox ; Note: utf-7 encoded. - (imap-mailbox-unselect) - (error "nnimap: Group %s is not uid-valid" decoded-group)) - (nnheader-report 'nnimap (imap-error-text)))))))) - -(defun nnimap-replace-whitespace (string) - "Return STRING with all whitespace replaced with space." - (when string - (while (string-match "[\r\n\t]+" string) - (setq string (replace-match " " t t string))) - string)) - -;; Required backend functions - -(defun nnimap-retrieve-headers-progress () - "Hook to insert NOV line for current article into `nntp-server-buffer'." - (and (numberp nnmail-large-newsgroup) - (zerop (% (incf nnimap-counter) nnimap-progress-how-often)) - (> nnimap-length nnmail-large-newsgroup) - (nnheader-message 6 "nnimap: Retrieving headers... %c" - (nth (/ (% nnimap-counter - (* (length nnimap-progress-chars) - nnimap-progress-how-often)) - nnimap-progress-how-often) - nnimap-progress-chars))) - (with-current-buffer nntp-server-buffer - (let (headers lines chars uid mbx) - (with-current-buffer nnimap-server-buffer - (setq uid imap-current-message - mbx (nnimap-encode-group-name (imap-current-mailbox)) - headers (if (imap-capability 'IMAP4rev1) - ;; xxx don't just use car? alist doesn't contain - ;; anything else now, but it might... - (nth 2 (car (imap-message-get uid 'BODYDETAIL))) - (imap-message-get uid 'RFC822.HEADER)) - lines (imap-body-lines (imap-message-body imap-current-message)) - chars (imap-message-get imap-current-message 'RFC822.SIZE))) - (nnheader-insert-nov - ;; At this stage, we only have bytes, so let's use unibyte buffers - ;; to make it more clear. - (mm-with-unibyte-buffer - (buffer-disable-undo) - ;; headers can be nil if article is write-only - (when headers (insert headers)) - (let ((head (nnheader-parse-naked-head uid))) - (mail-header-set-number head uid) - (mail-header-set-chars head chars) - (mail-header-set-lines head lines) - (mail-header-set-xref - head (format "%s %s:%d" (system-name) mbx uid)) - head)))))) - -(defun nnimap-retrieve-which-headers (articles fetch-old) - "Get a range of articles to fetch based on ARTICLES and FETCH-OLD." - (with-current-buffer nnimap-server-buffer - (if (numberp (car-safe articles)) - (imap-search - (concat "UID " - (imap-range-to-message-set - (gnus-compress-sequence - (append (gnus-uncompress-sequence - (and fetch-old - (cons (if (numberp fetch-old) - (max 1 (- (car articles) fetch-old)) - 1) - (1- (car articles))))) - articles))))) - (mapcar (lambda (msgid) - (imap-search - (format "HEADER Message-Id \"%s\"" msgid))) - articles)))) - -(defun nnimap-group-overview-filename (group server) - "Make file name for GROUP on SERVER." - (let* ((dir (file-name-as-directory (expand-file-name nnimap-directory))) - (uidvalidity (gnus-group-get-parameter - (nnimap-group-prefixed-name group server) - 'uidvalidity)) - (name (nnheader-translate-file-chars - (concat nnimap-nov-file-name - (if (equal server "") - "unnamed" - server) "." group nnimap-nov-file-name-suffix) t)) - (nameuid (nnheader-translate-file-chars - (concat nnimap-nov-file-name - (if (equal server "") - "unnamed" - server) "." group "." uidvalidity - nnimap-nov-file-name-suffix) t)) - (oldfile (if (or nnmail-use-long-file-names - (file-exists-p (expand-file-name name dir))) - (expand-file-name name dir) - (expand-file-name - (mm-encode-coding-string - (nnheader-replace-chars-in-string name ?. ?/) - nnmail-pathname-coding-system) - dir))) - (newfile (if (or nnmail-use-long-file-names - (file-exists-p (expand-file-name nameuid dir))) - (expand-file-name nameuid dir) - (expand-file-name - (mm-encode-coding-string - (nnheader-replace-chars-in-string nameuid ?. ?/) - nnmail-pathname-coding-system) - dir)))) - (when (and (file-exists-p oldfile) (not (file-exists-p newfile))) - (message "nnimap: Upgrading novcache filename...") - (sit-for 1) - (gnus-make-directory (file-name-directory newfile)) - (unless (ignore-errors (rename-file oldfile newfile) t) - (if (ignore-errors (copy-file oldfile newfile) t) - (delete-file oldfile) - (error "Can't rename `%s' to `%s'" oldfile newfile)))) - newfile)) - -(defun nnimap-retrieve-headers-from-file (group server) +(defvar nnimap-object nil) + +(defvar nnimap-mark-alist + '((read "\\Seen") + (tick "\\Flagged") + (reply "\\Answered") + (expire "gnus-expire") + (dormant "gnus-dormant") + (score "gnus-score") + (save "gnus-save") + (download "gnus-download") + (forward "gnus-forward"))) + +(defvar nnimap-split-methods nil) + +(defun nnimap-buffer () + (nnimap-find-process-buffer nntp-server-buffer)) + +(defun nnimap-retrieve-headers (articles &optional group server fetch-old) (with-current-buffer nntp-server-buffer - (let ((nov (nnimap-group-overview-filename group server))) - (when (file-exists-p nov) - (mm-insert-file-contents nov) - (set-buffer-modified-p nil) - (let ((min (ignore-errors (goto-char (point-min)) - (read (current-buffer)))) - (max (ignore-errors (goto-char (point-max)) - (forward-line -1) - (read (current-buffer))))) - (if (and (numberp min) (numberp max)) - (cons min max) - ;; junk, remove it, it's saved later - (erase-buffer) - nil)))))) - -(defun nnimap-retrieve-headers-from-server (articles group server) - (with-current-buffer nnimap-server-buffer - (let ((imap-fetch-data-hook '(nnimap-retrieve-headers-progress)) - (nnimap-length (gnus-range-length articles)) - (nnimap-counter 0)) - (imap-fetch (imap-range-to-message-set articles) - (concat "(UID RFC822.SIZE BODY " - (let ((headers - (append '(Subject From Date Message-Id - References In-Reply-To Xref) - (copy-sequence - nnmail-extra-headers)))) - (if (imap-capability 'IMAP4rev1) - (format "BODY.PEEK[HEADER.FIELDS %s])" headers) - (format "RFC822.HEADER.LINES %s)" headers))))) - (with-current-buffer nntp-server-buffer - (sort-numeric-fields 1 (point-min) (point-max))) - (and (numberp nnmail-large-newsgroup) - (> nnimap-length nnmail-large-newsgroup) - (nnheader-message 6 "nnimap: Retrieving headers...done"))))) - -(defun nnimap-dont-use-nov-p (group server) - (or gnus-nov-is-evil nnimap-nov-is-evil - (unless (and (gnus-make-directory - (file-name-directory - (nnimap-group-overview-filename group server))) - (file-writable-p - (nnimap-group-overview-filename group server))) - (message "nnimap: Nov cache not writable, %s" - (nnimap-group-overview-filename group server))))) - -(deffoo nnimap-retrieve-headers (articles &optional group server fetch-old) - (when (nnimap-possibly-change-group group server) - (with-current-buffer nntp-server-buffer - (erase-buffer) - (if (nnimap-dont-use-nov-p group server) - (nnimap-retrieve-headers-from-server - (gnus-compress-sequence articles) group server) - (let (uids cached low high) - (when (setq uids (nnimap-retrieve-which-headers articles fetch-old) - low (car uids) - high (car (last uids))) - (if (setq cached (nnimap-retrieve-headers-from-file group server)) - (progn - ;; fetch articles with uids before cache block - (when (< low (car cached)) - (goto-char (point-min)) - (nnimap-retrieve-headers-from-server - (cons low (1- (car cached))) group server)) - ;; fetch articles with uids after cache block - (when (> high (cdr cached)) - (goto-char (point-max)) - (nnimap-retrieve-headers-from-server - (cons (1+ (cdr cached)) high) group server)) - (when nnimap-prune-cache - ;; remove nov's for articles which has expired on server - (goto-char (point-min)) - (dolist (uid (gnus-set-difference articles uids)) - (when (re-search-forward (format "^%d\t" uid) nil t) - (gnus-delete-line))))) - ;; nothing cached, fetch whole range from server - (nnimap-retrieve-headers-from-server - (cons low high) group server)) - (when (buffer-modified-p) - (nnmail-write-region - (point-min) (point-max) - (nnimap-group-overview-filename group server) nil 'nomesg)) - (nnheader-nov-delete-outside-range low high)))) - 'nov))) - -(declare-function netrc-parse "netrc" (file)) -(declare-function netrc-machine-user-or-password "netrc" - (mode authinfo-file-or-list machines ports defaults)) - -(defun nnimap-open-connection (server) - ;; Note: `nnimap-open-server' that calls this function binds - ;; `imap-logout-timeout' to `nnimap-logout-timeout'. - (if (not (imap-open nnimap-address nnimap-server-port nnimap-stream - nnimap-authenticator nnimap-server-buffer)) - (nnheader-report 'nnimap "Can't open connection to server %s" server) - (require 'netrc) - (unless (or (imap-capability 'IMAP4 nnimap-server-buffer) - (imap-capability 'IMAP4rev1 nnimap-server-buffer)) - (imap-close nnimap-server-buffer) - (nnheader-report 'nnimap "Server %s is not IMAP4 compliant" server)) - (let* ((list (progn (gnus-message 7 "Parsing authinfo file `%s'." - nnimap-authinfo-file) - (netrc-parse nnimap-authinfo-file))) - (port (if nnimap-server-port - (int-to-string nnimap-server-port) - "imap")) - (auth-info - (auth-source-user-or-password '("login" "password") server port)) - (auth-user (nth 0 auth-info)) - (auth-passwd (nth 1 auth-info)) - (user (or - auth-user ; this is preferred to netrc-* - (netrc-machine-user-or-password - "login" - list - (list server - (or nnimap-server-address - nnimap-address)) - (list port) - (list "imap" "imaps" "143" "993")))) - (passwd (or - auth-passwd ; this is preferred to netrc-* - (netrc-machine-user-or-password - "password" - list - (list server - (or nnimap-server-address - nnimap-address)) - (list port) - (list "imap" "imaps" "143" "993"))))) - (if (imap-authenticate user passwd nnimap-server-buffer) - (prog2 - (setq nnimap-server-buffer-alist - (nnimap-remove-server-from-buffer-alist - server - nnimap-server-buffer-alist)) - (push (list server nnimap-server-buffer) - nnimap-server-buffer-alist) - (imap-id nnimap-id nnimap-server-buffer) - (nnimap-possibly-change-server server)) - (imap-close nnimap-server-buffer) - (kill-buffer nnimap-server-buffer) - (nnheader-report 'nnimap "Could not authenticate to %s" server))))) - -(deffoo nnimap-open-server (server &optional defs) - (nnheader-init-server-buffer) + (erase-buffer) + (when (nnimap-possibly-change-group group server) + (with-current-buffer (nnimap-buffer) + (nnimap-send-command "SELECT %S" (utf7-encode group t)) + (erase-buffer) + (nnimap-wait-for-response + (nnimap-send-command + "UID FETCH %s %s" + (nnimap-article-ranges (gnus-compress-sequence articles)) + (format "(UID RFC822.SIZE BODYSTRUCTURE %s)" + (format + (if (member "IMAP4REV1" + (nnimap-capabilities nnimap-object)) + "BODY.PEEK[HEADER.FIELDS %s]" + "RFC822.HEADER.LINES %s") + (append '(Subject From Date Message-Id + References In-Reply-To Xref) + nnmail-extra-headers)))) + t) + (nnimap-transform-headers)) + (insert-buffer-substring + (nnimap-find-process-buffer (current-buffer)))) + t)) + +(defun nnimap-transform-headers () + (goto-char (point-min)) + (let (article bytes lines) + (block nil + (while (not (eobp)) + (while (not (looking-at "^\\* [0-9]+ FETCH.*UID \\([0-9]+\\)")) + (delete-region (point) (progn (forward-line 1) (point))) + (when (eobp) + (return))) + (setq article (match-string 1) + bytes (nnimap-get-length) + lines nil) + (beginning-of-line) + (when (search-forward "BODYSTRUCTURE" (line-end-position) t) + (let ((structure (ignore-errors (read (current-buffer))))) + (while (and (consp structure) + (not (stringp (car structure)))) + (setq structure (car structure))) + (setq lines (nth 7 structure)))) + (delete-region (line-beginning-position) (line-end-position)) + (insert (format "211 %s Article retrieved." article)) + (forward-line 1) + (insert (format "Bytes: %d\n" bytes)) + (when lines + (insert (format "Lines: %s\n" lines))) + (re-search-forward "^\r$") + (delete-region (line-beginning-position) (line-end-position)) + (insert ".") + (forward-line 1))))) + +(defun nnimap-get-length () + (and (re-search-forward "{\\([0-9]+\\)}" (line-end-position) t) + (string-to-number (match-string 1)))) + +(defun nnimap-article-ranges (ranges) + (let (result) + (cond + ((numberp ranges) + (number-to-string ranges)) + ((numberp (cdr ranges)) + (format "%d:%d" (car ranges) (cdr ranges))) + (t + (dolist (elem ranges) + (push + (if (consp elem) + (format "%d:%d" (car elem) (cdr elem)) + (number-to-string elem)) + result)) + (mapconcat #'identity (nreverse result) ","))))) + +(defun nnimap-open-server (server &optional defs) (if (nnimap-server-opened server) t - (unless (assq 'nnimap-server-buffer defs) - (push (list 'nnimap-server-buffer (concat " *nnimap* " server)) defs)) - ;; translate `nnimap-server-address' to `nnimap-address' in defs - ;; for people that configured nnimap with a very old version (unless (assq 'nnimap-address defs) - (if (assq 'nnimap-server-address defs) - (push (list 'nnimap-address - (cadr (assq 'nnimap-server-address defs))) defs) - (push (list 'nnimap-address server) defs))) + (setq defs (append defs (list (list 'nnimap-address server))))) (nnoo-change-server 'nnimap server defs) - (or nnimap-server-buffer - (setq nnimap-server-buffer (cadr (assq 'nnimap-server-buffer defs)))) - (with-current-buffer (get-buffer-create nnimap-server-buffer) - (nnoo-change-server 'nnimap server defs)) - (let ((imap-logout-timeout nnimap-logout-timeout)) - (or (and nnimap-server-buffer - (imap-opened nnimap-server-buffer) - (if (with-current-buffer nnimap-server-buffer - (memq imap-state '(auth selected examine))) - t - (imap-close nnimap-server-buffer) - (nnimap-open-connection server))) - (nnimap-open-connection server))))) - -(deffoo nnimap-server-opened (&optional server) - "Whether SERVER is opened. -If SERVER is the current virtual server, and the connection to the -physical server is alive, this function return a non-nil value. If -SERVER is nil, it is treated as the current server." - ;; clean up autologouts?? - (and (or server nnimap-current-server) - (nnoo-server-opened 'nnimap (or server nnimap-current-server)) - (imap-opened (nnimap-get-server-buffer server)))) - -(deffoo nnimap-close-server (&optional server) - "Close connection to server and free all resources connected to it. -Return nil if the server couldn't be closed for some reason." - (let ((server (or server nnimap-current-server)) - (imap-logout-timeout nnimap-logout-timeout)) - (when (or (nnimap-server-opened server) - (imap-opened (nnimap-get-server-buffer server))) - (imap-close (nnimap-get-server-buffer server)) - (kill-buffer (nnimap-get-server-buffer server)) - (setq nnimap-server-buffer nil - nnimap-current-server nil - nnimap-server-buffer-alist - (nnimap-remove-server-from-buffer-alist - server - nnimap-server-buffer-alist))) - (nnoo-close-server 'nnimap server))) - -(deffoo nnimap-request-close () - "Close connection to all servers and free all resources that the backend have reserved. -All buffers that have been created by that -backend should be killed. (Not the nntp-server-buffer, though.) This -function is generally only called when Gnus is shutting down." - (mapc (lambda (server) (nnimap-close-server (car server))) - nnimap-server-buffer-alist) - (setq nnimap-server-buffer-alist nil)) - -(deffoo nnimap-status-message (&optional server) - "This function returns the last error message from server." - (when (nnimap-possibly-change-server server) - (nnoo-status-message 'nnimap server))) - -;; We used to use a string-as-multibyte here, but it is really incorrect. -;; This function is used when we're about to insert a unibyte string -;; into a potentially multibyte buffer. The string is either an article -;; header or body (or both?), undecoded. When Emacs is asked to convert -;; a unibyte string to multibyte, it may either use the equivalent of -;; nothing (e.g. non-Mule XEmacs), string-make-unibyte (i.e. decode using -;; locale), string-as-multibyte (decode using emacs-internal coding system) -;; or string-to-multibyte (keep the data undecoded as a sequence of bytes). -;; Only the last one preserves the data such that we can reliably later on -;; decode the text using the mime info. -(defalias 'nnimap-demule 'mm-string-to-multibyte) - -(defun nnimap-make-callback (article gnus-callback buffer) - "Return a callback function." - `(lambda () - (nnimap-callback ,article ,gnus-callback ,buffer))) - -(defun nnimap-callback (article gnus-callback buffer) - (when (eq article (imap-current-message)) - (remove-hook 'imap-fetch-data-hook - (nnimap-make-callback article gnus-callback buffer)) - (with-current-buffer buffer - (insert - (with-current-buffer nnimap-server-buffer - (nnimap-demule - (if (imap-capability 'IMAP4rev1) - ;; xxx don't just use car? alist doesn't contain - ;; anything else now, but it might... - (nth 2 (car (imap-message-get article 'BODYDETAIL))) - (imap-message-get article 'RFC822))))) - (nnheader-ms-strip-cr) - (funcall gnus-callback t)))) - -(defun nnimap-request-article-part (article part prop &optional - group server to-buffer detail) - (when (nnimap-possibly-change-group group server) - (let ((article (if (stringp article) - (car-safe (imap-search - (format "HEADER Message-Id \"%s\"" article) - nnimap-server-buffer)) - article))) - (when article - (gnus-message 10 "nnimap: Fetching (part of) article %d from %s..." - article (or (nnimap-decode-group-name group) - (imap-current-mailbox) - (nnimap-decode-group-name - gnus-newsgroup-name))) - (if (not nnheader-callback-function) - (with-current-buffer (or to-buffer nntp-server-buffer) - (erase-buffer) - (let ((data (imap-fetch article part prop nil - nnimap-server-buffer))) - ;; data can be nil if article is write-only - (when data - (insert (nnimap-demule (if detail - (nth 2 (car data)) - data))))) - (nnheader-ms-strip-cr) - (gnus-message - 10 "nnimap: Fetching (part of) article %d from %s...done" - article (or (nnimap-decode-group-name group) - (imap-current-mailbox) - (nnimap-decode-group-name gnus-newsgroup-name))) - (if (bobp) - (nnheader-report 'nnimap "No such article %d in %s: %s" - article (or (nnimap-decode-group-name group) - (imap-current-mailbox) - (nnimap-decode-group-name - gnus-newsgroup-name)) - (imap-error-text nnimap-server-buffer)) - (cons group article))) - (add-hook 'imap-fetch-data-hook - (nnimap-make-callback article - nnheader-callback-function - nntp-server-buffer)) - (imap-fetch-asynch article part nil nnimap-server-buffer) - (cons group article)))))) - -(deffoo nnimap-asynchronous-p () + (or (nnimap-find-connection nntp-server-buffer) + (nnimap-open-connection nntp-server-buffer)))) + +(defun nnimap-make-process-buffer (buffer) + (with-current-buffer + (generate-new-buffer (format "*nnimap %s %s %s*" + nnimap-address nnimap-server-port + (gnus-buffer-exists-p buffer))) + (mm-disable-multibyte) + (buffer-disable-undo) + (gnus-add-buffer) + (set (make-local-variable 'after-change-functions) nil) + (set (make-local-variable 'nnimap-object) (make-nnimap)) + (push (list buffer (current-buffer)) nnimap-connection-alist) + (current-buffer))) + +(defun nnimap-open-shell-stream (name buffer host port) + (let ((process (start-process name buffer shell-file-name + shell-command-switch + (format-spec + nnimap-shell-program + (format-spec-make + ?s host + ?p port))))) + process)) + +(defun nnimap-open-connection (buffer) + (with-current-buffer (nnimap-make-process-buffer buffer) + (let* ((coding-system-for-read 'binary) + (coding-system-for-write 'binary) + (credentials + (cond + ((eq nnimap-stream 'network) + (open-network-stream "*nnimap*" (current-buffer) nnimap-address + (or nnimap-server-port + (if (netrc-find-service-number "imap") + "imap" + "143"))) + (auth-source-user-or-password + '("login" "password") nnimap-address "imap" nil t)) + ((eq nnimap-stream 'stream) + (nnimap-open-shell-stream + "*nnimap*" (current-buffer) nnimap-address + (or nnimap-server-port "imap")) + (auth-source-user-or-password + '("login" "password") nnimap-address "imap" nil t)) + ((eq nnimap-stream 'ssl) + (open-tls-stream "*nnimap*" (current-buffer) nnimap-address + (or nnimap-server-port + (if (netrc-find-service-number "imaps") + "imaps" + "993"))) + (or + (auth-source-user-or-password + '("login" "password") nnimap-address "imap") + (auth-source-user-or-password + '("login" "password") nnimap-address "imaps" nil t)))))) + (setf (nnimap-process nnimap-object) + (get-buffer-process (current-buffer))) + (unless credentials + (delete-process (nnimap-process nnimap-object))) + (when (and (nnimap-process nnimap-object) + (memq (process-status (nnimap-process nnimap-object)) + '(open run))) + (gnus-set-process-query-on-exit-flag (nnimap-process nnimap-object) nil) + (let ((result (nnimap-command "LOGIN %S %S" + (car credentials) (cadr credentials)))) + (if (not (car result)) + (progn + (delete-process (nnimap-process nnimap-object)) + nil) + (setf (nnimap-capabilities nnimap-object) + (mapcar + #'upcase + (or (nnimap-find-parameter "CAPABILITY" (cdr result)) + (nnimap-find-parameter + "CAPABILITY" (cdr (nnimap-command "CAPABILITY")))))) + (when (member "QRESYNC" (nnimap-capabilities nnimap-object)) + (nnimap-command "ENABLE QRESYNC")) + t)))))) + +(defun nnimap-find-parameter (parameter elems) + (let (result) + (dolist (elem elems) + (cond + ((equal (car elem) parameter) + (setq result (cdr elem))) + ((and (equal (car elem) "OK") + (consp (cadr elem)) + (equal (caadr elem) parameter)) + (setq result (cdr (cadr elem)))))) + result)) + +(defun nnimap-close-server (&optional server) t) -(deffoo nnimap-request-article (article &optional group server to-buffer) - (if (imap-capability 'IMAP4rev1 nnimap-server-buffer) - (nnimap-request-article-part - article "BODY.PEEK[]" 'BODYDETAIL group server to-buffer 'detail) - (nnimap-request-article-part - article "RFC822.PEEK" 'RFC822 group server to-buffer))) - -(deffoo nnimap-request-head (article &optional group server to-buffer) - (if (imap-capability 'IMAP4rev1 nnimap-server-buffer) - (nnimap-request-article-part - article "BODY.PEEK[HEADER]" 'BODYDETAIL group server to-buffer 'detail) - (nnimap-request-article-part - article "RFC822.HEADER" 'RFC822.HEADER group server to-buffer))) - -(deffoo nnimap-request-body (article &optional group server to-buffer) - (if (imap-capability 'IMAP4rev1 nnimap-server-buffer) - (nnimap-request-article-part - article "BODY.PEEK[TEXT]" 'BODYDETAIL group server to-buffer 'detail) - (nnimap-request-article-part - article "RFC822.TEXT.PEEK" 'RFC822.TEXT group server to-buffer))) - -(deffoo nnimap-request-group (group &optional server fast) - (nnimap-request-update-info-internal - group - (gnus-get-info (nnimap-group-prefixed-name group server)) - server) - (when (nnimap-possibly-change-group group server) - (nnimap-before-find-minmax-bugworkaround) - (let (info) - (cond (fast group) - ((null (setq info (nnimap-find-minmax-uid group t))) - (nnheader-report 'nnimap "Could not get active info for %s" - group)) - (t - (nnheader-insert "211 %d %d %d %s\n" (or (nth 0 info) 0) - (max 1 (or (nth 1 info) 1)) - (or (nth 2 info) 0) group) - (nnheader-report 'nnimap "Group %s selected" group) - t))))) - -(defun nnimap-update-unseen (group &optional server) - "Update the unseen count in `nnimap-mailbox-info'." - (gnus-sethash - (gnus-group-prefixed-name group server) - (let ((old (gnus-gethash-safe (gnus-group-prefixed-name group server) - nnimap-mailbox-info))) - (list (nth 0 old) (nth 1 old) - (imap-mailbox-status (nnimap-decode-group-name group) - 'unseen nnimap-server-buffer))) - nnimap-mailbox-info)) +(defun nnimap-request-close () + t) -(defun nnimap-close-group (group &optional server) - (with-current-buffer nnimap-server-buffer - (when (and (imap-opened) - (nnimap-possibly-change-group group server)) - (nnimap-update-unseen group server) - (case nnimap-expunge-on-close - (always (progn - (imap-mailbox-expunge nnimap-close-asynchronous) - (unless nnimap-dont-close - (imap-mailbox-close nnimap-close-asynchronous)))) - (ask (if (and (imap-search "DELETED") - (gnus-y-or-n-p (format "Expunge articles in group `%s'? " - (imap-current-mailbox)))) - (progn - (imap-mailbox-expunge nnimap-close-asynchronous) - (unless nnimap-dont-close - (imap-mailbox-close nnimap-close-asynchronous))) - (imap-mailbox-unselect))) - (t (imap-mailbox-unselect))) - (not imap-current-mailbox)))) - -(defun nnimap-pattern-to-list-arguments (pattern) - (mapcar (lambda (p) - (cons (car-safe p) (or (cdr-safe p) p))) - (if (and (listp pattern) - (listp (cdr pattern))) - pattern - (list pattern)))) - -(deffoo nnimap-request-list (&optional server) - (when (nnimap-possibly-change-server server) - (with-current-buffer nntp-server-buffer - (erase-buffer)) - (gnus-message 5 "nnimap: Generating active list%s..." - (if (> (length server) 0) (concat " for " server) "")) - (nnimap-before-find-minmax-bugworkaround) - (with-current-buffer nnimap-server-buffer - (dolist (pattern (nnimap-pattern-to-list-arguments nnimap-list-pattern)) - (dolist (mbx (funcall nnimap-request-list-method - (cdr pattern) (car pattern))) - (unless (member "\\noselect" - (mapcar #'downcase - (imap-mailbox-get 'list-flags mbx))) - (let* ((encoded-mbx (nnimap-encode-group-name mbx)) - (info (nnimap-find-minmax-uid encoded-mbx 'examine))) - (when info - (with-current-buffer nntp-server-buffer - (insert (format "\"%s\" %d %d y\n" - encoded-mbx (or (nth 2 info) 0) - (max 1 (or (nth 1 info) 1))))))))))) - (gnus-message 5 "nnimap: Generating active list%s...done" - (if (> (length server) 0) (concat " for " server) "")) - t)) +(defun nnimap-server-opened (&optional server) + (and (nnoo-current-server-p 'nnimap server) + nntp-server-buffer + (gnus-buffer-live-p nntp-server-buffer) + (nnimap-find-connection nntp-server-buffer))) -(deffoo nnimap-request-post (&optional server) - (let ((success t)) - (dolist (mbx (message-unquote-tokens - (message-tokenize-header - (message-fetch-field "Newsgroups") ", ")) success) - (let ((to-newsgroup (gnus-group-prefixed-name mbx gnus-command-method))) - (or (gnus-active to-newsgroup) - (gnus-activate-group to-newsgroup) - (if (gnus-y-or-n-p (format "No such group: %s. Create it? " - to-newsgroup)) - (or (and (gnus-request-create-group - to-newsgroup gnus-command-method) - (gnus-activate-group to-newsgroup nil nil - gnus-command-method)) - (error "Couldn't create group %s" to-newsgroup))) - (error "No such group: %s" to-newsgroup)) - (unless (nnimap-request-accept-article mbx (nth 1 gnus-command-method)) - (setq success nil)))))) - -;; Optional backend functions - -(defun nnimap-string-lessp-numerical (s1 s2) - "Return t if first arg string is less than second in numerical order." - (cond ((string= s1 s2) - nil) - ((> (length s1) (length s2)) - nil) - ((< (length s1) (length s2)) - t) - ((< (string-to-number (substring s1 0 1)) - (string-to-number (substring s2 0 1))) - t) - ((> (string-to-number (substring s1 0 1)) - (string-to-number (substring s2 0 1))) - nil) - (t - (nnimap-string-lessp-numerical (substring s1 1) (substring s2 1))))) - -(deffoo nnimap-retrieve-groups (groups &optional server) - (when (nnimap-possibly-change-server server) - (gnus-message 5 "nnimap: Checking mailboxes...") - (with-current-buffer nntp-server-buffer - (erase-buffer) - (nnimap-before-find-minmax-bugworkaround) - (let (asyncgroups slowgroups decoded-group) - (if (null nnimap-retrieve-groups-asynchronous) - (setq slowgroups groups) - (dolist (group groups) - (setq decoded-group (nnimap-decode-group-name group)) - (gnus-message 9 "nnimap: Quickly checking mailbox %s" - decoded-group) - (add-to-list (if (gnus-group-get-parameter - (nnimap-group-prefixed-name group) - 'imap-status) - 'asyncgroups - 'slowgroups) - (list group (imap-mailbox-status-asynch - decoded-group - '(uidvalidity uidnext unseen) - nnimap-server-buffer)))) - (dolist (asyncgroup asyncgroups) - (let* ((group (nth 0 asyncgroup)) - (tag (nth 1 asyncgroup)) - (gnusgroup (nnimap-group-prefixed-name group)) - (saved-uidvalidity (gnus-group-get-parameter gnusgroup - 'uidvalidity)) - (saved-imap-status (gnus-group-get-parameter gnusgroup - 'imap-status)) - (saved-info (and saved-imap-status - (split-string saved-imap-status " ")))) - (setq decoded-group (nnimap-decode-group-name group)) - (when (imap-ok-p (imap-wait-for-tag tag nnimap-server-buffer)) - (if (or (not (equal - saved-uidvalidity - (imap-mailbox-get 'uidvalidity decoded-group - nnimap-server-buffer))) - (not (equal - (nth 0 saved-info) - (imap-mailbox-get 'uidnext decoded-group - nnimap-server-buffer)))) - (push (list group) slowgroups) - (gnus-sethash - (gnus-group-prefixed-name group server) - (list (imap-mailbox-get 'uidvalidity - decoded-group nnimap-server-buffer) - (imap-mailbox-get 'uidnext - decoded-group nnimap-server-buffer) - (imap-mailbox-get 'unseen - decoded-group nnimap-server-buffer)) - nnimap-mailbox-info) - (insert (format "\"%s\" %s %s y\n" group - (nth 2 saved-info) - (nth 1 saved-info)))))))) - (dolist (group slowgroups) - (if nnimap-retrieve-groups-asynchronous - (setq group (car group))) - (setq decoded-group (nnimap-decode-group-name group)) - (gnus-message 7 "nnimap: Mailbox %s modified" decoded-group) - (or (member "\\NoSelect" (imap-mailbox-get 'list-flags decoded-group - nnimap-server-buffer)) - (let* ((gnusgroup (nnimap-group-prefixed-name group)) - (status (imap-mailbox-status - decoded-group '(uidvalidity uidnext unseen) - nnimap-server-buffer)) - (info (nnimap-find-minmax-uid group 'examine)) - (min-uid (max 1 (or (nth 1 info) 1))) - (max-uid (or (nth 2 info) 0))) - (when (> (or (imap-mailbox-get 'recent decoded-group - nnimap-server-buffer) 0) - 0) - (push (list (cons decoded-group 0)) nnmail-split-history)) - (insert (format "\"%s\" %d %d y\n" group max-uid min-uid)) - (gnus-sethash - (gnus-group-prefixed-name group server) - status - nnimap-mailbox-info) - (if (not (equal (nth 0 status) - (gnus-group-get-parameter gnusgroup - 'uidvalidity))) - (nnimap-verify-uidvalidity group nnimap-current-server)) - ;; The imap-status parameter is a string on the form - ;; "<uidnext> <min-uid> <max-uid>". - (gnus-group-add-parameter - gnusgroup - (cons 'imap-status - (format "%s %s %s" (nth 1 status) min-uid max-uid)))))))) - (gnus-message 5 "nnimap: Checking mailboxes...done") - 'active)) - -(deffoo nnimap-request-update-info-internal (group info &optional server) - (when (nnimap-possibly-change-group group server) - (when info ;; xxx what does this mean? should we create a info? - (with-current-buffer nnimap-server-buffer - (gnus-message 5 "nnimap: Updating info for %s..." - (nnimap-decode-group-name (gnus-info-group info))) - - (when (nnimap-mark-permanent-p 'read) - (let (seen unseen) - ;; read info could contain articles marked unread by other - ;; imap clients! we correct this - (setq unseen (gnus-compress-sequence - (imap-search "UNSEEN UNDELETED")) - seen (gnus-range-difference (gnus-info-read info) unseen) - seen (gnus-range-add seen - (gnus-compress-sequence - (imap-search "SEEN"))) - seen (if (and (integerp (car seen)) - (null (cdr seen))) - (list (cons (car seen) (car seen))) - seen)) - (gnus-info-set-read info seen))) - - (dolist (pred gnus-article-mark-lists) - (when (or (eq (cdr pred) 'recent) - (and (nnimap-mark-permanent-p (cdr pred)) - (member (nnimap-mark-to-flag (cdr pred)) - (imap-mailbox-get 'flags)))) - (gnus-info-set-marks - info - (gnus-update-alist-soft - (cdr pred) - (gnus-compress-sequence - (imap-search (nnimap-mark-to-predicate (cdr pred)))) - (gnus-info-marks info)) - t))) - - (when nnimap-importantize-dormant - ;; nnimap mark dormant article as ticked too (for other clients) - ;; so we remove that mark for gnus since we support dormant - (gnus-info-set-marks - info - (gnus-update-alist-soft - 'tick - (gnus-remove-from-range - (cdr-safe (assoc 'tick (gnus-info-marks info))) - (cdr-safe (assoc 'dormant (gnus-info-marks info)))) - (gnus-info-marks info)) - t)) - - (gnus-message 5 "nnimap: Updating info for %s...done" - (nnimap-decode-group-name (gnus-info-group info))) - - info)))) - -(deffoo nnimap-request-type (group &optional article) - (if (and nnimap-news-groups (string-match nnimap-news-groups group)) - 'news - 'mail)) - -(deffoo nnimap-request-set-mark (group actions &optional server) - (when (nnimap-possibly-change-group group server) - (with-current-buffer nnimap-server-buffer - (let (action) - (gnus-message 7 "nnimap: Setting marks in %s..." - (nnimap-decode-group-name group)) - (while (setq action (pop actions)) - (let ((range (nth 0 action)) - (what (nth 1 action)) - (cmdmarks (nth 2 action)) - marks) - ;; bookmark can't be stored (not list/range - (setq cmdmarks (delq 'bookmark cmdmarks)) - ;; killed can't be stored (not list/range - (setq cmdmarks (delq 'killed cmdmarks)) - ;; unsent are for nndraft groups only - (setq cmdmarks (delq 'unsent cmdmarks)) - ;; cache flags are pointless on the server - (setq cmdmarks (delq 'cache cmdmarks)) - ;; seen flags are local to each gnus - (setq cmdmarks (delq 'seen cmdmarks)) - ;; recent marks can't be set - (setq cmdmarks (delq 'recent cmdmarks)) - (when nnimap-importantize-dormant - ;; flag dormant articles as ticked - (if (memq 'dormant cmdmarks) - (setq cmdmarks (cons 'tick cmdmarks)))) - ;; remove stuff we are forbidden to store - (mapc (lambda (mark) - (if (imap-message-flag-permanent-p - (nnimap-mark-to-flag mark)) - (setq marks (cons mark marks)))) - cmdmarks) - (when (and range marks) - (cond ((eq what 'del) - (imap-message-flags-del - (imap-range-to-message-set range) - (nnimap-mark-to-flag marks nil t))) - ((eq what 'add) - (imap-message-flags-add - (imap-range-to-message-set range) - (nnimap-mark-to-flag marks nil t))) - ((eq what 'set) - (imap-message-flags-set - (imap-range-to-message-set range) - (nnimap-mark-to-flag marks nil t))))))) - (gnus-message 7 "nnimap: Setting marks in %s...done" - (nnimap-decode-group-name group))))) - nil) +(defun nnimap-status-message (&optional server) + nnimap-status-string) -(defun nnimap-split-fancy () - "Like the function `nnmail-split-fancy', but uses `nnimap-split-fancy'." - (let ((nnmail-split-fancy nnimap-split-fancy)) - (nnmail-split-fancy))) - -(defun nnimap-split-to-groups (rules) - ;; tries to match all rules in nnimap-split-rule against content of - ;; nntp-server-buffer, returns a list of groups that matched. - ;; Note: This function takes and returns decoded group names. +(defun nnimap-request-article (article &optional group server to-buffer) (with-current-buffer nntp-server-buffer - ;; Fold continuation lines. - (goto-char (point-min)) - (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t) - (replace-match " " t t)) - (if (functionp rules) - (funcall rules) - (let (to-groups regrepp) - (catch 'split-done - (dolist (rule rules to-groups) - (let ((group (car rule)) - (regexp (cadr rule))) + (let ((result (nnimap-possibly-change-group group server))) + (when (stringp article) + (setq article (nnimap-find-article-by-message-id group article))) + (when (and result + article) + (erase-buffer) + (with-current-buffer (nnimap-buffer) + (erase-buffer) + (setq result + (nnimap-command + (if (member "IMAP4REV1" (nnimap-capabilities nnimap-object)) + "UID FETCH %d BODY.PEEK[]" + "UID FETCH %d RFC822.PEEK") + article))) + (let ((buffer (nnimap-find-process-buffer (current-buffer)))) + (when (car result) + (with-current-buffer to-buffer + (insert-buffer-substring buffer) (goto-char (point-min)) - (when (and (if (stringp regexp) - (progn - (if (not (stringp group)) - (setq group (eval group)) - (setq regrepp - (string-match "\\\\[0-9&]" group))) - (re-search-forward regexp nil t)) - (funcall regexp group)) - ;; Don't enter the article into the same group twice. - (not (assoc group to-groups))) - (push (if regrepp - (nnmail-expand-newtext group) - group) - to-groups) - (or nnimap-split-crosspost - (throw 'split-done to-groups)))))))))) - -(defun nnimap-assoc-match (key alist) - (let (element) - (while (and alist (not element)) - (if (string-match (car (car alist)) key) - (setq element (car alist))) - (setq alist (cdr alist))) - element)) - -(defun nnimap-split-find-rule (server inbox) - (if (and (listp nnimap-split-rule) (listp (car nnimap-split-rule)) - (list (cdar nnimap-split-rule)) (listp (cadar nnimap-split-rule))) - ;; extended format - (cadr (nnimap-assoc-match inbox (cdr (nnimap-assoc-match - server nnimap-split-rule)))) - nnimap-split-rule)) - -(defun nnimap-split-find-inbox (server) - (if (listp nnimap-split-inbox) - nnimap-split-inbox - (list nnimap-split-inbox))) - -(defun nnimap-split-articles (&optional group server) - ;; Note: Assumes decoded group names in nnimap-split-inbox, - ;; nnimap-split-rule, nnimap-split-fancy, and nnmail-split-history. - (when (nnimap-possibly-change-server server) - (with-current-buffer nnimap-server-buffer - (let (rule inbox removeorig - (inboxes (nnimap-split-find-inbox server))) - ;; iterate over inboxes - (while (and (setq inbox (pop inboxes)) - (nnimap-possibly-change-group - (nnimap-encode-group-name inbox))) ;; SELECT - ;; find split rule for this server / inbox - (when (setq rule (nnimap-split-find-rule server inbox)) - ;; iterate over articles - (dolist (article (imap-search nnimap-split-predicate)) - (when (if (if (eq nnimap-split-download-body 'default) - nnimap-split-download-body-default - nnimap-split-download-body) - (and (nnimap-request-article article) - (with-current-buffer nntp-server-buffer (mail-narrow-to-head))) - (nnimap-request-head article)) - ;; copy article to right group(s) - (setq removeorig nil) - (dolist (to-group (nnimap-split-to-groups rule)) - (cond ((eq to-group 'junk) - (message "IMAP split removed %s:%s:%d" server inbox - article) - (setq removeorig t)) - ((imap-message-copy (number-to-string article) - to-group nil 'nocopyuid) - (message "IMAP split moved %s:%s:%d to %s" server - inbox article to-group) - (setq removeorig t) - (when nnmail-cache-accepted-message-ids - (with-current-buffer nntp-server-buffer - (let (msgid) - (and (setq msgid - (nnmail-fetch-field "message-id")) - (nnmail-cache-insert msgid - (nnimap-encode-group-name to-group) - (nnmail-fetch-field "subject")))))) - ;; Add the group-art list to the history list. - (push (list (cons to-group 0)) nnmail-split-history)) - (t - (message "IMAP split failed to move %s:%s:%d to %s" - server inbox article to-group)))) - (if (if (eq nnimap-split-download-body 'default) - nnimap-split-download-body-default - nnimap-split-download-body) - (widen)) - ;; remove article if it was successfully copied somewhere - (and removeorig - (imap-message-flags-add (format "%d" article) - "\\Seen \\Deleted"))))) - (when (imap-mailbox-select inbox) ;; just in case - ;; todo: UID EXPUNGE (if available) to remove splitted articles - (imap-mailbox-expunge) - (imap-mailbox-close))) - (when nnmail-cache-accepted-message-ids - (nnmail-cache-close)) + (let ((bytes (nnimap-get-length))) + (delete-region (line-beginning-position) + (progn (forward-line 1) (point))) + (goto-char (+ (point) bytes)) + (delete-region (point) (point-max)) + (nnheader-ms-strip-cr)) + t))))))) + +(defun nnimap-request-group (group &optional server dont-check) + (with-current-buffer nntp-server-buffer + (let ((result (nnimap-possibly-change-group group server)) + articles) + (when result + (setq articles (nnimap-get-flags "1:*")) + (erase-buffer) + (insert + (format + "211 %d %d %d %S\n" + (length articles) + (or (caar articles) 0) + (or (caar (last articles)) 0) + group)) t)))) -(deffoo nnimap-request-scan (&optional group server) - (nnimap-split-articles group server)) - -(deffoo nnimap-request-newgroups (date &optional server) - (when (nnimap-possibly-change-server server) - (with-current-buffer nntp-server-buffer - (gnus-message 5 "nnimap: Listing subscribed mailboxes%s%s..." - (if (> (length server) 0) " on " "") server) +(defun nnimap-get-flags (spec) + (let ((articles nil) + elems) + (with-current-buffer (nnimap-buffer) (erase-buffer) - (nnimap-before-find-minmax-bugworkaround) - (dolist (pattern (nnimap-pattern-to-list-arguments - nnimap-list-pattern)) - (dolist (mbx (funcall nnimap-request-list-method (cdr pattern) (car pattern) nil - nnimap-server-buffer)) - (or (catch 'found - (dolist (mailbox (imap-mailbox-get 'list-flags mbx - nnimap-server-buffer)) - (if (string= (downcase mailbox) "\\noselect") - (throw 'found t))) - nil) - (let* ((encoded-mbx (nnimap-encode-group-name mbx)) - (info (nnimap-find-minmax-uid encoded-mbx 'examine))) - (when info - (insert (format "\"%s\" %d %d y\n" - encoded-mbx (or (nth 2 info) 0) - (max 1 (or (nth 1 info) 1))))))))) - (gnus-message 5 "nnimap: Listing subscribed mailboxes%s%s...done" - (if (> (length server) 0) " on " "") server)) - t)) + (nnimap-wait-for-response (nnimap-send-command + "UID FETCH %s FLAGS" spec)) + (goto-char (point-min)) + (while (re-search-forward "^\\* [0-9]+ FETCH (\\(.*\\))" nil t) + (setq elems (nnimap-parse-line (match-string 1))) + (push (cons (string-to-number (cadr (member "UID" elems))) + (cadr (member "FLAGS" elems))) + articles))) + (nreverse articles))) -(deffoo nnimap-request-create-group (group &optional server args) - (when (nnimap-possibly-change-server server) - (let ((decoded-group (nnimap-decode-group-name group))) - (or (imap-mailbox-status decoded-group 'uidvalidity nnimap-server-buffer) - (imap-mailbox-create decoded-group nnimap-server-buffer) - (nnheader-report 'nnimap "%S" - (imap-error-text nnimap-server-buffer)))))) - -(defun nnimap-time-substract (time1 time2) - "Return TIME for TIME1 - TIME2." - (let* ((ms (- (car time1) (car time2))) - (ls (- (nth 1 time1) (nth 1 time2)))) - (if (< ls 0) - (list (- ms 1) (+ (expt 2 16) ls)) - (list ms ls)))) - -(eval-when-compile (require 'parse-time)) -(defun nnimap-date-days-ago (daysago) - "Return date, in format \"3-Aug-1998\", for DAYSAGO days ago." - (require 'parse-time) - (let* ((time (nnimap-time-substract (current-time) (days-to-time daysago))) - (date (format-time-string - (format "%%d-%s-%%Y" - (capitalize (car (rassoc (nth 4 (decode-time time)) - parse-time-months)))) - time))) - (if (eq ?0 (string-to-char date)) - (substring date 1) - date))) - -(defun nnimap-request-expire-articles-progress () - (gnus-message 5 "nnimap: Marking article %d for deletion..." - imap-current-message)) - -(defun nnimap-expiry-target (arts group server) - (unless (eq nnmail-expiry-target 'delete) - (with-temp-buffer - (dolist (art arts) - (nnimap-request-article art group server (current-buffer)) - ;; hints for optimization in `nnimap-request-accept-article' - (let ((nnimap-current-move-article art) - (nnimap-current-move-group group) - (nnimap-current-move-server server)) - (nnmail-expiry-target-group nnmail-expiry-target group)))) - ;; It is not clear if `nnmail-expiry-target' somehow cause the - ;; current group to be changed or not, so we make sure here. - (nnimap-possibly-change-group group server))) - -;; Notice that we don't actually delete anything, we just mark them deleted. -(deffoo nnimap-request-expire-articles (articles group &optional server force) - (let ((artseq (gnus-compress-sequence articles))) - (when (and artseq (nnimap-possibly-change-group group server)) - (with-current-buffer nnimap-server-buffer - (let ((days (or (and nnmail-expiry-wait-function - (funcall nnmail-expiry-wait-function group)) - nnmail-expiry-wait))) - (cond ((or force (eq days 'immediate)) - (let ((oldarts (imap-search - (concat "UID " - (imap-range-to-message-set artseq))))) - (when oldarts - (nnimap-expiry-target oldarts group server) - (when (imap-message-flags-add - (imap-range-to-message-set - (gnus-compress-sequence oldarts)) "\\Deleted") - (setq articles (gnus-set-difference - articles oldarts)))))) - ((and nnimap-search-uids-not-since-is-evil (numberp days)) - (let* ((all-new-articles - (gnus-compress-sequence - (imap-search (format "SINCE %s" - (nnimap-date-days-ago days))))) - (oldartseq - (gnus-range-difference artseq all-new-articles)) - (oldarts (gnus-uncompress-range oldartseq))) - (when oldarts - (nnimap-expiry-target oldarts group server) - (when (imap-message-flags-add - (imap-range-to-message-set oldartseq) - "\\Deleted") - (setq articles (gnus-set-difference - articles oldarts)))))) - ((numberp days) - (let ((oldarts (imap-search - (format nnimap-expunge-search-string - (imap-range-to-message-set artseq) - (nnimap-date-days-ago days)))) - (imap-fetch-data-hook - '(nnimap-request-expire-articles-progress))) - (when oldarts - (nnimap-expiry-target oldarts group server) - (when (imap-message-flags-add - (imap-range-to-message-set - (gnus-compress-sequence oldarts)) "\\Deleted") - (setq articles (gnus-set-difference - articles oldarts))))))))))) - ;; return articles not deleted - articles) +(defun nnimap-close-group (group &optional server) + t) (deffoo nnimap-request-move-article (article group server accept-form - &optional last move-is-internal) - (when (nnimap-possibly-change-server server) - (save-excursion - (let ((buf (get-buffer-create " *nnimap move*")) - (nnimap-current-move-article article) - (nnimap-current-move-group group) - (nnimap-current-move-server nnimap-current-server) - result) - (gnus-message 10 "nnimap-request-move-article: this is an %s move" - (if move-is-internal - "internal" - "external")) - ;; request the article only when the move is NOT internal - (and (or move-is-internal - (nnimap-request-article article group server)) - (with-current-buffer buf - (buffer-disable-undo (current-buffer)) - (insert-buffer-substring nntp-server-buffer) - (setq result (eval accept-form)) - (kill-buffer buf) - result) - (nnimap-possibly-change-group group server) - (imap-message-flags-add - (imap-range-to-message-set (list article)) - "\\Deleted" 'silent nnimap-server-buffer)) - result)))) + &optional last internal-move-group) + (when (nnimap-possibly-change-group group server) + ;; If the move is internal (on the same server), just do it the easy + ;; way. + (let ((message-id (message-field-value "message-id"))) + (if internal-move-group + (let ((result + (with-current-buffer (nnimap-buffer) + (nnimap-command "UID COPY %d %S" + article + (utf7-encode internal-move-group t))))) + (when (car result) + (nnimap-delete-article article) + (cons internal-move-group + (nnimap-find-article-by-message-id + internal-move-group message-id)))) + (with-temp-buffer + (let ((result (eval accept-form))) + (when result + (nnimap-delete-article article) + result))))))) + +(deffoo nnimap-request-expire-articles (articles group &optional server force) + (cond + ((not (nnimap-possibly-change-group group server)) + articles) + (force + (unless (nnimap-delete-article articles) + (message "Article marked for deletion, but not expunged.")) + nil) + (t + articles))) + +(defun nnimap-find-article-by-message-id (group message-id) + (when (nnimap-possibly-change-group group nil) + (with-current-buffer (nnimap-buffer) + (let ((result + (nnimap-command "UID SEARCH HEADER Message-Id %S" message-id)) + article) + (when (car result) + ;; Select the last instance of the message in the group. + (and (setq article + (car (last (assoc "SEARCH" (cdr result))))) + (string-to-number article))))))) + +(defun nnimap-delete-article (articles) + (with-current-buffer (nnimap-buffer) + (nnimap-command "UID STORE %s +FLAGS.SILENT (\\Deleted)" + (nnimap-article-ranges articles)) + (when (member "UIDPLUS" (nnimap-capabilities nnimap-object)) + (nnimap-send-command "UID EXPUNGE %s" + (nnimap-article-ranges articles)) + t))) + +(deffoo nnimap-request-scan (&optional group server) + (when (and (nnimap-possibly-change-group nil server) + (equal group nnimap-inbox) + nnimap-inbox + nnimap-split-methods) + (nnimap-split-incoming-mail))) + +(defun nnimap-marks-to-flags (marks) + (let (flags flag) + (dolist (mark marks) + (when (setq flag (cadr (assq mark nnimap-mark-alist))) + (push flag flags))) + flags)) + +(defun nnimap-request-set-mark (group actions &optional server) + (when (nnimap-possibly-change-group group server) + (let (sequence) + (with-current-buffer (nnimap-buffer) + ;; Just send all the STORE commands without waiting for + ;; response. If they're successful, they're successful. + (dolist (action actions) + (destructuring-bind (range action marks) action + (let ((flags (nnimap-marks-to-flags marks))) + (when flags + (setq sequence (nnimap-send-command + "UID STORE %s %sFLAGS.SILENT (%s)" + (nnimap-article-ranges range) + (if (eq action 'del) + "-" + "+") + (mapconcat #'identity flags " "))))))) + ;; Wait for the last command to complete to avoid later + ;; syncronisation problems with the stream. + (nnimap-wait-for-response sequence))))) (deffoo nnimap-request-accept-article (group &optional server last) - (when (nnimap-possibly-change-server server) - (let (uid) - (if (setq uid - (if (string= nnimap-current-server nnimap-current-move-server) - ;; moving article within same server, speed it up... - (and (nnimap-possibly-change-group - nnimap-current-move-group) - (imap-message-copy (number-to-string - nnimap-current-move-article) - (nnimap-decode-group-name group) - 'dontcreate nil - nnimap-server-buffer)) - (with-current-buffer (current-buffer) - (goto-char (point-min)) - ;; remove any 'From blabla' lines, some IMAP servers - ;; reject the entire message otherwise. - (when (looking-at "^From[^:]") - (delete-region (point) (progn (forward-line) (point)))) - ;; turn into rfc822 format (\r\n eol's) - (while (search-forward "\n" nil t) - (replace-match "\r\n")) - (when nnmail-cache-accepted-message-ids - (nnmail-cache-insert (nnmail-fetch-field "message-id") - group - (nnmail-fetch-field "subject")))) - (when (and last nnmail-cache-accepted-message-ids) - (nnmail-cache-close)) - ;; this 'or' is for Cyrus server bug - (or (null (imap-current-mailbox nnimap-server-buffer)) - (imap-mailbox-unselect nnimap-server-buffer)) - (imap-message-append (nnimap-decode-group-name group) - (current-buffer) nil nil - nnimap-server-buffer))) - (cons group (nth 1 uid)) - (nnheader-report 'nnimap (imap-error-text nnimap-server-buffer)))))) - -(deffoo nnimap-request-delete-group (group force &optional server) - (when (nnimap-possibly-change-server server) - (setq group (nnimap-decode-group-name group)) - (when (string= group (imap-current-mailbox nnimap-server-buffer)) - (imap-mailbox-unselect nnimap-server-buffer)) - (with-current-buffer nnimap-server-buffer - (if force - (or (null (imap-mailbox-status group 'uidvalidity)) - (imap-mailbox-delete group)) - ;; UNSUBSCRIBE? + (when (nnimap-possibly-change-group nil server) + (nnmail-check-syntax) + (let ((message (buffer-string)) + (message-id (message-field-value "message-id")) + sequence) + (with-current-buffer (nnimap-buffer) + (setq sequence (nnimap-send-command + "APPEND %S {%d}" (utf7-encode group t) + (length message))) + (process-send-string (get-buffer-process (current-buffer)) message) + (process-send-string (get-buffer-process (current-buffer)) "\r\n") + (let ((result (nnimap-get-response sequence))) + (when result + (cons group + (nnimap-find-article-by-message-id group message-id)))))))) + +(defun nnimap-add-cr () + (goto-char (point-min)) + (while (re-search-forward "\r?\n" nil t) + (replace-match "\r\n" t t))) + +(defun nnimap-get-groups () + (let ((result (nnimap-command "LIST \"\" \"*\"")) + groups) + (when (car result) + (dolist (line (cdr result)) + (when (and (equal (car line) "LIST") + (not (and (caadr line) + (string-match "noselect" (caadr line))))) + (push (car (last line)) groups))) + (nreverse groups)))) + +(defun nnimap-request-list (&optional server) + (nnimap-possibly-change-group nil server) + (with-current-buffer nntp-server-buffer + (erase-buffer) + (let ((groups + (with-current-buffer (nnimap-buffer) + (nnimap-get-groups))) + sequences responses) + (when groups + (with-current-buffer (nnimap-buffer) + (dolist (group groups) + (push (list (nnimap-send-command "EXAMINE %S" (utf7-encode group t)) + group) + sequences)) + (nnimap-wait-for-response (caar sequences)) + (setq responses + (nnimap-get-responses (mapcar #'car sequences)))) + (dolist (response responses) + (let* ((sequence (car response)) + (response (cadr response)) + (group (cadr (assoc sequence sequences)))) + (when (and group + (equal (caar response) "OK")) + (let ((uidnext (nnimap-find-parameter "UIDNEXT" response)) + highest exists) + (dolist (elem response) + (when (equal (cadr elem) "EXISTS") + (setq exists (string-to-number (car elem))))) + (when uidnext + (setq highest (1- (string-to-number (car uidnext))))) + (cond + ((null highest) + (insert (format "%S 0 1 y\n" (utf7-decode group t)))) + ((zerop exists) + ;; Empty group. + (insert (format "%S %d %d y\n" + (utf7-decode group t) highest (1+ highest)))) + (t + ;; Return the widest possible range. + (insert (format "%S %d 1 y\n" (utf7-decode group t) + (or highest exists))))))))) t)))) -(deffoo nnimap-request-rename-group (group new-name &optional server) - (when (nnimap-possibly-change-server server) - (imap-mailbox-rename (nnimap-decode-group-name group) - (nnimap-decode-group-name new-name) - nnimap-server-buffer))) - -(defun nnimap-expunge (mailbox server) - (when (nnimap-possibly-change-group mailbox server) - (imap-mailbox-expunge nil nnimap-server-buffer))) - -(defun nnimap-acl-get (mailbox server) - (when (nnimap-possibly-change-server server) - (and (imap-capability 'ACL nnimap-server-buffer) - (imap-mailbox-acl-get (nnimap-decode-group-name mailbox) - nnimap-server-buffer)))) - -(defun nnimap-acl-edit (mailbox method old-acls new-acls) - (when (nnimap-possibly-change-server (cadr method)) - (unless (imap-capability 'ACL nnimap-server-buffer) - (error "Your server does not support ACL editing")) - (with-current-buffer nnimap-server-buffer - ;; delete all removed identifiers - (mapc (lambda (old-acl) - (unless (assoc (car old-acl) new-acls) - (or (imap-mailbox-acl-delete (car old-acl) - (nnimap-decode-group-name mailbox)) - (error "Can't delete ACL for %s" (car old-acl))))) - old-acls) - ;; set all changed acl's - (mapc (lambda (new-acl) - (let ((new-rights (cdr new-acl)) - (old-rights (cdr (assoc (car new-acl) old-acls)))) - (unless (and old-rights new-rights - (string= old-rights new-rights)) - (or (imap-mailbox-acl-set (car new-acl) new-rights - (nnimap-decode-group-name mailbox)) - (error "Can't set ACL for %s to %s" (car new-acl) - new-rights))))) - new-acls) - t))) +(defun nnimap-retrieve-group-data-early (server infos) + (when (nnimap-possibly-change-group nil server) + (with-current-buffer (nnimap-buffer) + ;; QRESYNC handling isn't implemented. + (let ((qresyncp (member "notQRESYNC" (nnimap-capabilities nnimap-object))) + marks groups sequences) + ;; Go through the infos and gather the data needed to know + ;; what and how to request the data. + (dolist (info infos) + (setq marks (gnus-info-marks info)) + (push (list (gnus-group-real-name (gnus-info-group info)) + (cdr (assq 'active marks)) + (cdr (assq 'uid marks))) + groups)) + ;; Then request the data. + (erase-buffer) + (dolist (elem groups) + (if (and qresyncp + (nth 2 elem)) + (push + (list 'qresync + (nnimap-send-command "EXAMINE %S (QRESYNC (%s %s))" + (car elem) + (car (nth 2 elem)) + (cdr (nth 2 elem))) + nil + (car elem)) + sequences) + (let ((start + (if (nth 1 elem) + ;; Fetch the last 100 flags. + (max 1 (- (cdr (nth 1 elem)) 100)) + 1))) + (push (list (nnimap-send-command "EXAMINE %S" (car elem)) + (nnimap-send-command "UID FETCH %d:* FLAGS" start) + start + (car elem)) + sequences)))) + sequences)))) + +(defun nnimap-finish-retrieve-group-infos (server infos sequences) + (when (and sequences + (nnimap-possibly-change-group nil server)) + (with-current-buffer (nnimap-buffer) + ;; Wait for the final data to trickle in. + (nnimap-wait-for-response (cadar sequences)) + ;; Now we should have all the data we need, no matter whether + ;; we're QRESYNCING, fetching all the flags from scratch, or + ;; just fetching the last 100 flags per group. + (nnimap-update-infos (nnimap-flags-to-marks + (nnimap-parse-flags + (nreverse sequences))) + infos)))) + +(defun nnimap-update-infos (flags infos) + (dolist (info infos) + (let ((group (gnus-group-real-name (gnus-info-group info)))) + (nnimap-update-info info (cdr (assoc group flags)))))) + +(defun nnimap-update-info (info marks) + (when marks + (destructuring-bind (existing flags high low uidnext start-article) marks + (let ((group (gnus-info-group info)) + (completep (and start-article + (= start-article 1)))) + ;; First set the active ranges based on high/low. + (if (or completep + (not (gnus-active group))) + (gnus-set-active group + (if high + (cons low high) + ;; No articles in this group. + (cons (1- uidnext) uidnext))) + (setcdr (gnus-active group) high)) + ;; Then update the list of read articles. + (let* ((unread + (gnus-compress-sequence + (gnus-set-difference + (gnus-set-difference + existing + (cdr (assoc "\\Seen" flags))) + (cdr (assoc "\\Flagged" flags))))) + (read (gnus-range-difference + (cons start-article high) unread))) + (when (> start-article 1) + (setq read + (gnus-range-nconcat + (gnus-sorted-range-intersection + (cons 1 start-article) + (gnus-info-read info)) + read))) + (gnus-info-set-read info read) + ;; Update the marks. + (setq marks (gnus-info-marks info)) + ;; Note the active level for the next run-through. + (let ((active (assq 'active marks))) + (if active + (setcdr active (gnus-active group)) + (push (cons 'active (gnus-active group)) marks))) + (dolist (type (cdr nnimap-mark-alist)) + (let ((old-marks (assoc (car type) marks)) + (new-marks (gnus-compress-sequence + (cdr (assoc (cadr type) flags))))) + (setq marks (delq old-marks marks)) + (pop old-marks) + (when (and old-marks + (> start-article 1)) + (setq old-marks (gnus-range-difference + (cons start-article high) + old-marks)) + (setq new-marks (gnus-range-nconcat old-marks new-marks))) + (when new-marks + (push (cons (car type) new-marks) marks))) + (gnus-info-set-marks info marks))))))) + +(defun nnimap-flags-to-marks (groups) + (let (data group totalp uidnext articles start-article mark) + (dolist (elem groups) + (setq group (car elem) + uidnext (cadr elem) + start-article (caddr elem) + articles (cdddr elem)) + (let ((high (caar articles)) + marks low existing) + (dolist (article articles) + (setq low (car article)) + (push (car article) existing) + (dolist (flag (cdr article)) + (setq mark (assoc flag marks)) + (if (not mark) + (push (list flag (car article)) marks) + (setcdr mark (cons (car article) (cdr mark))))) + (push (list group existing marks high low uidnext start-article) + data)))) + data)) + +(defun nnimap-parse-flags (sequences) + (goto-char (point-min)) + (let (start end articles groups uidnext elems) + (dolist (elem sequences) + (destructuring-bind (group-sequence flag-sequence totalp group) elem + ;; The EXAMINE was successful. + (when (and (search-forward (format "\n%d OK " group-sequence) nil t) + (progn + (forward-line 1) + (setq start (point)) + (if (re-search-backward "UIDNEXT \\([0-9]+\\)" + (or end (point-min)) t) + (setq uidnext (string-to-number (match-string 1))) + (setq uidnext nil)) + (goto-char start)) + ;; The UID FETCH FLAGS was successful. + (search-forward (format "\n%d OK " flag-sequence) nil t)) + (setq end (point)) + (goto-char start) + (while (re-search-forward "^\\* [0-9]+ FETCH (\\(.*\\))" end t) + (setq elems (nnimap-parse-line (match-string 1))) + (push (cons (string-to-number (cadr (member "UID" elems))) + (cadr (member "FLAGS" elems))) + articles)) + (push (nconc (list group uidnext totalp) articles) groups) + (setq articles nil)))) + groups)) + +(defun nnimap-find-process-buffer (buffer) + (cadr (assoc buffer nnimap-connection-alist))) + +(defun nnimap-request-post (&optional server) + (setq nnimap-status-string "Read-only server") + nil) - -;;; Internal functions - -;; -;; This is confusing. -;; -;; mark => read, tick, draft, reply etc -;; flag => "\\Seen", "\\Flagged", "\\Draft", "gnus-expire" etc -;; predicate => "SEEN", "FLAGGED", "DRAFT", "KEYWORD gnus-expire" etc -;; -;; Mark should not really contain 'read since it's not a "mark" in the Gnus -;; world, but we cheat. Mark == gnus-article-mark-lists + '(read . read). -;; - -(defconst nnimap-mark-to-predicate-alist - (mapcar - (lambda (pair) ; cdr is the mark - (or (assoc (cdr pair) - '((read . "SEEN") - (tick . "FLAGGED") - (draft . "DRAFT") - (recent . "RECENT") - (reply . "ANSWERED"))) - (cons (cdr pair) - (format "KEYWORD gnus-%s" (symbol-name (cdr pair)))))) - (cons '(read . read) gnus-article-mark-lists))) - -(defun nnimap-mark-to-predicate (pred) - "Convert a Gnus mark (a symbol such as read, tick, expire) to a IMAP predicate. -This is a string such as \"SEEN\", \"FLAGGED\", \"KEYWORD gnus-expire\", -to be used within a IMAP SEARCH query." - (cdr (assq pred nnimap-mark-to-predicate-alist))) - -(defconst nnimap-mark-to-flag-alist - (mapcar - (lambda (pair) - (or (assoc (cdr pair) - '((read . "\\Seen") - (tick . "\\Flagged") - (draft . "\\Draft") - (recent . "\\Recent") - (reply . "\\Answered"))) - (cons (cdr pair) - (format "gnus-%s" (symbol-name (cdr pair)))))) - (cons '(read . read) gnus-article-mark-lists))) - -(defun nnimap-mark-to-flag-1 (preds) - (if (and (not (null preds)) (listp preds)) - (cons (nnimap-mark-to-flag (car preds)) - (nnimap-mark-to-flag (cdr preds))) - (cdr (assoc preds nnimap-mark-to-flag-alist)))) - -(defun nnimap-mark-to-flag (preds &optional always-list make-string) - "Convert a Gnus mark (a symbol such as read, tick, expire) to a IMAP flag. -This is a string such as \"\\Seen\", \"\\Flagged\", \"gnus-expire\", to -be used in a STORE FLAGS command." - (let ((result (nnimap-mark-to-flag-1 preds))) - (setq result (if (and (or make-string always-list) - (not (listp result))) - (list result) - result)) - (if make-string - (mapconcat (lambda (flag) - (if (listp flag) - (mapconcat 'identity flag " ") - flag)) - result " ") - result))) - -(defun nnimap-mark-permanent-p (mark &optional group) - "Return t if MARK can be permanently (between IMAP sessions) saved on articles, in GROUP." - (imap-message-flag-permanent-p (nnimap-mark-to-flag mark))) +(defun nnimap-possibly-change-group (group server) + (let ((open-result t)) + (when (and server + (not (nnimap-server-opened server))) + (setq open-result (nnimap-open-server server))) + (cond + ((not open-result) + nil) + ((not group) + t) + (t + (with-current-buffer (nnimap-buffer) + (if (equal group (nnimap-group nnimap-object)) + t + (let ((result (nnimap-command "SELECT %S" (utf7-encode group t)))) + (when (car result) + (setf (nnimap-group nnimap-object) group) + result)))))))) + +(defun nnimap-find-connection (buffer) + "Find the connection delivering to BUFFER." + (let ((entry (assoc buffer nnimap-connection-alist))) + (when entry + (if (and (buffer-name (cadr entry)) + (get-buffer-process (cadr entry)) + (memq (process-status (get-buffer-process (cadr entry))) + '(open run))) + (get-buffer-process (cadr entry)) + (setq nnimap-connection-alist (delq entry nnimap-connection-alist)) + nil)))) + +(defvar nnimap-sequence 0) + +(defun nnimap-send-command (&rest args) + (process-send-string + (get-buffer-process (current-buffer)) + (nnimap-log-command + (format "%d %s\r\n" + (incf nnimap-sequence) + (apply #'format args)))) + nnimap-sequence) + +(defun nnimap-log-command (command) + (with-current-buffer (get-buffer-create "*imap log*") + (goto-char (point-max)) + (insert (format-time-string "%H:%M:%S") " " command)) + command) + +(defun nnimap-command (&rest args) + (erase-buffer) + (let* ((sequence (apply #'nnimap-send-command args)) + (response (nnimap-get-response sequence))) + (if (equal (caar response) "OK") + (cons t response) + (nnheader-report 'nnimap "%s" + (mapconcat #'identity (car response) " ")) + nil))) + +(defun nnimap-get-response (sequence) + (nnimap-wait-for-response sequence) + (nnimap-parse-response)) + +(defun nnimap-wait-for-response (sequence &optional messagep) + (goto-char (point-max)) + (while (or (bobp) + (progn + (forward-line -1) + (not (looking-at (format "^%d .*\n" sequence))))) + (when messagep + (message "Read %dKB" (/ (buffer-size) 1000))) + (nnheader-accept-process-output (get-buffer-process (current-buffer))) + (goto-char (point-max)))) + +(defun nnimap-parse-response () + (let ((lines (split-string (nnimap-last-response-string) "\r\n" t)) + result) + (dolist (line lines) + (push (cdr (nnimap-parse-line line)) result)) + ;; Return the OK/error code first, and then all the "continuation + ;; lines" afterwards. + (cons (pop result) + (nreverse result)))) + +;; Parse an IMAP response line lightly. They look like +;; "* OK [UIDVALIDITY 1164213559] UIDs valid", typically, so parse +;; the lines into a list of strings and lists of string. +(defun nnimap-parse-line (line) + (let (char result) + (with-temp-buffer + (insert line) + (goto-char (point-min)) + (while (not (eobp)) + (if (eql (setq char (following-char)) ? ) + (forward-char 1) + (push + (cond + ((eql char ?\[) + (split-string (buffer-substring + (1+ (point)) (1- (search-forward "]"))))) + ((eql char ?\() + (split-string (buffer-substring + (1+ (point)) (1- (search-forward ")"))))) + ((eql char ?\") + (forward-char 1) + (buffer-substring (point) (1- (search-forward "\"")))) + (t + (buffer-substring (point) (if (search-forward " " nil t) + (1- (point)) + (goto-char (point-max)))))) + result))) + (nreverse result)))) + +(defun nnimap-last-response-string () + (save-excursion + (forward-line 1) + (let ((end (point))) + (forward-line -1) + (when (not (bobp)) + (forward-line -1) + (while (and (not (bobp)) + (eql (following-char) ?*)) + (forward-line -1)) + (unless (eql (following-char) ?*) + (forward-line 1))) + (buffer-substring (point) end)))) + +(defun nnimap-get-responses (sequences) + (let (responses) + (dolist (sequence sequences) + (goto-char (point-min)) + (when (re-search-forward (format "^%d " sequence) nil t) + (push (list sequence (nnimap-parse-response)) + responses))) + responses)) + +(defvar nnimap-incoming-split-list nil) + +(defun nnimap-fetch-inbox (articles) + (erase-buffer) + (nnimap-wait-for-response + (nnimap-send-command + "UID FETCH %s %s" + (nnimap-article-ranges articles) + (format "(UID %s%s)" + (format + (if (member "IMAP4REV1" + (nnimap-capabilities nnimap-object)) + "BODY.PEEK[HEADER] BODY.PEEK" + "RFC822.PEEK")) + (if nnimap-split-download-body-default + "" + "[1]"))) + t)) + +(defun nnimap-split-incoming-mail () + (with-current-buffer (nnimap-buffer) + (let ((nnimap-incoming-split-list nil) + (nnmail-split-methods nnimap-split-methods) + (nnmail-inhibit-default-split-group t) + (groups (nnimap-get-groups)) + new-articles) + (erase-buffer) + (nnimap-command "SELECT %S" nnimap-inbox) + (setq new-articles (nnimap-new-articles (nnimap-get-flags "1:*"))) + (when new-articles + (nnimap-fetch-inbox new-articles) + (nnimap-transform-split-mail) + (nnheader-ms-strip-cr) + (nnmail-cache-open) + (nnmail-split-incoming (current-buffer) + #'nnimap-save-mail-spec + nil nil + #'nnimap-dummy-active-number) + (when nnimap-incoming-split-list + (let ((specs (nnimap-make-split-specs nnimap-incoming-split-list)) + sequences) + ;; Create any groups that doesn't already exist on the + ;; server first. + (dolist (spec specs) + (unless (member (car spec) groups) + (nnimap-command "CREATE %S" (utf7-encode (car spec) t)))) + ;; Then copy over all the messages. + (erase-buffer) + (dolist (spec specs) + (let ((group (car spec)) + (ranges (cdr spec))) + (push (list (nnimap-send-command "UID COPY %s %S" + (nnimap-article-ranges ranges) + (utf7-encode group t)) + ranges) + sequences))) + ;; Wait for the last COPY response... + (when sequences + (nnimap-wait-for-response (caar sequences)) + ;; And then mark the successful copy actions as deleted, + ;; and possibly expunge them. + (nnimap-mark-and-expunge-incoming + (nnimap-parse-copied-articles sequences))))))))) + +(defun nnimap-mark-and-expunge-incoming (range) + (when range + (setq range (nnimap-article-ranges range)) + (nnimap-send-command + "UID STORE %s +FLAGS.SILENT (\\Deleted)" range) + (cond + ;; If the server supports it, we now delete the message we have + ;; just copied over. + ((member "UIDPLUS" (nnimap-capabilities nnimap-object)) + (nnimap-send-command "UID EXPUNGE %s" range)) + ;; If it doesn't support UID EXPUNGE, then we only expunge if the + ;; user has configured it. + (nnimap-expunge-inbox + (nnimap-send-command "EXPUNGE"))))) + +(defun nnimap-parse-copied-articles (sequences) + (let (sequence copied range) + (goto-char (point-min)) + (while (re-search-forward "^\\([0-9]+\\) OK " nil t) + (setq sequence (string-to-number (match-string 1))) + (when (setq range (cadr (assq sequence sequences))) + (push (gnus-uncompress-range range) copied))) + (gnus-compress-sequence (sort (apply #'nconc copied) #'<)))) + +(defun nnimap-new-articles (flags) + (let (new) + (dolist (elem flags) + (when (or (null (cdr elem)) + (and (not (member "\\Deleted" (cdr elem))) + (not (member "\\Seen" (cdr elem))))) + (push (car elem) new))) + (gnus-compress-sequence (nreverse new)))) + +(defun nnimap-make-split-specs (list) + (let ((specs nil) + entry) + (dolist (elem list) + (destructuring-bind (article spec) elem + (dolist (group (delete nil (mapcar #'car spec))) + (unless (setq entry (assoc group specs)) + (push (setq entry (list group)) specs)) + (setcdr entry (cons article (cdr entry)))))) + (dolist (entry specs) + (setcdr entry (gnus-compress-sequence (sort (cdr entry) #'<)))) + specs)) + +(defun nnimap-transform-split-mail () + (goto-char (point-min)) + (let (article bytes) + (block nil + (while (not (eobp)) + (while (not (looking-at "^\\* [0-9]+ FETCH.*UID \\([0-9]+\\)")) + (delete-region (point) (progn (forward-line 1) (point))) + (when (eobp) + (return))) + (setq article (match-string 1) + bytes (nnimap-get-length)) + (delete-region (line-beginning-position) (line-end-position)) + ;; Insert MMDF separator, and a way to remember what this + ;; article UID is. + (insert (format "\^A\^A\^A\^A\n\nX-nnimap-article: %s" article)) + (forward-char (1+ bytes)) + (setq bytes (nnimap-get-length)) + (delete-region (line-beginning-position) (line-end-position)) + (forward-char (1+ bytes)) + (delete-region (line-beginning-position) (line-end-position)))))) + +(defun nnimap-dummy-active-number (group &optional server) + 1) + +(defun nnimap-save-mail-spec (group-art &optional server full-nov) + (let (article) + (goto-char (point-min)) + (if (not (re-search-forward "X-nnimap-article: \\([0-9]+\\)" nil t)) + (error "Invalid nnimap mail") + (setq article (string-to-number (match-string 1)))) + (push (list article group-art) + nnimap-incoming-split-list))) (provide 'nnimap) diff --git a/lisp/gnus/nnir.el b/lisp/gnus/nnir.el index 6096c6fb374..27610e7aba2 100644 --- a/lisp/gnus/nnir.el +++ b/lisp/gnus/nnir.el @@ -744,8 +744,7 @@ and show thread that contains this article." nnir-artlist ;; Cache miss. (setq nnir-artlist (nnir-run-query group))) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (if (zerop (length nnir-artlist)) (progn (setq nnir-current-query nil diff --git a/lisp/gnus/nnmail.el b/lisp/gnus/nnmail.el index b7d834ecd8c..3e6cee82521 100644 --- a/lisp/gnus/nnmail.el +++ b/lisp/gnus/nnmail.el @@ -614,6 +614,7 @@ using different case (i.e. mailing-list@domain vs Mailing-List@Domain)." (defvar nnmail-split-tracing nil) (defvar nnmail-split-trace nil) +(defvar nnmail-inhibit-default-split-group nil) @@ -674,8 +675,7 @@ using different case (i.e. mailing-list@domain vs Mailing-List@Domain)." "Returns an assoc of group names and active ranges. nn*-request-list should have been called before calling this function." ;; Go through all groups from the active list. - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (nnmail-parse-active))) (defun nnmail-parse-active () @@ -1058,7 +1058,9 @@ If SOURCE is a directory spec, try to return the group name component." (defun nnmail-split-incoming (incoming func &optional exit-func group artnum-func) "Go through the entire INCOMING file and pick out each individual mail. -FUNC will be called with the buffer narrowed to each mail." +FUNC will be called with the buffer narrowed to each mail. +INCOMING can also be a buffer object. In that case, the mail +will be copied over from that buffer." (let ( ;; If this is a group-specific split, we bind the split ;; methods to just this group. (nnmail-split-methods (if (and group @@ -1066,12 +1068,13 @@ FUNC will be called with the buffer narrowed to each mail." (list (list group "")) nnmail-split-methods)) (nnmail-group-names-not-encoded-p t)) - (save-excursion - ;; Insert the incoming file. - (set-buffer (get-buffer-create nnmail-article-buffer)) + ;; Insert the incoming file. + (with-current-buffer (get-buffer-create nnmail-article-buffer) (erase-buffer) - (let ((coding-system-for-read nnmail-incoming-coding-system)) - (mm-insert-file-contents incoming)) + (if (bufferp incoming) + (insert-buffer-substring incoming) + (let ((coding-system-for-read nnmail-incoming-coding-system)) + (mm-insert-file-contents incoming))) (prog1 (if (zerop (buffer-size)) 0 @@ -1100,15 +1103,15 @@ FUNC will be called with the group name to determine the article number." (obuf (current-buffer)) group-art method grp) (if (and (sequencep methods) - (= (length methods) 1)) + (= (length methods) 1) + (not nnmail-inhibit-default-split-group)) ;; If there is only just one group to put everything in, we ;; just return a list with just this one method in. (setq group-art (list (cons (caar methods) (funcall func (caar methods))))) ;; We do actual comparison. - (save-excursion - ;; Copy the article into the work buffer. - (set-buffer nntp-server-buffer) + ;; Copy the article into the work buffer. + (with-current-buffer nntp-server-buffer (erase-buffer) (insert-buffer-substring obuf) ;; Narrow to headers. @@ -1149,7 +1152,8 @@ FUNC will be called with the group name to determine the article number." ;; just call this function here and use the ;; result. (or (funcall nnmail-split-methods) - '("bogus")) + (and (not nnmail-inhibit-default-split-group) + '("bogus"))) (error (nnheader-message 5 "Error in `nnmail-split-methods'; using `bogus' mail group: %S" error-info) @@ -1194,12 +1198,14 @@ FUNC will be called with the group name to determine the article number." group-art)) ;; This is the final group, which is used as a ;; catch-all. - (unless group-art + (when (and (not group-art) + (not nnmail-inhibit-default-split-group)) (setq group-art (list (cons (car method) (funcall func (car method)))))))) ;; Fall back on "bogus" if all else fails. - (unless group-art + (when (and (not group-art) + (not nnmail-inhibit-default-split-group)) (setq group-art (list (cons "bogus" (funcall func "bogus")))))) ;; Produce a trace if non-empty. (when (and trace nnmail-split-trace) @@ -1572,10 +1578,9 @@ See the documentation for the variable `nnmail-split-fancy' for details." (and nnmail-cache-buffer (buffer-name nnmail-cache-buffer))) () ; The buffer is open. - (save-excursion - (set-buffer + (with-current-buffer (setq nnmail-cache-buffer - (get-buffer-create " *nnmail message-id cache*"))) + (get-buffer-create " *nnmail message-id cache*")) (gnus-add-buffer) (when (file-exists-p nnmail-message-id-cache-file) (nnheader-insert-file-contents nnmail-message-id-cache-file)) @@ -1587,8 +1592,7 @@ See the documentation for the variable `nnmail-split-fancy' for details." nnmail-treat-duplicates (buffer-name nnmail-cache-buffer) (buffer-modified-p nnmail-cache-buffer)) - (save-excursion - (set-buffer nnmail-cache-buffer) + (with-current-buffer nnmail-cache-buffer ;; Weed out the excess number of Message-IDs. (goto-char (point-max)) (when (search-backward "\n" nil t nnmail-message-id-cache-length) @@ -1623,8 +1627,7 @@ See the documentation for the variable `nnmail-split-fancy' for details." ;; pass the first (of possibly >1) group which matches. -Josh (unless (gnus-buffer-live-p nnmail-cache-buffer) (nnmail-cache-open)) - (save-excursion - (set-buffer nnmail-cache-buffer) + (with-current-buffer nnmail-cache-buffer (goto-char (point-max)) (if (and grp (not (string= "" grp)) (gnus-methods-equal-p gnus-command-method @@ -1657,8 +1660,7 @@ See the documentation for the variable `nnmail-split-fancy' for details." ;; cache. (defun nnmail-cache-fetch-group (id) (when (and nnmail-treat-duplicates nnmail-cache-buffer) - (save-excursion - (set-buffer nnmail-cache-buffer) + (with-current-buffer nnmail-cache-buffer (goto-char (point-max)) (when (search-backward id nil t) (beginning-of-line) @@ -1702,8 +1704,7 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details." (defun nnmail-cache-id-exists-p (id) (when nnmail-treat-duplicates - (save-excursion - (set-buffer nnmail-cache-buffer) + (with-current-buffer nnmail-cache-buffer (goto-char (point-max)) (search-backward id nil t)))) diff --git a/lisp/gnus/nnmaildir.el b/lisp/gnus/nnmaildir.el index 827eafdc7ed..b79e7103cef 100644 --- a/lisp/gnus/nnmaildir.el +++ b/lisp/gnus/nnmaildir.el @@ -208,20 +208,16 @@ by nnmaildir-request-article.") (eval param)) (defmacro nnmaildir--with-nntp-buffer (&rest body) - `(save-excursion - (set-buffer nntp-server-buffer) + `(with-current-buffer nntp-server-buffer ,@body)) (defmacro nnmaildir--with-work-buffer (&rest body) - `(save-excursion - (set-buffer (get-buffer-create " *nnmaildir work*")) + `(with-current-buffer (get-buffer-create " *nnmaildir work*") ,@body)) (defmacro nnmaildir--with-nov-buffer (&rest body) - `(save-excursion - (set-buffer (get-buffer-create " *nnmaildir nov*")) + `(with-current-buffer (get-buffer-create " *nnmaildir nov*") ,@body)) (defmacro nnmaildir--with-move-buffer (&rest body) - `(save-excursion - (set-buffer (get-buffer-create " *nnmaildir move*")) + `(with-current-buffer (get-buffer-create " *nnmaildir move*") ,@body)) (defmacro nnmaildir--subdir (dir subdir) @@ -1249,8 +1245,7 @@ by nnmaildir-request-article.") (setf (nnmaildir--srv-error nnmaildir--cur-server) "Article has expired") (throw 'return nil)) - (save-excursion - (set-buffer (or to-buffer nntp-server-buffer)) + (with-current-buffer (or to-buffer nntp-server-buffer) (erase-buffer) (nnheader-insert-file-contents nnmaildir-article-file-name)) (cons gname num-msgid)))) @@ -1289,8 +1284,7 @@ by nnmaildir-request-article.") (setf (nnmaildir--srv-error nnmaildir--cur-server) (concat "File exists: " tmpfile)) (throw 'return nil)) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (gmm-write-region (point-min) (point-max) tmpfile nil 'no-message nil 'excl)) (unix-sync) ;; no fsync :( diff --git a/lisp/gnus/nnmairix.el b/lisp/gnus/nnmairix.el index 04db76b942a..b43a83e3a33 100644 --- a/lisp/gnus/nnmairix.el +++ b/lisp/gnus/nnmairix.el @@ -445,8 +445,7 @@ Other back ends might or might not work.") nil) ((not query) ;; No query -> return empty group - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (insert (concat "211 0 1 0 " group)) t)) @@ -501,9 +500,9 @@ Other back ends might or might not work.") (nnmairix-request-group-with-article-number-correction folder qualgroup))) ((and (= rval 1) - (save-excursion (set-buffer nnmairix-mairix-output-buffer) - (goto-char (point-min)) - (looking-at "^Matched 0 messages"))) + (with-current-buffer nnmairix-mairix-output-buffer + (goto-char (point-min)) + (looking-at "^Matched 0 messages"))) ;; No messages found -> return empty group (nnheader-message 5 "Mairix: No matches found.") (set-buffer nntp-server-buffer) @@ -584,8 +583,7 @@ Other back ends might or might not work.") (when server (nnmairix-open-server server)) (if (nnmairix-call-backend "request-list" nnmairix-backend-server) (let (cpoint cur qualgroup folder) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (setq cpoint (point)) (while (re-search-forward nnmairix-group-regexp (point-max) t) @@ -699,8 +697,7 @@ Other back ends might or might not work.") (when (or (eq nnmairix-propagate-marks-upon-close t) (and (eq nnmairix-propagate-marks-upon-close 'ask) (y-or-n-p "Propagate marks to original articles? "))) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (nnmairix-propagate-marks) ;; update mairix group (gnus-group-jump-to-group qualgroup) @@ -998,8 +995,7 @@ with m:msgid of the current article and enabled threads." (if server (if (gnus-buffer-live-p gnus-article-buffer) (progn - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (gnus-summary-toggle-header 1) (setq mid (message-fetch-field "Message-ID"))) (while (string-match "[<>]" mid) @@ -1021,8 +1017,7 @@ f:current_from." (if server (if (gnus-buffer-live-p gnus-article-buffer) (progn - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (gnus-summary-toggle-header 1) (setq from (cadr (gnus-extract-address-components (gnus-fetch-field "From")))) @@ -1046,8 +1041,7 @@ before deleting a group on the back end. SERVER specifies nnmairix server." (when (nnmairix-call-backend "request-list" nnmairix-backend-server) (let (cur qualgroup folder) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (while (re-search-forward nnmairix-group-regexp (point-max) t) (setq cur (match-string 0) @@ -1152,8 +1146,7 @@ nnmairix server. Only marks from current session will be set." (push (list (car ogroup) (list (list number) (nth 1 mid-marks) (nth 2 mid-marks))) number-cache))))) ;; now we set the marks - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (nnheader-message 5 "nnmairix: Propagating marks...") (dolist (cur number-cache) (setq method (gnus-find-method-for-group (car cur))) @@ -1272,9 +1265,8 @@ Marks propagation has to be enabled for this to work." "Call mairix binary with COMMAND, using FOLDER and SEARCHQUERY. If THREADS is non-nil, enable full threads." (let ((args (cons (car command) '(nil t nil)))) - (save-excursion - (set-buffer - (get-buffer-create nnmairix-mairix-output-buffer)) + (with-current-buffer + (get-buffer-create nnmairix-mairix-output-buffer) (erase-buffer) (when (> (length command) 1) (setq args (append args (cdr command)))) @@ -1291,9 +1283,8 @@ If THREADS is non-nil, enable full threads." (defun nnmairix-call-mairix-binary-raw (command query) "Call mairix binary with COMMAND and QUERY in raw mode." (let ((args (cons (car command) '(nil t nil)))) - (save-excursion - (set-buffer - (get-buffer-create nnmairix-mairix-output-buffer)) + (with-current-buffer + (get-buffer-create nnmairix-mairix-output-buffer) (erase-buffer) (when (> (length command) 1) (setq args (append args (cdr command)))) @@ -1430,8 +1421,7 @@ MAIRIXGROUP. NUMC contains values for article number correction." (corr (not (zerop numc))) (name (buffer-name nntp-server-buffer)) header cur xref) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (erase-buffer) (set-buffer nntp-server-buffer) (goto-char (point-min)) @@ -1621,8 +1611,7 @@ search in raw mode." (let ((server (nth 1 gnus-current-select-method)) mid rval group allgroups) ;; get message id - (save-excursion - (set-buffer gnus-article-buffer) + (with-current-buffer gnus-article-buffer (gnus-summary-toggle-header 1) (setq mid (message-fetch-field "Message-ID")) ;; first check the registry (if available) @@ -1678,8 +1667,7 @@ SERVER." (if (zerop (nnmairix-call-mairix-binary-raw (split-string nnmairix-mairix-command) (list (concat "m:" mid)))) - (save-excursion - (set-buffer nnmairix-mairix-output-buffer) + (with-current-buffer nnmairix-mairix-output-buffer (goto-char (point-min)) (while (re-search-forward "^/.*$" nil t) (push (nnmairix-get-group-from-file-path (match-string 0)) diff --git a/lisp/gnus/nnmbox.el b/lisp/gnus/nnmbox.el index 7d71dc1c1e4..4b01bfa5c6e 100644 --- a/lisp/gnus/nnmbox.el +++ b/lisp/gnus/nnmbox.el @@ -79,8 +79,7 @@ (nnoo-define-basics nnmbox) (deffoo nnmbox-retrieve-headers (sequence &optional newsgroup server fetch-old) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let ((number (length sequence)) (count 0) @@ -149,8 +148,7 @@ (deffoo nnmbox-request-article (article &optional newsgroup server buffer) (nnmbox-possibly-change-newsgroup newsgroup server) - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (when (nnmbox-find-article article) (let (start stop) (re-search-backward (concat "^" message-unix-mail-delimiter) nil t) @@ -208,8 +206,7 @@ (nnmail-get-new-mail 'nnmbox (lambda () - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (nnmbox-save-buffer))) (file-name-directory nnmbox-mbox-file) group @@ -253,8 +250,7 @@ rest) (nnmail-activate 'nnmbox) - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (while (and articles is-old) (when (nnmbox-find-article (car articles)) (if (setq is-old @@ -292,8 +288,7 @@ result) (and (nnmbox-request-article article group server) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (erase-buffer) (insert-buffer-substring nntp-server-buffer) (goto-char (point-min)) @@ -364,8 +359,7 @@ (deffoo nnmbox-request-replace-article (article group buffer) (nnmbox-possibly-change-newsgroup group) - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (if (not (nnmbox-find-article article)) nil (nnmbox-delete-mail t t) @@ -391,8 +385,7 @@ ;; Delete all articles in GROUP. (if (not force) () ; Don't delete the articles. - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (goto-char (point-min)) ;; Delete all articles in this group. (let ((ident (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":")) @@ -412,8 +405,7 @@ (deffoo nnmbox-request-rename-group (group new-name &optional server) (nnmbox-possibly-change-newsgroup group server) - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (goto-char (point-min)) (let ((ident (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":")) (new-ident (concat "\nX-Gnus-Newsgroup: " new-name ":")) @@ -633,8 +625,7 @@ (nnmbox-create-mbox) (if (and nnmbox-mbox-buffer (buffer-name nnmbox-mbox-buffer) - (save-excursion - (set-buffer nnmbox-mbox-buffer) + (with-current-buffer nnmbox-mbox-buffer (= (buffer-size) (nnheader-file-size nnmbox-mbox-file)))) () (save-excursion diff --git a/lisp/gnus/nnml.el b/lisp/gnus/nnml.el index 6d676bb8514..5d62192819e 100644 --- a/lisp/gnus/nnml.el +++ b/lisp/gnus/nnml.el @@ -160,8 +160,7 @@ non-nil.") (deffoo nnml-retrieve-headers (sequence &optional group server fetch-old) (when (nnml-possibly-change-directory group server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let* ((file nil) (number (length sequence)) @@ -405,8 +404,7 @@ non-nil.") (let (nnml-current-directory nnml-current-group nnml-article-file-alist) - (save-excursion - (set-buffer buf) + (with-current-buffer buf (insert-buffer-substring nntp-server-buffer) (setq result (eval accept-form)) (kill-buffer (current-buffer)) @@ -462,8 +460,7 @@ non-nil.") (deffoo nnml-request-replace-article (article group buffer) (nnml-possibly-change-directory group) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (nnml-possibly-create-directory group) (let ((chars (nnmail-insert-lines)) (art (concat (int-to-string article) "\t")) @@ -478,8 +475,7 @@ non-nil.") t) (setq headers (nnml-parse-head chars article)) ;; Replace the NOV line in the NOV file. - (save-excursion - (set-buffer (nnml-open-nov group)) + (with-current-buffer (nnml-open-nov group) (goto-char (point-min)) (if (or (looking-at art) (search-forward (concat "\n" art) nil t)) @@ -614,8 +610,7 @@ non-nil.") ;; Find an article number in the current group given the Message-ID. (defun nnml-find-group-number (id server) - (save-excursion - (set-buffer (get-buffer-create " *nnml id*")) + (with-current-buffer (get-buffer-create " *nnml id*") (let ((alist nnml-group-alist) number) ;; We want to look through all .overview files, but we want to @@ -657,8 +652,7 @@ non-nil.") nil (let ((nov (expand-file-name nnml-nov-file-name nnml-current-directory))) (when (file-exists-p nov) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (nnheader-insert-file-contents nov) (if (and fetch-old @@ -804,16 +798,14 @@ article number. This function is called narrowed to an article." (defun nnml-add-incremental-nov (group article headers) "Add a nov line for the GROUP nov headers, incrementally." - (save-excursion - (set-buffer (nnml-open-incremental-nov group)) + (with-current-buffer (nnml-open-incremental-nov group) (goto-char (point-max)) (mail-header-set-number headers article) (nnheader-insert-nov headers))) (defun nnml-add-nov (group article headers) "Add a nov line for the GROUP base." - (save-excursion - (set-buffer (nnml-open-nov group)) + (with-current-buffer (nnml-open-nov group) (goto-char (point-max)) (mail-header-set-number headers article) (nnheader-insert-nov headers))) @@ -844,8 +836,7 @@ article number. This function is called narrowed to an article." "") decoded))) (file-name-coding-system nnmail-pathname-coding-system)) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (set (make-local-variable 'nnml-nov-buffer-file-name) (nnmail-group-pathname decoded nnml-directory nnml-nov-file-name)) (erase-buffer) @@ -942,9 +933,8 @@ Unless no-active is non-nil, update the active file too." (nov (concat dir nnml-nov-file-name)) (nov-buffer (get-buffer-create " *nov*")) chars file headers) - (save-excursion + (with-current-buffer nov-buffer ;; Init the nov buffer. - (set-buffer nov-buffer) (buffer-disable-undo) (erase-buffer) (set-buffer nntp-server-buffer) @@ -964,20 +954,17 @@ Unless no-active is non-nil, update the active file too." (unless (zerop (buffer-size)) (goto-char (point-min)) (setq headers (nnml-parse-head chars (caar files))) - (save-excursion - (set-buffer nov-buffer) + (with-current-buffer nov-buffer (goto-char (point-max)) (nnheader-insert-nov headers))) (widen)) (setq files (cdr files))) - (save-excursion - (set-buffer nov-buffer) + (with-current-buffer nov-buffer (nnmail-write-region (point-min) (point-max) nov nil 'nomesg) (kill-buffer (current-buffer)))))) (defun nnml-nov-delete-article (group article) - (save-excursion - (set-buffer (nnml-open-nov group)) + (with-current-buffer (nnml-open-nov group) (when (nnheader-find-nov-line article) (delete-region (point) (progn (forward-line 1) (point))) (when (bobp) @@ -1260,8 +1247,7 @@ Use the nov database for the current group if available." (gnus-info-set-marks info newmarks)) ;; 3/ Update the NOV entry for this article: (unless nnml-nov-is-evil - (save-excursion - (set-buffer (nnml-open-nov group)) + (with-current-buffer (nnml-open-nov group) (when (nnheader-find-nov-line old-number) ;; Replace the article number: (looking-at old-number-string) diff --git a/lisp/gnus/nnnil.el b/lisp/gnus/nnnil.el index f6bc35aec3c..dd5e9841c15 100644 --- a/lisp/gnus/nnnil.el +++ b/lisp/gnus/nnnil.el @@ -58,8 +58,7 @@ (defun nnnil-request-group (group &optional server fast) (let (deactivate-mark) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (insert "411 no such news group\n"))) (setq nnnil-status-string "No such group") diff --git a/lisp/gnus/nnspool.el b/lisp/gnus/nnspool.el index cdf2b829ecc..ee1e36f55c7 100644 --- a/lisp/gnus/nnspool.el +++ b/lisp/gnus/nnspool.el @@ -109,8 +109,7 @@ there.") (deffoo nnspool-retrieve-headers (articles &optional group server fetch-old) "Retrieve the headers of ARTICLES." - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (when (nnspool-possibly-change-directory group) (let* ((number (length articles)) @@ -209,8 +208,7 @@ there.") (nnspool-possibly-change-directory group) (let ((res (nnspool-request-article id))) (when res - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (when (search-forward "\n\n" nil t) (delete-region (point-min) (point))) @@ -221,8 +219,7 @@ there.") (nnspool-possibly-change-directory group) (let ((res (nnspool-request-article id))) (when res - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (goto-char (point-min)) (when (search-forward "\n\n" nil t) (delete-region (1- (point)) (point-max))) @@ -343,8 +340,7 @@ there.") ;;; Internal functions. (defun nnspool-inews-sentinel (proc status) - (save-excursion - (set-buffer (process-buffer proc)) + (with-current-buffer (process-buffer proc) (goto-char (point-min)) (if (or (zerop (buffer-size)) (search-forward "spooled" nil t)) @@ -367,8 +363,7 @@ there.") last) (if (not (file-exists-p nov)) () - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (if nnspool-sift-nov-with-sed (nnspool-sift-nov-with-sed articles nov) diff --git a/lisp/gnus/nntp.el b/lisp/gnus/nntp.el index 3cdd63084ef..59f803d8c6a 100644 --- a/lisp/gnus/nntp.el +++ b/lisp/gnus/nntp.el @@ -1014,7 +1014,8 @@ command whose response triggered the error." (unless (assq 'nntp-address defs) (setq defs (append defs (list (list 'nntp-address server))))) (nnoo-change-server 'nntp server defs) - (unless connectionless + (if connectionless + t (or (nntp-find-connection nntp-server-buffer) (nntp-open-connection nntp-server-buffer))))) diff --git a/lisp/gnus/nnvirtual.el b/lisp/gnus/nnvirtual.el index c94d1837fa9..18faa23a80e 100644 --- a/lisp/gnus/nnvirtual.el +++ b/lisp/gnus/nnvirtual.el @@ -93,8 +93,7 @@ component group will show up when you enter the virtual group.") (deffoo nnvirtual-retrieve-headers (articles &optional newsgroup server fetch-old) (when (nnvirtual-possibly-change-server server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (if (stringp (car articles)) 'headers @@ -170,8 +169,7 @@ component group will show up when you enter the virtual group.") ;; the nntp-server-buffer, which is where Gnus expects to find ;; them. (prog1 - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (insert-buffer-substring vbuf) ;; FIX FIX FIX, we should be able to sort faster than @@ -215,8 +213,7 @@ component group will show up when you enter the virtual group.") (t (setq nnvirtual-last-accessed-component-group cgroup) (if buffer - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer ;; We bind this here to avoid double decoding. (let ((gnus-article-decode-hook nil)) (gnus-request-article-this-buffer (cdr amap) cgroup))) @@ -335,8 +332,7 @@ component group will show up when you enter the virtual group.") (when (not (numberp (gnus-group-unread g))) (gnus-activate-group g))) nnvirtual-component-groups) - (save-excursion - (set-buffer gnus-group-buffer) + (with-current-buffer gnus-group-buffer (gnus-group-catchup-current nil all))))) diff --git a/lisp/gnus/nnweb.el b/lisp/gnus/nnweb.el index 3b4f71c80aa..e6289c57bca 100644 --- a/lisp/gnus/nnweb.el +++ b/lisp/gnus/nnweb.el @@ -104,8 +104,7 @@ Valid types include `google', `dejanews', and `gmane'.") (deffoo nnweb-retrieve-headers (articles &optional group server fetch-old) (nnweb-possibly-change-server group server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (erase-buffer) (let (article header) (mm-with-unibyte-current-buffer @@ -147,16 +146,14 @@ Valid types include `google', `dejanews', and `gmane'.") (deffoo nnweb-close-group (group &optional server) (nnweb-possibly-change-server group server) (when (gnus-buffer-live-p nnweb-buffer) - (save-excursion - (set-buffer nnweb-buffer) + (with-current-buffer nnweb-buffer (set-buffer-modified-p nil) (kill-buffer nnweb-buffer))) t) (deffoo nnweb-request-article (article &optional group server buffer) (nnweb-possibly-change-server group server) - (save-excursion - (set-buffer (or buffer nntp-server-buffer)) + (with-current-buffer (or buffer nntp-server-buffer) (let* ((header (cadr (assq article nnweb-articles))) (url (and header (mail-header-xref header)))) (when (or (and url @@ -185,16 +182,14 @@ Valid types include `google', `dejanews', and `gmane'.") (deffoo nnweb-close-server (&optional server) (when (and (nnweb-server-opened server) (gnus-buffer-live-p nnweb-buffer)) - (save-excursion - (set-buffer nnweb-buffer) + (with-current-buffer nnweb-buffer (set-buffer-modified-p nil) (kill-buffer nnweb-buffer))) (nnoo-close-server 'nnweb server)) (deffoo nnweb-request-list (&optional server) (nnweb-possibly-change-server nil server) - (save-excursion - (set-buffer nntp-server-buffer) + (with-current-buffer nntp-server-buffer (nnmail-generate-active (list (assoc server nnweb-group-alist))) t)) @@ -402,8 +397,7 @@ Valid types include `google', `dejanews', and `gmane'.") (defun nnweb-google-create-mapping () "Perform the search and create a number-to-url alist." - (save-excursion - (set-buffer nnweb-buffer) + (with-current-buffer nnweb-buffer (erase-buffer) (nnheader-message 7 "Searching google...") (when (funcall (nnweb-definition 'search) nnweb-search) @@ -459,8 +453,7 @@ Valid types include `google', `dejanews', and `gmane'.") ;;; (defun nnweb-gmane-create-mapping () "Perform the search and create a number-to-url alist." - (save-excursion - (set-buffer nnweb-buffer) + (with-current-buffer nnweb-buffer (let ((case-fold-search t) (active (or (cadr (assoc nnweb-group nnweb-group-alist)) (cons 1 0))) diff --git a/lisp/gnus/pop3.el b/lisp/gnus/pop3.el index 950cae25c4e..63ed8004a9f 100644 --- a/lisp/gnus/pop3.el +++ b/lisp/gnus/pop3.el @@ -279,9 +279,9 @@ Returns the process associated with the connection." (let ((coding-system-for-read 'binary) (coding-system-for-write 'binary) process) - (with-current-buffer - (get-buffer-create (concat " trace of POP session to " - mailhost)) + (save-excursion + (set-buffer (get-buffer-create (concat " trace of POP session to " + mailhost))) (erase-buffer) (setq pop3-read-point (point-min)) (setq process @@ -353,7 +353,8 @@ Returns the process associated with the connection." Return the response string if optional second argument is non-nil." (let ((case-fold-search nil) match-end) - (with-current-buffer (process-buffer process) + (save-excursion + (set-buffer (process-buffer process)) (goto-char pop3-read-point) (while (and (memq (process-status process) '(open run)) (not (search-forward "\r\n" nil t))) @@ -510,7 +511,8 @@ Otherwise, return the size of the message-id MSG" (if msg (string-to-number (nth 2 (split-string response " "))) (let ((start pop3-read-point) end) - (with-current-buffer (process-buffer process) + (save-excursion + (set-buffer (process-buffer process)) (while (not (re-search-forward "^\\.\r\n" nil t)) (pop3-accept-process-output process) (goto-char start)) @@ -528,7 +530,8 @@ Otherwise, return the size of the message-id MSG" (pop3-send-command process (format "RETR %s" msg)) (pop3-read-response process) (let ((start pop3-read-point) end) - (with-current-buffer (process-buffer process) + (save-excursion + (set-buffer (process-buffer process)) (while (not (re-search-forward "^\\.\r\n" nil t)) (pop3-accept-process-output process) (goto-char start)) @@ -544,7 +547,8 @@ Otherwise, return the size of the message-id MSG" (setq end (point-marker)) (pop3-clean-region start end) (pop3-munge-message-separator start end) - (with-current-buffer crashbuf + (save-excursion + (set-buffer crashbuf) (erase-buffer)) (copy-to-buffer crashbuf start end) (delete-region start end) @@ -581,7 +585,8 @@ and close the connection." (pop3-send-command process "QUIT") (pop3-read-response process t) (if process - (with-current-buffer (process-buffer process) + (save-excursion + (set-buffer (process-buffer process)) (goto-char (point-max)) (delete-process process)))) diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el index d836f320164..a2668199469 100644 --- a/lisp/gnus/smime.el +++ b/lisp/gnus/smime.el @@ -708,8 +708,7 @@ The following commands are available: "Go to the SMIME buffer." (interactive) (unless (get-buffer smime-buffer) - (save-excursion - (set-buffer (get-buffer-create smime-buffer)) + (with-current-buffer (get-buffer-create smime-buffer) (smime-mode))) (smime-draw-buffer) (switch-to-buffer smime-buffer)) diff --git a/lisp/gnus/spam-report.el b/lisp/gnus/spam-report.el index 0e32e934040..e73444e85c0 100644 --- a/lisp/gnus/spam-report.el +++ b/lisp/gnus/spam-report.el @@ -109,8 +109,7 @@ Reports is as ham when HAM is set." ;; select this particular article (gnus-summary-select-article nil nil nil article) ;; resend it to the destination address - (save-excursion - (set-buffer gnus-original-article-buffer) + (with-current-buffer gnus-original-article-buffer (message-resend spam-report-resend-to)))) (defun spam-report-resend-ham (articles) @@ -292,8 +291,7 @@ symbol `ask', query before flushing the queue file." (gnus-message 7 "Processing requests using `%s'." spam-report-url-ping-function)) (or file (setq file spam-report-requests-file)) - (save-excursion - (set-buffer (find-file-noselect file)) + (with-current-buffer (find-file-noselect file) (goto-char (point-min)) (while (and (not (eobp)) (re-search-forward diff --git a/lisp/gnus/spam.el b/lisp/gnus/spam.el index d079be2fcd2..b7908e5507b 100644 --- a/lisp/gnus/spam.el +++ b/lisp/gnus/spam.el @@ -1605,8 +1605,7 @@ to find it out)." article)))) (defun spam-fetch-article-header (article) - (save-excursion - (set-buffer gnus-summary-buffer) + (with-current-buffer gnus-summary-buffer (gnus-read-header article) (nth 3 (assq article gnus-newsgroup-data)))) ;;}}} @@ -2172,8 +2171,7 @@ See `spam-ifile-database'." (with-temp-buffer (let ((temp-buffer-name (buffer-name)) (db-param (spam-get-ifile-database-parameter))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (apply 'call-process-region (point-min) (point-max) spam-ifile-program nil temp-buffer-name nil "-c" @@ -2318,9 +2316,8 @@ With a non-nil REMOVE, remove the ADDRESSES." ;; else, we have a list of addresses here (unless (file-exists-p (file-name-directory file)) (make-directory (file-name-directory file) t)) - (save-excursion - (set-buffer - (find-file-noselect file)) + (with-current-buffer + (find-file-noselect file) (dolist (a addresses) (when (stringp a) (goto-char (point-min)) @@ -2521,8 +2518,7 @@ With a non-nil REMOVE, remove the ADDRESSES." return) (with-temp-buffer (let ((temp-buffer-name (buffer-name))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (apply 'call-process-region (point-min) (point-max) spam-bogofilter-program @@ -2579,8 +2575,7 @@ With a non-nil REMOVE, remove the ADDRESSES." (let ((article-buffer-name (buffer-name))) (with-temp-buffer (let ((temp-buffer-name (buffer-name))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (let ((status (apply 'call-process-region (point-min) (point-max) @@ -2656,8 +2651,7 @@ With a non-nil REMOVE, remove the ADDRESSES." (let ((article-buffer-name (buffer-name))) (with-temp-buffer (let ((temp-buffer-name (buffer-name))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (apply 'call-process-region (point-min) (point-max) spam-assassin-program nil temp-buffer-name nil spam-spamassassin-arguments)) @@ -2691,8 +2685,7 @@ With a non-nil REMOVE, remove the ADDRESSES." ;; group the articles into mbox format (dolist (article articles) (let (article-string) - (save-excursion - (set-buffer summary-buffer-name) + (with-current-buffer summary-buffer-name (setq article-string (spam-get-article-as-string article))) (when (stringp article-string) (insert "From \n") ; mbox separator (sa-learn only checks the @@ -2755,8 +2748,7 @@ With a non-nil REMOVE, remove the ADDRESSES." return) (with-temp-buffer (let ((temp-buffer-name (buffer-name))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (apply 'call-process-region (point-min) (point-max) spam-bsfilter-program @@ -2841,8 +2833,7 @@ With a non-nil REMOVE, remove the ADDRESSES." return) (with-temp-buffer (let ((temp-buffer-name (buffer-name))) - (save-excursion - (set-buffer article-buffer-name) + (with-current-buffer article-buffer-name (apply 'call-process-region (point-min) (point-max) spam-crm114-program diff --git a/lisp/gnus/starttls.el b/lisp/gnus/starttls.el index 02a557de5cc..bf1982f54dd 100644 --- a/lisp/gnus/starttls.el +++ b/lisp/gnus/starttls.el @@ -254,8 +254,7 @@ handshake, or nil on failure." (starttls-set-process-query-on-exit-flag process nil) (while (and (processp process) (eq (process-status process) 'run) - (save-excursion - (set-buffer buffer) + (with-current-buffer buffer (goto-char old-max) (not (setq done (re-search-forward starttls-connect nil t))))) diff --git a/lisp/gnus/utf7.el b/lisp/gnus/utf7.el index cca647d94b2..74bd092a3dd 100644 --- a/lisp/gnus/utf7.el +++ b/lisp/gnus/utf7.el @@ -205,6 +205,7 @@ Characters are in raw byte pairs in narrowed buffer." (mm-decode-coding-region (point-min) (point-max) 'iso-8859-1) (mm-enable-multibyte)) +;;;###autoload (defun utf7-encode (string &optional for-imap) "Encode UTF-7 STRING. Use IMAP modification if FOR-IMAP is non-nil." (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap)) diff --git a/lisp/net/netrc.el b/lisp/net/netrc.el index 408eca9bac7..3636c892726 100644 --- a/lisp/net/netrc.el +++ b/lisp/net/netrc.el @@ -228,6 +228,7 @@ MODE can be \"login\" or \"password\", suitable for passing to (eq type (car (cddr service))))))) (cadr service))) +;;;###autoload (defun netrc-credentials (machine &rest ports) "Return a user name/password pair. Port specifications will be prioritised in the order they are |