* gnus-art.el (article-decode-charset): Don't use ignore-errors when calling
[gnus] / lisp / rfc2231.el
1 ;;; rfc2231.el --- Functions for decoding rfc2231 headers
2
3 ;; Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005,
4 ;;   2006 Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (eval-when-compile (require 'cl))
29 (require 'ietf-drums)
30 (require 'rfc2047)
31 (autoload 'mm-encode-body "mm-bodies")
32 (autoload 'mail-header-remove-whitespace "mail-parse")
33 (autoload 'mail-header-remove-comments "mail-parse")
34
35 (defun rfc2231-get-value (ct attribute)
36   "Return the value of ATTRIBUTE from CT."
37   (cdr (assq attribute (cdr ct))))
38
39 (defun rfc2231-parse-qp-string (string)
40   "Parse QP-encoded string using `rfc2231-parse-string'.
41 N.B.  This is in violation with RFC2047, but it seem to be in common use."
42   (rfc2231-parse-string (rfc2047-decode-string string)))
43
44 (defun rfc2231-parse-string (string &optional signal-error)
45   "Parse STRING and return a list.
46 The list will be on the form
47  `(name (attribute . value) (attribute . value)...)'.
48
49 If the optional SIGNAL-ERROR is non-nil, signal an error when this
50 function fails in parsing of parameters."
51   (with-temp-buffer
52     (let ((ttoken (ietf-drums-token-to-list ietf-drums-text-token))
53           (stoken (ietf-drums-token-to-list ietf-drums-tspecials))
54           (ntoken (ietf-drums-token-to-list "0-9"))
55           (prev-value "")
56           display-name mailbox c display-string parameters
57           attribute value type subtype number encoded
58           prev-attribute prev-encoded)
59       (ietf-drums-init (mail-header-remove-whitespace
60                         (mail-header-remove-comments string)))
61       (let ((table (copy-syntax-table ietf-drums-syntax-table)))
62         (modify-syntax-entry ?\' "w" table)
63         (modify-syntax-entry ?* " " table)
64         (modify-syntax-entry ?\; " " table)
65         (modify-syntax-entry ?= " " table)
66         ;; The following isn't valid, but one should be liberal
67         ;; in what one receives.
68         (modify-syntax-entry ?\: "w" table)
69         (set-syntax-table table))
70       (setq c (char-after))
71       (when (and (memq c ttoken)
72                  (not (memq c stoken)))
73         (setq type (downcase (buffer-substring
74                               (point) (progn (forward-sexp 1) (point)))))
75         ;; Do the params
76         (condition-case err
77             (progn
78               (while (not (eobp))
79                 (setq c (char-after))
80                 (unless (eq c ?\;)
81                   (error "Invalid header: %s" string))
82                 (forward-char 1)
83                 ;; If c in nil, then this is an invalid header, but
84                 ;; since elm generates invalid headers on this form,
85                 ;; we allow it.
86                 (when (setq c (char-after))
87                   (if (and (memq c ttoken)
88                            (not (memq c stoken)))
89                       (setq attribute
90                             (intern
91                              (downcase
92                               (buffer-substring
93                                (point) (progn (forward-sexp 1) (point))))))
94                     (error "Invalid header: %s" string))
95                   (setq c (char-after))
96                   (when (eq c ?*)
97                     (forward-char 1)
98                     (setq c (char-after))
99                     (if (not (memq c ntoken))
100                         (setq encoded t
101                               number nil)
102                       (setq number
103                             (string-to-number
104                              (buffer-substring
105                               (point) (progn (forward-sexp 1) (point)))))
106                       (setq c (char-after))
107                       (when (eq c ?*)
108                         (setq encoded t)
109                         (forward-char 1)
110                         (setq c (char-after)))))
111                   ;; See if we have any previous continuations.
112                   (when (and prev-attribute
113                              (not (eq prev-attribute attribute)))
114                     (push (cons prev-attribute
115                                 (if prev-encoded
116                                     (rfc2231-decode-encoded-string prev-value)
117                                   prev-value))
118                           parameters)
119                     (setq prev-attribute nil
120                           prev-value ""
121                           prev-encoded nil))
122                   (unless (eq c ?=)
123                     (error "Invalid header: %s" string))
124                   (forward-char 1)
125                   (setq c (char-after))
126                   (cond
127                    ((eq c ?\")
128                     (setq value (buffer-substring (1+ (point))
129                                                   (progn
130                                                     (forward-sexp 1)
131                                                     (1- (point))))))
132                    ((and (or (memq c ttoken)
133                              ;; EXTENSION: Support non-ascii chars.
134                              (> c ?\177))
135                          (not (memq c stoken)))
136                     (setq value
137                           (buffer-substring
138                            (point)
139                            (progn
140                              (forward-sexp)
141                              ;; We might not have reached at the end of
142                              ;; the value because of non-ascii chars,
143                              ;; so we should jump over them if any.
144                              (while (and (not (eobp))
145                                          (> (char-after) ?\177))
146                                (forward-char 1)
147                                (forward-sexp))
148                              (point)))))
149                    (t
150                     (error "Invalid header: %s" string)))
151                   (if number
152                       (setq prev-attribute attribute
153                             prev-value (concat prev-value value)
154                             prev-encoded encoded)
155                     (push (cons attribute
156                                 (if encoded
157                                     (rfc2231-decode-encoded-string value)
158                                   value))
159                           parameters))))
160
161               ;; Take care of any final continuations.
162               (when prev-attribute
163                 (push (cons prev-attribute
164                             (if prev-encoded
165                                 (rfc2231-decode-encoded-string prev-value)
166                               prev-value))
167                       parameters)))
168           (error
169            (setq parameters nil)
170            (if signal-error
171                (signal (car err) (cdr err))
172              ;;(message "%s" (error-message-string err))
173              )))
174
175         (when type
176           `(,type ,@(nreverse parameters)))))))
177
178 (defun rfc2231-decode-encoded-string (string)
179   "Decode an RFC2231-encoded string.
180 These look like \"us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A\"."
181   (with-temp-buffer
182     (let ((elems (split-string string "'")))
183       ;; The encoded string may contain zero to two single-quote
184       ;; marks.  This should give us the encoded word stripped
185       ;; of any preceding values.
186       (insert (car (last elems)))
187       (goto-char (point-min))
188       (while (search-forward "%" nil t)
189         (insert
190          (prog1
191              (string-to-number (buffer-substring (point) (+ (point) 2)) 16)
192            (delete-region (1- (point)) (+ (point) 2)))))
193       ;; Encode using the charset, if any.
194       (when (and (mm-multibyte-p)
195                  (> (length elems) 1)
196                  (not (equal (intern (downcase (car elems))) 'us-ascii)))
197         (mm-decode-coding-region (point-min) (point-max)
198                                  (intern (downcase (car elems)))))
199       (buffer-string))))
200
201 (defun rfc2231-encode-string (param value)
202   "Return and PARAM=VALUE string encoded according to RFC2231.
203 Use `mml-insert-parameter' or `mml-insert-parameter-string' to insert
204 the result of this function."
205   (let ((control (ietf-drums-token-to-list ietf-drums-no-ws-ctl-token))
206         (tspecial (ietf-drums-token-to-list ietf-drums-tspecials))
207         (special (ietf-drums-token-to-list "*'%\n\t"))
208         (ascii (ietf-drums-token-to-list ietf-drums-text-token))
209         (num -1)
210         ;; Don't make lines exceeding 76 column.
211         (limit (- 74 (length param)))
212         spacep encodep charsetp charset broken)
213     (with-temp-buffer
214       (insert value)
215       (goto-char (point-min))
216       (while (not (eobp))
217         (cond
218          ((or (memq (following-char) control)
219               (memq (following-char) tspecial)
220               (memq (following-char) special))
221           (setq encodep t))
222          ((eq (following-char) ? )
223           (setq spacep t))
224          ((not (memq (following-char) ascii))
225           (setq charsetp t)))
226         (forward-char 1))
227       (when charsetp
228         (setq charset (mm-encode-body)))
229       (cond
230        ((or encodep charsetp
231             (progn
232               (end-of-line)
233               (> (current-column) (if spacep (- limit 2) limit))))
234         (setq limit (- limit 6))
235         (goto-char (point-min))
236         (insert (symbol-name (or charset 'us-ascii)) "''")
237         (while (not (eobp))
238           (if (or (not (memq (following-char) ascii))
239                   (memq (following-char) control)
240                   (memq (following-char) tspecial)
241                   (memq (following-char) special)
242                   (eq (following-char) ? ))
243               (progn
244                 (when (>= (current-column) (1- limit))
245                   (insert ";\n")
246                   (setq broken t))
247                 (insert "%" (format "%02x" (following-char)))
248                 (delete-char 1))
249             (when (> (current-column) limit)
250               (insert ";\n")
251               (setq broken t))
252             (forward-char 1)))
253         (goto-char (point-min))
254         (if (not broken)
255             (insert param "*=")
256           (while (not (eobp))
257             (insert (if (>= num 0) " " "")
258                     param "*" (format "%d" (incf num)) "*=")
259             (forward-line 1))))
260        (spacep
261         (goto-char (point-min))
262         (insert param "=\"")
263         (goto-char (point-max))
264         (insert "\""))
265        (t
266         (goto-char (point-min))
267         (insert param "=")))
268       (buffer-string))))
269
270 (provide 'rfc2231)
271
272 ;;; arch-tag: c3ab751d-d108-406a-b301-68882ad8cd63
273 ;;; rfc2231.el ends here