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 elmo-msgdb-prefer-in-reply-to-for-parent nil
47 "*Non-nil to prefer In-Reply-To header for finding parent message on thread,
48 rather than References header."
52 (defvar modb-entity-default-cache-internal nil)
54 (defun elmo-message-entity-handler (&optional entity)
55 "Get modb entity handler instance which corresponds to the ENTITY."
58 (not (eq (car entity) t))
59 (not (stringp (car entity))))
61 (or modb-entity-default-cache-internal
62 (setq modb-entity-default-cache-internal
63 (luna-make-entity modb-entity-default-handler)))))
65 (luna-define-generic elmo-msgdb-make-message-entity (handler &rest args)
66 "Make a message entity using HANDLER.")
68 (luna-define-generic elmo-msgdb-message-entity-number (handler entity)
69 "Number of the ENTITY.")
71 (luna-define-generic elmo-msgdb-message-entity-set-number (handler
73 "Set number of the ENTITY.")
75 (luna-define-generic elmo-msgdb-message-entity-field (handler
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 DECODE is no-nil, the field value is decoded.")
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-copy-message-entity (handler entity)
94 HANDLER is the message entity handler.
95 ENTITY is the message entity structure.")
97 (luna-define-generic elmo-msgdb-create-message-entity-from-file (handler
100 "Create message entity from file.
101 HANDLER is the message entity handler.
102 NUMBER is the number of the newly created message entity.
103 FILE is the message file.")
105 (luna-define-generic elmo-msgdb-create-message-entity-from-buffer (handler
108 "Create message entity from current buffer.
109 HANDLER is the message entity handler.
110 NUMBER is the number of the newly created message entity.
111 Rest of the ARGS is a plist of message entity field for initial value.
112 Header region is supposed to be narrowed.")
114 ;; Transitional interface.
115 (luna-define-generic elmo-msgdb-message-match-condition (handler
120 "Return non-nil when the entity matches the condition.")
122 ;; Generic implementation.
123 (luna-define-method elmo-msgdb-create-message-entity-from-file
124 ((handler modb-entity-handler) number file)
125 (let (insert-file-contents-pre-hook ; To avoid autoconv-xmas...
126 insert-file-contents-post-hook header-end
127 (attrib (file-attributes file))
130 (if (not (file-exists-p file))
132 (setq size (nth 7 attrib))
133 (setq mtime (timezone-make-date-arpa-standard
134 (current-time-string (nth 5 attrib)) (current-time-zone)))
135 ;; insert header from file.
138 (elmo-msgdb-insert-file-header file)
139 (error (throw 'done nil)))
140 (goto-char (point-min))
142 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
145 (narrow-to-region (point-min) header-end)
146 (elmo-msgdb-create-message-entity-from-buffer
147 handler number :size size :date mtime))))))
149 (luna-define-method elmo-msgdb-make-message-entity ((handler
154 (luna-define-method elmo-msgdb-message-entity-field ((handler
158 (plist-get (cdr entity) (intern (concat ":" (symbol-name field)))))
160 (luna-define-method elmo-msgdb-message-entity-number ((handler
163 (plist-get (cdr entity) :number))
165 ;; Legacy implementation.
166 (eval-and-compile (luna-define-class modb-legacy-entity-handler
167 (modb-entity-handler)))
172 (defvar elmo-msgdb-decoded-cache-hashtb nil)
173 (make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)
175 (defsubst elmo-msgdb-get-decoded-cache (string)
176 (if elmo-use-decoded-cache
177 (let ((hashtb (or elmo-msgdb-decoded-cache-hashtb
178 (setq elmo-msgdb-decoded-cache-hashtb
179 (elmo-make-hash 2048))))
181 (or (elmo-get-hash-val string hashtb)
186 (decode-mime-charset-string string elmo-mime-charset))
189 (decode-mime-charset-string string elmo-mime-charset)))
191 (defsubst modb-legacy-make-message-entity (args)
192 "Make an message entity."
193 (cons (plist-get args :message-id)
194 (vector (plist-get args :number)
195 (plist-get args :references)
196 (plist-get args :from)
197 (plist-get args :subject)
198 (plist-get args :date)
201 (plist-get args :size)
202 (plist-get args :extra))))
204 (luna-define-method elmo-msgdb-make-message-entity
205 ((handler modb-legacy-entity-handler) args)
206 (modb-legacy-make-message-entity args))
208 (luna-define-method elmo-msgdb-create-message-entity-from-buffer
209 ((handler modb-legacy-entity-handler) number args)
210 (let ((extras elmo-msgdb-extra-fields)
211 (default-mime-charset default-mime-charset)
212 entity message-id references from subject to cc date
213 extra field-body charset size)
215 (setq entity (modb-legacy-make-message-entity args))
216 (set-buffer-multibyte default-enable-multibyte-characters)
217 (setq message-id (elmo-msgdb-get-message-id-from-buffer))
218 (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
219 (setq charset (intern-soft charset))
220 (setq default-mime-charset charset))
222 (if elmo-msgdb-prefer-in-reply-to-for-parent
223 (or (elmo-msgdb-get-last-message-id
224 (elmo-field-body "in-reply-to"))
225 (elmo-msgdb-get-last-message-id
226 (elmo-field-body "references")))
227 (or (elmo-msgdb-get-last-message-id
228 (elmo-field-body "references"))
229 (elmo-msgdb-get-last-message-id
230 (elmo-field-body "in-reply-to"))))
231 from (elmo-replace-in-string
232 (elmo-mime-string (or (elmo-field-body "from")
235 subject (elmo-replace-in-string
236 (elmo-mime-string (or (elmo-field-body "subject")
239 date (elmo-unfold-field-body "date")
240 to (mapconcat 'identity (elmo-multiple-field-body "to") ",")
241 cc (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
242 (unless (elmo-msgdb-message-entity-field handler entity 'size)
243 (if (setq size (elmo-field-body "content-length"))
244 (setq size (string-to-int size))
247 (if (setq field-body (elmo-field-body (car extras)))
248 (elmo-msgdb-message-entity-set-field
249 handler entity (intern (downcase (car extras))) field-body))
250 (setq extras (cdr extras)))
251 (dolist (field '(message-id number references from subject
253 (when (symbol-value field)
254 (elmo-msgdb-message-entity-set-field
255 handler entity field (symbol-value field))))
258 (luna-define-method elmo-msgdb-message-entity-number
259 ((handler modb-legacy-entity-handler) entity)
260 (and entity (aref (cdr entity) 0)))
262 (luna-define-method elmo-msgdb-message-entity-set-number
263 ((handler modb-legacy-entity-handler) entity number)
264 (and entity (aset (cdr entity) 0 number))
267 (luna-define-method elmo-msgdb-message-entity-field
268 ((handler modb-legacy-entity-handler) entity field &optional decode)
272 (to (aref (cdr entity) 5))
273 (cc (aref (cdr entity) 6))
274 (date (aref (cdr entity) 4))
275 (subject (aref (cdr entity) 3))
276 (from (aref (cdr entity) 2))
277 (message-id (car entity))
278 (references (aref (cdr entity) 1))
279 (size (aref (cdr entity) 7))
280 (t (cdr (assoc (symbol-name field) (aref (cdr entity) 8)))))))
281 (if (and decode (memq field '(from subject)))
282 (elmo-msgdb-get-decoded-cache field-value)
285 (luna-define-method elmo-msgdb-message-entity-set-field
286 ((handler modb-legacy-entity-handler) entity field value)
289 (number (aset (cdr entity) 0 value))
290 (to (aset (cdr entity) 5 value))
291 (cc (aset (cdr entity) 6 value))
292 (date (aset (cdr entity) 4 value))
293 (subject (aset (cdr entity) 3 value))
294 (from (aset (cdr entity) 2 value))
295 (message-id (setcar entity value))
296 (references (aset (cdr entity) 1 value))
297 (size (aset (cdr entity) 7 value))
299 (let ((extras (and entity (aref (cdr entity) 8)))
301 (if (setq extra (assoc (symbol-name field) extras))
303 (aset (cdr entity) 8 (cons (cons (symbol-name field)
304 value) extras))))))))
306 (luna-define-method elmo-msgdb-copy-message-entity
307 ((handler modb-legacy-entity-handler) entity)
309 (copy-sequence (cdr entity))))
311 (luna-define-method elmo-msgdb-message-match-condition
312 ((handler modb-legacy-entity-handler) condition entity flags numbers)
315 (elmo-msgdb-match-condition-primitive handler condition
316 entity flags numbers))
317 ((eq (car condition) 'and)
318 (let ((lhs (elmo-msgdb-message-match-condition handler
320 entity flags numbers)))
322 ((elmo-filter-condition-p lhs)
323 (let ((rhs (elmo-msgdb-message-match-condition
324 handler (nth 2 condition) entity flags numbers)))
325 (cond ((elmo-filter-condition-p rhs)
330 (elmo-msgdb-message-match-condition handler (nth 2 condition)
331 entity flags numbers)))))
332 ((eq (car condition) 'or)
333 (let ((lhs (elmo-msgdb-message-match-condition handler (nth 1 condition)
334 entity flags numbers)))
336 ((elmo-filter-condition-p lhs)
337 (let ((rhs (elmo-msgdb-message-match-condition handler
339 entity flags numbers)))
340 (cond ((elmo-filter-condition-p rhs)
349 (elmo-msgdb-message-match-condition handler
351 entity flags numbers)))))))
354 (defun elmo-msgdb-match-condition-primitive (handler
360 (let ((key (elmo-filter-key condition))
364 ((string= key "last")
365 (setq result (<= (length (memq
366 (elmo-msgdb-message-entity-number
369 (string-to-int (elmo-filter-value condition)))))
370 ((string= key "first")
374 (elmo-msgdb-message-entity-number
377 (string-to-int (elmo-filter-value condition)))))
378 ((string= key "flag")
381 ((string= (elmo-filter-value condition) "any")
382 (or (memq 'important flags)
383 (memq 'answered flags)
384 (memq 'unread flags)))
385 ((string= (elmo-filter-value condition) "digest")
386 (or (memq 'important flags)
387 (memq 'unread flags)))
388 ((string= (elmo-filter-value condition) "unread")
389 (memq 'unread flags))
390 ((string= (elmo-filter-value condition) "important")
391 (memq 'important flags))
392 ((string= (elmo-filter-value condition) "answered")
393 (memq 'answered flags)))))
394 ((string= key "from")
395 (setq result (string-match
396 (elmo-filter-value condition)
397 (elmo-msgdb-message-entity-field
398 handler entity 'from t))))
399 ((string= key "subject")
400 (setq result (string-match
401 (elmo-filter-value condition)
402 (elmo-msgdb-message-entity-field
403 handler entity 'subject t))))
405 (setq result (string-match
406 (elmo-filter-value condition)
407 (elmo-msgdb-message-entity-field
408 handler entity 'to))))
410 (setq result (string-match
411 (elmo-filter-value condition)
412 (elmo-msgdb-message-entity-field
413 handler entity 'cc))))
414 ((or (string= key "since")
415 (string= key "before"))
416 (let ((field-date (elmo-date-make-sortable-string
418 (elmo-msgdb-message-entity-field
419 handler entity 'date)
420 (current-time-zone) nil)))
422 (elmo-date-make-sortable-string
423 (elmo-date-get-datevec
424 (elmo-filter-value condition)))))
425 (setq result (if (string= key "since")
426 (or (string= specified-date field-date)
427 (string< specified-date field-date))
428 (string< field-date specified-date)))))
429 ((member key elmo-msgdb-extra-fields)
430 (let ((extval (elmo-msgdb-message-entity-field handler
433 (when (stringp extval)
434 (setq result (string-match
435 (elmo-filter-value condition)
438 (throw 'unresolved condition)))
439 (if (eq (elmo-filter-type condition) 'unmatch)
444 (product-provide (provide 'modb-entity) (require 'elmo-version))
446 ;;; modb-entity.el ends here