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