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