1 ;;; modb-entity.el --- Message Entity Interface.
3 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news
9 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
29 ;; Message entity handling.
33 (eval-when-compile (require 'cl))
39 (eval-and-compile (luna-define-class modb-entity-handler))
41 (defcustom modb-entity-default-handler 'modb-legacy-entity-handler
42 "Default entity handler."
46 (defcustom modb-entity-field-extractor-alist
47 '((ml-info . modb-entity-extract-mailing-list-info))
48 "*An alist of field name and function to extract field body from buffer."
49 :type '(repeat (cons (symbol :tag "Field Name")
50 (function :tag "Function")))
53 (defvar modb-entity-default-cache-internal nil)
55 (defun elmo-message-entity-handler (&optional entity)
56 "Get modb entity handler instance which corresponds to the ENTITY."
59 (not (eq (car entity) t))
60 (not (stringp (car entity))))
62 (or modb-entity-default-cache-internal
63 (setq modb-entity-default-cache-internal
64 (luna-make-entity modb-entity-default-handler)))))
66 (luna-define-generic elmo-msgdb-make-message-entity (handler &rest args)
67 "Make a message entity using HANDLER.")
69 (luna-define-generic elmo-msgdb-message-entity-number (handler entity)
70 "Number of the ENTITY.")
72 (luna-define-generic elmo-msgdb-message-entity-set-number (handler
74 "Set number of the ENTITY.")
76 (luna-define-generic elmo-msgdb-message-entity-field (handler entity field
78 "Retrieve field value of the message entity.
79 HANDLER is the message entity handler.
80 ENTITY is the message entity structure.
81 FIELD is a symbol of the field.
82 If optional argument TYPE is specified, return converted value.")
84 (luna-define-generic elmo-msgdb-message-entity-set-field (handler
86 "Set the field value of the message entity.
87 HANDLER is the message entity handler.
88 ENTITY is the message entity structure.
89 FIELD is a symbol of the field.
90 VALUE is the field value to set.")
92 (luna-define-generic elmo-msgdb-message-entity-update-fields (handler
94 "Update message entity by VALUES.
95 HANDLER is the message entity handler.
96 ENTITY is the message entity structure.
97 VALUES is an alist of field-name and field-value.")
99 (luna-define-generic elmo-msgdb-copy-message-entity (handler entity
102 "Copy message entity.
103 HANDLER is the message entity handler.
104 ENTITY is the message entity structure.
105 If optional argument MAKE-HANDLER is specified, use it to make new entity.")
107 (luna-define-generic elmo-msgdb-create-message-entity-from-file (handler
110 "Create message entity from file.
111 HANDLER is the message entity handler.
112 NUMBER is the number of the newly created message entity.
113 FILE is the message file.")
115 (luna-define-generic elmo-msgdb-create-message-entity-from-buffer (handler
118 "Create message entity from current buffer.
119 HANDLER is the message entity handler.
120 NUMBER is the number of the newly created message entity.
121 Rest of the ARGS is a plist of message entity field for initial value.
122 Header region is supposed to be narrowed.")
124 ;; Transitional interface.
125 (luna-define-generic elmo-msgdb-message-match-condition (handler
130 "Return non-nil when the entity matches the condition.")
132 ;; Generic implementation.
133 (luna-define-method elmo-msgdb-create-message-entity-from-file
134 ((handler modb-entity-handler) number file)
135 (let (insert-file-contents-pre-hook ; To avoid autoconv-xmas...
136 insert-file-contents-post-hook header-end
137 (attrib (file-attributes file))
140 (if (not (file-exists-p file))
142 (setq size (nth 7 attrib))
143 (setq mtime (timezone-make-date-arpa-standard
144 (current-time-string (nth 5 attrib)) (current-time-zone)))
145 ;; insert header from file.
148 (elmo-msgdb-insert-file-header file)
149 (error (throw 'done nil)))
150 (goto-char (point-min))
152 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
155 (narrow-to-region (point-min) header-end)
156 (elmo-msgdb-create-message-entity-from-buffer
157 handler number :size size :date mtime))))))
159 (luna-define-method elmo-msgdb-make-message-entity ((handler
164 (luna-define-method elmo-msgdb-message-entity-field ((handler
168 (plist-get (cdr entity) (intern (concat ":" (symbol-name field)))))
170 (luna-define-method elmo-msgdb-message-entity-number ((handler
173 (plist-get (cdr entity) :number))
175 (luna-define-method elmo-msgdb-message-entity-update-fields
176 ((handler modb-entity-handler) entity values)
178 (dolist (pair values)
181 (elmo-msgdb-message-entity-field handler entity (car pair)))
182 (elmo-msgdb-message-entity-set-field handler entity
183 (car pair) (cdr pair))
188 ;; field in/out converter
189 (defun modb-set-field-converter (converter type &rest specs)
190 "Set convert function of TYPE into CONVERTER.
191 SPECS must be like `FIELD1 FUNCTION1 FIELD2 FUNCTION2 ...'.
192 If each field is t, function is set as default converter."
194 (let ((alist (symbol-value converter))
197 (let ((field (pop specs))
198 (function (pop specs))
200 (if (setq cell (assq type alist))
201 (setcdr cell (put-alist field function (cdr cell)))
202 (setq cell (cons type (list (cons field function)))
203 alist (cons cell alist)))
204 ;; support colon keyword (syntax sugar).
205 (unless (or (eq field t)
206 (string-match "^:" (symbol-name field)))
207 (setcdr cell (put-alist (intern (concat ":" (symbol-name field)))
210 (set converter alist))))
211 (put 'modb-set-field-converter 'lisp-indent-function 2)
213 (defsubst modb-convert-field-value (converter field value &optional type)
215 (let* ((alist (cdr (assq (or type t) converter)))
216 (function (cdr (or (assq field alist)
219 (funcall function field value)
223 (defvar elmo-msgdb-decoded-cache-hashtb nil)
224 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
226 (defsubst elmo-msgdb-get-decoded-cache (string)
227 (if elmo-use-decoded-cache
228 (let ((hashtb (or elmo-msgdb-decoded-cache-hashtb
229 (setq elmo-msgdb-decoded-cache-hashtb
230 (elmo-make-hash 2048))))
232 (or (elmo-get-hash-val string hashtb)
235 (elmo-with-enable-multibyte
236 (decode-mime-charset-string string elmo-mime-charset)))
237 (elmo-set-hash-val string decoded hashtb))))
238 (elmo-with-enable-multibyte
239 (decode-mime-charset-string string elmo-mime-charset))))
241 (defun modb-entity-string-decoder (field value)
242 (elmo-msgdb-get-decoded-cache value))
244 (defun modb-entity-string-encoder (field value)
245 (elmo-with-enable-multibyte
246 (encode-mime-charset-string value elmo-mime-charset)))
248 (defun modb-entity-parse-date-string (field value)
250 (elmo-time-parse-date-string value)
253 (defun modb-entity-make-date-string (field value)
256 (elmo-time-make-date-string value)))
258 (defun modb-entity-mime-decoder (field value)
259 (mime-decode-field-body value (symbol-name field) 'summary))
261 (defun modb-entity-mime-encoder (field value)
262 (mime-encode-field-body value (symbol-name field)))
264 (defun modb-entity-address-list-decoder (field value)
266 (mapcar (lambda (address)
267 (mime-decode-field-body address (symbol-name field)))
268 (elmo-parse-addresses value))
271 (defun modb-entity-address-list-encoder (field value)
274 (mime-encode-field-body (mapconcat 'identity value ", ")
275 (symbol-name field))))
277 (defun modb-entity-parse-address-string (field value)
279 (elmo-parse-addresses value)
282 (defun modb-entity-make-address-string (field value)
285 (mapconcat 'identity value ", ")))
288 (defun modb-entity-create-field-indices (slots)
292 (setq indices (cons (cons (car slots) index) indices)
297 (mapcar (lambda (cell)
298 (cons (intern (concat ":" (symbol-name (car cell))))
303 ;; Legacy implementation.
305 (luna-define-class modb-legacy-entity-handler (modb-entity-handler)))
307 (defconst modb-legacy-entity-field-slots
318 (defconst modb-legacy-entity-field-indices
319 (modb-entity-create-field-indices modb-legacy-entity-field-slots))
321 (defvar modb-legacy-entity-normalizer nil)
322 (modb-set-field-converter 'modb-legacy-entity-normalizer nil
326 'from #'modb-entity-string-encoder
327 'subject #'modb-entity-string-encoder
328 'date #'modb-entity-make-date-string
329 'to #'modb-entity-address-list-encoder
330 'cc #'modb-entity-address-list-encoder
332 t #'modb-entity-mime-encoder)
334 (defvar modb-legacy-entity-specializer nil)
336 (modb-set-field-converter 'modb-legacy-entity-specializer nil
340 'from #'modb-entity-string-decoder
341 'subject #'modb-entity-string-decoder
342 'date #'modb-entity-parse-date-string
343 'to #'modb-entity-address-list-decoder
344 'cc #'modb-entity-address-list-decoder
346 t #'modb-entity-mime-decoder)
348 (modb-set-field-converter 'modb-legacy-entity-specializer 'string
350 'number nil ; not supported
352 'from #'modb-entity-string-decoder
353 'subject #'modb-entity-string-decoder
355 'size nil ; not supported
356 t #'modb-entity-mime-decoder)
359 (defmacro modb-legacy-entity-field-index (field)
360 `(cdr (assq ,field modb-legacy-entity-field-indices)))
362 (defsubst modb-legacy-entity-set-field (entity field value &optional as-is)
366 (setq value (modb-convert-field-value
367 modb-legacy-entity-normalizer
369 (cond ((memq field '(message-id :message-id))
370 (setcar entity value))
371 ((setq index (modb-legacy-entity-field-index field))
372 (aset (cdr entity) index value))
374 (setq index (modb-legacy-entity-field-index :extra))
375 (let ((extras (and entity (aref (cdr entity) index)))
377 (if (setq extra (assoc (symbol-name field) extras))
379 (aset (cdr entity) index (cons (cons (symbol-name field)
380 value) extras)))))))))
382 (defsubst modb-legacy-make-message-entity (args)
383 "Make an message entity."
384 (let ((entity (cons nil (make-vector 9 nil)))
387 (setq field (pop args)
390 (modb-legacy-entity-set-field entity field value)))
393 (luna-define-method elmo-msgdb-make-message-entity
394 ((handler modb-legacy-entity-handler) args)
395 (modb-legacy-make-message-entity args))
397 (luna-define-method elmo-msgdb-create-message-entity-from-buffer
398 ((handler modb-legacy-entity-handler) number args)
399 (let ((extras elmo-msgdb-extra-fields)
400 (default-mime-charset default-mime-charset)
401 entity message-id references from subject to cc date
402 extra field-body charset size)
404 (setq entity (modb-legacy-make-message-entity args))
405 (set-buffer-multibyte default-enable-multibyte-characters)
406 (setq message-id (elmo-msgdb-get-message-id-from-buffer))
407 (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
408 (setq charset (intern-soft charset))
409 (setq default-mime-charset charset))
411 (elmo-msgdb-get-references-from-buffer)
412 from (elmo-replace-in-string
413 (elmo-mime-string (or (elmo-field-body "from")
416 subject (elmo-replace-in-string
417 (elmo-mime-string (or (elmo-field-body "subject")
420 date (elmo-decoded-field-body "date")
421 to (mapconcat 'identity (elmo-multiple-field-body "to") ",")
422 cc (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
423 (unless (elmo-msgdb-message-entity-field handler entity 'size)
424 (if (setq size (elmo-field-body "content-length"))
425 (setq size (string-to-int size))
428 (if (setq field-body (elmo-field-body (car extras)))
429 (modb-legacy-entity-set-field
430 entity (intern (downcase (car extras))) field-body 'as-is))
431 (setq extras (cdr extras)))
432 (dolist (field '(message-id number references from subject
434 (when (symbol-value field)
435 (modb-legacy-entity-set-field
436 entity field (symbol-value field) 'as-is)))
439 (luna-define-method elmo-msgdb-message-entity-number
440 ((handler modb-legacy-entity-handler) entity)
441 (and entity (aref (cdr entity) 0)))
443 (luna-define-method elmo-msgdb-message-entity-set-number
444 ((handler modb-legacy-entity-handler) entity number)
445 (and entity (aset (cdr entity) 0 number))
448 (luna-define-method elmo-msgdb-message-entity-field
449 ((handler modb-legacy-entity-handler) entity field &optional type)
452 (modb-convert-field-value
453 modb-legacy-entity-specializer
455 (cond ((memq field '(message-id :message-id))
457 ((setq index (modb-legacy-entity-field-index field))
458 (aref (cdr entity) index))
460 (setq index (modb-legacy-entity-field-index :extra))
461 (cdr (assoc (symbol-name field)
462 (aref (cdr entity) index)))))
465 (luna-define-method elmo-msgdb-message-entity-set-field
466 ((handler modb-legacy-entity-handler) entity field value)
467 (modb-legacy-entity-set-field entity field value))
469 (luna-define-method elmo-msgdb-copy-message-entity
470 ((handler modb-legacy-entity-handler) entity &optional make-handler)
472 (let ((copy (elmo-msgdb-make-message-entity make-handler)))
473 (dolist (field (append '(message-id number references from subject
475 (mapcar (lambda (extra) (intern (car extra)))
476 (aref (cdr entity) 8))))
477 (elmo-msgdb-message-entity-set-field
478 make-handler copy field
479 (elmo-msgdb-message-entity-field handler entity field)))
482 (copy-sequence (cdr entity)))))
484 (luna-define-method elmo-msgdb-message-match-condition
485 ((handler modb-entity-handler) condition entity flags numbers)
488 (elmo-msgdb-match-condition-primitive handler condition
489 entity flags numbers))
490 ((eq (car condition) 'and)
491 (let ((lhs (elmo-msgdb-message-match-condition handler
493 entity flags numbers)))
495 ((elmo-filter-condition-p lhs)
496 (let ((rhs (elmo-msgdb-message-match-condition
497 handler (nth 2 condition) entity flags numbers)))
498 (cond ((elmo-filter-condition-p rhs)
503 (elmo-msgdb-message-match-condition handler (nth 2 condition)
504 entity flags numbers)))))
505 ((eq (car condition) 'or)
506 (let ((lhs (elmo-msgdb-message-match-condition handler (nth 1 condition)
507 entity flags numbers)))
509 ((elmo-filter-condition-p lhs)
510 (let ((rhs (elmo-msgdb-message-match-condition handler
512 entity flags numbers)))
513 (cond ((elmo-filter-condition-p rhs)
522 (elmo-msgdb-message-match-condition handler
524 entity flags numbers)))))))
527 (defun elmo-msgdb-match-condition-primitive (handler
533 (let ((key (elmo-filter-key condition))
537 ((string= key "last")
538 (setq result (<= (length (memq
539 (elmo-msgdb-message-entity-number
542 (string-to-int (elmo-filter-value condition)))))
543 ((string= key "first")
547 (elmo-msgdb-message-entity-number
550 (string-to-int (elmo-filter-value condition)))))
551 ((string= key "flag")
554 ((string= (elmo-filter-value condition) "any")
555 (or (memq 'important flags)
556 (memq 'answered flags)
557 (memq 'unread flags)))
558 ((string= (elmo-filter-value condition) "digest")
559 (or (memq 'important flags)
560 (memq 'unread flags)))
561 ((string= (elmo-filter-value condition) "unread")
562 (memq 'unread flags))
563 ((string= (elmo-filter-value condition) "important")
564 (memq 'important flags))
565 ((string= (elmo-filter-value condition) "answered")
566 (memq 'answered flags)))))
567 ((string= key "from")
568 (setq result (string-match
569 (elmo-filter-value condition)
570 (elmo-msgdb-message-entity-field
571 handler entity 'from))))
572 ((string= key "subject")
573 (setq result (string-match
574 (elmo-filter-value condition)
575 (elmo-msgdb-message-entity-field
576 handler entity 'subject))))
578 (setq result (string-match
579 (elmo-filter-value condition)
580 (elmo-msgdb-message-entity-field
581 handler entity 'to 'string))))
583 (setq result (string-match
584 (elmo-filter-value condition)
585 (elmo-msgdb-message-entity-field
586 handler entity 'cc 'string))))
587 ((or (string= key "since")
588 (string= key "before"))
589 (let ((field-date (elmo-msgdb-message-entity-field
590 handler entity 'date))
592 (elmo-datevec-to-time
593 (elmo-date-get-datevec
594 (elmo-filter-value condition)))))
595 (setq result (if (string= key "since")
596 (not (elmo-time< field-date specified-date))
597 (elmo-time< field-date specified-date)))))
598 ((member key elmo-msgdb-extra-fields)
599 (let ((extval (elmo-msgdb-message-entity-field handler
603 (when (stringp extval)
604 (setq result (string-match
605 (elmo-filter-value condition)
608 (throw 'unresolved condition)))
609 (if (eq (elmo-filter-type condition) 'unmatch)
614 ;; Standard implementation.
616 (luna-define-class modb-standard-entity-handler (modb-entity-handler)))
618 (defconst modb-standard-entity-field-slots
631 (defconst modb-standard-entity-field-indices
632 (modb-entity-create-field-indices modb-standard-entity-field-slots))
634 (defvar modb-standard-entity-normalizer nil)
635 (modb-set-field-converter 'modb-standard-entity-normalizer nil
636 'date #'modb-entity-parse-date-string
637 'to #'modb-entity-parse-address-string
638 'cc #'modb-entity-parse-address-string
641 (defvar modb-standard-entity-specializer nil)
642 (modb-set-field-converter 'modb-standard-entity-specializer nil t nil)
643 (modb-set-field-converter 'modb-standard-entity-specializer 'string
644 'date #'modb-entity-make-date-string
645 'to #'modb-entity-make-address-string
646 'cc #'modb-entity-make-address-string
647 'ml-info #'modb-entity-make-mailing-list-info-string
650 (defmacro modb-standard-entity-field-index (field)
651 `(cdr (assq ,field modb-standard-entity-field-indices)))
653 (defsubst modb-standard-entity-set-field (entity field value &optional as-is)
657 (setq value (modb-convert-field-value modb-standard-entity-normalizer
659 (cond ((memq field '(message-id :message-id))
660 (setcar (cdr entity) value))
661 ((setq index (modb-standard-entity-field-index field))
662 (aset (cdr (cdr entity)) index value))
664 (setq index (modb-standard-entity-field-index :extra))
665 (let ((extras (aref (cdr (cdr entity)) index))
667 (if (setq cell (assq field extras))
669 (aset (cdr (cdr entity))
671 (cons (cons field value) extras)))))))))
673 (defsubst modb-standard-make-message-entity (handler args)
674 (let ((entity (cons handler
677 (length modb-standard-entity-field-slots)
681 (setq field (pop args)
684 (modb-standard-entity-set-field entity field value)))
687 (luna-define-method elmo-msgdb-make-message-entity
688 ((handler modb-standard-entity-handler) args)
689 (modb-standard-make-message-entity handler args))
691 (luna-define-method elmo-msgdb-message-entity-number
692 ((handler modb-standard-entity-handler) entity)
693 (and entity (aref (cdr (cdr entity)) 0)))
695 (luna-define-method elmo-msgdb-message-entity-set-number
696 ((handler modb-standard-entity-handler) entity number)
697 (and entity (aset (cdr (cdr entity)) 0 number)))
699 (luna-define-method elmo-msgdb-message-entity-field
700 ((handler modb-standard-entity-handler) entity field &optional type)
703 (modb-convert-field-value
704 modb-standard-entity-specializer
706 (cond ((memq field '(message-id :message-id))
708 ((setq index (modb-standard-entity-field-index field))
709 (aref (cdr (cdr entity)) index))
711 (setq index (modb-standard-entity-field-index :extra))
712 (cdr (assq field (aref (cdr (cdr entity)) index)))))
715 (luna-define-method elmo-msgdb-message-entity-set-field
716 ((handler modb-standard-entity-handler) entity field value)
717 (modb-standard-entity-set-field entity field value))
719 (luna-define-method elmo-msgdb-copy-message-entity
720 ((handler modb-standard-entity-handler) entity &optional make-handler)
722 (let ((copy (elmo-msgdb-make-message-entity make-handler)))
723 (dolist (field (nconc
725 (copy-sequence modb-standard-entity-field-slots))
729 (modb-standard-entity-field-index :extra)))
731 (elmo-msgdb-message-entity-set-field
732 make-handler copy field
733 (elmo-msgdb-message-entity-field handler entity field)))
736 (cons (car (cdr entity))
737 (copy-sequence (cdr (cdr entity)))))))
739 (luna-define-method elmo-msgdb-create-message-entity-from-buffer
740 ((handler modb-standard-entity-handler) number args)
741 (let ((default-mime-charset default-mime-charset)
742 entity content-type charset)
744 (set-buffer-multibyte default-enable-multibyte-characters)
745 (and (setq content-type (elmo-decoded-field-body
746 "content-type" 'summary))
747 (setq charset (mime-content-type-parameter
748 (mime-parse-Content-Type content-type) "charset"))
749 (setq charset (intern-soft charset))
750 (mime-charset-p charset)
751 (setq default-mime-charset charset))
753 (modb-standard-make-message-entity
761 (elmo-msgdb-get-message-id-from-buffer)
763 (elmo-msgdb-get-references-from-buffer)
765 (elmo-replace-in-string
766 (or (elmo-decoded-field-body "from" 'summary)
770 (elmo-replace-in-string
771 (or (elmo-decoded-field-body "subject" 'summary)
775 (elmo-decoded-field-body "date" 'summary)
779 (mime-decode-field-body field-body "to" 'summary))
780 (elmo-multiple-field-body "to") ",")
784 (mime-decode-field-body field-body "cc" 'summary))
785 (elmo-multiple-field-body "cc") ",")
789 (let ((size (elmo-field-body "content-length")))
792 (or (plist-get args :size) 0)))))))
793 (let (field-name field-body extractor)
794 (dolist (extra (cons "newsgroups" elmo-msgdb-extra-fields))
795 (setq field-name (intern (downcase extra))
796 extractor (cdr (assq field-name
797 modb-entity-field-extractor-alist))
798 field-body (if extractor
799 (funcall extractor field-name)
800 (elmo-decoded-field-body extra 'summary)))
802 (modb-standard-entity-set-field entity field-name field-body))))
806 ;; mailing list info handling
807 (defun modb-entity-extract-ml-info-from-x-sequence ()
808 (let ((sequence (elmo-decoded-field-body "x-sequence" 'summary))
811 (elmo-set-list '(name count) (split-string sequence " "))
814 (defun modb-entity-extract-ml-info-from-subject ()
815 (let ((subject (elmo-decoded-field-body "subject" 'summary)))
817 (string-match "^\\s(\\(\\S)+\\)[ :]\\([0-9]+\\)\\s)[ \t]*"
819 (cons (match-string 1 subject) (match-string 2 subject)))))
821 (defun modb-entity-extract-ml-info-from-return-path ()
822 (let ((return-path (elmo-decoded-field-body "return-path" 'summary)))
823 (when (and return-path
824 (string-match "^<\\([^@>]+\\)-return-\\([0-9]+\\)-"
826 (cons (match-string 1 return-path)
827 (match-string 2 return-path)))))
829 (defun modb-entity-extract-ml-info-from-delivered-to ()
830 (let ((delivered-to (elmo-decoded-field-body "delivered-to" 'summary)))
831 (when (and delivered-to
832 (string-match "^mailing list \\([^@]+\\)@" delivered-to))
833 (cons (match-string 1 delivered-to) nil))))
835 (defun modb-entity-extract-ml-info-from-mailing-list ()
836 (let ((mailing-list (elmo-decoded-field-body "mailing-list" 'summary)))
837 ;; *-help@, *-owner@, etc.
838 (when (and mailing-list
839 (string-match "\\(^\\|; \\)contact \\([^@]+\\)-[^-@]+@"
841 (cons (match-string 2 mailing-list) nil))))
843 (defvar modb-entity-extract-mailing-list-info-functions
844 '(modb-entity-extract-ml-info-from-x-sequence
845 modb-entity-extract-ml-info-from-subject
846 modb-entity-extract-ml-info-from-return-path
847 modb-entity-extract-ml-info-from-delivered-to
848 modb-entity-extract-ml-info-from-mailing-list))
850 (defun modb-entity-extract-mailing-list-info (field)
851 (let ((ml-name (elmo-decoded-field-body "x-ml-name" 'summary))
852 (ml-count (or (elmo-decoded-field-body "x-mail-count" 'summary)
853 (elmo-decoded-field-body "x-ml-count" 'summary)))
854 (functions modb-entity-extract-mailing-list-info-functions)
856 (while (and functions
857 (or (null ml-name) (null ml-count)))
858 (when (setq result (funcall (car functions)))
860 (setq ml-name (car result)))
862 (setq ml-count (cdr result))))
863 (setq functions (cdr functions)))
864 (when (or ml-name ml-count)
865 (cons (and ml-name (car (split-string ml-name " ")))
866 (and ml-count (string-to-int ml-count))))))
868 (defun modb-entity-make-mailing-list-info-string (field value)
870 (format (if (cdr value) "(%s %05.0f)" "(%s)")
871 (car value) (cdr value))))
874 (product-provide (provide 'modb-entity) (require 'elmo-version))
876 ;;; modb-entity.el ends here