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