* test-utf7.el (test-utf7-encode-string-alpha): Fix indent.
[elisp/wanderlust.git] / elmo / modb-entity.el
index 1a08083..77d53ce 100644 (file)
   :group 'elmo)
 
 (defcustom modb-entity-field-extractor-alist
-  '((ml-info . modb-entity-extract-mailing-list-info))
+  '((ml-info modb-entity-extract-mailing-list-info
+            modb-entity-ml-info-real-fields))
   "*An alist of field name and function to extract field body from buffer."
-  :type '(repeat (cons (symbol :tag "Field Name")
-                      (function :tag "Function")))
+  :type '(repeat (list (symbol :tag "Field Name")
+                      (function :tag "Extractor")
+                      (choice :tag "Real Field"
+                              (repeat :tag "Field Name List" string)
+                              (function :tag "Function"))))
   :group 'elmo)
 
 (defvar modb-entity-default-cache-internal nil)
@@ -129,12 +133,16 @@ Header region is supposed to be narrowed.")
 ;; Transitional interface.
 (luna-define-generic elmo-msgdb-message-match-condition (handler
                                                         condition
-                                                        entity
-                                                        flags
-                                                        numbers)
+                                                        entity)
   "Return non-nil when the entity matches the condition.")
 
 ;; Generic implementation.
+(luna-define-method initialize-instance :after ((handler modb-entity-handler)
+                                               &rest init-args)
+  (unless (modb-entity-handler-mime-charset-internal handler)
+    (modb-entity-handler-set-mime-charset-internal handler elmo-mime-charset))
+  handler)
+
 (luna-define-method modb-entity-handler-list-parameters
   ((handler modb-entity-handler))
   (list 'mime-charset))
@@ -193,6 +201,11 @@ Header region is supposed to be narrowed.")
        (setq updated t)))
     updated))
 
