(mime-parse-multipart): Refer body-start instead of header-end.
[elisp/flim.git] / mmbuffer.el
1 ;;; mmbuffer.el --- MIME entity module for binary buffer
2
3 ;; Copyright (C) 1998,1999 Free Software Foundation, Inc.
4 ;; Copyright (C) 1999 Electrotechnical Laboratory, JAPAN.
5 ;; Licensed to the Free Software Foundation.
6
7 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
8 ;; Keywords: MIME, multimedia, mail, news
9
10 ;; This file is part of FLIM (Faithful Library about Internet Message).
11
12 ;; This program is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 2, or (at
15 ;; your option) any later version.
16
17 ;; This program is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ;; General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Code:
28
29 (require 'mime)
30
31 (eval-and-compile
32   (luna-define-class mime-buffer-entity (mime-entity)
33                      (buffer
34                       header-start
35                       header-end
36                       body-start
37                       body-end))
38
39   (luna-define-internal-accessors 'mime-buffer-entity)
40   )
41
42 (luna-define-method initialize-instance :after ((entity mime-buffer-entity)
43                                                 &rest init-args)
44   (mime-buffer-entity-set-buffer-internal
45    entity (mime-entity-location-internal entity))
46   (save-excursion
47     (set-buffer (mime-buffer-entity-buffer-internal entity))
48     (setq mime-message-structure entity)
49     (let ((header-start (point-min))
50           header-end
51           body-start
52           (body-end (point-max)))
53       (goto-char header-start)
54       (if (re-search-forward "^$" nil t)
55           (setq header-end (match-end 0)
56                 body-start (if (= header-end body-end)
57                                body-end
58                              (1+ header-end)))
59         (setq header-end (point-min)
60               body-start (point-min)))
61       (save-restriction
62         (narrow-to-region header-start header-end)
63         (mime-entity-set-content-type-internal
64          entity
65          (let ((str (std11-fetch-field "Content-Type")))
66            (if str
67                (mime-parse-Content-Type str)
68              )))
69         )
70       (mime-buffer-entity-set-header-start-internal entity header-start)
71       (mime-buffer-entity-set-header-end-internal entity header-end)
72       (mime-buffer-entity-set-body-start-internal entity body-start)
73       (mime-buffer-entity-set-body-end-internal entity body-end)
74       ))
75   entity)
76
77 (luna-define-method mime-entity-name ((entity mime-buffer-entity))
78   (buffer-name (mime-buffer-entity-buffer-internal entity))
79   )
80
81
82 ;;; @ message parser
83 ;;;
84
85 (defun mime-parse-multipart (entity)
86   (goto-char (point-min))
87   (let* ((representation-type
88           (mime-entity-representation-type-internal entity))
89          (content-type (mime-entity-content-type-internal entity))
90          (dash-boundary
91           (concat "--" (mime-content-type-parameter content-type "boundary")))
92          (delimiter       (concat "\n" (regexp-quote dash-boundary)))
93          (close-delimiter (concat delimiter "--[ \t]*$"))
94          (rsep (concat delimiter "[ \t]*\n"))
95          (dc-ctl
96           (if (eq (mime-content-type-subtype content-type) 'digest)
97               (make-mime-content-type 'message 'rfc822)
98             (make-mime-content-type 'text 'plain)
99             ))
100          (body-start (mime-buffer-entity-body-start-internal entity))
101          (body-end (mime-buffer-entity-body-end-internal entity)))
102     (save-restriction
103       (goto-char body-end)
104       (narrow-to-region body-start
105                         (if (re-search-backward close-delimiter nil t)
106                             (match-beginning 0)
107                           body-end))
108       (goto-char body-start)
109       (if (re-search-forward
110            (concat "^" (regexp-quote dash-boundary) "[ \t]*\n")
111            nil t)
112           (let ((cb (match-end 0))
113                 ce ncb ret children
114                 (node-id (mime-entity-node-id-internal entity))
115                 (i 0))
116             (while (re-search-forward rsep nil t)
117               (setq ce (match-beginning 0))
118               (setq ncb (match-end 0))
119               (save-restriction
120                 (narrow-to-region cb ce)
121                 (setq ret (mime-parse-message representation-type dc-ctl
122                                               entity (cons i node-id)))
123                 )
124               (setq children (cons ret children))
125               (goto-char (setq cb ncb))
126               (setq i (1+ i))
127               )
128             (setq ce (point-max))
129             (save-restriction
130               (narrow-to-region cb ce)
131               (setq ret (mime-parse-message representation-type dc-ctl
132                                             entity (cons i node-id)))
133               )
134             (setq children (cons ret children))
135             (mime-entity-set-children-internal entity (nreverse children))
136             )
137         (mime-entity-set-content-type-internal
138          entity (make-mime-content-type 'message 'x-broken))
139         nil)
140       )))
141
142 (defun mime-parse-encapsulated (entity)
143   (mime-entity-set-children-internal
144    entity
145    (save-restriction
146      (narrow-to-region (mime-buffer-entity-body-start-internal entity)
147                        (mime-buffer-entity-body-end-internal entity))
148      (list (mime-parse-message
149             (mime-entity-representation-type-internal entity) nil
150             entity (cons 0 (mime-entity-node-id-internal entity))))
151      )))
152
153 (defun mime-parse-message (representation-type &optional default-ctl 
154                                                parent node-id)
155   (let ((header-start (point-min))
156         header-end
157         body-start
158         (body-end (point-max))
159         content-type)
160     (goto-char header-start)
161     (if (re-search-forward "^$" nil t)
162         (setq header-end (match-end 0)
163               body-start (if (= header-end body-end)
164                              body-end
165                            (1+ header-end)))
166       (setq header-end (point-min)
167             body-start (point-min)))
168     (save-restriction
169       (narrow-to-region header-start header-end)
170       (setq content-type (or (let ((str (std11-fetch-field "Content-Type")))
171                                (if str
172                                    (mime-parse-Content-Type str)
173                                  ))
174                              default-ctl))
175       )
176     (luna-make-entity representation-type
177                       :location (current-buffer)
178                       :content-type content-type
179                       :parent parent
180                       :node-id node-id
181                       :buffer (current-buffer)
182                       :header-start header-start
183                       :header-end header-end
184                       :body-start body-start
185                       :body-end body-end)
186     ))
187
188 (luna-define-method mime-entity-children ((entity mime-buffer-entity))
189   (let* ((content-type (mime-entity-content-type entity))
190          (primary-type (mime-content-type-primary-type content-type)))
191     (cond ((eq primary-type 'multipart)
192            (mime-parse-multipart entity)
193            )
194           ((and (eq primary-type 'message)
195                 (memq (mime-content-type-subtype content-type)
196                       '(rfc822 news external-body)
197                       ))
198            (mime-parse-encapsulated entity)
199            ))
200     ))
201
202
203 (luna-define-method mime-goto-header-start-point ((entity mime-buffer-entity))
204   (set-buffer (mime-buffer-entity-buffer-internal entity))
205   (goto-char (mime-buffer-entity-header-start-internal entity))
206   )
207
208 (defun mime-visible-field-p (field-name visible-fields invisible-fields)
209   (or (catch 'found
210         (while visible-fields
211           (let ((regexp (car visible-fields)))
212             (if (string-match regexp field-name)
213                 (throw 'found t)
214               ))
215           (setq visible-fields (cdr visible-fields))
216           ))
217       (catch 'found
218         (while invisible-fields
219           (let ((regexp (car invisible-fields)))
220             (if (string-match regexp field-name)
221                 (throw 'found nil)
222               ))
223           (setq invisible-fields (cdr invisible-fields))
224           )
225         t)))
226
227 (defun mime-insert-header-from-buffer (buffer start end
228                                               &optional invisible-fields
229                                               visible-fields)
230   (let ((the-buf (current-buffer))
231         (mode-obj (mime-find-field-presentation-method 'wide))
232         field-decoder
233         f-b p f-e field-name len field field-body)
234     (save-excursion
235       (set-buffer buffer)
236       (save-restriction
237         (narrow-to-region start end)
238         (goto-char start)
239         (while (re-search-forward std11-field-head-regexp nil t)
240           (setq f-b (match-beginning 0)
241                 p (match-end 0)
242                 field-name (buffer-substring f-b p)
243                 len (string-width field-name)
244                 f-e (std11-field-end))
245           (when (mime-visible-field-p field-name
246                                       visible-fields invisible-fields)
247             (setq field (intern
248                          (capitalize (buffer-substring f-b (1- p))))
249                   field-body (buffer-substring p f-e)
250                   field-decoder (inline (mime-find-field-decoder-internal
251                                          field mode-obj)))
252             (with-current-buffer the-buf
253               (insert field-name)
254               (insert (if field-decoder
255                           (funcall field-decoder field-body len)
256                         ;; Don't decode
257                         field-body))
258               (insert "\n")
259               )))))))
260
261 (luna-define-method mime-insert-header ((entity mime-buffer-entity)
262                                         &optional invisible-fields
263                                         visible-fields)
264   (mime-insert-header-from-buffer
265    (mime-buffer-entity-buffer-internal entity)
266    (mime-buffer-entity-header-start-internal entity)
267    (mime-buffer-entity-header-end-internal entity)
268    invisible-fields visible-fields)
269   )
270
271 (luna-define-method mime-entity-content ((entity mime-buffer-entity))
272   (save-excursion
273     (set-buffer (mime-buffer-entity-buffer-internal entity))
274     (mime-decode-string
275      (buffer-substring (mime-buffer-entity-body-start-internal entity)
276                        (mime-buffer-entity-body-end-internal entity))
277      (mime-entity-encoding entity))))
278
279 (luna-define-method mime-insert-text-content ((entity mime-buffer-entity))
280   (insert
281    (decode-mime-charset-string (mime-entity-content entity)
282                                (or (mime-content-type-parameter
283                                     (mime-entity-content-type entity)
284                                     "charset")
285                                    default-mime-charset)
286                                'CRLF)
287    ))
288
289 ;;; redefine to speed up
290
291 (mm-define-method entity-point-min ((entity buffer))
292   (mime-buffer-entity-header-start-internal entity))
293
294 (mm-define-method entity-point-max ((entity buffer))
295   (mime-buffer-entity-body-end-internal entity))
296
297 (luna-define-method mime-entity-fetch-field ((entity mime-buffer-entity)
298                                              field-name)
299   (save-excursion
300     (set-buffer (mime-buffer-entity-buffer-internal entity))
301     (save-restriction
302       (narrow-to-region (mime-buffer-entity-header-start-internal entity)
303                         (mime-buffer-entity-header-end-internal entity))
304       (std11-fetch-field field-name)
305       )))
306
307 (mm-define-method insert-entity-content ((entity buffer))
308   (insert (with-current-buffer (mime-buffer-entity-buffer-internal entity)
309             (mime-decode-string
310              (buffer-substring (mime-buffer-entity-body-start-internal entity)
311                                (mime-buffer-entity-body-end-internal entity))
312              (mime-entity-encoding entity)))))
313
314 (mm-define-method write-entity-content ((entity buffer) filename)
315   (save-excursion
316     (set-buffer (mime-buffer-entity-buffer-internal entity))
317     (mime-write-decoded-region (mime-buffer-entity-body-start-internal entity)
318                                (mime-buffer-entity-body-end-internal entity)
319                                filename
320                                (or (mime-entity-encoding entity) "7bit"))
321     ))
322
323 (mm-define-method insert-entity ((entity buffer))
324   (insert-buffer-substring (mime-buffer-entity-buffer-internal entity)
325                            (mime-buffer-entity-header-start-internal entity)
326                            (mime-buffer-entity-body-end-internal entity))
327   )
328
329 (mm-define-method write-entity ((entity buffer) filename)
330   (save-excursion
331     (set-buffer (mime-buffer-entity-buffer-internal entity))
332     (write-region-as-raw-text-CRLF
333      (mime-buffer-entity-header-start-internal entity)
334      (mime-buffer-entity-body-end-internal entity)
335      filename)
336     ))
337
338 (mm-define-method write-entity-body ((entity buffer) filename)
339   (save-excursion
340     (set-buffer (mime-buffer-entity-buffer-internal entity))
341     (write-region-as-binary (mime-buffer-entity-body-start-internal entity)
342                             (mime-buffer-entity-body-end-internal entity)
343                             filename)
344     ))
345
346
347 ;;; @ buffer
348 ;;;
349
350 (luna-define-method mime-entity-header-buffer ((entity mime-buffer-entity))
351   (mime-buffer-entity-buffer-internal entity)
352   )
353
354 (luna-define-method mime-entity-body-buffer ((entity mime-buffer-entity))
355   (mime-buffer-entity-buffer-internal entity)
356   )
357
358 (luna-define-method mime-entity-buffer ((entity mime-buffer-entity))
359   (mime-buffer-entity-buffer-internal entity)
360   )
361
362 (luna-define-method mime-entity-point-min ((entity mime-buffer-entity))
363   (mime-buffer-entity-header-start-internal entity)
364   )
365
366 (luna-define-method mime-entity-point-max ((entity mime-buffer-entity))
367   (mime-buffer-entity-body-end-internal entity)
368   )
369
370
371 ;;; @ utility
372 ;;;
373
374 ;;;###autoload
375 (defun mime-parse-buffer (&optional buffer representation-type)
376   "Parse BUFFER as a MIME message.
377 If buffer is omitted, it parses current-buffer."
378   (save-excursion
379     (if buffer (set-buffer buffer))
380     (setq mime-message-structure
381           (mime-parse-message (or representation-type 'buffer) nil))
382     ))
383
384
385 ;;; @ end
386 ;;;
387
388 (provide 'mmbuffer)
389
390 ;;; mmbuffer.el ends here