;;; 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 <larsi@gnus.org>
;; Simon Josefsson <simon@josefsson.org>
;;; 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
(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
(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
(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
(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"))
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)))))
(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 ".")
"*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"
(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
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)
(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)
(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)
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.
(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)
(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