+;; helper functions
+(defsubst modb-entity-handler-mime-charset (handler)
+  (or (modb-entity-handler-mime-charset-internal handler)
+      elmo-mime-charset))
+
 (defun modb-entity-handler-equal-p (handler other)
   "Return non-nil, if OTHER hanlder is equal this HANDLER."
   (and (eq (luna-class-name handler)
@@ -317,26 +330,21 @@ If each field is t, function is set as default converter."
       (mapconcat 'identity value ", "))))
 
 (defun modb-entity-decode-string-recursive (field value)
-  (cond ((stringp value)
-        (elmo-msgdb-get-decoded-cache value))
-       ((consp value)
-        (setcar value (modb-entity-decode-string-recursive field (car value)))
-        (setcdr value (modb-entity-decode-string-recursive field (cdr value)))
-        value)
-       (t
-        value)))
+  (elmo-map-recursive
+   (lambda (element)
+     (if (stringp element)
+        (elmo-msgdb-get-decoded-cache element)
+       element))
+   value))
 
 (defun modb-entity-encode-string-recursive (field value)
-  (cond ((stringp value)
+  (elmo-map-recursive
+   (lambda (element)
+     (if (stringp element)
         (elmo-with-enable-multibyte
-          (encode-mime-charset-string value elmo-mime-charset)))
-       ((consp value)
-        (setcar value (modb-entity-encode-string-recursive field (car value)))
-        (setcdr value (modb-entity-encode-string-recursive field (cdr value)))
-        value)
-       (t
-        value)))
-
+          (encode-mime-charset-string element elmo-mime-charset))
+       element))
+   value))
 
 (defun modb-entity-create-field-indices (slots)
   (let ((index 0)
@@ -475,7 +483,7 @@ If each field is t, function is set as default converter."
            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 (string-to-number size))
          (setq size 0)))
       (while extras
        (if (setq field-body (elmo-field-body (car extras)))
@@ -534,133 +542,37 @@ If each field is t, function is set as default converter."
          (copy-sequence (cdr entity)))))
 
 (luna-define-method elmo-msgdb-message-match-condition
-  ((handler modb-entity-handler) condition entity flags numbers)
-  (cond
-   ((vectorp condition)
-    (elmo-msgdb-match-condition-primitive handler 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 (handler
-                                            condition
-                                            entity
-                                            flags
-                                            numbers)
-  (catch 'unresolved
-    (let ((key (elmo-filter-key condition))
-         (case-fold-search t)
-         result)
-      (cond
-       ((string= key "last")
-       (setq result (<= (length (memq
-                                 (elmo-msgdb-message-entity-number
-                                  handler entity)
-                                 numbers))
-                        (string-to-int (elmo-filter-value condition)))))
-       ((string= key "first")
-       (setq result (< (-
-                        (length numbers)
-                        (length (memq
-                                 (elmo-msgdb-message-entity-number
-                                  handler entity)
-                                 numbers)))
-                       (string-to-int (elmo-filter-value condition)))))
-       ((string= key "flag")
-       (setq result
-             (cond
-              ((string= (elmo-filter-value condition) "any")
-               (or (memq 'important flags)
-                   (memq 'answered flags)
-                   (memq 'unread flags)))
-              ((string= (elmo-filter-value condition) "digest")
-               (or (memq 'important flags)
-                   (memq 'unread flags)))
-              ((string= (elmo-filter-value condition) "unread")
-               (memq 'unread flags))
-              ((string= (elmo-filter-value condition) "important")
-               (memq 'important flags))
-              ((string= (elmo-filter-value condition) "answered")
-               (memq 'answered flags)))))
-       ((string= key "from")
-       (setq result (string-match
-                     (elmo-filter-value condition)
-                     (elmo-msgdb-message-entity-field
-                      handler entity 'from))))
-       ((string= key "subject")
-       (setq result (string-match
-                     (elmo-filter-value condition)
-                     (elmo-msgdb-message-entity-field
-                      handler entity 'subject))))
-       ((string= key "to")
-       (setq result (string-match
-                     (elmo-filter-value condition)
-                     (elmo-msgdb-message-entity-field
-                      handler entity 'to 'string))))
-       ((string= key "cc")
-       (setq result (string-match
-                     (elmo-filter-value condition)
-                     (elmo-msgdb-message-entity-field
-                      handler entity 'cc 'string))))
-       ((or (string= key "since")
-           (string= key "before"))
-       (let ((field-date (elmo-msgdb-message-entity-field
-                          handler entity 'date))
-             (specified-date
-              (elmo-datevec-to-time
-               (elmo-date-get-datevec
-                (elmo-filter-value condition)))))
-         (setq result (if (string= key "since")
-                          (not (elmo-time< field-date specified-date))
-                        (elmo-time< field-date specified-date)))))
-       ((member key elmo-msgdb-extra-fields)
-       (let ((extval (elmo-msgdb-message-entity-field handler
-                                                      entity
-                                                      (intern key)
-                                                      'string)))
-         (when (stringp extval)
-           (setq result (string-match
-                         (elmo-filter-value condition)
-                         extval)))))
-       (t
-       (throw 'unresolved condition)))
-      (if (eq (elmo-filter-type condition) 'unmatch)
-         (not result)
-       result))))
+  ((handler modb-entity-handler) condition entity)
+  (let ((key (elmo-filter-key condition))
+       (case-fold-search t)
+       field-value)
+    (cond
+     ((or (string= key "since")
+         (string= key "before"))
+      (let ((field-date (elmo-msgdb-message-entity-field
+                        handler entity 'date))
+           (specified-date
+            (elmo-datevec-to-time
+             (elmo-date-get-datevec
+              (elmo-filter-value condition)))))
+       (if (string= key "since")
+           (not (elmo-time< field-date specified-date))
+         (elmo-time< field-date specified-date))))
+     ((or (string= key "larger")
+         (string= key "smaller"))
+      (let ((bytes (elmo-msgdb-message-entity-field handler entity 'size))
+           (threshold (string-to-number (elmo-filter-value condition))))
+       (if (string= key "larger")
+           (> bytes threshold)
+         (< bytes threshold))))
+     ((setq field-value (elmo-msgdb-message-entity-field handler
+                                                        entity
+                                                        (intern key)
+                                                        'string))
+      (and (stringp field-value)
+          (string-match (elmo-filter-value condition) field-value)))
+     (t
+      condition))))
 
 
 ;; Standard implementation.
@@ -724,8 +636,7 @@ If each field is t, function is set as default converter."
     (let (index)
       (unless as-is
        (let ((elmo-mime-charset
-              (or (modb-entity-handler-mime-charset-internal (car entity))
-                  elmo-mime-charset)))
+              (modb-entity-handler-mime-charset (car entity))))
          (setq value (modb-convert-field-value modb-standard-entity-normalizer
                                                field value))))
       (cond ((memq field '(message-id :message-id))
@@ -772,8 +683,7 @@ If each field is t, function is set as default converter."
   ((handler modb-standard-entity-handler) entity field &optional type)
   (and entity
        (let ((elmo-mime-charset
-             (or (modb-entity-handler-mime-charset-internal handler)
-                 elmo-mime-charset))
+             (modb-entity-handler-mime-charset handler))
             index)
         (modb-convert-field-value
          modb-standard-entity-specializer
@@ -813,17 +723,9 @@ If each field is t, function is set as default converter."
 
 (luna-define-method elmo-msgdb-create-message-entity-from-buffer
   ((handler modb-standard-entity-handler) number args)
-  (let ((default-mime-charset default-mime-charset)
-       entity content-type charset)
+  (let (entity)
     (save-excursion
       (set-buffer-multibyte default-enable-multibyte-characters)
-      (and (setq content-type (elmo-decoded-field-body
-                              "content-type" 'summary))
-          (setq charset (mime-content-type-parameter
-                         (mime-parse-Content-Type content-type) "charset"))
-          (setq charset (intern-soft charset))
-          (mime-charset-p charset)
-          (setq default-mime-charset charset))
       (setq entity
            (modb-standard-make-message-entity
             handler
@@ -859,17 +761,17 @@ If each field is t, function is set as default converter."
                  (mime-decode-field-body field-body "cc" 'summary))
                (elmo-multiple-field-body "cc") ",")
               :content-type
-              content-type
+              (elmo-decoded-field-body "content-type" 'summary)
               :size
               (let ((size (elmo-field-body "content-length")))
                 (if size
-                    (string-to-int size)
+                    (string-to-number size)
                   (or (plist-get args :size) 0)))))))
       (let (field-name field-body extractor)
        (dolist (extra (cons "newsgroups" elmo-msgdb-extra-fields))
          (setq field-name (intern (downcase extra))
-               extractor  (cdr (assq field-name
-                                     modb-entity-field-extractor-alist))
+               extractor  (nth 1 (assq field-name
+                                       modb-entity-field-extractor-alist))
                field-body (if extractor
                               (funcall extractor field-name)
                             (elmo-decoded-field-body extra 'summary)))
@@ -879,66 +781,24 @@ If each field is t, function is set as default converter."
 
 
 ;; mailing list info handling
-(defun modb-entity-extract-ml-info-from-x-sequence ()
-  (let ((sequence (elmo-decoded-field-body "x-sequence" 'summary))
-       name count)
-    (when sequence
-      (elmo-set-list '(name count) (split-string sequence " "))
-      (cons name count))))
-
-(defun modb-entity-extract-ml-info-from-subject ()
-  (let ((subject (elmo-decoded-field-body "subject" 'summary)))
-    (when (and subject
-              (string-match "^\\s(\\(\\S)+\\)[ :]\\([0-9]+\\)\\s)[ \t]*"
-                            subject))
-      (cons (match-string 1 subject) (match-string 2 subject)))))
-
-(defun modb-entity-extract-ml-info-from-return-path ()
-  (let ((return-path (elmo-decoded-field-body "return-path" 'summary)))
-    (when (and return-path
-              (string-match "^<\\([^@>]+\\)-return-\\([0-9]+\\)-"
-                            return-path))
-      (cons (match-string 1 return-path)
-           (match-string 2 return-path)))))
-
-(defun modb-entity-extract-ml-info-from-delivered-to ()
-  (let ((delivered-to (elmo-decoded-field-body "delivered-to" 'summary)))
-    (when (and delivered-to
-              (string-match "^mailing list \\([^@]+\\)@" delivered-to))
-      (cons (match-string 1 delivered-to) nil))))
-
-(defun modb-entity-extract-ml-info-from-mailing-list ()
-  (let ((mailing-list (elmo-decoded-field-body "mailing-list" 'summary)))
-    ;; *-help@, *-owner@, etc.
-    (when (and mailing-list
-              (string-match "\\(^\\|; \\)contact \\([^@]+\\)-[^-@]+@"
-                            mailing-list))
-      (cons (match-string 2 mailing-list) nil))))
-
-(defvar modb-entity-extract-mailing-list-info-functions
-  '(modb-entity-extract-ml-info-from-x-sequence
-    modb-entity-extract-ml-info-from-subject
-    modb-entity-extract-ml-info-from-return-path
-    modb-entity-extract-ml-info-from-delivered-to
-    modb-entity-extract-ml-info-from-mailing-list))
-
 (defun modb-entity-extract-mailing-list-info (field)
-  (let ((ml-name (elmo-decoded-field-body "x-ml-name" 'summary))
-       (ml-count (or (elmo-decoded-field-body "x-mail-count" 'summary)
-                     (elmo-decoded-field-body "x-ml-count" 'summary)))
-       (functions modb-entity-extract-mailing-list-info-functions)
-       result)
-    (while (and functions
-               (or (null ml-name) (null ml-count)))
-      (when (setq result (funcall (car functions)))
-       (unless ml-name
-         (setq ml-name (car result)))
-       (unless ml-count
-         (setq ml-count (cdr result))))
-      (setq functions (cdr functions)))
-    (when (or ml-name ml-count)
-      (cons (and ml-name (car (split-string ml-name " ")))
-           (and ml-count (string-to-int ml-count))))))
+  (let* ((getter (lambda (field)
+                  (elmo-decoded-field-body (symbol-name field) 'summary)))
+        (name (elmo-find-list-match-value
+               elmo-mailing-list-name-spec-list
+               getter))
+        (count (elmo-find-list-match-value
+                 elmo-mailing-list-count-spec-list
+                 getter)))
+    (when (or name count)
+      (cons name (and count (string-to-number count))))))
+
+(defun modb-entity-ml-info-real-fields (field)
+  (elmo-uniq-list
+   (mapcar (lambda (entry)
+            (symbol-name (if (consp entry) (car entry) entry)))
+          (append elmo-mailing-list-name-spec-list
+                  elmo-mailing-list-count-spec-list))))
 
 (defun modb-entity-make-mailing-list-info-string (field value)
   (when (car value)
@@ -946,6 +806,66 @@ If each field is t, function is set as default converter."
            (elmo-msgdb-get-decoded-cache (car value))
            (cdr value))))
 
+;; message buffer handler
+(eval-and-compile
+  (luna-define-class modb-buffer-entity-handler (modb-entity-handler)))
+
+(defvar modb-buffer-entity-specializer nil)
+(modb-set-field-converter 'modb-buffer-entity-specializer nil
+  'date        #'elmo-time-parse-date-string)
+
+(luna-define-method elmo-msgdb-make-message-entity
+  ((handler modb-buffer-entity-handler) args)
+  (cons handler (cons (or (plist-get args :number)
+                         (plist-get args 'number))
+                     (or (plist-get args :buffer)
+                         (plist-get args 'buffer)
+                         (current-buffer)))))
+
+(luna-define-method elmo-msgdb-message-entity-number
+  ((handler modb-buffer-entity-handler) entity)
+  (car (cdr entity)))
+
+(luna-define-method elmo-msgdb-message-entity-set-number
+  ((handler modb-buffer-entity-handler) entity number)
+  (and entity (setcar (cdr entity) number)))
+
+(luna-define-method elmo-msgdb-message-entity-field
+  ((handler modb-buffer-entity-handler) entity field &optional type)
+  (and entity
+       (let ((elmo-mime-charset
+             (modb-entity-handler-mime-charset handler)))
+        (modb-convert-field-value
+         modb-buffer-entity-specializer
+         field
+         (if (memq field '(number :number))
+             (car (cdr entity))
+           (with-current-buffer (cdr (cdr entity))
+             (let ((extractor
+                    (nth 1 (assq field modb-entity-field-extractor-alist))))
+               (if extractor
+                   (funcall extractor field)
+                 (mapconcat
+                  (lambda (field-body)
+                    (mime-decode-field-body field-body (symbol-name field)
+                                            'summary))
+                  (elmo-multiple-field-body (symbol-name field))
+                  "\n")))))
+         type))))
+
+(luna-define-method elmo-msgdb-message-match-condition :around
+  ((handler modb-buffer-entity-handler) condition entity)
+  (let ((key (elmo-filter-key condition))
+       (case-fold-search t))
+    (cond
+     ((string= (elmo-filter-key condition) "body")
+      (with-current-buffer (cdr (cdr entity))
+       (goto-char (point-min))
+       (and (re-search-forward "^$" nil t)        ; goto body
+            (search-forward (elmo-filter-value condition) nil t))))
+     (t
+      (luna-call-next-method)))))
+
 (require 'product)
 (product-provide (provide 'modb-entity) (require 'elmo-version))