Fix my email address.
[elisp/flim.git] / mime-def.el
1 ;;; mime-def.el --- definition module about MIME -*- coding: iso-2022-jp; -*-
2
3 ;; Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
6 ;;      Shuhei KOBAYASHI <shuhei@aqua.ocn.ne.jp>
7 ;; Keywords: definition, MIME, multimedia, mail, news
8
9 ;; This file is part of FLIM (Faithful Library about Internet Message).
10
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
15
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Code:
27
28 (require 'poe)
29 (require 'poem)
30 (require 'pcustom)
31 (require 'mcharset)
32 (require 'alist)
33
34 (eval-when-compile (require 'luna))     ; luna-arglist-to-arguments
35
36 (eval-and-compile
37   (defconst mime-library-product ["CLIME" (1 14 5) "\e$BK\;{0f\e(B"]
38     "Product name, version number and code name of MIME-library package."))
39
40 (defmacro mime-product-name (product)
41   (` (aref (, product) 0)))
42
43 (defmacro mime-product-version (product)
44   (` (aref (, product) 1)))
45
46 (defmacro mime-product-code-name (product)
47   (` (aref (, product) 2)))
48
49 (defconst mime-library-version
50   (eval-when-compile
51     (concat (mime-product-name mime-library-product) " "
52             (mapconcat (function int-to-string)
53                        (mime-product-version mime-library-product) ".")
54             " - \"" (mime-product-code-name mime-library-product) "\"")))
55
56
57 ;;; @ variables
58 ;;;
59
60 (defgroup mime '((default-mime-charset custom-variable))
61   "Emacs MIME Interfaces"
62   :group 'news
63   :group 'mail)
64
65 (defcustom mime-uuencode-encoding-name-list '("x-uue" "x-uuencode")
66   "*List of encoding names for uuencode format."
67   :group 'mime
68   :type '(repeat string))
69
70
71 ;;; @@ for encoded-word
72 ;;;
73
74 (defgroup mime-header nil
75   "Header representation, specially encoded-word"
76   :group 'mime)
77
78 ;;; @@@ decoding
79 ;;;
80
81 (defcustom mime-field-decoding-max-size 1000
82   "*Max size to decode header field."
83   :group 'mime-header
84   :type '(choice (integer :tag "Limit (bytes)")
85                  (const :tag "Don't limit" nil)))
86
87 ;;; @@@ encoding
88 ;;;
89
90 (defcustom mime-field-encoding-method-alist
91   '(("X-Nsubject" . iso-2022-jp-2)
92     ("Newsgroups" . nil)
93     ("Message-ID" . nil)
94     (t            . mime)
95     )
96   "*Alist to specify field encoding method.
97 Its key is field-name, value is encoding method.
98
99 If method is `mime', this field will be encoded into MIME format.
100
101 If method is a MIME-charset, this field will be encoded as the charset
102 when it must be convert into network-code.
103
104 If method is `default-mime-charset', this field will be encoded as
105 variable `default-mime-charset' when it must be convert into
106 network-code.
107
108 If method is nil, this field will not be encoded."
109   :group 'mime-header
110   :type '(repeat (cons (choice :tag "Field"
111                                (string :tag "Name")
112                                (const :tag "Default" t))
113                        (choice :tag "Method"
114                                (const :tag "MIME conversion" mime)
115                                (symbol :tag "non-MIME conversion")
116                                (const :tag "no-conversion" nil)))))
117
118
119 ;;; @ required functions
120 ;;;
121
122 (defsubst regexp-* (regexp)
123   (concat regexp "*"))
124
125 (defsubst regexp-or (&rest args)
126   (concat "\\(" (mapconcat (function identity) args "\\|") "\\)"))
127
128
129 ;;; @ MIME constants
130 ;;;
131
132 (defconst mime-tspecial-char-list
133   '(?\] ?\[ ?\( ?\) ?< ?> ?@ ?, ?\; ?: ?\\ ?\" ?/ ?? ?=))
134 (defconst mime-token-regexp
135   (concat "[^" mime-tspecial-char-list "\000-\040]+"))
136 (defconst mime-attribute-char-regexp
137   (concat "[^" mime-tspecial-char-list "\000-\040"
138           "*'%"                         ; introduced in RFC 2231.
139           "]"))
140
141 (defconst mime-charset-regexp
142   (concat "[^" mime-tspecial-char-list "\000-\040"
143           "*'%"                         ; should not include "%"?
144           "]+"))
145
146 ;; More precisely, length of "[A-Za-z]+" is limited to at most 8.
147 ;; (defconst mime-language-regexp "[A-Za-z]+\\(-[A-Za-z]+\\)*")
148 (defconst mime-language-regexp "[-A-Za-z]+")
149
150 (defconst mime-encoding-regexp mime-token-regexp)
151
152
153 ;;; @@ base64 / B
154 ;;;
155
156 (defconst base64-token-regexp "[A-Za-z0-9+/]")
157 (defconst base64-token-padding-regexp "[A-Za-z0-9+/=]")
158
159 (defconst B-encoded-text-regexp
160   (concat "\\(\\("
161           base64-token-regexp
162           base64-token-regexp
163           base64-token-regexp
164           base64-token-regexp
165           "\\)*"
166           base64-token-regexp
167           base64-token-regexp
168           base64-token-padding-regexp
169           base64-token-padding-regexp
170           "\\)"))
171
172 ;; (defconst eword-B-encoding-and-encoded-text-regexp
173 ;;   (concat "\\(B\\)\\?" eword-B-encoded-text-regexp))
174
175
176 ;;; @@ Quoted-Printable / Q
177 ;;;
178
179 (defconst quoted-printable-hex-chars "0123456789ABCDEF")
180
181 (defconst quoted-printable-octet-regexp
182   (concat "=[" quoted-printable-hex-chars
183           "][" quoted-printable-hex-chars "]"))
184
185 (defconst Q-encoded-text-regexp
186   (concat "\\([^=?]\\|" quoted-printable-octet-regexp "\\)+"))
187
188 ;; (defconst eword-Q-encoding-and-encoded-text-regexp
189 ;;   (concat "\\(Q\\)\\?" eword-Q-encoded-text-regexp))
190
191
192 ;;; @ Content-Type
193 ;;;
194
195 (defsubst make-mime-content-type (type subtype &optional parameters)
196   (cons (cons 'type type)
197         (cons (cons 'subtype subtype)
198               parameters)))
199
200 (defsubst mime-content-type-primary-type (content-type)
201   "Return primary-type of CONTENT-TYPE."
202   (cdr (car content-type)))
203
204 (defsubst mime-content-type-subtype (content-type)
205   "Return subtype of CONTENT-TYPE."
206   (cdr (car (cdr content-type))))
207
208 (defsubst mime-content-type-parameters (content-type)
209   "Return parameters of CONTENT-TYPE."
210   (cdr (cdr content-type)))
211
212 (defsubst mime-content-type-parameter (content-type parameter)
213   "Return PARAMETER value of CONTENT-TYPE."
214   (cdr (assoc parameter (cdr (cdr content-type)))))
215
216
217 (defsubst mime-type/subtype-string (type &optional subtype)
218   "Return type/subtype string from TYPE and SUBTYPE."
219   (if type
220       (if subtype
221           (format "%s/%s" type subtype)
222         (format "%s" type))))
223
224
225 ;;; @ Content-Disposition
226 ;;;
227
228 (defsubst make-mime-content-disposition (type &optional parameters)
229   (cons (cons 'type type)
230         parameters))
231
232 (defsubst mime-content-disposition-type (content-disposition)
233   "Return disposition-type of CONTENT-DISPOSITION."
234   (cdr (car content-disposition)))
235
236 (defsubst mime-content-disposition-parameters (content-disposition)
237   "Return disposition-parameters of CONTENT-DISPOSITION."
238   (cdr content-disposition))
239
240 (defsubst mime-content-disposition-parameter (content-disposition parameter)
241   "Return PARAMETER value of CONTENT-DISPOSITION."
242   (cdr (assoc parameter (cdr content-disposition))))
243
244 (defsubst mime-content-disposition-filename (content-disposition)
245   "Return filename of CONTENT-DISPOSITION."
246   (mime-content-disposition-parameter content-disposition "filename"))
247
248
249 ;;; @ message structure
250 ;;;
251
252 (static-condition-case nil
253     :symbol-for-testing-whether-colon-keyword-is-available-or-not
254   (void-variable
255    (defconst :location ':location)
256    (defconst :content-type ':content-type)
257    (defconst :parent ':parent)
258    (defconst :node-id ':node-id)
259    (defconst :buffer ':buffer)
260    (defconst :header-start ':header-start)
261    (defconst :header-end ':header-end)
262    (defconst :body-start ':body-start)
263    (defconst :body-end ':body-end)))
264
265 (defvar mime-message-structure nil
266   "Information about structure of message.
267 Please use reference function `mime-entity-SLOT' to get value of SLOT.
268
269 Following is a list of slots of the structure:
270
271 node-id                 node-id (list of integers)
272 content-type            content-type (content-type)
273 content-disposition     content-disposition (content-disposition)
274 encoding                Content-Transfer-Encoding (string or nil)
275 children                entities included in this entity (list of entity)
276
277 If an entity includes other entities in its body, such as multipart or
278 message/rfc822, `mime-entity' structures of them are included in
279 `children', so the `mime-entity' structure become a tree.")
280
281 (make-variable-buffer-local 'mime-message-structure)
282
283 (make-obsolete-variable 'mime-message-structure "should not use it.")
284
285
286 ;;; @ for mel-backend
287 ;;;
288
289 (defvar mel-service-list nil)
290
291 (defmacro mel-define-service (name &optional args &rest rest)
292   "Define NAME as a service for Content-Transfer-Encodings.
293 If ARGS is specified, NAME is defined as a generic function for the
294 service."
295   (` (progn
296        (add-to-list 'mel-service-list '(, name))
297        (defvar (, (intern (format "%s-obarray" name))) (make-vector 7 0))
298        (,@ (if args
299                (` ((defun (, name) (, args)
300                      (,@ rest)
301                      (funcall (mel-find-function '(, name)
302                                                  (, (car (last args))))
303                               (,@ (luna-arglist-to-arguments
304                                    (butlast args))))))))))))
305
306 (put 'mel-define-service 'lisp-indent-function 'defun)
307
308
309 (defvar mel-encoding-module-alist nil)
310
311 (defsubst mel-find-function-from-obarray (ob-array encoding)
312   (let* ((f (intern-soft encoding ob-array)))
313     (or f
314         (let ((rest (cdr (assoc encoding mel-encoding-module-alist))))
315           (while (and rest
316                       (progn
317                         (require (car rest))
318                         (null (setq f (intern-soft encoding ob-array)))))
319             (setq rest (cdr rest)))
320           f))))
321
322 (defsubst mel-copy-method (service src-backend dst-backend)
323   (let* ((oa (symbol-value (intern (format "%s-obarray" service))))
324          (f (mel-find-function-from-obarray oa src-backend))
325          sym)
326     (when f
327       (setq sym (intern dst-backend oa))
328       (or (fboundp sym)
329           (fset sym (symbol-function f))))))
330
331 (defsubst mel-copy-backend (src-backend dst-backend)
332   (let ((services mel-service-list))
333     (while services
334       (mel-copy-method (car services) src-backend dst-backend)
335       (setq services (cdr services)))))
336
337 (defmacro mel-define-backend (type &optional parents)
338   "Define TYPE as a mel-backend.
339 If PARENTS is specified, TYPE inherits PARENTS.
340 Each parent must be backend name (string)."
341   (cons 'progn
342         (mapcar (function
343                  (lambda (parent)
344                    (` (mel-copy-backend (, parent) (, type)))))
345                 parents)))
346
347 (defmacro mel-define-method (name args &rest body)
348   "Define NAME as a method function of (nth 1 (car (last ARGS))) backend.
349 ARGS is like an argument list of lambda, but (car (last ARGS)) must be
350 specialized parameter.  (car (car (last ARGS))) is name of variable
351 and (nth 1 (car (last ARGS))) is name of backend (encoding)."
352   (let* ((specializer (car (last args)))
353          (class (nth 1 specializer)))
354     (` (progn
355          (mel-define-service (, name))
356          (fset (intern (, class) (, (intern (format "%s-obarray" name))))
357                (function
358                 (lambda (, (butlast args))
359                   (,@ body))))))))
360
361 (put 'mel-define-method 'lisp-indent-function 'defun)
362
363 (defmacro mel-define-method-function (spec function)
364   "Set SPEC's function definition to FUNCTION.
365 First element of SPEC is service.
366 Rest of ARGS is like an argument list of lambda, but (car (last ARGS))
367 must be specialized parameter.  (car (car (last ARGS))) is name of
368 variable and (nth 1 (car (last ARGS))) is name of backend (encoding)."
369   (let* ((name (car spec))
370          (args (cdr spec))
371          (specializer (car (last args)))
372          (class (nth 1 specializer)))
373     (` (let (sym)
374          (mel-define-service (, name))
375          (setq sym (intern (, class) (, (intern (format "%s-obarray" name)))))
376          (or (fboundp sym)
377              (fset sym (symbol-function (, function))))))))
378
379 (defmacro mel-define-function (function spec)
380   (let* ((name (car spec))
381          (args (cdr spec))
382          (specializer (car (last args)))
383          (class (nth 1 specializer)))
384     (` (progn
385          (define-function (, function)
386            (intern (, class) (, (intern (format "%s-obarray" name)))))))))
387
388 (defvar base64-dl-module
389   (if (and (fboundp 'base64-encode-string)
390            (subrp (symbol-function 'base64-encode-string)))
391       nil
392     (if (fboundp 'dynamic-link)
393         (let ((path (expand-file-name "base64.so" exec-directory)))
394           (and (file-exists-p path)
395                path)))))
396
397
398 ;;; @ end
399 ;;;
400
401 (provide 'mime-def)
402
403 ;;; mime-def.el ends here