;;; nnimap.el --- imap backend for Gnus
-;; Copyright (C) 1998,1999 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
;; Author: Simon Josefsson <jas@pdc.kth.se>
;; Jim Radford <radford@robby.caltech.edu>
;;
;; Todo, minor things:
;;
+;; o Don't require half of Gnus -- backends should be standalone
;; o Support escape characters in `message-tokenize-header'
-;; o Split-fancy.
-;; o Support NOV nnmail-extra-headers.
;; 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 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?
+;; o What about Gnus's article editing, can we support it? NO!
;; o Use \Draft to support the draft group??
;;; Code:
(require 'nnheader)
(require 'mm-util)
(require 'gnus)
-(require 'gnus-async)
(require 'gnus-range)
(require 'gnus-start)
(require 'gnus-int)
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.")
+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.")
+
+(defvar 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.")
+
+(defvar nnimap-split-fancy nil
+ "Like `nnmail-split-fancy', which see.")
;; Authorization / Privacy variables
"Gnus callback the nnimap asynchronous callback should call.")
(defvar nnimap-callback-buffer nil
"Which buffer the asynchronous article prefetch callback should work in.")
-
-;; Various server variables.
+(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
\f
-;; Internal variables.
-(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:
-(defun nnimap-replace-in-string (string regexp to)
- "Replace substrings in STRING matching REGEXP with TO."
- (if (string-match regexp string)
- (concat (substring string 0 (match-beginning 0))
- to
- (nnimap-replace-in-string (substring string (match-end 0))
- regexp to))
- string))
-
(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)))
(old-uidvalidity (gnus-group-get-parameter gnusgroup 'uidvalidity)))
(if old-uidvalidity
(if (not (equal old-uidvalidity new-uidvalidity))
- nil;; uidvalidity clash
+ nil ;; uidvalidity clash
(gnus-group-set-parameter gnusgroup 'uidvalidity new-uidvalidity)
t)
(gnus-group-add-parameter gnusgroup (cons 'uidvalidity new-uidvalidity))
nnimap-progress-how-often)
nnimap-progress-chars)))
(with-current-buffer nntp-server-buffer
- (nnheader-insert-nov
- (with-current-buffer nnimap-server-buffer
- (vector imap-current-message
- (nnimap-replace-whitespace
- (imap-message-envelope-subject imap-current-message))
- (nnimap-replace-whitespace
- (imap-envelope-from
- (car-safe (imap-message-envelope-from
- imap-current-message))))
- (nnimap-replace-whitespace
- (imap-message-envelope-date imap-current-message))
- (nnimap-replace-whitespace
- (imap-message-envelope-message-id imap-current-message))
- (nnimap-replace-whitespace
- (let ((str (if (imap-capability 'IMAP4rev1)
- (nth 2 (assoc
- "HEADER.FIELDS REFERENCES"
- (imap-message-get
- imap-current-message 'BODYDETAIL)))
- (imap-message-get imap-current-message
- 'RFC822.HEADER))))
- (if (> (length str) (length "References: "))
- (substring str (length "References: "))
- (if (and (setq str (imap-message-envelope-in-reply-to
- imap-current-message))
- (string-match "<[^>]+>" str))
- (substring str (match-beginning 0) (match-end 0))))))
- (imap-message-get imap-current-message 'RFC822.SIZE)
- (imap-body-lines (imap-message-body imap-current-message))
- nil;; xref
- nil)))));; extra-headers
+ (let (headers lines chars uid mbx)
+ (with-current-buffer nnimap-server-buffer
+ (setq uid imap-current-message
+ mbx 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
+ (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)))
+ (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."
(nnimap-length (gnus-range-length articles))
(nnimap-counter 0))
(imap-fetch (nnimap-range-to-string articles)
- (concat "(UID RFC822.SIZE ENVELOPE BODY "
- (if (imap-capability 'IMAP4rev1)
- "BODY.PEEK[HEADER.FIELDS (References)])"
- "RFC822.HEADER.LINES (References))")))
+ (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)))))
(and (numberp nnmail-large-newsgroup)
(> nnimap-length nnmail-large-newsgroup)
(nnheader-message 6 "nnimap: Retrieving headers...done")))))
;; 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)))))
+ (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))
(nnheader-ms-strip-cr)
(funcall nnimap-callback-callback-function t)))
-(defun nnimap-request-article-part (article part prop
- &optional group server to-buffer)
+(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
(if (not nnheader-callback-function)
(with-current-buffer (or to-buffer nntp-server-buffer)
(erase-buffer)
- (insert (nnimap-demule (imap-fetch article part prop nil
- nnimap-server-buffer)))
- (nnheader-ms-strip-cr)
+ (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 9 "nnimap: Fetching (part of) article %d...done"
article)
(if (bobp)
t)
(deffoo nnimap-request-article (article &optional group server to-buffer)
- (nnimap-request-article-part
- article "RFC822.PEEK" 'RFC822 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)
- (nnimap-request-article-part
- article "RFC822.HEADER" 'RFC822.HEADER 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)
- (nnimap-request-article-part
- article "RFC822.TEXT.PEEK" 'RFC822.TEXT 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
(or (member "\\NoSelect" (imap-mailbox-get 'list-flags mbx))
(let ((info (nnimap-find-minmax-uid mbx 'examine)))
(when info
- ;; Escape SPC in mailboxes xxx relies on gnus internals
(with-current-buffer nntp-server-buffer
- (insert (format "%s %d %d y\n"
- (nnimap-replace-in-string mbx " " "\\ ")
- (or (nth 2 info) 0)
- (max 1 (or (nth 1 info) 1)))))))))))
+ (insert (format "\"%s\" %d %d y\n"
+ 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))
(or (member "\\NoSelect"
(imap-mailbox-get 'list-flags group nnimap-server-buffer))
(let ((info (nnimap-find-minmax-uid group 'examine)))
- ;; Escape SPC in mailboxes xxx relies on gnus internals
- (insert (format "211 %d %d %d %s\n" (or (nth 0 info) 0)
- (max 1 (or (nth 1 info) 1))
+ (insert (format "\"%s\" %d %d y\n" group
(or (nth 2 info) 0)
- (nnimap-replace-in-string group " " "\\ ")))))))
+ (max 1 (or (nth 1 info) 1))))))))
(gnus-message 5 "nnimap: Checking mailboxes...done")
- 'groups))
+ 'active))
(deffoo nnimap-request-update-info-internal (group info &optional server)
(when (nnimap-possibly-change-group group server)
seen))
(gnus-info-set-read info seen)))
- (mapc (lambda (pred)
- (when (and (nnimap-mark-permanent-p (cdr pred))
- (member (nnimap-mark-to-flag (cdr pred))
- (imap-mailbox-get 'flags)))
- (gnus-info-set-marks
- info
- (nnimap-update-alist-soft
- (cdr pred)
- (gnus-compress-sequence
- (imap-search (nnimap-mark-to-predicate (cdr pred))))
- (gnus-info-marks info))
- t)))
- gnus-article-mark-lists)
+ (mapcar (lambda (pred)
+ (when (and (nnimap-mark-permanent-p (cdr pred))
+ (member (nnimap-mark-to-flag (cdr pred))
+ (imap-mailbox-get 'flags)))
+ (gnus-info-set-marks
+ info
+ (nnimap-update-alist-soft
+ (cdr pred)
+ (gnus-compress-sequence
+ (imap-search (nnimap-mark-to-predicate (cdr pred))))
+ (gnus-info-marks info))
+ t)))
+ gnus-article-mark-lists)
(gnus-message 5 "nnimap: Updating info for %s...done"
(gnus-info-group info))
(gnus-message 7 "nnimap: Setting marks in %s...done" group))))
nil)
+(defun nnimap-split-fancy ()
+ "Like 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.
;; find split rule for this server / inbox
(when (setq rule (nnimap-split-find-rule server inbox))
;; iterate over articles
- (dolist (article (imap-search "UNSEEN UNDELETED"))
+ (dolist (article (imap-search nnimap-split-predicate))
(when (nnimap-request-head article)
;; copy article to right group(s)
(setq removeorig nil)
(string= (downcase mailbox) "\\noselect"))
(imap-mailbox-get 'list-flags mbx
nnimap-server-buffer))
- ;; Escape SPC in mailboxes xxx relies on gnus internals
(let ((info (nnimap-find-minmax-uid mbx 'examine)))
(when info
- (insert (format "%s %d %d y\n"
- (nnimap-replace-in-string mbx " " "\\ ")
- (or (nth 2 info) 0)
- (max 1 (or (nth 1 info) 1)))))))))
+ (insert (format "\"%s\" %d %d y\n"
+ 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))
(when nnimap-debug
(require 'trace)
(buffer-disable-undo (get-buffer-create nnimap-debug))
- (mapc (lambda (f) (trace-function-background f nnimap-debug))
+ (mapcar (lambda (f) (trace-function-background f nnimap-debug))
'(
- nnimap-replace-in-string
nnimap-possibly-change-server
nnimap-verify-uidvalidity
nnimap-find-minmax-uid