1 ;;; elmo-mime.el --- MIME module for ELMO.
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
38 (luna-define-class elmo-mime-entity))
40 (luna-define-generic elmo-mime-entity-display-p (entity mime-mode)
41 "Return non-nil if ENTITY is able to display with MIME-MODE.
43 MIME-MODE is a symbol which is one of the following:
44 `mime' (Can display each MIME part)
45 `as-is' (Can display raw message)")
47 (luna-define-generic elmo-mime-entity-reassembled-p (entity)
48 "Return non-nil if ENTITY is reassembled message/partial pieces.")
50 (luna-define-generic elmo-mime-entity-display (entity preview-buffer
54 "Display MIME message ENTITY.
55 PREVIEW-BUFFER is a view buffer.
56 Optional argument ORIGINAL-MAJOR-MODE is major-mode of representation
57 buffer of ENTITY. If it is nil, current `major-mode' is used.
58 If optional argument KEYMAP is specified,
59 use for keymap of representation buffer.")
61 (luna-define-generic elmo-mime-entity-display-as-is (entity
66 "Display MIME message ENTITY as is.
67 PREVIEW-BUFFER is a view buffer.
68 Optional argument ORIGINAL-MAJOR-MODE is major-mode of representation
69 buffer of ENTITY. If it is nil, current `major-mode' is used.
70 If optional argument KEYMAP is specified,
71 use for keymap of representation buffer.")
73 (luna-define-method elmo-mime-entity-display ((entity elmo-mime-entity)
78 (let ((elmo-message-displaying t))
79 (mime-display-message entity
83 original-major-mode)))
85 (defun elmo-mime-entity-fragment-p (entity)
86 (and (not (elmo-mime-entity-reassembled-p entity))
87 (eq (mime-entity-media-type entity) 'message)
88 (eq (mime-entity-media-subtype entity) 'partial)))
91 (luna-define-class mime-elmo-buffer-entity (mime-buffer-entity
94 (luna-define-internal-accessors 'mime-elmo-buffer-entity)
95 (luna-define-class mime-elmo-imap-entity (mime-imap-entity
99 (provide 'mmelmo-imap)
100 (provide 'mmelmo-buffer)
102 (defvar elmo-message-ignored-field-list mime-view-ignored-field-list)
103 (defvar elmo-message-visible-field-list mime-view-visible-field-list)
104 (defvar elmo-message-sorted-field-list nil)
105 (defvar elmo-mime-display-header-analysis t)
107 (defcustom elmo-mime-header-max-column fill-column
108 "*Header max column number. Default is `fill-colmn'.
109 If a symbol of function is specified, the function is called and its return
111 :type '(choice (integer :tag "Column Number")
112 (function :tag "Function"))
115 (luna-define-method initialize-instance :after ((entity mime-elmo-buffer-entity)
119 (luna-define-method initialize-instance :around ((entity mime-elmo-imap-entity)
121 (luna-call-next-method))
123 ;;; Insert sorted header.
124 (defsubst elmo-mime-insert-header-from-buffer (buffer
126 &optional invisible-fields
129 (let ((the-buf (current-buffer))
130 (mode-obj (mime-find-field-presentation-method 'wide))
132 f-b p f-e field-name field field-body
133 vf-alist (sl sort-fields))
137 (narrow-to-region start end)
139 (while (re-search-forward std11-field-head-regexp nil t)
140 (setq f-b (match-beginning 0)
142 field-name (buffer-substring f-b p)
143 f-e (std11-field-end))
144 (when (mime-visible-field-p field-name
145 visible-fields invisible-fields)
147 (capitalize (buffer-substring f-b (1- p))))
148 field-body (buffer-substring p f-e)
150 (if elmo-mime-display-header-analysis
151 (inline (mime-find-field-decoder-internal
153 (inline (lambda (x y z) x))))
154 (setq vf-alist (append (list
156 (list field-body field-decoder)))
161 (function (lambda (s d)
166 (while (setq re (nth n sl))
168 (and (string-match re sf)
170 (and (string-match re df)
173 (with-current-buffer the-buf
175 (let* ((vf (car vf-alist))
176 (field-name (car vf))
177 (field-body (car (cdr vf)))
178 (field-decoder (car (cdr (cdr vf)))))
180 (insert (if field-decoder
181 (funcall field-decoder field-body
182 (string-width field-name)
183 (if (functionp elmo-mime-header-max-column)
184 (funcall elmo-mime-header-max-column)
185 elmo-mime-header-max-column))
189 (setq vf-alist (cdr vf-alist)))
190 (run-hooks 'mmelmo-header-inserted-hook))))))
192 (luna-define-generic elmo-mime-insert-sorted-header (entity
193 &optional invisible-fields
196 "Insert sorted header fields of the ENTITY.")
198 (luna-define-method elmo-mime-insert-sorted-header ((entity
199 mime-elmo-buffer-entity)
200 &optional invisible-fields
203 (elmo-mime-insert-header-from-buffer
204 (mime-buffer-entity-buffer-internal entity)
205 (mime-buffer-entity-header-start-internal entity)
206 (mime-buffer-entity-header-end-internal entity)
207 invisible-fields visible-fields sorted-fields))
209 (luna-define-method elmo-mime-insert-sorted-header ((entity
210 mime-elmo-imap-entity)
211 &optional invisible-fields
214 (let ((the-buf (current-buffer))
217 (insert (mime-imap-entity-header-string entity))
218 (setq buf (current-buffer)
222 (elmo-mime-insert-header-from-buffer buf p-min p-max
227 (luna-define-method mime-insert-text-content :around
228 ((entity mime-elmo-buffer-entity))
229 (luna-call-next-method)
230 (run-hooks 'elmo-message-text-content-inserted-hook))
232 (luna-define-method mime-insert-text-content :around
233 ((entity mime-elmo-imap-entity))
234 (luna-call-next-method)
235 (run-hooks 'elmo-message-text-content-inserted-hook))
237 (defun elmo-mime-insert-header (entity situation)
238 (elmo-mime-insert-sorted-header
240 elmo-message-ignored-field-list
241 elmo-message-visible-field-list
242 elmo-message-sorted-field-list)
243 (run-hooks 'elmo-message-header-inserted-hook))
245 ;; mime-elmo-buffer-entity
246 (luna-define-method elmo-mime-entity-display-p
247 ((entity mime-elmo-buffer-entity) mime-mode)
251 (luna-define-method elmo-mime-entity-reassembled-p ((entity
252 mime-elmo-buffer-entity))
253 (mime-elmo-buffer-entity-reassembled-internal entity))
255 (luna-define-method elmo-mime-entity-display-as-is ((entity
256 mime-elmo-buffer-entity)
261 (elmo-mime-display-as-is-internal entity
265 original-major-mode))
267 ;; mime-elmo-imap-entity
268 (luna-define-method elmo-mime-entity-display-p
269 ((entity mime-elmo-imap-entity) mime-mode)
270 (not (eq mime-mode 'as-is)))
272 (luna-define-method elmo-mime-entity-display-as-is ((entity
273 mime-elmo-imap-entity)
278 (error "Does not support this method"))
281 (defun elmo-message-mime-entity (folder number rawbuf reassemble
283 ignore-cache unread entire)
284 "Return the mime-entity structure of the message in the FOLDER with NUMBER.
285 RAWBUF is the output buffer for original message.
286 If REASSEMBLE is non-nil and MIME media type of the message is message/partial,
287 the mime-entity is reassembled partial message.
288 If optional argument IGNORE-CACHE is non-nil, existing cache is ignored.
289 If second optional argument UNREAD is non-nil,
290 keep status of the message as unread.
291 If third optional argument ENTIRE is non-nil, fetch entire message at once."
292 (let (id message entity content-type)
294 (setq entity (elmo-message-entity folder number))
295 (setq id (if (setq content-type (elmo-message-entity-field
296 entity 'content-type))
297 (and (string-match "message/partial" content-type)
298 (mime-content-type-parameter
299 (mime-parse-Content-Type content-type) "id"))
300 (and (setq message (elmo-message-mime-entity-internal
302 ignore-cache unread entire))
303 (eq (mime-entity-media-type message) 'message)
304 (eq (mime-entity-media-subtype message) 'partial)
305 (mime-content-type-parameter
306 (mime-entity-content-type message) "id"))))
307 (elmo-message-reassembled-mime-entity
309 (elmo-message-entity-field entity 'subject 'decode)
313 (elmo-message-mime-entity-internal
314 folder number rawbuf ignore-cache unread entire))))
317 (defun elmo-message-mime-entity-internal (folder number rawbuf
319 ignore-cache unread entire)
320 (let ((strategy (elmo-find-fetch-strategy folder number
323 (cond ((null strategy) nil)
324 ((eq (elmo-fetch-strategy-entireness strategy) 'section)
327 (luna-make-entity 'mime-elmo-imap-location
331 :strategy strategy)))
333 (with-current-buffer rawbuf
334 (let (buffer-read-only)
336 (elmo-message-fetch folder number strategy unread)))
337 (mime-open-entity 'elmo-buffer rawbuf)))))
340 (defconst elmo-mime-inherit-field-list-from-enclosed
341 '("^Content-.*:" "^Message-Id:" "^Subject:"
342 "^Encrypted.*:" "^MIME-Version:"))
344 (defsubst elmo-mime-make-reassembled-mime-entity (buffer)
345 (let ((entity (mime-open-entity 'elmo-buffer buffer)))
346 (mime-elmo-buffer-entity-set-reassembled-internal entity t)
349 (defun elmo-message-reassembled-mime-entity (folder id rawbuf subject
353 (let ((cache (elmo-file-cache-get (concat "<" id ">")))
355 (if (and (not ignore-cache)
356 (eq (elmo-file-cache-status cache) 'entire))
358 (with-current-buffer rawbuf
359 (let (buffer-read-only)
361 (elmo-file-cache-load (elmo-file-cache-path cache) nil))
362 (elmo-mime-make-reassembled-mime-entity rawbuf))
363 ;; reassemble fragment of the entity
364 (when (setq pieces (elmo-mime-collect-message/partial-pieces
367 (if (string-match "[0-9\n]+" subject)
368 (substring subject 0 (match-beginning 0))
370 ignore-cache unread))
371 (with-current-buffer rawbuf
372 (let (buffer-read-only
373 (outer-header (car pieces))
374 (pieces (sort (cdr pieces) (lambda (l r) (< (car l) (car r)))))
378 (insert (cdr (car pieces)))
379 (setq pieces (cdr pieces)))
380 (let ((case-fold-search t))
382 (std11-narrow-to-header)
383 (goto-char (point-min))
384 (while (re-search-forward std11-field-head-regexp nil t)
385 (let ((field-start (match-beginning 0)))
386 (unless (mime-visible-field-p
387 (buffer-substring field-start (match-end 0))
388 elmo-mime-inherit-field-list-from-enclosed
390 (delete-region field-start (1+ (std11-field-end))))))))
391 (goto-char (point-min))
392 (insert outer-header)
394 (elmo-file-cache-save (elmo-file-cache-path cache) nil)
395 (elmo-mime-make-reassembled-mime-entity rawbuf)))))))
397 (defun elmo-mime-collect-message/partial-pieces (folder id subject-regexp
403 (set-buffer-multibyte nil)
404 (let (total header pieces)
405 (elmo-folder-do-each-message-entity (entity folder)
408 (elmo-message-entity-field entity 'subject 'decode))
410 (let* ((message (elmo-message-mime-entity-internal
412 (elmo-message-entity-number entity)
416 (ct (mime-entity-content-type message))
417 (the-id (or (mime-content-type-parameter ct "id") ""))
419 (when (string= (downcase the-id)
421 (setq number (string-to-number
422 (mime-content-type-parameter ct "number")))
423 (setq pieces (cons (cons number (mime-entity-body message))
426 (let ((case-fold-search t))
428 (std11-narrow-to-header)
429 (goto-char (point-min))
430 (while (re-search-forward std11-field-head-regexp nil t)
431 (let ((field-start (match-beginning 0)))
432 (when (mime-visible-field-p
433 (buffer-substring field-start (match-end 0))
435 elmo-mime-inherit-field-list-from-enclosed)
439 field-start (std11-field-end))
442 (setq total (ignore-errors
444 (mime-content-type-parameter ct "total")))))
447 (>= (length pieces) total))
448 (throw 'complete (cons header pieces)))))))))
453 ;; Replacement of mime-display-message.
454 (defun elmo-mime-display-as-is-internal (message
455 &optional preview-buffer
456 mother default-keymap-or-function
457 original-major-mode keymap)
458 (mime-maybe-hide-echo-buffer)
459 (let ((win-conf (current-window-configuration)))
462 (concat "*Preview-" (mime-entity-name message) "*")))
463 (or original-major-mode
464 (setq original-major-mode major-mode))
465 (let ((inhibit-read-only t))
466 (set-buffer (get-buffer-create preview-buffer))
470 (setq mime-mother-buffer mother))
471 (setq mime-preview-original-window-configuration win-conf)
472 (setq major-mode 'mime-view-mode)
473 (setq mode-name "MIME-View")
476 (set-buffer-multibyte nil)
477 (insert (mime-entity-body message))
478 (set-buffer-multibyte t)
479 (decode-coding-region (point-min) (point-max)
480 elmo-mime-display-as-is-coding-system)
481 (goto-char (point-min))
483 (goto-char (point-min))
485 (let ((method (cdr (assq original-major-mode
486 mime-header-presentation-method-alist))))
487 (if (functionp method)
488 (funcall method message nil)))
490 ;; set original major mode for mime-preview-quit
491 (put-text-property (point-min) (point-max)
493 `((major-mode . ,original-major-mode)))
494 (put-text-property (point-min) (point-max)
495 'elmo-as-is-entity message)
498 (if default-keymap-or-function
499 (mime-view-define-keymap default-keymap-or-function)
500 mime-view-mode-default-map)))
501 (goto-char (point-min))
502 (search-forward "\n\n" nil t)
503 (run-hooks 'mime-view-mode-hook)
504 (set-buffer-modified-p nil)
505 (setq buffer-read-only t)
509 (product-provide (provide 'elmo-mime) (require 'elmo-version))
511 ;; elmo-mime.el ends here