d338f7d5a7085f7f8224a3f0433e61a87e3acc86
[elisp/wanderlust.git] / elmo / elmo-mime.el
1 ;;; elmo-mime.el --- MIME module for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
9
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)
13 ;; any later version.
14 ;;
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.
19 ;;
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.
24 ;;
25
26 ;;; Commentary:
27 ;;
28
29 ;;; Code:
30 ;;
31 (require 'elmo-vars)
32 (require 'mmbuffer)
33 (require 'mmimap)
34 (require 'mime-view)
35
36 (eval-and-compile
37   (luna-define-class mime-elmo-buffer-entity (mime-buffer-entity) ())
38   (luna-define-class mime-elmo-imap-entity (mime-imap-entity) ()))
39
40 ;; Provide backend
41 (provide 'mmelmo-imap)
42 (provide 'mmelmo-buffer)
43
44 (defvar elmo-message-ignored-field-list mime-view-ignored-field-list)
45 (defvar elmo-message-visible-field-list mime-view-visible-field-list)
46 (defvar elmo-message-sorted-field-list nil)
47
48 (defcustom elmo-mime-header-max-column fill-column
49   "*Header max column number. Default is `fill-colmn'.
50 If a symbol of function is specified, the function is called and its return
51 value is used."
52   :type '(choice (integer :tag "Column Number")
53                  (function :tag "Function"))
54   :group 'elmo)
55
56 (defcustom elmo-mime-display-as-is-coding-system (if (boundp 'MULE)
57                                                      '*autoconv* 'undecided)
58   "*Coding system used when message is displayed as is."
59   :type 'symbol
60   :group 'elmo)
61
62 (luna-define-method initialize-instance :after ((entity mime-elmo-buffer-entity)
63                                                 &rest init-args)
64   entity)
65
66 (luna-define-method initialize-instance :around ((entity mime-elmo-imap-entity)
67                                                  &rest init-args)
68   (luna-call-next-method))
69
70 ;;; Insert sorted header.
71 (defsubst elmo-mime-insert-header-from-buffer (buffer
72                                                start end
73                                                &optional invisible-fields
74                                                visible-fields
75                                                sort-fields)
76   (let ((the-buf (current-buffer))
77         (mode-obj (mime-find-field-presentation-method 'wide))
78         field-decoder
79         f-b p f-e field-name field field-body
80         vf-alist (sl sort-fields))
81     (save-excursion
82       (set-buffer buffer)
83       (save-restriction
84         (narrow-to-region start end)
85         (goto-char start)
86         (while (re-search-forward std11-field-head-regexp nil t)
87           (setq f-b (match-beginning 0)
88                 p (match-end 0)
89                 field-name (buffer-substring f-b p)
90                 f-e (std11-field-end))
91           (when (mime-visible-field-p field-name
92                                       visible-fields invisible-fields)
93             (setq field (intern
94                          (capitalize (buffer-substring f-b (1- p))))
95                   field-body (buffer-substring p f-e)
96                   field-decoder (inline (mime-find-field-decoder-internal
97                                          field mode-obj)))
98             (setq vf-alist (append (list
99                                     (cons field-name
100                                           (list field-body field-decoder)))
101                                    vf-alist))))
102         (and vf-alist
103              (setq vf-alist
104                    (sort vf-alist
105                          (function (lambda (s d)
106                                      (let ((n 0) re
107                                            (sf (car s))
108                                            (df (car d)))
109                                        (catch 'done
110                                          (while (setq re (nth n sl))
111                                            (setq n (1+ n))
112                                            (and (string-match re sf)
113                                                 (throw 'done t))
114                                            (and (string-match re df)
115                                                 (throw 'done nil)))
116                                          t)))))))
117         (with-current-buffer the-buf
118           (while vf-alist
119             (let* ((vf (car vf-alist))
120                    (field-name (car vf))
121                    (field-body (car (cdr vf)))
122                    (field-decoder (car (cdr (cdr vf)))))
123               (insert field-name)
124               (insert (if field-decoder
125                           (funcall field-decoder field-body
126                                    (string-width field-name)
127                                    (if (functionp elmo-mime-header-max-column)
128                                        (funcall elmo-mime-header-max-column)
129                                      elmo-mime-header-max-column))
130                         ;; Don't decode
131                         field-body))
132               (insert "\n"))
133             (setq vf-alist (cdr vf-alist)))
134           (run-hooks 'mmelmo-header-inserted-hook))))))
135
136 (luna-define-generic elmo-mime-insert-sorted-header (entity
137                                                      &optional invisible-fields
138                                                      visible-fields
139                                                      sorted-fields)
140   "Insert sorted header fields of the ENTITY.")
141
142 (luna-define-method elmo-mime-insert-sorted-header ((entity
143                                                      mime-elmo-buffer-entity)
144                                                     &optional invisible-fields
145                                                     visible-fields
146                                                     sorted-fields)
147   (elmo-mime-insert-header-from-buffer
148    (mime-buffer-entity-buffer-internal entity)
149    (mime-buffer-entity-header-start-internal entity)
150    (mime-buffer-entity-header-end-internal entity)
151    invisible-fields visible-fields sorted-fields))
152
153 (luna-define-method elmo-mime-insert-sorted-header ((entity
154                                                      mime-elmo-imap-entity)
155                                                     &optional invisible-fields
156                                                     visible-fields
157                                                     sorted-fields)
158   (let ((the-buf (current-buffer))
159         buf p-min p-max)
160     (with-temp-buffer
161       (insert (mime-imap-entity-header-string entity))
162       (setq buf (current-buffer)
163             p-min (point-min)
164             p-max (point-max))
165       (set-buffer the-buf)
166       (elmo-mime-insert-header-from-buffer buf p-min p-max
167                                            invisible-fields
168                                            visible-fields
169                                            sorted-fields))))
170
171 (luna-define-method mime-insert-text-content :around
172   ((entity mime-elmo-buffer-entity))
173   (luna-call-next-method)
174   (run-hooks 'elmo-message-text-content-inserted-hook))
175
176 (luna-define-method mime-insert-text-content :around
177   ((entity mime-elmo-imap-entity))
178   (luna-call-next-method)
179   (run-hooks 'elmo-message-text-content-inserted-hook))
180
181 (defun elmo-mime-insert-header (entity situation)
182   (elmo-mime-insert-sorted-header
183    entity
184    elmo-message-ignored-field-list
185    elmo-message-visible-field-list
186    elmo-message-sorted-field-list)
187   (run-hooks 'elmo-message-header-inserted-hook))
188
189 (defun elmo-make-mime-message-location (folder number strategy rawbuf unread)
190 ;; Return the MIME message location structure.
191 ;; FOLDER is the ELMO folder structure.
192 ;; NUMBER is the number of the message in the FOLDER.
193 ;; STRATEGY is the message fetching strategy.
194 ;; RAWBUF is the output buffer for original message.
195 ;; If second optional argument UNREAD is non-nil, message is not marked
196 ;; as read.
197   (if (and strategy
198            (eq (elmo-fetch-strategy-entireness strategy) 'section))
199       (luna-make-entity
200        'mime-elmo-imap-location
201        :folder folder
202        :number number
203        :rawbuf rawbuf
204        :strategy strategy)
205     (with-current-buffer rawbuf
206       (let (buffer-read-only)
207         (erase-buffer)
208         (if strategy
209             (elmo-message-fetch folder number strategy
210                                 nil (current-buffer)
211                                 unread))))
212     rawbuf))
213
214 (defun elmo-mime-message-display (folder number viewbuf rawbuf original-mode
215                                          &optional ignore-cache unread keymap)
216   "Display MIME message.
217 A message in the FOLDER with NUMBER is displayed on the VIEWBUF using RAWBUF.
218 VIEWBUF is a view buffer and RAWBUF is a raw buffer.
219 ORIGINAL is the major mode of RAWBUF.
220 If optional argument IGNORE-CACHE is specified, existing cache is ignored.
221 If second optional argument UNREAD is specified, message is displayed but
222 keep it as unread.
223 Return non-nil if not entire message was fetched."
224   (let (mime-display-header-hook ; Do nothing.
225         (elmo-message-displaying t)
226         entity strategy)
227     (unless (zerop (elmo-folder-length folder))
228       (setq entity (elmo-message-entity folder number)))
229     (setq strategy (if entity (elmo-find-fetch-strategy folder entity
230                                                         ignore-cache)
231                      (elmo-make-fetch-strategy 'entire)))
232     (mime-display-message
233      (mime-open-entity
234       (if (and strategy
235                (eq (elmo-fetch-strategy-entireness strategy) 'section))
236           'elmo-imap
237         'elmo-buffer)
238       (elmo-make-mime-message-location
239        folder number strategy rawbuf unread))
240      viewbuf nil keymap
241      original-mode)
242     (if strategy
243         (or (elmo-fetch-strategy-use-cache strategy)
244             (eq (elmo-fetch-strategy-entireness strategy)
245                 'section)))))
246
247 (defun elmo-mime-display-as-is (folder number viewbuf rawbuf original-mode
248                                        &optional ignore-cache unread keymap)
249   "Display MIME message.
250 A message in the FOLDER with NUMBER is displayed on the VIEWBUF using RAWBUF.
251 VIEWBUF is a view buffer and RAWBUF is a raw buffer.
252 ORIGINAL is the major mode of RAWBUF.
253 If optional argument IGNORE-CACHE is specified, existing cache is ignored.
254 If second optional argument UNREAD is specified, message is displayed but
255 keep it as unread.
256 Return non-nil if cache is used."
257   (let ((entity (elmo-msgdb-message-entity (elmo-folder-msgdb folder) number))
258         mime-display-header-hook ; Do nothing.
259         cache-file strategy use-cache)
260     (when entity
261       (setq cache-file (elmo-file-cache-get
262                         (elmo-message-entity-field entity 'message-id)))
263       (setq use-cache (and (elmo-message-use-cache-p folder number)
264                            (eq (elmo-file-cache-status cache-file) 'entire))))
265     (setq strategy (elmo-make-fetch-strategy
266                     'entire use-cache
267                     (elmo-message-use-cache-p folder number)
268                     (elmo-file-cache-path cache-file)))
269     (elmo-mime-display-as-is-internal
270      (mime-open-entity
271       'elmo-buffer
272       (elmo-make-mime-message-location
273        folder number strategy rawbuf unread))
274      viewbuf nil keymap original-mode)
275     (when strategy
276       (elmo-fetch-strategy-use-cache strategy))))
277
278 ;; Replacement of mime-display-message.
279 (defun elmo-mime-display-as-is-internal (message
280                                          &optional preview-buffer
281                                          mother default-keymap-or-function
282                                          original-major-mode keymap)
283   (mime-maybe-hide-echo-buffer)
284   (let ((win-conf (current-window-configuration)))
285     (or preview-buffer
286         (setq preview-buffer
287               (concat "*Preview-" (mime-entity-name message) "*")))
288     (or original-major-mode
289         (setq original-major-mode major-mode))
290     (let ((inhibit-read-only t))
291       (set-buffer (get-buffer-create preview-buffer))
292       (widen)
293       (erase-buffer)
294       (if mother
295           (setq mime-mother-buffer mother))
296       (setq mime-preview-original-window-configuration win-conf)
297       (setq major-mode 'mime-view-mode)
298       (setq mode-name "MIME-View")
299
300       ;; Humm...
301       (set-buffer-multibyte nil)
302       (mime-insert-entity message)
303       (set-buffer-multibyte t)
304       (decode-coding-region (point-min) (point-max)
305                             elmo-mime-display-as-is-coding-system)
306       (save-restriction
307         (std11-narrow-to-header)
308         (run-hooks 'elmo-message-header-inserted-hook))
309       ;; set original major mode for mime-preview-quit
310       (put-text-property (point-min) (point-max)
311                          'mime-view-situation
312                          `((major-mode . ,original-major-mode)))
313       (use-local-map
314        (or keymap
315            (if default-keymap-or-function
316                (mime-view-define-keymap default-keymap-or-function)
317              mime-view-mode-default-map)))
318       (let ((point
319              (next-single-property-change (point-min) 'mime-view-entity)))
320         (if point
321             (goto-char point)
322           (goto-char (point-min))
323           (search-forward "\n\n" nil t)))
324       (run-hooks 'mime-view-mode-hook)
325       (set-buffer-modified-p nil)
326       (setq buffer-read-only t)
327       preview-buffer)))
328
329 (require 'product)
330 (product-provide (provide 'elmo-mime) (require 'elmo-version))
331
332 ;; elmo-mime.el ends here