1 ;;; mime-parse.el --- MIME message parser
3 ;; Copyright (C) 1994,1995,1996,1997,1998,1999 Free Software Foundation, Inc.
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Keywords: parse, MIME, multimedia, mail, news
8 ;; This file is part of SEMI (Spadework for Emacs MIME Interfaces).
10 ;; This program is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
15 ;; This program is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; 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.
30 (autoload 'mime-entity-body-buffer "mime")
31 (autoload 'mime-entity-body-start-point "mime")
32 (autoload 'mime-entity-body-end-point "mime")
35 ;;; @ lexical analyzer
38 (defcustom mime-lexical-analyzer
39 '(std11-analyze-quoted-string
40 std11-analyze-domain-literal
45 "*List of functions to return result of lexical analyze.
46 Each function must have two arguments: STRING and START.
47 STRING is the target string to be analyzed.
48 START is start position of STRING to analyze.
50 Previous function is preferred to next function. If a function
51 returns nil, next function is used. Otherwise the return value will
54 :type '(repeat function))
56 (defun mime-analyze-tspecial (string start)
57 (if (and (> (length string) start)
58 (memq (aref string start) mime-tspecial-char-list))
59 (cons (cons 'tpecials (substring string start (1+ start)))
63 (defun mime-analyze-token (string start)
64 (if (and (string-match mime-token-regexp string start)
65 (= (match-beginning 0) start))
66 (let ((end (match-end 0)))
67 (cons (cons 'mime-token (substring string start end))
68 ;;(substring string end)
76 (defconst mime/content-parameter-value-regexp
78 std11-quoted-string-regexp
82 (defconst mime::attribute-char-regexp "[^][*'%()<>@,;:\\\"/?=\000- ]")
83 (defconst mime::attribute-regexp (concat mime::attribute-char-regexp "+")
86 (defconst mime::ext-octet-regexp "%[0-9a-f][0-9a-f]")
87 (defconst mime::extended-other-values-regexp
88 (concat "\\(" mime::attribute-char-regexp "\\|"
89 mime::ext-octet-regexp "\\)+")
91 (defconst mime::extended-initial-value-regexp
92 (concat "\\(" mime-charset-regexp "\\)'\\(" mime-charset-regexp "\\)'\\("
93 mime::extended-other-values-regexp "\\)")
96 (defconst mime::parameter-regexp
97 (concat "[ \t]*\;[ \t]*\\(" mime::attribute-regexp "\\)"
98 "\\(\\*\\([0-9]+\\)\\)?\\(\\*\\)?"
99 "[ \t]*=[ \t]*\\(" mime/content-parameter-value-regexp "\\)")
102 (defun mime-parse-parameters (str)
106 (goto-char (point-min))
107 (while (looking-at mime::parameter-regexp)
108 (let* ((name (buffer-substring (match-beginning 1) (match-end 1)))
109 (no (or (and (match-beginning 3)
111 (buffer-substring (match-beginning 3)
115 (encoded (and (match-beginning 4) t))
117 (parm (or (assoc name rest)
119 (cons (make-mime-parameter name) rest)
121 (mime-parameter-append-raw-value
128 (goto-char (match-beginning 5))
129 (looking-at mime::extended-initial-value-regexp)
132 (mime-parameter-set-charset
135 (buffer-substring (match-beginning 1)
138 (mime-parameter-set-language
141 (buffer-substring (match-beginning 2)
144 (buffer-substring (match-beginning 3) (match-end 3))
146 (buffer-substring (match-beginning 5) (match-end 5))
148 (std11-strip-quoted-string
149 (buffer-substring (match-beginning 5) (match-end 5))
159 (defun mime-parse-Content-Type (string)
160 "Parse STRING as field-body of Content-Type field."
161 (setq string (std11-unfold-string string))
162 (if (string-match `,(concat "^\\(" mime-token-regexp
163 "\\)/\\(" mime-token-regexp "\\)") string)
164 (let* ((type (downcase
165 (substring string (match-beginning 1) (match-end 1))))
167 (substring string (match-beginning 2) (match-end 2))))
169 (setq string (substring string (match-end 0)))
170 (make-mime-content-type (intern type)(intern subtype)
171 (nreverse (mime-parse-parameters string))
175 (defun mime-read-Content-Type ()
176 "Read field-body of Content-Type field from current-buffer,
177 and return parsed it."
178 (let ((str (std11-field-body "Content-Type")))
180 (mime-parse-Content-Type str)
184 ;;; @ Content-Disposition
188 (defconst mime-disposition-type-regexp mime-token-regexp)
192 (defun mime-parse-Content-Disposition (string)
193 "Parse STRING as field-body of Content-Disposition field."
194 (setq string (std11-unfold-string string))
195 (if (string-match (eval-when-compile
196 (concat "^" mime-disposition-type-regexp)) string)
197 (let* ((e (match-end 0))
198 (type (downcase (substring string 0 e)))
200 (setq string (substring string e))
201 (cons (cons 'type (intern type))
202 (nreverse (mime-parse-parameters string)))
206 (defun mime-read-Content-Disposition ()
207 "Read field-body of Content-Disposition field from current-buffer,
208 and return parsed it."
209 (let ((str (std11-field-body "Content-Disposition")))
211 (mime-parse-Content-Disposition str)
215 ;;; @ Content-Transfer-Encoding
219 (defun mime-parse-Content-Transfer-Encoding (string)
220 "Parse STRING as field-body of Content-Transfer-Encoding field."
221 (let ((tokens (std11-lexical-analyze string mime-lexical-analyzer))
224 (setq token (car tokens))
225 (std11-ignored-token-p token))
226 (setq tokens (cdr tokens)))
228 (if (eq (car token) 'mime-token)
229 (downcase (cdr token))
233 (defun mime-read-Content-Transfer-Encoding (&optional default-encoding)
234 "Read field-body of Content-Transfer-Encoding field from
235 current-buffer, and return it.
236 If is is not found, return DEFAULT-ENCODING."
237 (let ((str (std11-field-body "Content-Transfer-Encoding")))
239 (mime-parse-Content-Transfer-Encoding str)
243 ;;; @ Content-Id / Message-Id
247 (defun mime-parse-msg-id (tokens)
248 "Parse TOKENS as msg-id of Content-Id or Message-Id field."
249 (car (std11-parse-msg-id tokens)))
252 (defun mime-uri-parse-cid (string)
253 "Parse STRING as cid URI."
255 (mime-parse-msg-id (cons '(specials . "<")
257 (cdr (cdr (std11-lexical-analyze string)))
258 '((specials . ">")))))))
264 (defun mime-parse-multipart (entity)
265 (with-current-buffer (mime-entity-body-buffer entity)
266 (let* ((representation-type
267 (mime-entity-representation-type-internal entity))
268 (content-type (mime-entity-content-type-internal entity))
271 (mime-content-type-parameter content-type "boundary")))
272 (delimiter (concat "\n" (regexp-quote dash-boundary)))
273 (close-delimiter (concat delimiter "--[ \t]*$"))
274 (rsep (concat delimiter "[ \t]*\n"))
276 (if (eq (mime-content-type-subtype content-type) 'digest)
277 (make-mime-content-type 'message 'rfc822)
278 (make-mime-content-type 'text 'plain)
280 (body-start (mime-entity-body-start-point entity))
281 (body-end (mime-entity-body-end-point entity)))
284 (narrow-to-region body-start
285 (if (re-search-backward close-delimiter nil t)
288 (goto-char body-start)
289 (if (re-search-forward
290 (concat "^" (regexp-quote dash-boundary) "[ \t]*\n")
292 (let ((cb (match-end 0))
294 (node-id (mime-entity-node-id-internal entity))
296 (while (re-search-forward rsep nil t)
297 (setq ce (match-beginning 0))
298 (setq ncb (match-end 0))
300 (narrow-to-region cb ce)
301 (setq ret (mime-parse-message representation-type dc-ctl
302 entity (cons i node-id)))
304 (setq children (cons ret children))
305 (goto-char (setq cb ncb))
308 (setq ce (point-max))
310 (narrow-to-region cb ce)
311 (setq ret (mime-parse-message representation-type dc-ctl
312 entity (cons i node-id)))
314 (setq children (cons ret children))
315 (mime-entity-set-children-internal entity (nreverse children))
317 (mime-entity-set-content-type-internal
318 entity (make-mime-content-type 'message 'x-broken))
322 (defun mime-parse-encapsulated (entity)
323 (mime-entity-set-children-internal
325 (with-current-buffer (mime-entity-body-buffer entity)
327 (narrow-to-region (mime-entity-body-start-point entity)
328 (mime-entity-body-end-point entity))
329 (list (mime-parse-message
330 (mime-entity-representation-type-internal entity) nil
331 entity (cons 0 (mime-entity-node-id-internal entity))))
334 (defun mime-parse-message (representation-type &optional default-ctl
336 (let ((header-start (point-min))
339 (body-end (point-max))
341 (goto-char header-start)
342 (if (re-search-forward "^$" nil t)
343 (setq header-end (match-end 0)
344 body-start (if (= header-end body-end)
347 (setq header-end (point-min)
348 body-start (point-min)))
350 (narrow-to-region header-start header-end)
351 (setq content-type (or (let ((str (std11-fetch-field "Content-Type")))
353 (mime-parse-Content-Type str)
357 (luna-make-entity representation-type
358 :location (current-buffer)
359 :content-type content-type
362 :buffer (current-buffer)
363 :header-start header-start
364 :header-end header-end
365 :body-start body-start
374 (defun mime-parse-buffer (&optional buffer representation-type)
375 "Parse BUFFER as a MIME message.
376 If buffer is omitted, it parses current-buffer."
378 (if buffer (set-buffer buffer))
379 (setq mime-message-structure
380 (mime-parse-message (or representation-type
381 'mime-buffer-entity) nil))
388 (provide 'mime-parse)
390 ;;; mime-parse.el ends here