X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fnnimap.el;h=c476be6bc8d26ab5bba260f52cb328bc9421b19d;hb=87b53a7cf6962e5529f201fea6ad5b7cfdaa3bae;hp=f8c2b24cc9f860d6608ba6573575d7c6fe790576;hpb=baf05b14d1562dbcef68a2e409e7053a58c11273;p=gnus diff --git a/lisp/nnimap.el b/lisp/nnimap.el index f8c2b24cc..c476be6bc 100644 --- a/lisp/nnimap.el +++ b/lisp/nnimap.el @@ -1,6 +1,6 @@ ;;; nnimap.el --- IMAP interface for Gnus -;; Copyright (C) 2010-2013 Free Software Foundation, Inc. +;; Copyright (C) 2010-2015 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; Simon Josefsson @@ -26,10 +26,6 @@ ;;; Code: -;; For Emacs <22.2 and XEmacs. -(eval-and-compile - (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) - (eval-and-compile (require 'nnheader) ;; In Emacs 24, `open-protocol-stream' is an autoloaded alias for @@ -95,7 +91,7 @@ Uses the same syntax as `nnmail-split-methods'.") (defvoo nnimap-unsplittable-articles '(%Deleted %Seen) "Articles with the flags in the list will not be considered when splitting.") -(make-obsolete-variable 'nnimap-split-rule "see `nnimap-split-methods'" +(make-obsolete-variable 'nnimap-split-rule "see `nnimap-split-methods'." "Emacs 24.1") (defvoo nnimap-authenticator nil @@ -170,14 +166,21 @@ textual parts.") (nnimap-find-process-buffer nntp-server-buffer)) (defun nnimap-header-parameters () - (format "(UID RFC822.SIZE BODYSTRUCTURE %s)" - (format + (let (params) + (push "UID" params) + (push "RFC822.SIZE" params) + (when (nnimap-capability "X-GM-EXT-1") + (push "X-GM-LABELS" params)) + (push "BODYSTRUCTURE" params) + (push (format (if (nnimap-ver4-p) "BODY.PEEK[HEADER.FIELDS %s]" "RFC822.HEADER.LINES %s") (append '(Subject From Date Message-Id References In-Reply-To Xref) - nnmail-extra-headers)))) + nnmail-extra-headers)) + params) + (format "%s" (nreverse params)))) (deffoo nnimap-retrieve-headers (articles &optional group server fetch-old) (when group @@ -193,6 +196,8 @@ textual parts.") (nnimap-article-ranges (gnus-compress-sequence articles)) (nnimap-header-parameters)) t) + (unless (process-live-p (get-buffer-process (current-buffer))) + (error "Server closed connection")) (nnimap-transform-headers) (nnheader-remove-cr-followed-by-lf)) (insert-buffer-substring @@ -201,7 +206,7 @@ textual parts.") (defun nnimap-transform-headers () (goto-char (point-min)) - (let (article lines size string) + (let (article lines size string labels) (block nil (while (not (eobp)) (while (not (looking-at "\\* [0-9]+ FETCH")) @@ -236,6 +241,9 @@ textual parts.") t) (match-string 1))) (beginning-of-line) + (when (search-forward "X-GM-LABELS" (line-end-position) t) + (setq labels (ignore-errors (read (current-buffer))))) + (beginning-of-line) (when (search-forward "BODYSTRUCTURE" (line-end-position) t) (let ((structure (ignore-errors (read (current-buffer))))) @@ -255,7 +263,11 @@ textual parts.") (insert (format "Chars: %s\n" size))) (when lines (insert (format "Lines: %s\n" lines))) - (unless (re-search-forward "^\r$" nil t) + (when labels + (insert (format "X-GM-LABELS: %s\n" labels))) + ;; Most servers have a blank line after the headers, but + ;; Davmail doesn't. + (unless (re-search-forward "^\r$\\|^)\r?$" nil t) (goto-char (point-max))) (delete-region (line-beginning-position) (line-end-position)) (insert ".") @@ -407,6 +419,7 @@ textual parts.") "*nnimap*" (current-buffer) nnimap-address (nnimap-map-port (car ports)) :type nnimap-stream + :warn-unless-encrypted t :return-list t :shell-command nnimap-shell-program :capability-command "1 CAPABILITY\r\n" @@ -456,8 +469,8 @@ textual parts.") (nnimap-credentials (gnus-delete-duplicates (list - nnimap-address - (nnoo-current-server 'nnimap))) + (nnoo-current-server 'nnimap) + nnimap-address)) ports nnimap-user)))) (setq nnimap-object nil) @@ -626,6 +639,26 @@ textual parts.") (nnheader-ms-strip-cr) (cons group article))))))) +(deffoo nnimap-request-articles (articles &optional group server) + (when group + (setq group (nnimap-decode-gnus-group group))) + (with-current-buffer nntp-server-buffer + (let ((result (nnimap-change-group group server))) + (when result + (erase-buffer) + (with-current-buffer (nnimap-buffer) + (erase-buffer) + (when (nnimap-command + (if (nnimap-ver4-p) + "UID FETCH %s BODY.PEEK[]" + "UID FETCH %s RFC822.PEEK") + (nnimap-article-ranges (gnus-compress-sequence articles))) + (let ((buffer (current-buffer))) + (with-current-buffer nntp-server-buffer + (nnheader-insert-buffer-substring buffer) + (nnheader-ms-strip-cr))) + t)))))) + (defun nnimap-get-whole-article (article &optional command) (let ((result (nnimap-command @@ -773,41 +806,55 @@ textual parts.") articles active marks high low) (with-current-buffer nntp-server-buffer (when result - (if (and dont-check - (setq active (nth 2 (assoc group nnimap-current-infos)))) - (insert (format "211 %d %d %d %S\n" - (- (cdr active) (car active)) - (car active) - (cdr active) - group)) - (with-current-buffer (nnimap-buffer) - (erase-buffer) - (let ((group-sequence - (nnimap-send-command "SELECT %S" (utf7-encode group t))) - (flag-sequence - (nnimap-send-command "UID FETCH 1:* FLAGS"))) - (setf (nnimap-group nnimap-object) group) - (nnimap-wait-for-response flag-sequence) - (setq marks - (nnimap-flags-to-marks - (nnimap-parse-flags - (list (list group-sequence flag-sequence - 1 group "SELECT"))))) - (when (and info - marks) - (nnimap-update-infos marks (list info)) - (nnimap-store-info info (gnus-active (gnus-info-group info)))) - (goto-char (point-max)) - (let ((uidnext (nth 5 (car marks)))) - (setq high (or (if uidnext - (1- uidnext) - (nth 3 (car marks))) - 0) - low (or (nth 4 (car marks)) uidnext 1))))) - (erase-buffer) - (insert - (format - "211 %d %d %d %S\n" (1+ (- high low)) low high group))) + (when (or (not dont-check) + (not (setq active + (nth 2 (assoc group nnimap-current-infos))))) + (let ((sequences (nnimap-retrieve-group-data-early + server (list info)))) + (nnimap-finish-retrieve-group-infos server (list info) sequences + t) + (setq active (nth 2 (assoc group nnimap-current-infos))))) + (erase-buffer) + (insert (format "211 %d %d %d %S\n" + (- (cdr active) (car active)) + (car active) + (cdr active) + group)) + t)))) + +(deffoo nnimap-request-group-scan (group &optional server info) + (setq group (nnimap-decode-gnus-group group)) + (when (nnimap-change-group nil server) + (let (marks high low) + (with-current-buffer (nnimap-buffer) + (erase-buffer) + (let ((group-sequence + (nnimap-send-command "SELECT %S" (utf7-encode group t))) + (flag-sequence + (nnimap-send-command "UID FETCH 1:* FLAGS"))) + (setf (nnimap-group nnimap-object) group) + (nnimap-wait-for-response flag-sequence) + (setq marks + (nnimap-flags-to-marks + (nnimap-parse-flags + (list (list group-sequence flag-sequence + 1 group "SELECT"))))) + (when (and info + marks) + (nnimap-update-infos marks (list info)) + (nnimap-store-info info (gnus-active (gnus-info-group info)))) + (goto-char (point-max)) + (let ((uidnext (nth 5 (car marks)))) + (setq high (or (if uidnext + (1- uidnext) + (nth 3 (car marks))) + 0) + low (or (nth 4 (car marks)) uidnext 1))))) + (with-current-buffer nntp-server-buffer + (erase-buffer) + (insert + (format + "211 %d %d %d %S\n" (1+ (- high low)) low high group)) t)))) (deffoo nnimap-request-create-group (group &optional server args) @@ -968,29 +1015,29 @@ textual parts.") (setq target nil)) (nnheader-message 7 "Expiring article %s:%d" group article)) (when target - (push article deleted-articles)))))))) + (push article deleted-articles)))))) + (setq deleted-articles (nreverse deleted-articles)))) ;; Change back to the current group again. (nnimap-change-group group server) - (setq deleted-articles (nreverse deleted-articles)) (nnimap-delete-article (gnus-compress-sequence deleted-articles)) deleted-articles)) (defun nnimap-find-expired-articles (group) (let ((cutoff (nnmail-expired-article-p group nil nil))) - (with-current-buffer (nnimap-buffer) - (let ((result - (nnimap-command - "UID SEARCH SENTBEFORE %s" - (format-time-string - (format "%%d-%s-%%Y" - (upcase - (car (rassoc (nth 4 (decode-time cutoff)) - parse-time-months)))) - cutoff)))) - (and (car result) - (delete 0 (mapcar #'string-to-number - (cdr (assoc "SEARCH" (cdr result)))))))))) - + (when cutoff + (with-current-buffer (nnimap-buffer) + (let ((result + (nnimap-command + "UID SEARCH SENTBEFORE %s" + (format-time-string + (format "%%d-%s-%%Y" + (upcase + (car (rassoc (nth 4 (decode-time cutoff)) + parse-time-months)))) + cutoff)))) + (and (car result) + (delete 0 (mapcar #'string-to-number + (cdr (assoc "SEARCH" (cdr result))))))))))) (defun nnimap-find-article-by-message-id (group server message-id &optional limit) @@ -1097,6 +1144,17 @@ If LIMIT, first try to limit the search to the N last articles." (nnimap-wait-for-response sequence)))))) (deffoo nnimap-request-accept-article (group &optional server last) + (unless group + ;; We're respooling. Find out where mail splitting would place + ;; this article. + (setq group + (caar + (nnmail-article-group + ;; We don't really care about the article number, because + ;; that's determined by the IMAP server later. So just + ;; return the group name. + `(lambda (group) + (list (list group))))))) (setq group (nnimap-decode-gnus-group group)) (when (nnimap-change-group nil server) (nnmail-check-syntax) @@ -1204,7 +1262,12 @@ If LIMIT, first try to limit the search to the N last articles." (while (search-forward "* LIST " nil t) (let ((flags (read (current-buffer))) (separator (read (current-buffer))) - (group (read (current-buffer)))) + (group (buffer-substring-no-properties + (progn (skip-chars-forward " \"") + (point)) + (progn (end-of-line) + (skip-chars-backward " \"") + (point))))) (unless (member '%NoSelect flags) (push (utf7-decode (if (stringp group) group @@ -1344,7 +1407,8 @@ If LIMIT, first try to limit the search to the N last articles." command (nth 2 quirk)))) -(deffoo nnimap-finish-retrieve-group-infos (server infos sequences) +(deffoo nnimap-finish-retrieve-group-infos (server infos sequences + &optional dont-insert) (when (and sequences (nnimap-change-group nil server t) ;; Check that the process is still alive. @@ -1364,19 +1428,20 @@ If LIMIT, first try to limit the search to the N last articles." (nnimap-parse-flags (nreverse sequences))) infos) - ;; Finally, just return something resembling an active file in - ;; the nntp buffer, so that the agent can save the info, too. - (with-current-buffer nntp-server-buffer - (erase-buffer) - (dolist (info infos) - (let* ((group (gnus-info-group info)) - (active (gnus-active group))) - (when active - (insert (format "%S %d %d y\n" - (decode-coding-string - (gnus-group-real-name group) 'utf-8) - (cdr active) - (car active))))))))))) + (unless dont-insert + ;; Finally, just return something resembling an active file in + ;; the nntp buffer, so that the agent can save the info, too. + (with-current-buffer nntp-server-buffer + (erase-buffer) + (dolist (info infos) + (let* ((group (gnus-info-group info)) + (active (gnus-active group))) + (when active + (insert (format "%S %d %d y\n" + (decode-coding-string + (gnus-group-real-name group) 'utf-8) + (cdr active) + (car active)))))))))))) (defun nnimap-update-infos (flags infos) (dolist (info infos) @@ -1862,7 +1927,7 @@ Return the server's response to the SELECT or EXAMINE command." (while (and (not (bobp)) (progn (forward-line -1) - (looking-at "\\*")))) + (looking-at "\\*\\|[0-9]+ OK NOOP")))) (not (looking-at (format "%d .*\n" sequence))))) (when messagep (nnheader-message-maybe @@ -2021,12 +2086,15 @@ Return the server's response to the SELECT or EXAMINE command." (ranges (cdr spec))) (if (eq group 'junk) (setq junk-articles ranges) - (push (list (nnimap-send-command - "UID COPY %s %S" - (nnimap-article-ranges ranges) - (utf7-encode group t)) - ranges) - sequences)))) + ;; Don't copy if the message is already in its + ;; target group. + (unless (string= group nnimap-inbox) + (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))