* elmo-msgdb.el (elmo-msgdb-get-message-id-from-buffer): Abolish
[elisp/wanderlust.git] / elmo / modb-entity.el
index 546a6f4..b44cc02 100644 (file)
 
 ;;; Commentary:
 ;;
+;; Message entity handling.
 
 ;;; Code:
-;;
 
 (eval-when-compile (require 'cl))
 
+(require 'luna)
 (require 'elmo-vars)
 (require 'elmo-util)
-(require 'mime)
+
+(eval-and-compile (luna-define-class modb-entity-handler))
+
+(defcustom modb-entity-default-handler 'modb-legacy-entity-handler
+  "Default entity handler."
+  :type 'symbol
+  :group 'elmo)
+
+(defvar modb-entity-default-cache-internal nil)
+
+(defun elmo-message-entity-handler (&optional entity)
+  "Get modb entity handler instance which corresponds to the ENTITY."
+  (if (and entity
+          (not (stringp (car entity))))
+      (car entity)
+    (or modb-entity-default-cache-internal
+       (setq modb-entity-default-cache-internal
+             (luna-make-entity modb-entity-default-handler)))))
+
+(luna-define-generic elmo-msgdb-make-message-entity (handler &rest args)
+  "Make a message entity using HANDLER.")
+
+(luna-define-generic elmo-msgdb-message-entity-number (handler entity)
+  "Number of the ENTITY.")
+
+(luna-define-generic elmo-msgdb-message-entity-set-number (handler
+                                                          entity number)
+  "Set number of the ENTITY.")
+
+(luna-define-generic elmo-msgdb-message-entity-field (handler
+                                                     entity field
+                                                     &optional decode)
+  "Retrieve field value of the message entity.
+HANDLER is the message entity handler.
+ENTITY is the message entity structure.
+FIELD is a symbol of the field.
+If optional DECODE is no-nil, the field value is decoded.")
+
+(luna-define-generic elmo-msgdb-message-entity-set-field (handler
+                                                         entity field value)
+  "Set the field value of the message entity.
+HANDLER is the message entity handler.
+ENTITY is the message entity structure.
+FIELD is a symbol of the field.
+VALUE is the field value to set.")
+
+(luna-define-generic elmo-msgdb-copy-message-entity (handler entity)
+  "Copy message entity.
+HANDLER is the message entity handler.
+ENTITY is the message entity structure.")
+
+(luna-define-generic elmo-msgdb-create-message-entity-from-file (handler
+                                                                number
+                                                                file)
+  "Create message entity from file.
+HANDLER is the message entity handler.
+NUMBER is the number of the newly created message entity.
+FILE is the message file.")
+
+(luna-define-generic elmo-msgdb-create-message-entity-from-buffer (handler
+                                                                  number
+                                                                  &rest args)
+  "Create message entity from current buffer.
+HANDLER is the message entity handler.
+NUMBER is the number of the newly created message entity.
+Rest of the ARGS is a plist of message entity field for initial value.
+Header region is supposed to be narrowed.")
+
+;; Transitional interface.
+(luna-define-generic elmo-msgdb-message-match-condition (handler
+                                                        condition
+                                                        entity
+                                                        flags
+                                                        numbers)
+  "Return non-nil when the entity matches the condition.")
+
+;; Generic implementation.
+(luna-define-method elmo-msgdb-create-message-entity-from-file
+  ((handler modb-entity-handler) number file)
+  (let (insert-file-contents-pre-hook   ; To avoid autoconv-xmas...
+       insert-file-contents-post-hook header-end
+       (attrib (file-attributes file))
+       ret-val size mtime)
+    (with-temp-buffer
+      (if (not (file-exists-p file))
+         ()
+       (setq size (nth 7 attrib))
+       (setq mtime (timezone-make-date-arpa-standard
+                    (current-time-string (nth 5 attrib)) (current-time-zone)))
+       ;; insert header from file.
+       (catch 'done
+         (condition-case nil
+             (elmo-msgdb-insert-file-header file)
+           (error (throw 'done nil)))
+         (goto-char (point-min))
+         (setq header-end
+               (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
+                   (point)
+                 (point-max)))
+         (narrow-to-region (point-min) header-end)
+         (elmo-msgdb-create-message-entity-from-buffer
+          handler number :size size :date mtime))))))
+
+(luna-define-method elmo-msgdb-make-message-entity ((handler
+                                                    modb-entity-handler)
+                                                   args)
+  (cons handler args))
+
+(luna-define-method elmo-msgdb-message-entity-field ((handler
+                                                    modb-entity-handler)
+                                                    entity field
+                                                    &optional decode)
+  (plist-get (cdr entity) (intern (concat ":" (symbol-name field)))))
+
+(luna-define-method elmo-msgdb-message-entity-number ((handler
+                                                      modb-entity-handler)
+                                                     entity)
+  (plist-get (cdr entity) :number))
+
+;; Legacy implementation.
+(eval-and-compile (luna-define-class modb-legacy-entity-handler
+                                    (modb-entity-handler)))
 
 ;;
 ;; mime decode cache
-
+;;
 (defvar elmo-msgdb-decoded-cache-hashtb nil)
 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
 
              decoded)))
     (decode-mime-charset-string string elmo-mime-charset)))
 
-
-;;; Message entity interface
-;;
-(defun elmo-msgdb-make-message-entity (&rest args)
+(defsubst modb-legacy-make-message-entity (args)
   "Make an message entity."
   (cons (plist-get args :message-id)
        (vector (plist-get args :number)
                (plist-get args :size)
                (plist-get args :extra))))
 
-(defsubst elmo-msgdb-message-entity-field (entity field &optional decode)
+(luna-define-method elmo-msgdb-make-message-entity
+  ((handler modb-legacy-entity-handler) args)
+  (modb-legacy-make-message-entity args))
+
+(luna-define-method elmo-msgdb-create-message-entity-from-buffer
+  ((handler modb-legacy-entity-handler) number args)
+  (let ((extras elmo-msgdb-extra-fields)
+       (default-mime-charset default-mime-charset)
+       entity message-id references from subject to cc date
+       extra field-body charset size)
+    (save-excursion
+      (setq entity (modb-legacy-make-message-entity args))
+      (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+      (setq message-id (elmo-msgdb-get-message-id-from-buffer))
+      (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
+          (setq charset (intern-soft charset))
+          (setq default-mime-charset charset))
+      (setq references
+           (or (elmo-msgdb-get-last-message-id
+                (elmo-field-body "in-reply-to"))
+               (elmo-msgdb-get-last-message-id
+                (elmo-field-body "references")))
+           from (elmo-replace-in-string
+                 (elmo-mime-string (or (elmo-field-body "from")
+                                       elmo-no-from))
+                 "\t" " ")
+           subject (elmo-replace-in-string
+                    (elmo-mime-string (or (elmo-field-body "subject")
+                                          elmo-no-subject))
+                    "\t" " ")
+           date (elmo-unfold-field-body "date")
+           to   (mapconcat 'identity (elmo-multiple-field-body "to") ",")
+           cc   (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
+      (unless (elmo-msgdb-message-entity-field handler entity 'size)
+       (if (setq size (elmo-field-body "content-length"))
+           (setq size (string-to-int size))
+         (setq size 0)))
+      (while extras
+       (if (setq field-body (elmo-field-body (car extras)))
+           (elmo-msgdb-message-entity-set-field
+            handler entity (intern (downcase (car extras))) field-body))
+       (setq extras (cdr extras)))
+      (dolist (field '(message-id number references from subject
+                                 date to cc size))
+       (when (symbol-value field)
+         (elmo-msgdb-message-entity-set-field
+          handler entity field (symbol-value field))))
+      entity)))
+
+(luna-define-method elmo-msgdb-message-entity-number
+  ((handler modb-legacy-entity-handler) entity)
+  (and entity (aref (cdr entity) 0)))
+
+(luna-define-method elmo-msgdb-message-entity-set-number
+  ((handler modb-legacy-entity-handler) entity number)
+  (and entity (aset (cdr entity) 0 number))
+  entity)
+
+(luna-define-method elmo-msgdb-message-entity-field
+  ((handler modb-legacy-entity-handler) entity field &optional decode)
   (and entity
        (let ((field-value
              (case field
             (elmo-msgdb-get-decoded-cache field-value)
           field-value))))
 
-(defsubst elmo-msgdb-message-entity-set-field (entity field value)
+(luna-define-method elmo-msgdb-message-entity-set-field
+  ((handler modb-legacy-entity-handler) entity field value)
   (and entity
        (case field
+        (number (aset (cdr entity) 0 value))
         (to (aset (cdr entity) 5 value))
         (cc (aset (cdr entity) 6 value))
         (date (aset (cdr entity) 4 value))
         (t
          (let ((extras (and entity (aref (cdr entity) 8)))
                extra)
-           (if (setq extra (assoc field extras))
+           (if (setq extra (assoc (symbol-name field) extras))
                (setcdr extra value)
              (aset (cdr entity) 8 (cons (cons (symbol-name field)
                                               value) extras))))))))
 
-(defun elmo-msgdb-copy-overview-entity (entity)
+(luna-define-method elmo-msgdb-copy-message-entity
+  ((handler modb-legacy-entity-handler) entity)
   (cons (car entity)
        (copy-sequence (cdr entity))))
 
-;;; obsolete interface
-;;
-(defsubst elmo-msgdb-overview-entity-get-id (entity)
-  (and entity (car entity)))
-
-(defsubst elmo-msgdb-overview-entity-get-number (entity)
-  (and entity (aref (cdr entity) 0)))
-
-(defsubst elmo-msgdb-overview-entity-set-number (entity number)
-  (and entity (aset (cdr entity) 0 number))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-references (entity)
-  (and entity (aref (cdr entity) 1)))
-
-(defsubst elmo-msgdb-overview-entity-set-references (entity references)
-  (and entity (aset (cdr entity) 1 references))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-from-no-decode (entity)
-  (and entity (aref (cdr entity) 2)))
-
-(defsubst elmo-msgdb-overview-entity-get-from (entity)
-  (and entity
-       (aref (cdr entity) 2)
-       (elmo-msgdb-get-decoded-cache (aref (cdr entity) 2))))
-
-(defsubst elmo-msgdb-overview-entity-set-from (entity from)
-  (and entity (aset (cdr entity) 2 from))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-subject (entity)
-  (and entity
-       (aref (cdr entity) 3)
-       (elmo-msgdb-get-decoded-cache (aref (cdr entity) 3))))
-
-(defsubst elmo-msgdb-overview-entity-get-subject-no-decode (entity)
-  (and entity (aref (cdr entity) 3)))
-
-(defsubst elmo-msgdb-overview-entity-set-subject (entity subject)
-  (and entity (aset (cdr entity) 3 subject))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-date (entity)
-  (and entity (aref (cdr entity) 4)))
-
-(defsubst elmo-msgdb-overview-entity-set-date (entity date)
-  (and entity (aset (cdr entity) 4 date))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-to (entity)
-  (and entity (aref (cdr entity) 5)))
-
-(defsubst elmo-msgdb-overview-entity-get-cc (entity)
-  (and entity (aref (cdr entity) 6)))
-
-(defsubst elmo-msgdb-overview-entity-get-size (entity)
-  (and entity (aref (cdr entity) 7)))
-
-(defsubst elmo-msgdb-overview-entity-set-size (entity size)
-  (and entity (aset (cdr entity) 7 size))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-extra (entity)
-  (and entity (aref (cdr entity) 8)))
-
-(defsubst elmo-msgdb-overview-entity-set-extra (entity extra)
-  (and entity (aset (cdr entity) 8 extra))
-  entity)
-
-(defsubst elmo-msgdb-overview-entity-get-extra-field (entity field-name)
-  (let ((field-name (downcase field-name))
-       (extra (and entity (aref (cdr entity) 8))))
-    (and extra
-        (cdr (assoc field-name extra)))))
-
-(defsubst elmo-msgdb-overview-entity-set-extra-field (entity field-name value)
-  (let ((field-name (downcase field-name))
-       (extras (and entity (aref (cdr entity) 8)))
-       extra)
-    (if (setq extra (assoc field-name extras))
-       (setcdr extra value)
-      (elmo-msgdb-overview-entity-set-extra
-       entity
-       (cons (cons field-name value) extras)))))
-
+(luna-define-method elmo-msgdb-message-match-condition
+  ((handler modb-legacy-entity-handler) condition entity flags numbers)
+  (cond
+   ((vectorp condition)
+    (elmo-msgdb-match-condition-primitive condition entity flags numbers))
+   ((eq (car condition) 'and)
+    (let ((lhs (elmo-msgdb-message-match-condition handler
+                                                  (nth 1 condition)
+                                                  entity flags numbers)))
+      (cond
+       ((elmo-filter-condition-p lhs)
+       (let ((rhs (elmo-msgdb-message-match-condition
+                   handler (nth 2 condition) entity flags numbers)))
+         (cond ((elmo-filter-condition-p rhs)
+                (list 'and lhs rhs))
+               (rhs
+                lhs))))
+       (lhs
+       (elmo-msgdb-message-match-condition handler (nth 2 condition)
+                                           entity flags numbers)))))
+   ((eq (car condition) 'or)
+    (let ((lhs (elmo-msgdb-message-match-condition handler (nth 1 condition)
+                                                  entity flags numbers)))
+      (cond
+       ((elmo-filter-condition-p lhs)
+       (let ((rhs (elmo-msgdb-message-match-condition handler
+                                                      (nth 2 condition)
+                                                      entity flags numbers)))
+         (cond ((elmo-filter-condition-p rhs)
+                (list 'or lhs rhs))
+               (rhs
+                t)
+               (t
+                lhs))))
+       (lhs
+       t)
+       (t
+       (elmo-msgdb-message-match-condition handler
+                                            (nth 2 condition)
+                                            entity flags numbers)))))))
 
-;;;
 ;;
 (defun elmo-msgdb-match-condition-primitive (condition entity flags numbers)
   (catch 'unresolved
       (cond
        ((string= key "last")
        (setq result (<= (length (memq
-                                 (elmo-msgdb-overview-entity-get-number
+                                 (elmo-msgdb-message-entity-number
+                                  (elmo-message-entity-handler entity)
                                   entity)
                                  numbers))
                         (string-to-int (elmo-filter-value condition)))))
        (setq result (< (-
                         (length numbers)
                         (length (memq
-                                 (elmo-msgdb-overview-entity-get-number
+                                 (elmo-msgdb-message-entity-number
+                                  (elmo-message-entity-handler entity)
                                   entity)
                                  numbers)))
                        (string-to-int (elmo-filter-value condition)))))
        ((string= key "from")
        (setq result (string-match
                      (elmo-filter-value condition)
-                     (elmo-msgdb-overview-entity-get-from entity))))
+                     (elmo-msgdb-message-entity-field
+                      (elmo-message-entity-handler entity)
+                      entity 'from t))))
        ((string= key "subject")
        (setq result (string-match
                      (elmo-filter-value condition)
-                     (elmo-msgdb-overview-entity-get-subject entity))))
+                     (elmo-msgdb-message-entity-field
+                      (elmo-message-entity-handler entity)
+                      entity 'subject t))))
        ((string= key "to")
        (setq result (string-match
                      (elmo-filter-value condition)
-                     (elmo-msgdb-overview-entity-get-to entity))))
+                     (elmo-msgdb-message-entity-field
+                      (elmo-message-entity-handler entity)
+                      entity 'to))))
        ((string= key "cc")
        (setq result (string-match
                      (elmo-filter-value condition)
-                     (elmo-msgdb-overview-entity-get-cc entity))))
+                     (elmo-msgdb-message-entity-field
+                      (elmo-message-entity-handler entity)
+                      entity 'cc))))
        ((or (string= key "since")
            (string= key "before"))
        (let ((field-date (elmo-date-make-sortable-string
                           (timezone-fix-time
-                           (elmo-msgdb-overview-entity-get-date entity)
+                           (elmo-msgdb-message-entity-field
+                            (elmo-message-entity-handler entity)
+                            entity 'date)
                            (current-time-zone) nil)))
              (specified-date
               (elmo-date-make-sortable-string
                               (string< specified-date field-date))
                         (string< field-date specified-date)))))
        ((member key elmo-msgdb-extra-fields)
-       (let ((extval (elmo-msgdb-overview-entity-get-extra-field entity key)))
+       (let ((extval (elmo-msgdb-message-entity-field
+                      (elmo-message-entity-handler entity)
+                      entity (intern key))))
          (when (stringp extval)
            (setq result (string-match
                          (elmo-filter-value condition)
          (not result)
        result))))
 
-(defun elmo-msgdb-match-condition-internal (condition entity flags numbers)
-  (cond
-   ((vectorp condition)
-    (elmo-msgdb-match-condition-primitive condition entity flags numbers))
-   ((eq (car condition) 'and)
-    (let ((lhs (elmo-msgdb-match-condition-internal (nth 1 condition)
-                                                   entity flags numbers)))
-      (cond
-       ((elmo-filter-condition-p lhs)
-       (let ((rhs (elmo-msgdb-match-condition-internal
-                   (nth 2 condition) entity flags numbers)))
-         (cond ((elmo-filter-condition-p rhs)
-                (list 'and lhs rhs))
-               (rhs
-                lhs))))
-       (lhs
-       (elmo-msgdb-match-condition-internal (nth 2 condition)
-                                            entity flags numbers)))))
-   ((eq (car condition) 'or)
-    (let ((lhs (elmo-msgdb-match-condition-internal (nth 1 condition)
-                                                   entity flags numbers)))
-      (cond
-       ((elmo-filter-condition-p lhs)
-       (let ((rhs (elmo-msgdb-match-condition-internal (nth 2 condition)
-                                                       entity flags numbers)))
-         (cond ((elmo-filter-condition-p rhs)
-                (list 'or lhs rhs))
-               (rhs
-                t)
-               (t
-                lhs))))
-       (lhs
-       t)
-       (t
-       (elmo-msgdb-match-condition-internal (nth 2 condition)
-                                            entity flags numbers)))))))
-
-
 (require 'product)
 (product-provide (provide 'modb-entity) (require 'elmo-version))