;;; nnimap.el --- imap backend for Gnus
-;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+;; Free Software Foundation, Inc.
;; Author: Simon Josefsson <jas@pdc.kth.se>
;; Jim Radford <radford@robby.caltech.edu>
;; 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?) (unnecessery?)
+;; o Respooling (fix Gnus?) (unnecessary?)
;; o Add support for the following: (if applicable)
;; request-list-newsgroups, request-regenerate
;; list-active-group,
;; 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
;;; Code:
+(eval-when-compile (require 'cl))
+(eval-when-compile (require 'gnus-clfns))
(require 'imap)
+
(require 'nnoo)
(require 'nnmail)
(require 'nnheader)
-(require 'mm-util)
(require 'gnus)
(require 'gnus-range)
(require 'gnus-start)
(defvoo nnimap-server-port nil
"Port number on physical IMAP server.
-If nil, defaults to 993 for SSL connections and 143 otherwise.")
+If nil, defaults to 993 for TLS/SSL connections and 143 otherwise.")
;; Splitting variables
(functionp value))
(defcustom nnimap-split-rule nil
- "Mail will be split according to theese rules.
+ "Mail will be split according to these rules.
Mail is read from mailbox(es) specified in `nnimap-split-inbox'.
\(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, and the
-second is a regexp that nnimap will try to match on the header to find
-a fit.
+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
(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 "Server regexp")
(list (regexp :tag "Incoming Mailbox regexp")
(repeat :tag "Rules for matching server(s) and mailbox(es)"
(list (string :tag "Destination mailbox")
:type 'string)
(defcustom nnimap-split-fancy nil
- "Like `nnmail-split-fancy', which see."
+ "Like the variable `nnmail-split-fancy'."
:group 'nnimap
:type 'sexp)
-(defcustom nnimap-close-asynchronous nil
+(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
+analyses 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 behaviour 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."
+ :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 cought by nnimap when closing the mailbox will
not prevent Gnus from updating the group status, which may be harmful.
: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."
+ :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."
+ :type 'boolean
+ :group 'nnimap)
+
+(defvoo nnimap-need-unselect-to-notice-new-mail nil
+ "Unselect mailboxes before looking for new mail in them.
+Some servers seem to need this under some circumstances.")
+
;; Authorization / Privacy variables
(defvoo nnimap-auth-method nil
Change this if
-1) you want to connect with SSL. The SSL integration with IMAP is
- brain-dead so you'll have to tell it specifically.
+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: kerberos4, ssl, network")
+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.
installed the `imtest' program or your machine isn't configured for
Kerberos.
-Possible choices: kerberos4, cram-md5, login, anonymous.")
+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.
(defvoo nnimap-nov-file-name-suffix ".novcache"
"Suffix for NOV cache base filename.")
-(defvoo nnimap-nov-is-evil nil
- "If non-nil, nnimap will never generate or use a local nov database for this backend.
-Using nov databases will speed up header fetching considerably.
+(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.")
If this is 'imap-mailbox-lsub, then use a server-side subscription list to
restrict visible folders.")
+(defcustom nnimap-debug nil
+ "If non-nil, random debug spews are placed in *nnimap-debug* buffer."
+ :group 'nnimap
+ :type 'boolean)
+
;; Internal variables:
-(defvar nnimap-debug nil
- "Name of buffer to record debugging info.
-For example: (setq nnimap-debug \"*nnimap-debug*\")")
+(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-progress-chars '(?| ?/ ?- ?\\))
(defvar nnimap-progress-how-often 20)
(defvar nnimap-counter)
-(defvar nnimap-callback-callback-function nil
- "Gnus callback the nnimap asynchronous callback should call.")
-(defvar nnimap-callback-buffer nil
- "Which buffer the asynchronous article prefetch callback should work in.")
(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
+(defvar nnimap-current-server nil) ;; Current server
+(defvar nnimap-server-buffer nil) ;; Current servers' buffer
\f
(if (equal server "")
"unnamed"
server) "." group "." old-uidvalidity
- nnimap-nov-file-name-suffix) t))
+ 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
+ (encode-coding-string
(nnheader-replace-chars-in-string nameuid ?. ?/)
nnmail-pathname-coding-system)
dir))))
(defun nnimap-before-find-minmax-bugworkaround ()
"Function called before iterating through mailboxes with
`nnimap-find-minmax-uid'."
- ;; 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)))
+ (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 nummber in GROUP.
+ "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
- (when (imap-mailbox-select group examine)
+ (when (or (string= group (imap-current-mailbox))
+ (imap-mailbox-select group examine))
(let (minuid maxuid)
(when (> (imap-mailbox-get 'exists) 0)
(imap-fetch "1,*" "UID" nil 'nouidfetch)
group (or server nnimap-current-server))
(zerop (imap-mailbox-get 'exists group))
t ;; for OGnus to see if ignoring uidvalidity
- ;; changes has any bad effects.
+ ;; changes has any bad effects.
(yes-or-no-p
(format
"nnimap: Group %s is not uidvalid. Continue? " group)))
(with-temp-buffer
(buffer-disable-undo)
(insert headers)
- (nnheader-ms-strip-cr)
- (nnheader-fold-continuation-lines)
- (subst-char-in-region (point-min) (point-max) ?\t ? )
- (let ((head (nnheader-parse-head 'naked)))
+ (let ((head (nnheader-parse-naked-head)))
(mail-header-set-number head uid)
(mail-header-set-chars head chars)
(mail-header-set-lines head lines)
articles))))
(defun nnimap-group-overview-filename (group server)
- "Make pathname for GROUP on SERVER."
+ "Make file name for GROUP on SERVER."
(let* ((dir (file-name-as-directory (expand-file-name nnimap-directory)))
(uidvalidity (gnus-group-get-parameter
(gnus-group-prefixed-name
(if (equal server "")
"unnamed"
server) "." group "." uidvalidity
- nnimap-nov-file-name-suffix) t))
+ 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
+ (encode-coding-string
(nnheader-replace-chars-in-string name ?. ?/)
nnmail-pathname-coding-system)
dir)))
(file-exists-p (expand-file-name nameuid dir)))
(expand-file-name nameuid dir)
(expand-file-name
- (mm-encode-coding-string
+ (encode-coding-string
(nnheader-replace-chars-in-string nameuid ?. ?/)
nnmail-pathname-coding-system)
dir))))
(with-current-buffer nntp-server-buffer
(let ((nov (nnimap-group-overview-filename group server)))
(when (file-exists-p nov)
- (mm-insert-file-contents nov)
+ (nnheader-insert-file-contents nov)
(set-buffer-modified-p nil)
(let ((min (ignore-errors (goto-char (point-min))
(read (current-buffer))))
(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
+ ;; 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)
(cons low high) group server))
(when (buffer-modified-p)
(nnmail-write-region
- 1 (point-max) (nnimap-group-overview-filename group server)
- nil 'nomesg))
+ (point-min) (point-max)
+ (nnimap-group-overview-filename group server) nil 'nomesg))
(nnheader-nov-delete-outside-range low high))))
'nov)))
(with-current-buffer (get-buffer-create nnimap-server-buffer)
(nnoo-change-server 'nnimap server defs))
(or (and nnimap-server-buffer
- (imap-opened nnimap-server-buffer))
+ (imap-opened nnimap-server-buffer)
+ (if (with-current-buffer nnimap-server-buffer
+ (memq imap-state '(auth select examine)))
+ t
+ (imap-close nnimap-server-buffer)
+ (nnimap-open-connection server)))
(nnimap-open-connection server))))
(deffoo nnimap-server-opened (&optional server)
'identity)
(or string "")))
-(defun nnimap-callback ()
- (remove-hook 'imap-fetch-data-hook 'nnimap-callback)
- (with-current-buffer nnimap-callback-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 (imap-current-message) 'BODYDETAIL)))
- (imap-message-get (imap-current-message) 'RFC822)))))
- (nnheader-ms-strip-cr)
- (funcall nnimap-callback-callback-function t)))
+(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)
nnimap-server-buffer))
article)))
(when article
- (gnus-message 10 "nnimap: Fetching (part of) article %d..." article)
+ (gnus-message 10 "nnimap: Fetching (part of) article %d from %s..."
+ article (or group imap-current-mailbox
+ 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)))
- (insert (nnimap-demule (if detail
- (nth 2 (car data))
- data))))
- (nnheader-ms-strip-cr)
- (gnus-message 10 "nnimap: Fetching (part of) article %d...done"
- article)
- (if (bobp)
- (nnheader-report 'nnimap "No such article: %s"
- (imap-error-text nnimap-server-buffer))
- (cons group article)))
- (add-hook 'imap-fetch-data-hook 'nnimap-callback)
- (setq nnimap-callback-callback-function nnheader-callback-function
- nnimap-callback-buffer nntp-server-buffer)
+ (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 group imap-current-mailbox gnus-newsgroup-name))
+ (if (bobp)
+ (nnheader-report 'nnimap "No such article %d in %s: %s"
+ article (or group imap-current-mailbox
+ 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))))))
(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 group 'unseen nnimap-server-buffer)
+ (nth 3 old)))
+ nnimap-mailbox-info))
+
(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 (imap-mailbox-expunge nnimap-close-asynchronous)
- (imap-mailbox-close nnimap-close-asynchronous))
+ (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)
- (imap-mailbox-close nnimap-close-asynchronous))
- (imap-mailbox-unselect)))
+ (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))))
;; 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)
- (dolist (group groups)
- (gnus-message 7 "nnimap: Checking mailbox %s" group)
- (or (member "\\NoSelect"
- (imap-mailbox-get 'list-flags group nnimap-server-buffer))
- (let ((info (nnimap-find-minmax-uid group 'examine)))
- (when (> (or (imap-mailbox-get 'recent group
- nnimap-server-buffer) 0)
- 0)
- (push (list (cons group 0)) nnmail-split-history))
- (insert (format "\"%s\" %d %d y\n" group
- (or (nth 2 info) 0)
- (max 1 (or (nth 1 info) 1))))))))
+ (let (asyncgroups slowgroups)
+ (if (null nnimap-retrieve-groups-asynchronous)
+ (setq slowgroups groups)
+ (dolist (group groups)
+ (gnus-message 9 "nnimap: Quickly checking mailbox %s" group)
+ (add-to-list (if (gnus-gethash-safe
+ (gnus-group-prefixed-name group server)
+ nnimap-mailbox-info)
+ 'asyncgroups
+ 'slowgroups)
+ (list group (imap-mailbox-status-asynch
+ group '(uidvalidity uidnext unseen)
+ nnimap-server-buffer))))
+ (dolist (asyncgroup asyncgroups)
+ (let ((group (nth 0 asyncgroup))
+ (tag (nth 1 asyncgroup))
+ new old)
+ (when (imap-ok-p (imap-wait-for-tag tag nnimap-server-buffer))
+ (if (or (not (string=
+ (nth 0 (gnus-gethash (gnus-group-prefixed-name
+ group server)
+ nnimap-mailbox-info))
+ (imap-mailbox-get 'uidvalidity group
+ nnimap-server-buffer)))
+ (not (string=
+ (nth 1 (gnus-gethash (gnus-group-prefixed-name
+ group server)
+ nnimap-mailbox-info))
+ (imap-mailbox-get 'uidnext group
+ nnimap-server-buffer))))
+ (push (list group) slowgroups)
+ (insert (nth 3 (gnus-gethash (gnus-group-prefixed-name
+ group server)
+ nnimap-mailbox-info))))))))
+ (dolist (group slowgroups)
+ (if nnimap-retrieve-groups-asynchronous
+ (setq group (car group)))
+ (gnus-message 7 "nnimap: Mailbox %s modified" group)
+ (imap-mailbox-put 'uidnext nil group nnimap-server-buffer)
+ (or (member "\\NoSelect" (imap-mailbox-get 'list-flags group
+ nnimap-server-buffer))
+ (let* ((info (nnimap-find-minmax-uid group 'examine))
+ (str (format "\"%s\" %d %d y\n" group
+ (or (nth 2 info) 0)
+ (max 1 (or (nth 1 info) 1)))))
+ (when (> (or (imap-mailbox-get 'recent group
+ nnimap-server-buffer) 0)
+ 0)
+ (push (list (cons group 0)) nnmail-split-history))
+ (insert str)
+ (when nnimap-retrieve-groups-asynchronous
+ (gnus-sethash
+ (gnus-group-prefixed-name group server)
+ (list (or (imap-mailbox-get
+ 'uidvalidity group nnimap-server-buffer)
+ (imap-mailbox-status
+ group 'uidvalidity nnimap-server-buffer))
+ (or (imap-mailbox-get
+ 'uidnext group nnimap-server-buffer)
+ (imap-mailbox-status
+ group 'uidnext nnimap-server-buffer))
+ (or (imap-mailbox-get
+ 'unseen group nnimap-server-buffer)
+ (imap-mailbox-status
+ group 'unseen nnimap-server-buffer))
+ str)
+ nnimap-mailbox-info)))))))
(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?
+ (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..."
(gnus-info-group info))
gnus-article-mark-lists)
(when nnimap-importantize-dormant
- ;; nnimap mark dormant article as ticked too (for other clients)
+ ;; 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
nil)
(defun nnimap-split-fancy ()
- "Like nnmail-split-fancy, but uses nnimap-split-fancy."
+ "Like the function `nnmail-split-fancy', but uses `nnimap-split-fancy'."
(let ((nnmail-split-fancy nnimap-split-fancy))
(nnmail-split-fancy)))
(goto-char (point-min))
(when (and (if (stringp regexp)
(progn
- (setq regrepp (string-match "\\\\[0-9&]" group))
+ (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.
+ ;; Don't enter the article into the same group twice.
(not (assoc group to-groups)))
(push (if regrepp
(nnmail-expand-newtext group)
(let (rule inbox removeorig (inboxes (nnimap-split-find-inbox server)))
;; iterate over inboxes
(while (and (setq inbox (pop inboxes))
- (nnimap-possibly-change-group inbox)) ;; SELECT
+ (nnimap-possibly-change-group 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 (nnimap-request-head article)
+ (when (if (if (eq nnimap-split-download-body 'default)
+ nnimap-split-download-body-default
+ nnimap-split-download-body)
+ (and (nnimap-request-article article)
+ (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))
(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 to-group)))))
;; 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
+ (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))
t))))
(deffoo nnimap-request-scan (&optional group server)
(deffoo nnimap-request-create-group (group &optional server args)
(when (nnimap-possibly-change-server server)
(or (imap-mailbox-status group 'uidvalidity nnimap-server-buffer)
- (imap-mailbox-create group nnimap-server-buffer))))
+ (imap-mailbox-create 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."
(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 (gnus-uncompress-sequence arts))
- (nnimap-request-article art group server (current-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)
(let ((artseq (gnus-compress-sequence articles)))
(when (and artseq (nnimap-possibly-change-group group server))
(with-current-buffer nnimap-server-buffer
- (if force
- (progn
- (nnimap-expiry-target artseq group server)
- (when (imap-message-flags-add (imap-range-to-message-set artseq)
- "\\Deleted")
- (setq articles nil)))
- (let ((days (or (and nnmail-expiry-wait-function
- (funcall nnmail-expiry-wait-function group))
- nnmail-expiry-wait)))
- (cond ((eq days 'immediate)
- (nnimap-expiry-target artseq group server)
- (when (imap-message-flags-add
- (imap-range-to-message-set artseq) "\\Deleted")
- (setq articles nil)))
- ((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)))
+ (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))))))
+ ((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)
- (and oldarts
- (imap-message-flags-add
- (imap-range-to-message-set
- (gnus-compress-sequence oldarts))
- "\\Deleted")
- (setq articles (gnus-set-difference
- articles oldarts)))))))))))
+ (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)
(let (uid)
(if (setq uid
(if (string= nnimap-current-server nnimap-current-move-server)
- ;; moving article within same server, speed it up...
+ ;; moving article within same server, speed it up...
(and (nnimap-possibly-change-group
nnimap-current-move-group)
(imap-message-copy (number-to-string
nnimap-server-buffer))
(with-current-buffer (current-buffer)
(goto-char (point-min))
- ;; remove any 'From blabla' lines, some IMAP servers
+ ;; remove any 'From blabla' lines, some IMAP servers
;; reject the entire message otherwise.
(when (looking-at "^From[^:]")
- (kill-region (point) (progn (forward-line) (point))))
+ (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")))
+ (replace-match "\r\n"))
+ (when nnmail-cache-accepted-message-ids
+ (nnmail-cache-insert (nnmail-fetch-field "message-id")
+ group)))
+ (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))
(when nnimap-debug
(require 'trace)
- (buffer-disable-undo (get-buffer-create nnimap-debug))
- (mapcar (lambda (f) (trace-function-background f nnimap-debug))
+ (buffer-disable-undo (get-buffer-create nnimap-debug-buffer))
+ (mapcar (lambda (f) (trace-function-background f nnimap-debug-buffer))
'(
nnimap-possibly-change-server
nnimap-verify-uidvalidity