X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=elmo%2Felmo.el;h=5e9787aa9f07416047f30bd331737d784ac689eb;hb=5ee330d2b31a983dbcca7879f7e1b3e93ac3b586;hp=f3766810794f2289989831e1a8e59882ea6c5f42;hpb=375e520904b2c7fa56ca3b3450a645d2d4cfc2f8;p=elisp%2Fwanderlust.git diff --git a/elmo/elmo.el b/elmo/elmo.el index f376681..5e9787a 100644 --- a/elmo/elmo.el +++ b/elmo/elmo.el @@ -35,6 +35,7 @@ (require 'elmo-vars) (require 'elmo-util) (require 'elmo-msgdb) +(require 'elmo-signal) (eval-when-compile (require 'cl)) @@ -46,7 +47,8 @@ (defcustom elmo-message-fetch-threshold 30000 "Fetch threshold." - :type 'integer + :type '(choice (integer :tag "Threshold (bytes)") + (const :tag "No limitation" nil)) :group 'elmo) (defcustom elmo-message-fetch-confirm t @@ -58,7 +60,8 @@ Otherwise, entire fetching of the message is aborted without confirmation." (defcustom elmo-folder-update-threshold 500 "Update threshold." - :type 'integer + :type '(choice (integer :tag "Number of messages") + (const :tag "No limitation" nil)) :group 'elmo) (defcustom elmo-folder-update-confirm t @@ -66,6 +69,12 @@ Otherwise, entire fetching of the message is aborted without confirmation." :type 'boolean :group 'elmo) +(defcustom elmo-msgdb-path-encode-threshold nil + "*Encode msgdb path if its length is longer than this value." + :type '(choice (const :tag "No encode" nil) + number) + :group 'elmo) + (defvar elmo-message-displaying nil "A global switch to indicate message is displaying or not.") @@ -79,15 +88,37 @@ Otherwise, entire fetching of the message is aborted without confirmation." (elmo-define-error 'elmo-authenticate-error "Login failed" 'elmo-open-error) (elmo-define-error 'elmo-imap4-bye-error "IMAP4 session was terminated" 'elmo-open-error) +;; Event declarations +(elmo-define-signal flag-changing (number old-flags new-flags) + "Notify the changing flag of the messages with NUMBER.") + +(elmo-define-signal flag-changed (numbers) + "Notify the change flag of the messages with NUMBERS.") + +(elmo-define-signal status-changed (numbers) + "Notify the change status of the message with NUMBERS.") + +(elmo-define-signal update-overview (number) + "Notify update overview of the message with NUMBER.") + +(elmo-define-signal message-number-changed (old-number new-number) + "Notify change of message number within the folder.") + ;; autoloads (eval-and-compile + (autoload 'md5 "md5") (autoload 'elmo-dop-queue-flush "elmo-dop") (autoload 'elmo-nntp-post "elmo-nntp") - (autoload 'elmo-global-flag-initialize "elmo-flag") (autoload 'elmo-global-flag-p "elmo-flag") + (autoload 'elmo-local-flag-p "elmo-flag") (autoload 'elmo-global-flag-detach "elmo-flag") (autoload 'elmo-global-flag-detach-messages "elmo-flag") - (autoload 'elmo-global-flag-set "elmo-flag")) + (autoload 'elmo-global-flag-set "elmo-flag") + (autoload 'elmo-get-global-flags "elmo-flag") + (autoload 'elmo-global-flags-initialize "elmo-flag") + (autoload 'elmo-global-mark-migrate "elmo-flag") + (autoload 'elmo-folder-list-global-flag-messages "elmo-flag") + (autoload 'elmo-search-register-engine "elmo-search")) (defun elmo-define-folder (prefix backend) "Define a folder. @@ -102,8 +133,8 @@ If a folder name begins with PREFIX, use BACKEND." (defmacro elmo-folder-type (name) "Get folder type from NAME string." - (` (and (stringp (, name)) - (cdr (assoc (string-to-char (, name)) elmo-folder-type-alist))))) + `(and (stringp ,name) + (cdr (assoc (string-to-char ,name) elmo-folder-type-alist)))) ;;; ELMO folder ;; A elmo folder provides uniformed (orchestrated) access @@ -115,9 +146,11 @@ If a folder name begins with PREFIX, use BACKEND." path ; directory path for msgdb. msgdb ; msgdb (may be nil). killed-list ; killed list. + flag-table ; flag table. persistent ; non-nil if persistent. process-duplicates ; read or hide biff ; folder for biff + mime-charset ; charset for encode & decode )) (luna-define-internal-accessors 'elmo-folder)) @@ -130,16 +163,18 @@ If a folder name begins with PREFIX, use BACKEND." (` (luna-send (, folder) (, message) (, folder) (,@ args)))) ;;;###autoload -(defun elmo-make-folder (name &optional non-persistent) +(defun elmo-make-folder (name &optional non-persistent mime-charset) "Make an ELMO folder structure specified by NAME. -If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved." +If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved. +If optional argument MIME-CHARSET is specified, it is used for +encode and decode a multibyte string." (let ((type (elmo-folder-type name)) prefix split class folder original) (setq original (elmo-string name)) (if type (progn - (setq prefix (substring name 0 1)) - (setq name (substring name 1))) + (setq prefix (elmo-string (substring name 0 1))) + (setq name (elmo-string (substring name 1)))) (setq type (intern (car (setq split (split-string name ":"))))) (if (>= (length split) 2) (setq name (substring name (+ 1 (length (car split))))) @@ -151,10 +186,18 @@ If optional argument NON-PERSISTENT is non-nil, the folder msgdb is not saved." :type type :prefix prefix :name original - :persistent (not non-persistent))) + :persistent (not non-persistent) + :mime-charset mime-charset)) (save-match-data (elmo-folder-send folder 'elmo-folder-initialize name)))) +(defvar elmo-get-folder-function nil) + +(defun elmo-get-folder (name) + (or (and elmo-get-folder-function + (funcall elmo-get-folder-function name)) + (elmo-make-folder name))) + ;; Note that this function is for internal use only. (luna-define-generic elmo-folder-msgdb (folder) "Return the msgdb of FOLDER (on-demand loading). @@ -230,15 +273,17 @@ If second optional IN-MSGDB is non-nil, only messages in the msgdb are listed.") (setq list (elmo-msgdb-list-messages (elmo-folder-msgdb folder)))) (if visible-only (elmo-living-messages list killed-list) - (elmo-uniq-list - (nconc (elmo-number-set-to-number-list killed-list) list))))) + (if (and in-msgdb killed-list) + (elmo-uniq-sorted-list + (sort (nconc (elmo-number-set-to-number-list killed-list) list) #'<) + #'eq) + list)))) -(luna-define-generic elmo-folder-list-unreads (folder) - "Return a list of unread message numbers contained in FOLDER.") -(luna-define-generic elmo-folder-list-importants (folder) - "Return a list of important message numbers contained in FOLDER.") -(luna-define-generic elmo-folder-list-answereds (folder) - "Return a list of answered message numbers contained in FOLDER.") +(luna-define-generic elmo-folder-list-messages-internal (folder &optional + visible-only) + ;; Return a list of message numbers contained in FOLDER. + ;; Return t if the message list is not available. + ) (luna-define-generic elmo-folder-list-flagged (folder flag &optional in-msgdb) "List messages in the FOLDER with FLAG. @@ -249,30 +294,30 @@ FLAG is a symbol which is one of the following: `important' (marked as important) 'sugar' flags: `read' (not unread) - `digest' (unread + important) - `any' (digest + answered) - + `digest' (unread + important + other flags) + `any' (digest + answered + other flags) If optional IN-MSGDB is non-nil, retrieve flag information from msgdb.") (luna-define-method elmo-folder-list-flagged ((folder elmo-folder) flag &optional in-msgdb) - ;; Currently, only in-msgdb is implemented. - (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag)) - -(luna-define-method elmo-folder-list-unreads ((folder elmo-folder)) - (elmo-folder-list-flagged folder 'unread)) - -(luna-define-method elmo-folder-list-importants ((folder elmo-folder)) - (elmo-folder-list-flagged folder 'important)) + (let ((msgs (if in-msgdb + t + (elmo-folder-list-flagged-internal folder flag)))) + (unless (listp msgs) + (setq msgs (elmo-msgdb-list-flagged (elmo-folder-msgdb folder) flag))) + (if in-msgdb + msgs + (elmo-uniq-list + (nconc (elmo-folder-list-global-flag-messages folder flag) msgs) + #'delq)))) -(luna-define-method elmo-folder-list-answereds ((folder elmo-folder)) - (elmo-folder-list-flagged folder 'answered)) +(luna-define-generic elmo-folder-list-flagged-internal (folder flag) + "Return a list of message in the FOLDER with FLAG. +Return t if the message list is not available.") -(luna-define-generic elmo-folder-list-messages-internal (folder &optional - visible-only) - ;; Return a list of message numbers contained in FOLDER. - ;; Return t if the message list is not available. - ) +(luna-define-method elmo-folder-list-flagged-internal ((folder elmo-folder) + flag) + t) (luna-define-generic elmo-folder-list-subfolders (folder &optional one-level) "Returns a list of subfolders contained in FOLDER. @@ -308,12 +353,18 @@ Otherwise, all descendent folders are returned.") "Rename FOLDER to NEW-NAME (string).") (luna-define-generic elmo-folder-delete-messages (folder numbers) - "Delete messages. + "Delete messages with msgdb entity. FOLDER is the ELMO folder structure. NUMBERS is a list of message numbers to be deleted. It is not recommended to use this function other than internal use. Use `elmo-folder-move-messages' with dst-folder 'null instead.") +(luna-define-generic elmo-folder-delete-messages-internal (folder numbers) + "Delete messages, but no delete msgdb entity. +FOLDER is the ELMO folder structure. +NUMBERS is a list of message numbers to be deleted. +Override this method by each implement of `elmo-folder'.") + (luna-define-generic elmo-folder-search (folder condition &optional numbers) "Search and return list of message numbers. FOLDER is the ELMO folder structure. @@ -335,55 +386,52 @@ FOLDER is the ELMO folder structure. NUMBERS is a list of message numbers to create msgdb. FLAG-TABLE is a hashtable of message-id and flag.") -(luna-define-generic elmo-folder-unflag-important (folder - numbers - &optional is-local) - "Un-flag messages as important. -FOLDER is the ELMO folder structure. -NUMBERS is a list of message numbers to be processed. -If IS-LOCAL is non-nil, only the local flag is updated.") +(luna-define-generic elmo-folder-set-flag (folder numbers flag + &optional is-local) + "Set messages flag. +FOLDER is a ELMO folder structure. +NUMBERS is a list of message number to set flag. -(luna-define-generic elmo-folder-flag-as-important (folder - numbers - &optional is-local) - "Flag messages as important. -FOLDER is the ELMO folder structure. -NUMBERS is a list of message numbers to be processed. -If IS-LOCAL is non-nil, only the local flag is updated.") +FLAG is a symbol which is one of the following: + `unread' (set the message as unread) + `answered' (set the message as answered) + `important' (set the message as important) +'sugar' flag: + `read' (remove new and unread flags) +If optional IS-LOCAL is non-nil, update only local (not server) status.") -(luna-define-generic elmo-folder-unflag-read (folder numbers - &optional is-local) - "Un-flag messages as read. -FOLDER is the ELMO folder structure. -NUMBERS is a list of message numbers to be processed. -If IS-LOCAL is non-nil, only the local flag is updated.") +(luna-define-generic elmo-folder-unset-flag (folder numbers flag + &optional is-local) + "Unset messages flag. +FOLDER is a ELMO folder structure. +NUMBERS is a list of message number to unset flag. -(luna-define-generic elmo-folder-flag-as-read (folder numbers - &optional is-local) - "Flag messages as read. -FOLDER is the ELMO folder structure. -NUMBERS is a list of message numbers to be processed. -If IS-LOCAL is non-nil, only the local flag is updated.") +FLAG is a symbol which is one of the following: + `unread' (remove unread and new flag) + `answered' (remove answered flag) + `important' (remove important flag) +'sugar' flag: + `read' (set unread flag) + `all' (remove all flags) +If optional IS-LOCAL is non-nil, update only local (not server) status.") -(luna-define-generic elmo-folder-unflag-answered (folder numbers - &optional is-local) - "Un-flag messages as answered. -FOLDER is the ELMO folder structure. -If IS-LOCAL is non-nil, only the local flag is updated.") +(luna-define-generic elmo-message-flag-available-p (folder number flag) + "Return non-nil when a message in the FOLDER with NUMBER treats FLAG.") -(luna-define-generic elmo-folder-flag-as-answered (folder numbers - &optional is-local) - "Flag messages as answered. -FOLDER is the ELMO folder structure. -If IS-LOCAL is non-nil, only the local flag is updated.") +(luna-define-generic elmo-folder-next-message-number (folder) + "The next message number that will be assigned to a new message. +FOLDER is the ELMO folder structure.") -(luna-define-generic elmo-folder-append-buffer (folder &optional flag +(luna-define-generic elmo-folder-append-buffer (folder &optional flags number) "Append current buffer as a new message. -FOLDER is the destination folder(ELMO folder structure). -FLAG is the status of appended message. +FOLDER is the destination folder (ELMO folder structure). +FLAGS is the flag list for the appended message (list of symbols). +If FLAGS contain `read', the message is appended as `not-unread'. +If it is nil, the appended message will be treated as `new'. If optional argument NUMBER is specified, the new message number is set -\(if possible\).") +\(if possible\). +Return nil on failure.") (luna-define-generic elmo-folder-append-messages (folder src-folder @@ -397,7 +445,8 @@ Caller should make sure FOLDER is `writable'. SRC-FOLDER is the source ELMO folder structure. NUMBERS is the message numbers to be appended in the SRC-FOLDER. If second optional argument SAME-NUMBER is specified, -message number is preserved \(if possible\).") +message number is preserved \(if possible\). +Returns a list of message numbers successfully appended.") (luna-define-generic elmo-folder-pack-numbers (folder) "Pack message numbers of FOLDER.") @@ -460,6 +509,23 @@ Return newly created temporary directory name which contains temporary files.") FOLDER is a ELMO folder structure. NUMBER is a number of the message.") +(defun elmo-message-flags-for-append (folder number &optional message-id) + "Return a list of flags for `elmo-folder-append-buffer'. +FOLDER is a ELMO folder structure. +NUMBER is a number of the message. +If optional argument MESSAGES-ID is not specified, get it from current buffer." + (let ((this-id (elmo-message-field folder number 'message-id))) + (and this-id + (string= this-id (or message-id + (elmo-msgdb-get-message-id-from-buffer))) + (or (elmo-message-flags folder number) + ;; message exists, but no flag. + '(read))))) + +(luna-define-method elmo-message-flag-available-p ((folder elmo-folder) number + flag) + (elmo-msgdb-flag-available-p (elmo-folder-msgdb folder) flag)) + (luna-define-method elmo-message-flags ((folder elmo-folder) number) (elmo-msgdb-flags (elmo-folder-msgdb folder) number)) @@ -474,97 +540,94 @@ NUMBER is a message number to test." (t (memq flag cur-flags))))) -(luna-define-generic elmo-find-fetch-strategy - (folder entity &optional ignore-cache) -;; Returns the message fetching strategy suitable for the message. -;; FOLDER is the ELMO folder structure. -;; ENTITY is the overview entity of the message in the folder. -;; If optional argument IGNORE-CACHE is non-nil, cache is ignored. -;; Returned value is a elmo-fetch-strategy object. -;; If return value is nil, message should not be nil. - ) +(luna-define-generic elmo-find-fetch-strategy (folder number + &optional + ignore-cache + require-entireness) + "Return the message fetching strategy suitable for the message with NUMBER. +FOLDER is the ELMO folder structure. +If optional argument IGNORE-CACHE is non-nil, existing cache is ignored. +If second optional argument REQUIRE-ENTIRENESS is non-nil, +ensure that entireness of the returned strategy is entire. +Returned value is a elmo-fetch-strategy object. +If return value is nil, message should not be nil.") (defmacro elmo-make-fetch-strategy (entireness &optional use-cache save-cache cache-path) -;; Make elmo-message-fetching strategy. -;; ENTIRENESS is 'entire or 'section. -;; 'entire means fetch message entirely at once. -;; 'section means fetch message section by section. -;; If optional USE-CACHE is non-nil, existing cache is used and otherwise, -;; existing cache is thrown away. -;; If SAVE-CACHE is non-nil, fetched message is saved. -;; CACHE-PATH is the cache path to be used as a message cache file. - (` (vector (, entireness) - (, use-cache) (, save-cache) (, cache-path)))) + "Make elmo-message-fetching strategy. +ENTIRENESS is 'entire or 'section. +'entire means fetch message entirely at once. +'section means fetch message section by section. +If optional USE-CACHE is non-nil, existing cache is used and otherwise, +existing cache is thrown away. +If SAVE-CACHE is non-nil, fetched message is saved. +CACHE-PATH is the cache path to be used as a message cache file." + `(vector ,entireness ,use-cache ,save-cache ,cache-path)) (defmacro elmo-fetch-strategy-entireness (strategy) - ;; Return entireness of STRATEGY. - (` (aref (, strategy) 0))) + "Return entireness of STRATEGY." + `(aref ,strategy 0)) (defmacro elmo-fetch-strategy-use-cache (strategy) - ;; Return use-cache of STRATEGY. - (` (aref (, strategy) 1))) + "Return use-cache of STRATEGY." + `(aref ,strategy 1)) (defmacro elmo-fetch-strategy-save-cache (strategy) - ;; Return save-cache of STRATEGY. - (` (aref (, strategy) 2))) + "Return save-cache of STRATEGY." + `(aref ,strategy 2)) (defmacro elmo-fetch-strategy-cache-path (strategy) - ;; Return cache-path of STRATEGY. - (` (aref (, strategy) 3))) - -(luna-define-method elmo-find-fetch-strategy - ((folder elmo-folder) entity &optional ignore-cache) - (let (cache-file size message-id number) - (setq size (elmo-message-entity-field entity 'size)) - (setq message-id (elmo-message-entity-field entity 'message-id)) - (setq number (elmo-message-entity-number entity)) - (setq cache-file (elmo-file-cache-get message-id)) - (setq ignore-cache (or ignore-cache - (null (elmo-message-use-cache-p folder number)))) - (if (or ignore-cache - (null (elmo-file-cache-status cache-file))) - ;; No cache or ignore-cache. - (if (and (not (elmo-folder-local-p folder)) - elmo-message-fetch-threshold - (integerp size) - (>= size elmo-message-fetch-threshold) - (or (not elmo-message-fetch-confirm) - (not (prog1 (y-or-n-p - (format "Fetch entire message(%dbytes)? " - size)) - (message ""))))) - ;; Don't fetch message at all. - nil - ;; Don't use existing cache and fetch entire message at once. - (elmo-make-fetch-strategy - 'entire nil - (elmo-message-use-cache-p folder number) - (elmo-file-cache-path cache-file))) - ;; Cache exists. - (if (not ignore-cache) - (elmo-make-fetch-strategy - 'entire - ;; ...But ignore current section cache and re-fetch - ;; if section cache. - (not (eq (elmo-file-cache-status cache-file) 'section)) - ;; Save cache. - (elmo-message-use-cache-p folder number) - (elmo-file-cache-path cache-file)))))) + "Return cache-path of STRATEGY." + `(aref ,strategy 3)) + +(luna-define-method elmo-find-fetch-strategy ((folder elmo-folder) number + &optional + ignore-cache + require-entireness) + (let ((entity (elmo-message-entity folder number))) + (if (null entity) + (elmo-make-fetch-strategy 'entire) + (let* ((size (elmo-message-entity-field entity 'size)) + (message-id (elmo-message-entity-field entity 'message-id)) + (cache-file (elmo-file-cache-get message-id)) + (use-cache (elmo-message-use-cache-p folder number))) + (if (and (not ignore-cache) + use-cache + (eq (elmo-file-cache-status cache-file) 'entire)) + ;; Cache exists and use it. + (elmo-make-fetch-strategy + 'entire + t ; Use cache. + use-cache ; Save cache. + (elmo-file-cache-path cache-file)) + ;; No cache or ignore-cache. + (if (and (not (elmo-folder-local-p folder)) + (not require-entireness) + elmo-message-fetch-threshold + (integerp size) + (>= size elmo-message-fetch-threshold) + (or (not elmo-message-fetch-confirm) + (not (prog1 + (y-or-n-p + (format "Fetch entire message(%dbytes)? " + size)) + (message ""))))) + ;; Don't fetch message at all. + nil + ;; Don't use existing cache and fetch entire message at once. + (elmo-make-fetch-strategy + 'entire + nil ; Don't use cache. + use-cache ; Save cache. + (elmo-file-cache-path cache-file)))))))) (luna-define-method elmo-folder-list-messages-internal ((folder elmo-folder) &optional visible-only) t) -(defun elmo-folder-encache (folder numbers &optional unread) - "Encache messages in the FOLDER with NUMBERS. -If UNREAD is non-nil, messages are not flaged as read." - (dolist (number numbers) - (elmo-message-encache folder number unread))) - (luna-define-generic elmo-message-encache (folder number &optional read) "Encache message in the FOLDER with NUMBER. If READ is non-nil, message is flaged as read.") @@ -572,45 +635,32 @@ If READ is non-nil, message is flaged as read.") (luna-define-method elmo-message-encache ((folder elmo-folder) number &optional read) (let (path) - (elmo-message-fetch - folder number - (elmo-make-fetch-strategy 'entire - nil ;use-cache - t ;save-cache - (setq path (elmo-file-cache-get-path - (elmo-message-field - folder number 'message-id)))) - nil nil (not read)) + (with-temp-buffer + (elmo-message-fetch + folder number + (elmo-make-fetch-strategy 'entire + nil ;use-cache + t ;save-cache + (setq path (elmo-file-cache-get-path + (elmo-message-field + folder number 'message-id)))) + (not read))) path)) +(luna-define-generic elmo-message-fetch-bodystructure (folder number strategy) + "Fetch bodystructure of the message in FOLDER with NUMBER using STRATEGY.") + (luna-define-generic elmo-message-fetch (folder number strategy &optional - section - outbuf - unread) - "Fetch a message and return as a string. -FOLDER is the ELMO folder structure. -NUMBER is the number of the message in the FOLDER. -STRATEGY is the message fetching strategy. -If optional argument SECTION is specified, only the SECTION of the message -is fetched (if possible). -If second optional argument OUTBUF is specified, fetched message is -inserted to the buffer and returns t if fetch was ended successfully. -If third optional argument UNREAD is non-nil, message is not flaged as read. -Returns non-nil if fetching was succeed.") - -(luna-define-generic elmo-message-fetch-with-cache-process (folder - number strategy - &optional - section - unread) - "Fetch a message into current buffer with cache process. + unread + section) + "Fetch a message into current buffer. FOLDER is the ELMO folder structure. NUMBER is the number of the message in the FOLDER. STRATEGY is the message fetching strategy. -If optional argument SECTION is specified, only the SECTION of the message -is fetched (if possible). -If second optional argument UNREAD is non-nil, message is not flaged as read. +If optional argument UNREAD is non-nil, message is not flaged as read. +If second optional argument SECTION is specified, only the +SECTION of the message is fetched (if possible). Returns non-nil if fetching was succeed.") (luna-define-generic elmo-message-fetch-internal (folder number strategy @@ -684,16 +734,17 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (elmo-generic-folder-commit folder)) (defun elmo-generic-folder-commit (folder) - (let ((msgdb (elmo-folder-msgdb-internal folder))) - (when (and msgdb (elmo-folder-persistent-p folder)) - (when (elmo-msgdb-message-modified-p msgdb) - (elmo-folder-set-info-max-by-numdb - folder - (elmo-folder-list-messages folder nil 'in-msgdb)) - (elmo-msgdb-killed-list-save - (elmo-folder-msgdb-path folder) - (elmo-folder-killed-list-internal folder))) - (elmo-msgdb-save msgdb)))) + (when (elmo-folder-persistent-p folder) + (let ((msgdb (elmo-folder-msgdb-internal folder))) + (when msgdb + (when (elmo-msgdb-message-modified-p msgdb) + (elmo-folder-set-info-max-by-numdb + folder + (elmo-folder-list-messages folder nil 'in-msgdb))) + (elmo-msgdb-save msgdb))) + (elmo-msgdb-killed-list-save + (elmo-folder-msgdb-path folder) + (elmo-folder-killed-list-internal folder)))) (luna-define-method elmo-folder-close-internal ((folder elmo-folder)) ;; do nothing. @@ -734,16 +785,24 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") t)) (luna-define-method elmo-folder-rename ((folder elmo-folder) new-name) - (let* ((new-folder (elmo-make-folder new-name))) + (let* ((new-folder (elmo-make-folder + new-name + nil + (elmo-folder-mime-charset-internal folder)))) (unless (eq (elmo-folder-type-internal folder) (elmo-folder-type-internal new-folder)) (error "Not same folder type")) - (if (or (file-exists-p (elmo-folder-msgdb-path new-folder)) - (elmo-folder-exists-p new-folder)) - (error "Already exists folder: %s" new-name)) + (when (or (file-exists-p (elmo-folder-msgdb-path new-folder)) + (elmo-folder-exists-p new-folder)) + (error "Already exists folder: %s" new-name)) (elmo-folder-send folder 'elmo-folder-rename-internal new-folder) (elmo-msgdb-rename-path folder new-folder))) +(luna-define-method elmo-folder-delete-messages ((folder elmo-folder) + numbers) + (and (elmo-folder-delete-messages-internal folder numbers) + (elmo-folder-detach-messages folder numbers))) + (luna-define-method elmo-folder-search ((folder elmo-folder) condition &optional numbers) @@ -753,6 +812,14 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (setq results (elmo-msgdb-search msgdb condition numbers)) (if (listp results) results + (elmo-condition-optimize condition) + (when (and (consp condition) + (eq (car condition) 'and) + (listp (setq results (elmo-msgdb-search msgdb + (nth 1 condition) + numbers)))) + (setq numbers results + condition (nth 2 condition))) (let ((len (length numbers)) matched) (elmo-with-progress-display (> len elmo-display-progress-threshold) @@ -774,26 +841,50 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (message "Searching...done") (nreverse matched))))) +(defun elmo-message-buffer-match-condition (condition number) + (let* ((handler (luna-make-entity 'modb-buffer-entity-handler)) + (result (elmo-condition-match + condition + (lambda (condition handler entity) + (elmo-msgdb-message-match-condition handler + condition + entity)) + (list + handler + (elmo-msgdb-make-message-entity + handler + :number number + :buffer (current-buffer)))))) + (and result (not (elmo-filter-condition-p result))))) + (luna-define-method elmo-message-match-condition ((folder elmo-folder) number condition numbers) - (let ((filename (cond - ((elmo-message-file-name folder number)) - ((let* ((cache (elmo-file-cache-get - (elmo-message-field folder number - 'message-id))) - (cache-path (elmo-file-cache-path cache))) - (when (and cache-path - (not (elmo-cache-path-section-p cache-path))) - cache-path)))))) - (when (and filename - (file-readable-p filename)) + (let* (cache cache-path + (filename (cond + ((elmo-message-file-name folder number)) + ((progn + (setq cache (elmo-file-cache-get + (elmo-message-field folder number + 'message-id))) + (setq cache-path (elmo-file-cache-path cache)) + (and cache-path + (not (elmo-cache-path-section-p cache-path)))) + cache-path)))) + (when (and filename (file-readable-p filename)) (with-temp-buffer - (insert-file-contents-as-binary filename) - (elmo-set-buffer-multibyte default-enable-multibyte-characters) - ;; Should consider charset? - (decode-mime-charset-region (point-min) (point-max) elmo-mime-charset) - (elmo-buffer-field-condition-match condition number numbers))))) + (set-buffer-multibyte nil) + ;;(insert-file-contents-as-binary filename) + (elmo-message-fetch folder number + (elmo-make-fetch-strategy 'entire + (and cache t) + nil + cache-path) + 'unread) + (set-buffer-multibyte default-enable-multibyte-characters) + (decode-coding-region (point-min) (point-max) + elmo-mime-display-as-is-coding-system) + (elmo-message-buffer-match-condition condition number))))) (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder)) nil) ; default is noop. @@ -820,6 +911,39 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (luna-define-method elmo-folder-have-subfolder-p ((folder elmo-folder)) t) +;; Flag table +(luna-define-generic elmo-folder-flag-table (folder &optional if-exists) + "Return the flag-table of FOLDER. +If optional argument IF-EXISTS is nil, load on demand. +\(For internal use only.\)") + +(luna-define-generic elmo-folder-close-flag-table (folder) + "Close flag-table of FOLDER.") + +(luna-define-method elmo-folder-flag-table ((folder elmo-folder) + &optional if-exists) + (or (elmo-folder-flag-table-internal folder) + (unless if-exists + (elmo-folder-set-flag-table-internal + folder + (elmo-flag-table-load (elmo-folder-msgdb-path folder)))))) + +(luna-define-method elmo-folder-close-flag-table ((folder elmo-folder)) + (elmo-flag-table-save (elmo-folder-msgdb-path folder) + (elmo-folder-flag-table folder)) + (elmo-folder-set-flag-table-internal folder nil)) + +(defun elmo-folder-preserve-flags (folder msgid flags) + "Preserve FLAGS into FOLDER for a message that has MSGID." + (when (and msgid flags) + (let ((flag-table (elmo-folder-flag-table folder 'if-exists)) + load-now) + (when (setq load-now (null flag-table)) + (setq flag-table (elmo-folder-flag-table folder))) + (elmo-flag-table-set flag-table msgid flags) + (when load-now + (elmo-folder-close-flag-table folder))))) + ;;; Folder info ;; Folder info is a message number information cache (hashtable) (defsubst elmo-folder-get-info (folder &optional hashtb) @@ -843,7 +967,7 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") "Set FOLDER info by MSGDB-NUMBER in msgdb." (elmo-folder-set-info-hashtb folder - (or (car (sort numbers '>)) 0) + (if numbers (apply #'max numbers) 0) nil ;;(length num-db) )) @@ -890,13 +1014,12 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (defsubst elmo-strict-folder-diff (folder) "Return folder diff information strictly from FOLDER." - (let ((in-db (sort (elmo-msgdb-list-messages (elmo-folder-msgdb folder)) - '<)) + (let ((in-db (sort (elmo-folder-list-messages folder nil 'in-msgdb) '<)) (in-folder (elmo-folder-list-messages folder)) append-list delete-list diff) (cons (if (equal in-folder in-db) 0 - (setq diff (elmo-list-diff in-folder in-db nil)) + (setq diff (elmo-list-diff in-folder in-db)) (setq append-list (car diff)) (setq delete-list (cadr diff)) (if append-list @@ -922,9 +1045,8 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (let ((number-list (elmo-folder-list-messages folder nil 'in-msgdb))) ;; No info-cache. - (setq in-db (sort number-list '<)) - (setq in-db-max (or (nth (max 0 (1- (length in-db))) in-db) - 0)) + (setq in-db number-list) + (setq in-db-max (if in-db (apply #'max in-db) 0)) (elmo-folder-set-info-hashtb folder in-db-max nil)) (setq in-db-max cached-in-db-max)) (setq unsync (if (and in-db (car in-folder)) @@ -952,6 +1074,10 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (luna-define-method elmo-folder-contains-type ((folder elmo-folder) type) (eq (elmo-folder-type-internal folder) type)) +(luna-define-method elmo-folder-next-message-number ((folder elmo-folder)) + (+ 1 (elmo-max-of-list (or (elmo-folder-list-messages folder) + '(0))))) + (luna-define-method elmo-folder-append-messages ((folder elmo-folder) src-folder numbers @@ -963,17 +1089,16 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (defun elmo-generic-folder-append-messages (folder src-folder numbers same-number) (let ((src-msgdb-exists (not (zerop (elmo-folder-length src-folder)))) - unseen table flags + unseen table succeed-numbers failure cache id) - (setq table (elmo-flag-table-load (elmo-folder-msgdb-path folder))) + (setq table (elmo-folder-flag-table folder)) (with-temp-buffer (set-buffer-multibyte nil) (while numbers (setq failure nil id (and src-msgdb-exists (elmo-message-field src-folder (car numbers) - 'message-id)) - flags (elmo-message-flags src-folder (car numbers))) + 'message-id))) (condition-case nil (setq cache (elmo-file-cache-get id) failure @@ -992,23 +1117,20 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") 'entire t nil (elmo-file-cache-path cache))) (error "Unplugged"))) - nil (current-buffer) 'unread) (> (buffer-size) 0) (elmo-folder-append-buffer folder - (or flags '(read)) + (elmo-message-flags-for-append src-folder (car numbers)) (if same-number (car numbers)))))) (error (setq failure t))) ;; FETCH & APPEND finished (unless failure - (when id - (elmo-flag-table-set table id flags)) (setq succeed-numbers (cons (car numbers) succeed-numbers))) (elmo-progress-notify 'elmo-folder-move-messages) (setq numbers (cdr numbers))) (when (elmo-folder-persistent-p folder) - (elmo-flag-table-save (elmo-folder-msgdb-path folder) table)) + (elmo-folder-close-flag-table folder)) succeed-numbers))) ;; Arguments should be reduced. @@ -1038,8 +1160,7 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (elmo-folder-close dst-folder))) (if (and (not no-delete) succeeds) (progn - (if (and (elmo-folder-delete-messages src-folder succeeds) - (elmo-folder-detach-messages src-folder succeeds)) + (if (elmo-folder-delete-messages src-folder succeeds) (progn (elmo-global-flag-detach-messages src-folder succeeds (eq dst-folder 'null)) @@ -1063,7 +1184,19 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (or (elmo-folder-path-internal folder) (elmo-folder-set-path-internal folder - (elmo-folder-expand-msgdb-path folder)))) + (if (null elmo-msgdb-path-encode-threshold) + (elmo-folder-expand-msgdb-path folder) + (let* ((path (directory-file-name + (elmo-folder-expand-msgdb-path folder))) + (dirname (file-name-nondirectory path))) + (if (<= (length dirname) elmo-msgdb-path-encode-threshold) + path + (require 'md5) + (setq dirname (md5 dirname)) + (when (> (length dirname) elmo-msgdb-path-encode-threshold) + (error "Cannot shrink msgdb path for `%s'" + (elmo-folder-name-internal folder))) + (expand-file-name dirname (file-name-directory path)))))))) (luna-define-generic elmo-message-cached-p (folder number) "Return non-nil if the message is cached.") @@ -1071,11 +1204,20 @@ Return a cons cell of (NUMBER-CROSSPOSTS . NEW-FLAG-ALIST).") (luna-define-method elmo-message-cached-p ((folder elmo-folder) number) (elmo-message-flagged-p folder number 'cached)) +(luna-define-generic elmo-message-killed-p (folder number) + "Return non-nil if the message is killed.") + +(luna-define-method elmo-message-killed-p ((folder elmo-folder) number) + (let ((killed-list (elmo-folder-killed-list-internal folder))) + (and killed-list + (elmo-number-set-member number killed-list)))) + (defun elmo-message-accessible-p (folder number) "Get accessibility of the message. Return non-nil when message is accessible." (or (elmo-folder-plugged-p folder) (elmo-folder-local-p folder) + (< number 0) ; in dop spool (elmo-message-cached-p folder number))) (luna-define-generic elmo-message-set-cached (folder number cached) @@ -1088,12 +1230,19 @@ If CACHED is t, message is set as cached.") number cached) (if cached (elmo-msgdb-set-flag (elmo-folder-msgdb folder) number 'cached) - (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number 'cached))) + (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number 'cached)) + (elmo-emit-signal 'status-changed folder (list number))) (defun elmo-message-copy-entity (entity) (elmo-msgdb-copy-message-entity (elmo-message-entity-handler entity) entity)) +(luna-define-generic elmo-message-number (folder message-id) + "Get message number from MSGDB which corresponds to MESSAGE-ID.") + +(luna-define-method elmo-message-number ((folder elmo-folder) message-id) + (elmo-msgdb-message-number (elmo-folder-msgdb folder) message-id)) + (luna-define-generic elmo-message-entity (folder key) "Return the message-entity structure which matches to the KEY. KEY is a number or a string. @@ -1130,23 +1279,14 @@ ENTITY is the message-entity to get the parent.") (luna-define-generic elmo-folder-count-flags (folder) "Count flagged message number in the msgdb of the FOLDER. -Return a list of numbers (`new' `unread' `answered')") +Return alist of flag and numbers. +Example: +\(\(new . 10\) + \(unread . 20\) + \(answered . 3\)\)") (luna-define-method elmo-folder-count-flags ((folder elmo-folder)) - (let ((new 0) - (unreads 0) - (answered 0) - flags) - (dolist (number (elmo-folder-list-messages folder 'visible 'in-msgdb)) - (setq flags (elmo-message-flags folder number)) - (cond - ((memq 'new flags) - (incf new)) - ((memq 'unread flags) - (incf unreads)) - ((memq 'answered flags) - (incf answered)))) - (list new unreads answered))) + (elmo-msgdb-flag-count (elmo-folder-msgdb folder))) (defun elmo-message-set-flag (folder number flag &optional is-local) "Set message flag. @@ -1161,15 +1301,7 @@ FLAG is a symbol which is one of the following: `read' (remove new and unread flags) If optional IS-LOCAL is non-nil, update only local (not server) status." ;; XXX Transitional implementation. - (case flag - (unread - (elmo-folder-unflag-read folder (list number) is-local)) - (read - (elmo-folder-flag-as-read folder (list number) is-local)) - (answered - (elmo-folder-flag-as-answered folder (list number) is-local)) - (important - (elmo-folder-flag-as-important folder (list number) is-local)))) + (elmo-folder-set-flag folder (list number) flag is-local)) (defun elmo-message-unset-flag (folder number flag &optional is-local) "Unset message flag. @@ -1184,26 +1316,30 @@ FLAG is a symbol which is one of the following: `read' (set unread flag) If optional IS-LOCAL is non-nil, update only local (not server) status." ;; XXX Transitional implementation. - (case flag - (unread - (elmo-folder-flag-as-read folder (list number) is-local)) - (read - (elmo-folder-unflag-read folder (list number) is-local)) - (answered - (elmo-folder-unflag-answered folder (list number) is-local)) - (important - (elmo-folder-unflag-important folder (list number) is-local)))) - -(luna-define-generic elmo-message-field (folder number field) + (elmo-folder-unset-flag folder (list number) flag is-local)) + +(luna-define-generic elmo-message-field (folder number field &optional type) "Get message field value in the msgdb. FOLDER is the ELMO folder structure. NUMBER is a number of the message. -FIELD is a symbol of the field.") +FIELD is a symbol of the field. +If optional argument TYPE is specified, return converted value.") + +(luna-define-method elmo-message-field ((folder elmo-folder) + number field &optional type) + (elmo-msgdb-message-field (elmo-folder-msgdb folder) number field type)) + +(luna-define-generic elmo-message-set-field (folder number field value) + "Set message field value in the msgdb. +FOLDER is the ELMO folder structure. +NUMBER is a number of the message. +FIELD is a symbol of the field. +VALUE is a value to set.") -(luna-define-method elmo-message-field ((folder elmo-folder) number field) - (when (zerop (elmo-folder-length folder)) - (error "Cannot treat this folder correctly.")) - (elmo-message-entity-field (elmo-message-entity folder number) field)) +(luna-define-method elmo-message-set-field ((folder elmo-folder) number + field value) + (elmo-message-entity-set-field (elmo-message-entity folder number) + field value)) (luna-define-method elmo-message-use-cache-p ((folder elmo-folder) number) nil) ; default is not use cache. @@ -1211,70 +1347,66 @@ FIELD is a symbol of the field.") (luna-define-method elmo-message-folder ((folder elmo-folder) number) folder) ; default is folder -(luna-define-method elmo-folder-unflag-important ((folder elmo-folder) - numbers - &optional is-local) - (when (elmo-folder-msgdb-internal folder) - (dolist (number numbers) - (when (elmo-global-flag-p 'important) - (elmo-global-flag-detach 'important folder number 'remove-if-none)) - (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) - number - 'important)))) - -(luna-define-method elmo-folder-flag-as-important ((folder elmo-folder) - numbers - &optional is-local) - (let (path message-id) - (when (elmo-folder-msgdb-internal folder) - (dolist (number numbers) - ;; important message should always be a read message. - (if (eq (elmo-file-cache-exists-p - (setq message-id - (elmo-message-field folder number 'message-id))) - 'entire) - (elmo-folder-flag-as-read folder (list number))) - (when (elmo-global-flag-p 'important) - (elmo-global-flag-set 'important folder number message-id)) - (elmo-msgdb-set-flag (elmo-folder-msgdb folder) - number - 'important))))) - -(luna-define-method elmo-folder-unflag-read ((folder elmo-folder) - numbers - &optional is-local) +(luna-define-method elmo-folder-set-flag ((folder elmo-folder) + numbers + flag + &optional is-local) (when (elmo-folder-msgdb-internal folder) (dolist (number numbers) - (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) - number - 'read)))) - -(luna-define-method elmo-folder-flag-as-read ((folder elmo-folder) - numbers - &optional is-local) + (let ((old-flags (elmo-message-flags folder number))) + (when (elmo-global-flag-p flag) + (let ((message-id (elmo-message-field folder number 'message-id))) + (elmo-global-flag-set flag folder number message-id))) + (elmo-msgdb-set-flag (elmo-folder-msgdb folder) number flag) + (elmo-emit-signal 'flag-changing + folder + number + old-flags + (elmo-message-flags folder number)))) + (elmo-emit-signal 'flag-changed folder numbers))) + +(defun elmo-message-has-global-flag-p (folder number) + "Return non-nil when the message in the FOLDER with NUMBER has global flag." + (let ((flags (elmo-message-flags folder number)) + result) + (while flags + (when (and (elmo-global-flag-p (car flags)) + (not (memq (car flags) '(answered unread cached)))) + (setq result t + flags nil)) + (setq flags (cdr flags))) + result)) + +(defun elmo-message-set-global-flags (folder number flags &optional local) + "Set global flags of the message in the FOLDER with NUMBER as FLAGS. +If Optional LOCAL is non-nil, don't update server flag." + (dolist (flag flags) + (unless (elmo-global-flag-p flag) + (error "Not a global flag"))) + (let ((old-flags (elmo-get-global-flags (elmo-message-flags folder number)))) + (dolist (flag flags) + (unless (memq flag old-flags) + (elmo-message-set-flag folder number flag local))) + (dolist (flag old-flags) + (unless (memq flag flags) + (elmo-message-unset-flag folder number flag local))))) + +(luna-define-method elmo-folder-unset-flag ((folder elmo-folder) + numbers + flag + &optional is-local) (when (elmo-folder-msgdb-internal folder) (dolist (number numbers) - (elmo-msgdb-set-flag (elmo-folder-msgdb folder) - number - 'read)))) - -(luna-define-method elmo-folder-unflag-answered ((folder elmo-folder) - numbers - &optional is-local) - (when (elmo-folder-msgdb-internal folder) - (dolist (number numbers) - (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) - number - 'answered)))) - -(luna-define-method elmo-folder-flag-as-answered ((folder elmo-folder) - numbers - &optional is-local) - (when (elmo-folder-msgdb-internal folder) - (dolist (number numbers) - (elmo-msgdb-set-flag (elmo-folder-msgdb folder) - number - 'answered)))) + (let ((old-flags (elmo-message-flags folder number))) + (when (elmo-global-flag-p flag) + (elmo-global-flag-detach flag folder number 'always)) + (elmo-msgdb-unset-flag (elmo-folder-msgdb folder) number flag) + (elmo-emit-signal 'flag-changing + folder + number + old-flags + (elmo-message-flags folder number)))) + (elmo-emit-signal 'flag-changed folder numbers))) (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder)) ;; Do nothing. @@ -1302,24 +1434,26 @@ FIELD is a symbol of the field.") ;; Let duplicates be a temporary killed message. (elmo-folder-kill-messages folder duplicates) ;; Should be flag as read. - (elmo-folder-flag-as-read folder duplicates)) + (elmo-folder-unset-flag folder duplicates 'unread)) ((eq (elmo-folder-process-duplicates-internal folder) 'read) ;; Flag as read duplicates. - (elmo-folder-flag-as-read folder duplicates)) + (elmo-folder-unset-flag folder duplicates 'unread)) (t ;; Do nothing. (setq duplicates nil))) (length duplicates)) 0)) -(defun elmo-folder-confirm-appends (appends) +(defun elmo-folder-confirm-appends (folder appends) (let ((len (length appends)) in) (if (and elmo-folder-update-threshold (> len elmo-folder-update-threshold) elmo-folder-update-confirm) - (if (y-or-n-p (format "Too many messages(%d). Update all? " len)) + (if (y-or-n-p (format + "Too many messages(%d) in %s. Update all? " + len (elmo-folder-name-internal folder))) appends (setq in elmo-folder-update-threshold) (catch 'end @@ -1340,26 +1474,25 @@ FIELD is a symbol of the field.") (nthcdr (max (- len elmo-folder-update-threshold) 0) appends) appends)))) +(luna-define-method elmo-message-fetch-bodystructure ((folder elmo-folder) + number strategy) + nil) + +(defun elmo-message-fetch-string (folder number strategy + &optional + unread + section) + (with-temp-buffer + (set-buffer-multibyte nil) + (when (elmo-message-fetch folder number strategy unread section) + (buffer-string)))) + (luna-define-method elmo-message-fetch ((folder elmo-folder) number strategy &optional - section - outbuf - unread) - (if outbuf - (with-current-buffer outbuf - (erase-buffer) - (elmo-message-fetch-with-cache-process folder number - strategy section unread)) - (with-temp-buffer - (elmo-message-fetch-with-cache-process folder number - strategy section unread) - (buffer-string)))) - -(luna-define-method elmo-message-fetch-with-cache-process ((folder elmo-folder) - number strategy - &optional - section unread) + unread + section) + (erase-buffer) (let ((cache-path (elmo-fetch-strategy-cache-path strategy)) (method-priorities (cond ((eq (elmo-fetch-strategy-use-cache strategy) 'maybe) @@ -1368,29 +1501,33 @@ FIELD is a symbol of the field.") '(cache entity)) (t '(entity)))) - result err) + result err updated-server-flag) (while (and method-priorities - (null result)) + (not result)) (setq result (case (car method-priorities) (cache (elmo-file-cache-load cache-path section)) (entity - (when (and (condition-case error - (elmo-message-fetch-internal folder number - strategy - section - unread) - (error (setq err error) nil)) - (> (buffer-size) 0)) - (elmo-delete-cr-buffer) + (when (condition-case error + (elmo-message-fetch-internal folder number + strategy + section + unread) + (error (setq err error) nil)) + (setq updated-server-flag t) (when (and (elmo-fetch-strategy-save-cache strategy) cache-path) (elmo-file-cache-save cache-path section)) t))) method-priorities (cdr method-priorities))) - (or result - (and err (signal (car err) (cdr err)))))) + (if result + (when (and (not unread) + (elmo-message-flagged-p folder number 'unread)) + (elmo-message-unset-flag folder number 'unread updated-server-flag)) + (when err + (signal (car err) (cdr err)))) + result)) (defun elmo-folder-kill-messages-range (folder beg end) (elmo-folder-set-killed-list-internal @@ -1403,9 +1540,27 @@ FIELD is a symbol of the field.") "Kill(hide) messages in the FOLDER with NUMBERS." (elmo-folder-set-killed-list-internal folder - (elmo-number-set-append-list (elmo-folder-killed-list-internal - folder) numbers))) + (elmo-number-set-append-list + (elmo-folder-killed-list-internal folder) + numbers)) + (elmo-folder-unset-flag folder numbers 'all 'local-only)) + +(luna-define-generic elmo-folder-recover-messages (folder numbers) + "Recover killed messages in the FOLDER with NUMBERS.") +(luna-define-method elmo-folder-recover-messages ((folder elmo-folder) numbers) + (let ((msgdb (elmo-folder-msgdb folder))) + (elmo-folder-set-killed-list-internal + folder + (elmo-number-set-delete-list + (elmo-folder-killed-list-internal folder) + numbers)) + (dolist (number numbers) + (if (elmo-file-cache-exists-p + (elmo-message-field folder number 'message-id)) + (elmo-msgdb-set-flag msgdb number 'cached) + (elmo-msgdb-unset-flag msgdb number 'cached))) + (elmo-emit-signal 'status-changed folder numbers))) (luna-define-method elmo-folder-clear ((folder elmo-folder) &optional keep-killed) @@ -1421,7 +1576,8 @@ FIELD is a symbol of the field.") &optional disable-killed ignore-msgdb - no-check) + no-check + mask) "Synchronize the folder data to the newest status. FOLDER is the ELMO folder structure. @@ -1429,54 +1585,57 @@ If optional DISABLE-KILLED is non-nil, killed messages are also synchronized. If optional IGNORE-MSGDB is non-nil, current msgdb is thrown away except flag status. If NO-CHECK is non-nil, rechecking folder is skipped. -Return a list of a cross-posted message number. +If optional argument MASK is specified and is a list of message numbers, +synchronize messages only which are contained the list. +Return amount of cross-posted messages. If update process is interrupted, return nil.") (luna-define-method elmo-folder-synchronize ((folder elmo-folder) &optional disable-killed ignore-msgdb - no-check) - (let ((killed-list (elmo-folder-killed-list-internal folder)) - (before-append t) - old-msgdb diff diff-2 delete-list new-list new-msgdb flag - flag-table crossed after-append) - (setq old-msgdb (elmo-folder-msgdb folder)) - (setq flag-table (elmo-flag-table-load (elmo-folder-msgdb-path folder))) + no-check + mask) + (let ((old-msgdb (elmo-folder-msgdb folder)) + (killed-list (elmo-folder-killed-list-internal folder)) + (flag-table (elmo-flag-table-load (elmo-folder-msgdb-path folder))) + (before-append t)) (when ignore-msgdb (elmo-msgdb-flag-table (elmo-folder-msgdb folder) flag-table) (elmo-folder-clear folder (not disable-killed))) (unless no-check (elmo-folder-check folder)) (condition-case nil - (progn + (let ((killed-list (elmo-folder-killed-list-internal folder)) + diff-new diff-del + delete-list new-list new-msgdb crossed) (message "Checking folder diff...") - (setq diff (elmo-list-diff (elmo-folder-list-messages - folder - (not disable-killed)) - (elmo-folder-list-messages - folder - (not disable-killed) - 'in-msgdb))) + (elmo-set-list + '(diff-new diff-del) + (elmo-list-diff (elmo-folder-list-messages folder) + (elmo-folder-list-messages folder nil 'in-msgdb))) + (when diff-new + (setq diff-new (sort diff-new #'<)) + (unless disable-killed + (setq diff-new (elmo-living-messages diff-new killed-list))) + (when (and mask (not ignore-msgdb)) + (setq diff-new (elmo-list-filter mask diff-new)))) (message "Checking folder diff...done") - (setq new-list (elmo-folder-confirm-appends (car diff))) - ;; Set killed list as ((1 . MAX-OF-DISAPPEARED)) - (when (and (not (eq (length (car diff)) - (length new-list))) - (setq diff-2 (elmo-list-diff (car diff) new-list))) - (elmo-folder-kill-messages-range - folder - (car (car diff-2)) - (nth (- (length (car diff-2)) 1) (car diff-2)))) - (setq delete-list (cadr diff)) - (if (or (equal diff '(nil nil)) - (equal diff '(nil)) - (and (eq (length (car diff)) 0) - (eq (length (cadr diff)) 0))) + (setq new-list (elmo-folder-confirm-appends folder diff-new)) + ;; Append to killed list as (MIN-OF-DISAPPEARED . MAX-OF-DISAPPEARED) + (when (not (eq (length diff-new) + (length new-list))) + (let* ((diff (elmo-list-diff diff-new new-list)) + (disappeared (sort (car diff) #'<))) + (when disappeared + (elmo-folder-kill-messages-range folder + (car disappeared) + (elmo-last disappeared))))) + (setq delete-list diff-del) + (if (and (null diff-new) (null diff-del)) (progn (elmo-folder-update-number folder) (elmo-folder-process-crosspost folder) - 0 ; no updates. - ) + 0) ; `0' means no updates. (when delete-list (elmo-folder-detach-messages folder delete-list)) (when new-list @@ -1505,7 +1664,14 @@ If update process is interrupted, return nil.") (luna-define-method elmo-folder-detach-messages ((folder elmo-folder) numbers) - (elmo-msgdb-delete-messages (elmo-folder-msgdb folder) numbers)) + (when (elmo-msgdb-delete-messages (elmo-folder-msgdb folder) numbers) + ;; Remove NUMBERS from killed message list. + (elmo-folder-set-killed-list-internal + folder + (elmo-number-set-delete-list + (elmo-folder-killed-list-internal folder) + numbers)) + t)) (luna-define-generic elmo-folder-length (folder) "Return number of messages in the FOLDER.") @@ -1518,7 +1684,8 @@ If update process is interrupted, return nil.") (defun elmo-folder-msgdb-load (folder &optional silent) (unless silent (message "Loading msgdb for %s..." (elmo-folder-name-internal folder))) - (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder)))) + (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder) + (elmo-folder-mime-charset-internal folder)))) (elmo-folder-set-info-max-by-numdb folder (elmo-msgdb-list-messages msgdb)) @@ -1590,12 +1757,49 @@ Return a hashtable for newsgroups." (elmo-make-directory temp-dir) temp-dir)) +;; ELMO status structure. +(defmacro elmo-message-status (folder number &optional flags killed) + "Make ELMO status structure from FOLDER and NUMBER. +A value in this structure is cached at first access." + `(vector ,folder ,number ,flags ,killed)) + +(defmacro elmo-message-status-folder (status) + `(aref ,status 0)) + +(defmacro elmo-message-status-number (status) + `(aref ,status 1)) + +(defmacro elmo-message-status-set-flags (status flags) + `(aset ,status 2 (or ,flags '(read)))) + +(defsubst elmo-message-status-flags (status) + (or (aref status 2) + (elmo-message-status-set-flags + status + (elmo-message-flags (elmo-message-status-folder status) + (elmo-message-status-number status))))) + +(defsubst elmo-message-status-cached-p (status) + (memq 'cached (elmo-message-status-flags status))) + +(defmacro elmo-message-status-set-killed (status killed) + `(aset ,status 3 (if ,killed 'killed 'living))) + +(defsubst elmo-message-status-killed-p (status) + (eq 'killed + (or (aref status 3) + (elmo-message-status-set-killed + status + (elmo-message-killed-p (elmo-message-status-folder status) + (elmo-message-status-number status)))))) + +;;; (defun elmo-init () "Initialize ELMO module." (elmo-crosspost-message-alist-load) (elmo-resque-obsolete-variables) - (elmo-global-flag-initialize) - (elmo-dop-queue-load)) + (elmo-dop-queue-load) + (run-hooks 'elmo-init-hook)) (defun elmo-quit () "Quit and cleanup ELMO." @@ -1632,7 +1836,7 @@ Return a hashtable for newsgroups." (elmo-define-folder ?| 'pipe) (elmo-define-folder ?. 'maildir) (elmo-define-folder ?' 'internal) -(elmo-define-folder ?\[ 'nmz) +(elmo-define-folder ?\[ 'search) (elmo-define-folder ?@ 'shimbun) ;;; Obsolete variables. @@ -1670,6 +1874,15 @@ Return a hashtable for newsgroups." 'elmo-cache-directory) (elmo-define-obsolete-variable 'elmo-msgdb-dir 'elmo-msgdb-directory) +(elmo-define-obsolete-variable 'elmo-global-flag-list + 'elmo-global-flags) +(elmo-define-obsolete-variable 'elmo-nmz-default-index-path + 'elmo-search-namazu-default-index-path) +(elmo-define-obsolete-variable 'elmo-nmz-index-alias-alist + 'elmo-search-namazu-index-alias-alist) +(elmo-define-obsolete-variable 'elmo-nmz-use-drive-letter + 'elmo-search-use-drive-letter) + ;; Obsolete functions. ;; 2001-12-11: *-dir -> *-directory