(defvoo nnimap-inbox nil
"The mail box where incoming mail arrives and should be split out of.
-For example, \"INBOX\".")
+This can be a string or a list of strings
+For example, \"INBOX\" or (\"INBOX\" \"SENT\").")
(defvoo nnimap-split-methods nil
"How mail is split.
(defvoo nnimap-fetch-partial-articles nil
"If non-nil, Gnus will fetch partial articles.
-If t, nnimap will fetch only the first part. If a string, it
+If t, Gnus will fetch only the first part. If a string, it
will fetch all parts that have types that match that string. A
likely value would be \"text/\" to automatically fetch all
textual parts.")
(defun nnimap-transform-headers ()
(goto-char (point-min))
- (let (article bytes lines size string)
+ (let (article lines size string)
(block nil
(while (not (eobp))
- (while (not (looking-at "\\* [0-9]+ FETCH.+?UID \\([0-9]+\\)"))
+ (while (not (looking-at "\\* [0-9]+ FETCH"))
(delete-region (point) (progn (forward-line 1) (point)))
(when (eobp)
(return)))
- (setq article (match-string 1))
+ (goto-char (match-end 0))
;; Unfold quoted {number} strings.
- (while (re-search-forward "[^]][ (]{\\([0-9]+\\)}\r?\n"
- (1+ (line-end-position)) t)
+ (while (re-search-forward
+ "[^]][ (]{\\([0-9]+\\)}\r?\n"
+ (save-excursion
+ ;; Start of the header section.
+ (or (re-search-forward "] {[0-9]+}\r?\n" nil t)
+ ;; Start of the next FETCH.
+ (re-search-forward "\\* [0-9]+ FETCH" nil t)
+ (point-max)))
+ t)
(setq size (string-to-number (match-string 1)))
(delete-region (+ (match-beginning 0) 2) (point))
(setq string (buffer-substring (point) (+ (point) size)))
(delete-region (point) (+ (point) size))
- (insert (format "%S" string)))
- (setq bytes (nnimap-get-length)
- lines nil)
+ (insert (format "%S" (mm-subst-char-in-string ?\n ?\s string))))
(beginning-of-line)
+ (setq article
+ (and (re-search-forward "UID \\([0-9]+\\)" (line-end-position)
+ t)
+ (match-string 1)))
+ (setq lines nil)
(setq size
(and (re-search-forward "RFC822.SIZE \\([0-9]+\\)"
(line-end-position)
result))
(mapconcat #'identity (nreverse result) ",")))))
-(deffoo nnimap-open-server (server &optional defs)
+(deffoo nnimap-open-server (server &optional defs no-reconnect)
(if (nnimap-server-opened server)
t
(unless (assq 'nnimap-address defs)
(setq defs (append defs (list (list 'nnimap-address server)))))
(nnoo-change-server 'nnimap server defs)
- (or (nnimap-find-connection nntp-server-buffer)
- (nnimap-open-connection nntp-server-buffer))))
+ (if no-reconnect
+ (nnimap-find-connection nntp-server-buffer)
+ (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*"
+ (generate-new-buffer (format " *nnimap %s %s %s*"
nnimap-address nnimap-server-port
(gnus-buffer-exists-p buffer)))
(mm-disable-multibyte)
;; Move the article to a different method.
(let ((result (eval accept-form)))
(when result
+ (nnimap-possibly-change-group group server)
(nnimap-delete-article article)
result)))))))
nnimap-inbox
nnimap-split-methods)
(nnheader-message 7 "nnimap %s splitting mail..." server)
- (nnimap-split-incoming-mail)
+ (if (listp nnimap-inbox)
+ (dolist (nnimap-inbox nnimap-inbox)
+ (nnimap-split-incoming-mail))
+ (nnimap-split-incoming-mail))
(nnheader-message 7 "nnimap %s splitting mail...done" server)))
(defun nnimap-marks-to-flags (marks)
(dolist (response responses)
(let* ((sequence (car response))
(response (cadr response))
- (group (cadr (assoc sequence sequences))))
+ (group (cadr (assoc sequence sequences)))
+ (egroup (encode-coding-string group 'utf-8)))
(when (and group
(equal (caar response) "OK"))
(let ((uidnext (nnimap-find-parameter "UIDNEXT" response))
(setq highest (1- (string-to-number (car uidnext)))))
(cond
((null highest)
- (insert (format "%S 0 1 y\n" (utf7-decode group t))))
+ (insert (format "%S 0 1 y\n" egroup)))
((zerop exists)
;; Empty group.
- (insert (format "%S %d %d y\n"
- (utf7-decode group t)
+ (insert (format "%S %d %d y\n" egroup
highest (1+ highest))))
(t
;; Return the widest possible range.
- (insert (format "%S %d 1 y\n" (utf7-decode group t)
+ (insert (format "%S %d 1 y\n" egroup
(or highest exists)))))))))
t)))))
(nnimap-get-groups)))
(unless (assoc group nnimap-current-infos)
;; Insert dummy numbers here -- they don't matter.
- (insert (format "%S 0 1 y\n" (utf7-encode group)))))
+ (insert (format "%S 0 1 y\n" (encode-coding-string group 'utf-8)))))
t)))
(deffoo nnimap-retrieve-group-data-early (server infos)
(setf (nnimap-group nnimap-object) nil)
(setf (nnimap-initial-resync nnimap-object) 0)
(let ((qresyncp (nnimap-capability "QRESYNC"))
- params groups sequences active uidvalidity modseq group)
+ params groups sequences active uidvalidity modseq group
+ unexist)
;; Go through the infos and gather the data needed to know
;; what and how to request the data.
(dolist (info infos)
group (nnimap-decode-gnus-group
(gnus-group-real-name (gnus-info-group info)))
active (cdr (assq 'active params))
+ unexist (assq 'unexist (gnus-info-marks info))
uidvalidity (cdr (assq 'uidvalidity params))
modseq (cdr (assq 'modseq params)))
(setf (nnimap-examined nnimap-object) group)
(if (and qresyncp
uidvalidity
active
- modseq)
+ modseq
+ unexist)
(push
(list (nnimap-send-command "EXAMINE %S (%s (%s %s))"
(utf7-encode group t)
'qresync
nil group 'qresync)
sequences)
- (let ((start
- (if (and active uidvalidity)
- ;; Fetch the last 100 flags.
- (max 1 (- (cdr active) 100))
- 1))
- (command
+ (let ((command
(if uidvalidity
"EXAMINE"
;; If we don't have a UIDVALIDITY, then this is
;; have to do a SELECT (which is slower than an
;; examine), but will tell us whether the group
;; is read-only or not.
- "SELECT")))
- (setf (nnimap-initial-resync nnimap-object)
- (1+ (nnimap-initial-resync nnimap-object)))
+ "SELECT"))
+ start)
+ (if (and active uidvalidity unexist)
+ ;; Fetch the last 100 flags.
+ (setq start (max 1 (- (cdr active) 100)))
+ (incf (nnimap-initial-resync nnimap-object))
+ (setq start 1))
(push (list (nnimap-send-command "%s %S" command
(utf7-encode group t))
(nnimap-send-command "UID FETCH %d:* FLAGS" start)
(deffoo nnimap-finish-retrieve-group-infos (server infos sequences)
(when (and sequences
- (nnimap-possibly-change-group nil server)
+ (nnimap-possibly-change-group nil server t)
;; Check that the process is still alive.
(get-buffer-process (nnimap-buffer))
(memq (process-status (get-buffer-process (nnimap-buffer)))
(setq new-marks (gnus-range-nconcat old-marks new-marks)))
(when new-marks
(push (cons (car type) new-marks) marks)))))
+ ;; Keep track of non-existing articles.
+ (let* ((old-unexists (assq 'unexist marks))
+ (unexists
+ (if completep
+ (gnus-range-difference
+ (gnus-active group)
+ (gnus-compress-sequence existing))
+ (gnus-add-to-range
+ (cdr old-unexists)
+ (gnus-list-range-difference
+ existing (gnus-active group))))))
+ (if old-unexists
+ (setcdr old-unexists unexists)
+ (push (cons 'unexist unexists) marks)))
(gnus-info-set-marks info marks t))))
;; Tell Gnus whether there are any \Recent messages in any of
;; the groups.
(gnus-sorted-complement existing new-marks))))
(when ticks
(push (cons (car type) ticks) marks)))
+ (gnus-info-set-marks info marks t))
+ ;; Add vanished to the list of unexisting articles.
+ (when vanished
+ (let* ((old-unexists (assq 'unexist marks))
+ (unexists (gnus-range-add (cdr old-unexists) vanished)))
+ (if old-unexists
+ (setcdr old-unexists unexists)
+ (push (cons 'unexist unexists) marks)))
(gnus-info-set-marks info marks t))))
(defun nnimap-imap-ranges-to-gnus-ranges (irange)
(defun nnimap-parse-flags (sequences)
(goto-char (point-min))
- ;; Change \Delete etc to %Delete, so that the reader can read it.
+ ;; Change \Delete etc to %Delete, so that the Emacs Lisp reader can
+ ;; read it.
(subst-char-in-region (point-min) (point-max)
?\\ ?% t)
;; Remove any MODSEQ entries in the buffer, because they may contain
vanished highestmodseq)
articles)
groups)
- (goto-char end)
+ (if (eq flag-sequence 'qresync)
+ (goto-char end)
+ (setq end (point)))
(setq articles nil))))
groups))
(cdr (assoc "SEARCH" (cdr result))))))
nil t))))))
-(defun nnimap-possibly-change-group (group server)
+(defun nnimap-possibly-change-group (group server &optional no-reconnect)
(let ((open-result t))
(when (and server
(not (nnimap-server-opened server)))
- (setq open-result (nnimap-open-server server)))
+ (setq open-result (nnimap-open-server server nil no-reconnect)))
(cond
((not open-result)
nil)
(nnimap-wait-for-response nnimap-sequence))
nnimap-sequence)
+(defvar nnimap-record-commands nil
+ "If non-nil, log commands to the \"*imap log*\" buffer.")
+
(defun nnimap-log-command (command)
- (with-current-buffer (get-buffer-create "*imap log*")
- (goto-char (point-max))
- (insert (format-time-string "%H:%M:%S") " "
- (if nnimap-inhibit-logging
- "(inhibited)\n"
- command)))
+ (when nnimap-record-commands
+ (with-current-buffer (get-buffer-create "*imap log*")
+ (goto-char (point-max))
+ (insert (format-time-string "%H:%M:%S")
+ " [" nnimap-address "] "
+ (if nnimap-inhibit-logging
+ "(inhibited)\n"
+ command))))
command)
(defun nnimap-command (&rest args)
7 "nnimap read %dk from %s%s" (/ (buffer-size) 1000)
nnimap-address
(if (not (zerop (nnimap-initial-resync nnimap-object)))
- (format " (initial sync of %d groups; please wait)"
- (nnimap-initial-resync nnimap-object))
+ (format " (initial sync of %d group%s; please wait)"
+ (nnimap-initial-resync nnimap-object)
+ (if (= (nnimap-initial-resync nnimap-object) 1)
+ ""
+ "s"))
"")))
(nnheader-accept-process-output process)
(goto-char (point-max)))