(eval-when-compile (require 'cl))
(defvar elmo-imap4-disuse-server-flag-mailbox-regexp "^#mh" ; UW imapd
- "Regexp to match IMAP4 mailbox names whose message flags on server should be ignored.
-\(Except `\\Deleted' flag\).")
+ "Regexp to match IMAP4 mailbox names whose message flags on server should be ignored (For STATUS command).")
(defvar elmo-imap4-overview-fetch-chop-length 200
"*Number of overviews to fetch in one request.")
(defconst elmo-imap4-literal-threshold 1024
"Limitation of characters that can be used in a quoted string.")
+(defconst elmo-imap4-flag-specs '((important "\\Flagged")
+ (read "\\Seen")
+ (unread "\\Seen" 'remove)
+ (answered "\\Answered")))
+
;; For debugging.
(defvar elmo-imap4-debug nil
"Non-nil forces IMAP4 folder as debug mode.
;;; Session
(eval-and-compile
(luna-define-class elmo-imap4-session (elmo-network-session)
- (capability current-mailbox read-only))
+ (capability current-mailbox read-only flags))
(luna-define-internal-accessors 'elmo-imap4-session))
;;; MIME-ELMO-IMAP Location
(luna-define-method mime-imap-location-bodystructure
((location mime-elmo-imap-location))
- (elmo-imap4-fetch-bodystructure
+ (elmo-message-fetch-bodystructure
(mime-elmo-imap-location-folder-internal location)
(mime-elmo-imap-location-number-internal location)
(mime-elmo-imap-location-strategy-internal location)))
(car (nth 1 entry))))
response)))
-(defun elmo-imap4-fetch-bodystructure (folder number strategy)
- "Fetch BODYSTRUCTURE for the message in the FOLDER with NUMBER using STRATEGY."
+(luna-define-method elmo-message-fetch-bodystructure ((folder
+ elmo-imap4-folder)
+ number strategy)
(if (elmo-fetch-strategy-use-cache strategy)
(elmo-object-load
(elmo-file-cache-expand-path
(elmo-imap4-session-set-current-mailbox-internal session mailbox)
(elmo-imap4-session-set-read-only-internal
session
- (nth 1 (assq 'read-only (assq 'ok response)))))
+ (nth 1 (assq 'read-only (assq 'ok response))))
+ (elmo-imap4-session-set-flags-internal
+ session
+ (nth 1 (or (assq 'permanentflags response)
+ (assq 'flags response)))))
(elmo-imap4-session-set-current-mailbox-internal session nil)
(if (and (eq no-error 'notify-bye)
(elmo-imap4-response-bye-p response))
"search %s") flag))
'search)))
+(defun elmo-imap4-session-flag-available-p (session flag)
+ (case flag
+ ((read unread) (elmo-string-member-ignore-case
+ "\\seen" (elmo-imap4-session-flags-internal session)))
+ (important
+ (elmo-string-member-ignore-case
+ "\\flagged" (elmo-imap4-session-flags-internal session)))
+ (digest
+ (or (elmo-string-member-ignore-case
+ "\\seen" (elmo-imap4-session-flags-internal session))
+ (elmo-string-member-ignore-case
+ "\\flagged" (elmo-imap4-session-flags-internal session))))
+ (answered
+ (elmo-string-member-ignore-case
+ (concat "\\" (symbol-name flag))
+ (elmo-imap4-session-flags-internal session)))
+ (t
+ (member "\\*" (elmo-imap4-session-flags-internal session)))))
+
+(defun elmo-imap4-folder-list-flagged (folder flag)
+ "List flagged message numbers in the FOLDER.
+FLAG is one of the `unread', `read', `important', `answered', `any'."
+ (let ((session (elmo-imap4-get-session folder))
+ (criteria (case flag
+ (read "seen")
+ (unread "unseen")
+ (important "flagged")
+ (answered "answered")
+ (new "new")
+ (any "or answered or unseen flagged")
+ (digest "or unseen flagged")
+ (t (concat "keyword " (capitalize (symbol-name flag)))))))
+ ;; Add search keywords
+ (when (or (eq flag 'digest)(eq flag 'any))
+ (let ((flags (delq 'important (elmo-get-global-flags t t))))
+ (while flags
+ (setq criteria (concat "or keyword "
+ (symbol-name (car flags))
+ " "
+ criteria))
+ (setq flags (cdr flags)))))
+ (if (elmo-imap4-session-flag-available-p session flag)
+ (progn
+ (elmo-imap4-session-select-mailbox
+ session
+ (elmo-imap4-folder-mailbox-internal folder))
+ (elmo-imap4-response-value
+ (elmo-imap4-send-command-wait
+ session
+ (format (if elmo-imap4-use-uid "uid search %s"
+ "search %s") criteria))
+ 'search))
+ ;; List flagged messages in the msgdb.
+ (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag))))
+
(defvar elmo-imap4-rfc822-size "RFC822\.SIZE")
(defvar elmo-imap4-rfc822-text "RFC822\.TEXT")
(defvar elmo-imap4-rfc822-header "RFC822\.HEADER")
;;
;; app-data:
-;; cons of flag-table and result of use-flag-p.
+;; cons of flag-table and folder structure
(defsubst elmo-imap4-fetch-callback-1-subr (entity flags app-data)
"A msgdb entity callback function."
- (let* ((use-flag (cdr app-data))
- (app-data (car app-data))
- mark)
- (if (elmo-string-member-ignore-case "\\Flagged" flags)
- (elmo-msgdb-global-mark-set (car entity)
- elmo-msgdb-important-mark))
- (if (setq mark (elmo-msgdb-global-mark-get (car entity)))
- (unless (elmo-string-member-ignore-case "\\Seen" flags)
- (setq elmo-imap4-seen-messages
- (cons
- (elmo-msgdb-overview-entity-get-number entity)
- elmo-imap4-seen-messages)))
- (setq mark (or (if (elmo-file-cache-status
- (elmo-file-cache-get (car entity)))
- ;; cached.
- (if (and use-flag (member "\\Seen" flags))
- (if (elmo-string-member-ignore-case
- "\\Answered" flags)
- elmo-msgdb-answered-cached-mark
- nil)
- elmo-msgdb-unread-cached-mark)
- ;; uncached.
- (if (elmo-string-member-ignore-case "\\Answered" flags)
- elmo-msgdb-answered-uncached-mark
- (if (and use-flag
- (elmo-string-member-ignore-case
- "\\Seen" flags))
- (if (elmo-string-member-ignore-case
- "\\Answered" flags)
- elmo-msgdb-answered-uncached-mark
- (if elmo-imap4-use-cache
- elmo-msgdb-read-uncached-mark))
- elmo-msgdb-new-mark))))))
- (setq elmo-imap4-current-msgdb
- (elmo-msgdb-append
- elmo-imap4-current-msgdb
- (list (list entity)
- (list (cons (elmo-msgdb-overview-entity-get-number entity)
- (car entity)))
- (if mark
- (list
- (list (elmo-msgdb-overview-entity-get-number entity)
- mark))))))))
+ (let ((use-flag (elmo-folder-use-flag-p (cdr app-data)))
+ (flag-table (car app-data))
+ (msg-id (elmo-message-entity-field entity 'message-id))
+ saved-flags flag-list)
+;; (when (elmo-string-member-ignore-case "\\Flagged" flags)
+;; (elmo-msgdb-global-mark-set msg-id elmo-msgdb-important-mark))
+ (setq saved-flags (elmo-flag-table-get flag-table msg-id)
+ flag-list
+ (if use-flag
+ (append
+ (and (memq 'new saved-flags)
+ (not (elmo-string-member-ignore-case "\\Seen" flags))
+ '(new))
+ (and (elmo-string-member-ignore-case "\\Flagged" flags)
+ '(important))
+ (and (not (elmo-string-member-ignore-case "\\Seen" flags))
+ '(unread))
+ (and (elmo-string-member-ignore-case "\\Answered" flags)
+ '(answered))
+ (and (elmo-file-cache-exists-p msg-id)
+ '(cached)))
+ saved-flags))
+ (when (and (or (memq 'important flag-list)
+ (memq 'answered flag-list))
+ (memq 'unread flag-list))
+ (setq elmo-imap4-seen-messages
+ (cons (elmo-message-entity-number entity)
+ elmo-imap4-seen-messages)))
+ (elmo-msgdb-append-entity elmo-imap4-current-msgdb
+ entity
+ flag-list)))
;; Current buffer is process buffer.
(defun elmo-imap4-fetch-callback-1 (element app-data)
- (elmo-imap4-fetch-callback-1-subr
- (with-temp-buffer
- (insert (or (elmo-imap4-response-bodydetail-text element)
- ""))
- ;; Delete CR.
- (goto-char (point-min))
- (while (search-forward "\r\n" nil t)
- (replace-match "\n"))
- (elmo-msgdb-create-overview-from-buffer
- (elmo-imap4-response-value element 'uid)
- (elmo-imap4-response-value element 'rfc822size)))
- (elmo-imap4-response-value element 'flags)
- app-data))
+ (let ((handler (elmo-msgdb-message-entity-handler elmo-imap4-current-msgdb)))
+ (elmo-imap4-fetch-callback-1-subr
+ (with-temp-buffer
+ (insert (or (elmo-imap4-response-bodydetail-text element)
+ ""))
+ ;; Replace all CRLF with LF.
+ (elmo-delete-cr-buffer)
+ (elmo-msgdb-create-message-entity-from-buffer
+ handler
+ (elmo-imap4-response-value element 'uid)
+ :size (elmo-imap4-response-value element 'rfc822size)))
+ (elmo-imap4-response-value element 'flags)
+ app-data)))
(defun elmo-imap4-parse-capability (string)
(if (string-match "^\\*\\(.*\\)$" string)
(format "uid %d:*" (cdr (car killed)))
"all"))))
-(luna-define-method elmo-folder-list-unreads-plugged
- ((folder elmo-imap4-folder))
- (elmo-imap4-list folder "unseen"))
-
-(luna-define-method elmo-folder-list-importants-plugged
- ((folder elmo-imap4-folder))
- (elmo-imap4-list folder "flagged"))
-
-(luna-define-method elmo-folder-list-answereds-plugged
- ((folder elmo-imap4-folder))
- (elmo-imap4-list folder "answered"))
-
-(defun elmo-imap4-folder-list-any-plugged (folder)
- (elmo-imap4-list folder "or answered or unseen flagged"))
-
-(defun elmo-imap4-folder-list-digest-plugged (folder)
- (elmo-imap4-list folder "or unseen flagged"))
+(luna-define-method elmo-folder-list-flagged-plugged
+ ((folder elmo-imap4-folder) flag)
+ (elmo-imap4-folder-list-flagged folder flag))
(luna-define-method elmo-folder-use-flag-p ((folder elmo-imap4-folder))
(not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
(elmo-imap4-send-command-wait
session
(list "list " (elmo-imap4-mailbox root) " *"))))
+ ;; The response of Courier-imap doesn't contain a specified folder itself.
+ (unless (member root result)
+ (setq result
+ (append result
+ (elmo-imap4-response-get-selectable-mailbox-list
+ (elmo-imap4-send-command-wait
+ session
+ (list "list \"\" " (elmo-imap4-mailbox root)))))))
(when (or (not (string= (elmo-net-folder-user-internal folder)
elmo-imap4-default-user))
(not (eq (elmo-net-folder-auth-internal folder)
(elmo-imap4-session-select-mailbox session
(elmo-imap4-folder-mailbox-internal
folder))
- (setq set-list (elmo-imap4-make-number-set-list
- numbers
- elmo-imap4-number-set-chop-length))
- (while set-list
- (with-current-buffer (elmo-network-session-buffer session)
- (setq elmo-imap4-fetch-callback nil)
- (setq elmo-imap4-fetch-callback-data nil))
- (unless (elmo-imap4-response-ok-p
- (elmo-imap4-send-command-wait
- session
- (format
- (if elmo-imap4-use-uid
- "uid store %s %sflags.silent (%s)"
- "store %s %sflags.silent (%s)")
- (cdr (car set-list))
- (if remove "-" "+")
- flag)))
- (setq response 'fail))
- (setq set-list (cdr set-list)))
- (not (eq response 'fail))))
+ (when (or (elmo-string-member-ignore-case
+ flag
+ (elmo-imap4-session-flags-internal session))
+ (member "\\*" (elmo-imap4-session-flags-internal session))
+ (string= flag "\\Deleted")) ; XXX Humm..
+ (setq set-list (elmo-imap4-make-number-set-list
+ numbers
+ elmo-imap4-number-set-chop-length))
+ (while set-list
+ (with-current-buffer (elmo-network-session-buffer session)
+ (setq elmo-imap4-fetch-callback nil)
+ (setq elmo-imap4-fetch-callback-data nil))
+ (unless (elmo-imap4-response-ok-p
+ (elmo-imap4-send-command-wait
+ session
+ (format
+ (if elmo-imap4-use-uid
+ "uid store %s %sflags.silent (%s)"
+ "store %s %sflags.silent (%s)")
+ (cdr (car set-list))
+ (if remove "-" "+")
+ flag)))
+ (setq response 'fail))
+ (setq set-list (cdr set-list)))
+ (not (eq response 'fail)))))
(luna-define-method elmo-folder-delete-messages-plugged
((folder elmo-imap4-folder) numbers)
(defun elmo-imap4-search-internal-primitive (folder session filter from-msgs)
(let ((search-key (elmo-filter-key filter))
(imap-search-keys '("bcc" "body" "cc" "from" "subject" "to"
- "larger" "smaller" "mark"))
+ "larger" "smaller" "flag"))
(total 0)
(length (length from-msgs))
charset set-list end results)
(mapcar '(lambda (x) (delete x numbers)) rest)
numbers))
((string= "flag" search-key)
- (cond
- ((string= "unread" (elmo-filter-value filter))
- (elmo-folder-list-unreads folder))
- ((string= "important" (elmo-filter-value filter))
- (elmo-folder-list-importants folder))
- ((string= "answered" (elmo-filter-value filter))
- (elmo-folder-list-answereds folder))
- ((string= "digest" (elmo-filter-value filter))
- (elmo-imap4-folder-list-digest-plugged folder))
- ((string= "any" (elmo-filter-value filter))
- (elmo-imap4-folder-list-any-plugged folder))))
+ (elmo-imap4-folder-list-flagged
+ folder (intern (elmo-filter-value filter))))
((or (string= "since" search-key)
(string= "before" search-key))
(setq search-key (concat "sent" search-key)
elmo-imap4-overview-fetch-chop-length))
;; Setup callback.
(with-current-buffer (elmo-network-session-buffer session)
- (setq elmo-imap4-current-msgdb nil
+ (setq elmo-imap4-current-msgdb (elmo-make-msgdb)
elmo-imap4-seen-messages nil
elmo-imap4-fetch-callback 'elmo-imap4-fetch-callback-1
- elmo-imap4-fetch-callback-data (cons flag-table
- (elmo-folder-use-flag-p
- folder)))
+ elmo-imap4-fetch-callback-data (cons flag-table folder))
(while set-list
(elmo-imap4-send-command-wait
session
(message "Getting overview...done")
(when elmo-imap4-seen-messages
(elmo-imap4-set-flag folder elmo-imap4-seen-messages "\\Seen"))
+ ;; cannot setup the global flag while retrieval.
+ (dolist (number (elmo-msgdb-list-messages elmo-imap4-current-msgdb))
+ (elmo-global-flags-set (elmo-msgdb-flags elmo-imap4-current-msgdb
+ number)
+ folder number
+ (elmo-message-entity-field
+ (elmo-msgdb-message-entity
+ elmo-imap4-current-msgdb number)
+ 'message-id)))
elmo-imap4-current-msgdb))))
-(luna-define-method elmo-folder-unmark-important-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Flagged" 'remove))
-
-(luna-define-method elmo-folder-mark-as-important-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Flagged"))
-
-(luna-define-method elmo-folder-unmark-read-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Seen" 'remove))
-
-(luna-define-method elmo-folder-mark-as-read-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Seen"))
+(luna-define-method elmo-folder-set-flag-plugged ((folder elmo-imap4-folder)
+ numbers flag)
+ (let ((spec (cdr (assq flag elmo-imap4-flag-specs))))
+ (elmo-imap4-set-flag folder numbers (or (car spec)
+ (capitalize (symbol-name flag)))
+ (nth 1 spec))))
-(luna-define-method elmo-folder-unmark-answered-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Answered" 'remove))
-
-(luna-define-method elmo-folder-mark-as-answered-plugged
- ((folder elmo-imap4-folder) numbers)
- (elmo-imap4-set-flag folder numbers "\\Answered"))
+(luna-define-method elmo-folder-unset-flag-plugged ((folder elmo-imap4-folder)
+ numbers flag)
+ (let ((spec (cdr (assq flag elmo-imap4-flag-specs))))
+ (elmo-imap4-set-flag folder numbers (or (car spec)
+ (capitalize (symbol-name flag)))
+ (not (nth 1 spec)))))
(luna-define-method elmo-message-use-cache-p ((folder elmo-imap4-folder)
number)
(with-current-buffer (elmo-network-session-buffer session)
(setq elmo-imap4-status-callback nil)
(setq elmo-imap4-status-callback-data nil))
+ (if elmo-imap4-use-select-to-update-status
+ (elmo-imap4-session-select-mailbox
+ session
+ (elmo-imap4-folder-mailbox-internal folder)))
(setq response
(elmo-imap4-send-command-wait session
(list
(setq messages (elmo-imap4-response-value response 'messages))
(setq uidnext (elmo-imap4-response-value response 'uidnext))
(setq killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
- ;;
+ ;;
(when killed
(when (and (consp (car killed))
(eq (car (car killed)) 1))
(luna-define-method elmo-folder-diff-plugged ((folder elmo-imap4-folder))
(elmo-imap4-folder-diff-plugged folder))
-(luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder)
- &optional number-alist)
+(luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder))
(setq elmo-imap4-server-diff-async-callback
elmo-folder-diff-async-callback)
(setq elmo-imap4-server-diff-async-callback-data
(message "Selecting %s..."
(elmo-folder-name-internal folder))
(if load-msgdb
- (setq msgdb (elmo-msgdb-load folder 'silent)))
+ (setq msgdb (elmo-folder-msgdb-load folder 'silent)))
(elmo-folder-set-killed-list-internal
folder
(elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
session mailbox)
(elmo-imap4-session-set-read-only-internal
session
- (nth 1 (assq 'read-only (assq 'ok response)))))
+ (nth 1 (assq 'read-only (assq 'ok response))))
+ (elmo-imap4-session-set-flags-internal
+ session
+ (nth 1 (or (assq 'permanentflags response)
+ (assq 'flags response)))))
(elmo-imap4-session-set-current-mailbox-internal session nil)
(if (elmo-imap4-response-bye-p response)
(elmo-imap4-process-bye session)
(luna-define-method elmo-find-fetch-strategy
((folder elmo-imap4-folder) entity &optional ignore-cache)
- (let ((number (elmo-msgdb-overview-entity-get-number entity))
+ (let ((number (elmo-message-entity-number entity))
cache-file size message-id)
- (setq size (elmo-msgdb-overview-entity-get-size entity))
- (setq message-id (elmo-msgdb-overview-entity-get-id entity))
+ (setq size (elmo-message-entity-field entity 'size))
+ (setq message-id (elmo-message-entity-field entity 'message-id))
(setq cache-file (elmo-file-cache-get message-id))
(if (or ignore-cache
(null (elmo-file-cache-status cache-file)))
(elmo-imap4-folder-mailbox-internal folder)))))
(luna-define-method elmo-folder-append-buffer
- ((folder elmo-imap4-folder) &optional flag number)
+ ((folder elmo-imap4-folder) &optional flags number)
(if (elmo-folder-plugged-p folder)
(let ((session (elmo-imap4-get-session folder))
send-buffer result)
"append "
(elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal
folder))
- (cond
- ((eq flag 'read) " (\\Seen) ")
- ((eq flag 'answered) " (\\Answered)")
- (t " () "))
+ (if (and flags (elmo-folder-use-flag-p folder))
+ (concat " ("
+ (mapconcat
+ 'identity
+ (append
+ (and (memq 'important flags)
+ '("\\Flagged"))
+ (and (not (memq 'unread flags))
+ '("\\Seen"))
+ (and (memq 'answered flags)
+ '("\\Answered")))
+ " ")
+ ;; XX KEYWORD flags
+ ") ")
+ " () ")
(elmo-imap4-buffer-literal send-buffer))))
(kill-buffer send-buffer))
result)
;; Unplugged
(if elmo-enable-disconnected-operation
- (elmo-folder-append-buffer-dop folder flag number)
+ (elmo-folder-append-buffer-dop folder flags number)
(error "Unplugged"))))
(eval-when-compile
(string= (elmo-net-folder-user-internal (, folder1))
(elmo-net-folder-user-internal (, folder2)))))))
+(luna-define-method elmo-folder-next-message-number-plugged
+ ((folder elmo-imap4-folder))
+ (let ((session (elmo-imap4-get-session folder))
+ messages new unread response killed uidnext)
+ (with-current-buffer (elmo-network-session-buffer session)
+ (setq elmo-imap4-status-callback nil)
+ (setq elmo-imap4-status-callback-data nil))
+ (if elmo-imap4-use-select-to-update-status
+ (elmo-imap4-session-select-mailbox
+ session
+ (elmo-imap4-folder-mailbox-internal folder)))
+ (setq response
+ (elmo-imap4-send-command-wait session
+ (list
+ "status "
+ (elmo-imap4-mailbox
+ (elmo-imap4-folder-mailbox-internal
+ folder))
+ " (uidnext)"))
+ response (elmo-imap4-response-value response 'status))
+ (elmo-imap4-response-value response 'uidnext)))
+
(luna-define-method elmo-folder-append-messages :around
((folder elmo-imap4-folder) src-folder numbers &optional same-number)
(if (and (eq (elmo-folder-type-internal src-folder) 'imap4)
(with-current-buffer outbuf
(erase-buffer)
(insert response)
+ (elmo-delete-cr-buffer)
t))))
(luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder)
condition)
nil)
+(autoload 'elmo-global-flags-set "elmo-flag")
+(autoload 'elmo-get-global-flags "elmo-flag")
+
(require 'product)
(product-provide (provide 'elmo-imap4) (require 'elmo-version))