1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package javax.mail.internet;
19
20 import java.util.Collections;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.StringTokenizer;
26 import java.util.List;
27 import java.util.ArrayList;
28
29 import org.apache.geronimo.mail.util.ASCIIUtil;
30 import org.apache.geronimo.mail.util.RFC2231Encoder;
31 import org.apache.geronimo.mail.util.SessionUtil;
32
33
34
35
36
37 /**
38 * @version $Rev: 381393 $ $Date: 2006-02-27 09:38:03 -0800 (Mon, 27 Feb 2006) $
39 */
40 public class ParameterList {
41 private static final String MIME_ENCODEPARAMETERS = "mail.mime.encodeparameters";
42 private static final String MIME_DECODEPARAMETERS = "mail.mime.decodeparameters";
43 private static final String MIME_DECODEPARAMETERS_STRICT = "mail.mime.decodeparameters.strict";
44
45 private static final int HEADER_SIZE_LIMIT = 76;
46
47 private Map _parameters = new HashMap();
48
49 private boolean encodeParameters = false;
50 private boolean decodeParameters = false;
51 private boolean decodeParametersStrict = false;
52
53 public ParameterList() {
54
55 getInitialProperties();
56 }
57
58 public ParameterList(String list) throws ParseException {
59
60 getInitialProperties();
61
62 HeaderTokenizer tokenizer = new HeaderTokenizer(list, HeaderTokenizer.MIME);
63 while (true) {
64 HeaderTokenizer.Token token = tokenizer.next();
65
66 switch (token.getType()) {
67
68 case HeaderTokenizer.Token.EOF:
69 return;
70
71
72
73 case ';':
74
75 token = tokenizer.next();
76
77 if (token.getType() == HeaderTokenizer.Token.EOF) {
78 return;
79 }
80
81 if (token.getType() != HeaderTokenizer.Token.ATOM) {
82 throw new ParseException("Invalid parameter name: " + token.getValue());
83 }
84
85
86 String name = token.getValue().toLowerCase();
87
88 token = tokenizer.next();
89
90
91 if (token.getType() != '=') {
92 throw new ParseException("Missing '='");
93 }
94
95
96 token = tokenizer.next();
97
98 if (token.getType() != HeaderTokenizer.Token.ATOM && token.getType() != HeaderTokenizer.Token.QUOTEDSTRING) {
99 throw new ParseException("Invalid parameter value: " + token.getValue());
100 }
101
102 String value = token.getValue();
103 String encodedValue = null;
104
105
106
107 if (decodeParameters && name.endsWith("*")) {
108
109 name = name.substring(0, name.length() - 1);
110
111 RFC2231Encoder decoder = new RFC2231Encoder(HeaderTokenizer.MIME);
112
113 try {
114
115 encodedValue = decoder.decode(value);
116 } catch (Exception e) {
117
118
119 if (decodeParametersStrict) {
120 throw new ParseException("Invalid RFC2231 encoded parameter");
121 }
122 }
123 }
124 _parameters.put(name, new ParameterValue(name, value, encodedValue));
125
126 break;
127
128 default:
129 throw new ParseException("Missing ';'");
130
131 }
132 }
133 }
134
135 /**
136 * Get the initial parameters that control parsing and values.
137 * These parameters are controlled by System properties.
138 */
139 private void getInitialProperties() {
140 decodeParameters = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS, false);
141 decodeParametersStrict = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS_STRICT, false);
142 encodeParameters = SessionUtil.getBooleanProperty(MIME_ENCODEPARAMETERS, false);
143 }
144
145 public int size() {
146 return _parameters.size();
147 }
148
149 public String get(String name) {
150 ParameterValue value = (ParameterValue)_parameters.get(name.toLowerCase());
151 if (value != null) {
152 return value.value;
153 }
154 return null;
155 }
156
157 public void set(String name, String value) {
158 name = name.toLowerCase();
159 _parameters.put(name, new ParameterValue(name, value));
160 }
161
162 public void remove(String name) {
163 _parameters.remove(name);
164 }
165
166 public Enumeration getNames() {
167 return Collections.enumeration(_parameters.keySet());
168 }
169
170 public String toString() {
171
172 return toString(0);
173 }
174
175 public String toString(int used) {
176 StringBuffer stringValue = new StringBuffer();
177
178 Iterator values = _parameters.values().iterator();
179
180 while (values.hasNext()) {
181 ParameterValue parm = (ParameterValue)values.next();
182
183 String name = parm.getEncodedName();
184 String value = parm.toString();
185
186
187 stringValue.append("; ");
188 used += 2;
189
190
191 if ((used + name.length() + value.length() + 1) > HEADER_SIZE_LIMIT) {
192
193 stringValue.append("\r\n ");
194
195 used = 3;
196 }
197
198 stringValue.append(name);
199 stringValue.append("=");
200
201 used += name.length() + 1;
202
203
204
205 if (used + value.length() > HEADER_SIZE_LIMIT) {
206 String foldedValue = ASCIIUtil.fold(used, value);
207
208 stringValue.append(foldedValue);
209
210
211 int lastLineBreak = foldedValue.lastIndexOf('\n');
212
213 if (lastLineBreak != -1) {
214 used = foldedValue.length() - lastLineBreak + 1;
215 }
216 else {
217 used += foldedValue.length();
218 }
219 }
220 else {
221
222 stringValue.append(value);
223 used += value.length();
224 }
225 }
226
227 return stringValue.toString();
228 }
229
230
231 /**
232 * Utility class for representing parameter values in the list.
233 */
234 class ParameterValue {
235 public String name;
236 public String value;
237 public String encodedValue;
238
239 public ParameterValue(String name, String value) {
240 this.name = name;
241 this.value = value;
242 this.encodedValue = null;
243 }
244
245 public ParameterValue(String name, String value, String encodedValue) {
246 this.name = name;
247 this.value = value;
248 this.encodedValue = encodedValue;
249 }
250
251 public String toString() {
252 if (encodedValue != null) {
253 return MimeUtility.quote(encodedValue, HeaderTokenizer.MIME);
254 }
255 return MimeUtility.quote(value, HeaderTokenizer.MIME);
256 }
257
258 public String getEncodedName() {
259 if (encodedValue != null) {
260 return name + "*";
261 }
262 return name;
263 }
264 }
265 }