Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ByteArrayEncoderHTTP |
|
| 2.9473684210526314;2,947 |
1 | // | |
2 | // $Id: FileUploadThreadFTP.java 136 2007-05-12 20:15:36 +0000 (sam., 12 mai | |
3 | // 2007) etienne_sf $ | |
4 | // | |
5 | // jupload - A file upload applet. | |
6 | // Copyright 2007 The JUpload Team | |
7 | // | |
8 | // Created: 2007-12-11 | |
9 | // Creator: etienne_sf | |
10 | // Last modified: $Date: 2007-07-21 09:42:51 +0200 (sam., 21 juil. 2007) $ | |
11 | // | |
12 | // This program is free software; you can redistribute it and/or modify it under | |
13 | // the terms of the GNU General Public License as published by the Free Software | |
14 | // Foundation; either version 2 of the License, or (at your option) any later | |
15 | // version. This program is distributed in the hope that it will be useful, but | |
16 | // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
17 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
18 | // details. You should have received a copy of the GNU General Public License | |
19 | // along with this program; if not, write to the Free Software Foundation, Inc., | |
20 | // 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | package wjhk.jupload2.upload.helper; | |
23 | ||
24 | import java.io.ByteArrayOutputStream; | |
25 | import java.io.IOException; | |
26 | import java.io.OutputStreamWriter; | |
27 | import java.io.UnsupportedEncodingException; | |
28 | import java.io.Writer; | |
29 | ||
30 | import wjhk.jupload2.exception.JUploadIOException; | |
31 | import wjhk.jupload2.policies.UploadPolicy; | |
32 | ||
33 | /** | |
34 | * This class is a utility, which provide easy encoding for HTTP queries. The | |
35 | * way to use this class is: | |
36 | * <OL TYPE=1> | |
37 | * <LI>Instantiate a new object | |
38 | * <LI>Append data to it, using the append methods. Available for: String, | |
39 | * byte[], other ByteArrayEncode... | |
40 | * <LI>Close the stream. This will prevent any new data to be appended to it. | |
41 | * The encoded length can now be calculated. | |
42 | * <LI>Get the encoded length. | |
43 | * <LI>Get the encoded byte array | |
44 | * </OL> | |
45 | * | |
46 | * @author etienne_sf | |
47 | * | |
48 | */ | |
49 | ||
50 | public class ByteArrayEncoderHTTP implements ByteArrayEncoder { | |
51 | ||
52 | /** | |
53 | * The default encoding. It can be retrieved with | |
54 | * {@link #getDefaultEncoding()}. | |
55 | */ | |
56 | final static String DEFAULT_ENCODING = "UTF-8"; | |
57 | ||
58 | /** | |
59 | * The boundary, to put between to post variables. Can not be changed during | |
60 | * the object 'life'. | |
61 | */ | |
62 | 22 | private String bound = null; |
63 | ||
64 | /** | |
65 | * The current encoding. Can not be changed during the object 'life'. | |
66 | */ | |
67 | 22 | private String encoding = DEFAULT_ENCODING; |
68 | ||
69 | /** | |
70 | * Indicate whether the encoder is closed or not. If closed, it's impossible | |
71 | * to append new data to it. If not closed, it's impossible to get the | |
72 | * encoded length or the encoded byte array.<BR> | |
73 | * <B>Note:</B> a closed byte array can not be re-opened. | |
74 | */ | |
75 | 22 | boolean closed = false; |
76 | ||
77 | /** | |
78 | * The actual array, which will collect the encoded bytes. | |
79 | */ | |
80 | 22 | private ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
81 | ||
82 | /** | |
83 | * The byte array length. Calculated when the ByteArrayOutput is closed. | |
84 | */ | |
85 | 22 | private int encodedLength = -1; |
86 | ||
87 | /** | |
88 | * The encoded byte array. Calculated when the ByteArrayOutput is closed. | |
89 | */ | |
90 | 22 | private byte[] encodedByteArray = null; |
91 | ||
92 | /** | |
93 | * The current upload policy. | |
94 | */ | |
95 | private UploadPolicy uploadPolicy; | |
96 | ||
97 | /** | |
98 | * The writer, that will encode the input parameters to {@link #baos}. | |
99 | */ | |
100 | private Writer writer; | |
101 | ||
102 | // /////////////////////////////////////////////////////////////////////// | |
103 | // //////////////// VARIOUS UTILITIES //////////////////////////////////// | |
104 | // /////////////////////////////////////////////////////////////////////// | |
105 | ||
106 | // /////////////////////////////////////////////////////////////////////// | |
107 | // //////////////// CONSTRUCTORS ///////////////////////////////////////// | |
108 | // /////////////////////////////////////////////////////////////////////// | |
109 | ||
110 | /** | |
111 | * Create an encoder, using the DEFAULT_ENCODING encoding. | |
112 | * | |
113 | * @param uploadPolicy The current upload policy | |
114 | * @throws JUploadIOException Any IO exception | |
115 | */ | |
116 | public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy) | |
117 | 4 | throws JUploadIOException { |
118 | 4 | init(uploadPolicy, null, DEFAULT_ENCODING); |
119 | 4 | } |
120 | ||
121 | /** | |
122 | * Create an encoder, and specifies the encoding to use. | |
123 | * | |
124 | * @param uploadPolicy The current upload policy | |
125 | * @param bound Any specific boundary. May be null: in this case a default | |
126 | * boundary is used. | |
127 | * @throws JUploadIOException Any IO exception | |
128 | */ | |
129 | public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy, String bound) | |
130 | 2 | throws JUploadIOException { |
131 | 2 | init(uploadPolicy, bound, DEFAULT_ENCODING); |
132 | 2 | } |
133 | ||
134 | /** | |
135 | * Create an encoder, and specifies the boundary and encoding to use. | |
136 | * | |
137 | * @param uploadPolicy The current upload policy | |
138 | * @param bound Any specific boundary. May be null: in this case a default | |
139 | * boundary is used. | |
140 | * @param encoding The encoding to use. For instance, "UTF-8". | |
141 | * @throws JUploadIOException Any IO exception | |
142 | */ | |
143 | public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy, String bound, | |
144 | 16 | String encoding) throws JUploadIOException { |
145 | 16 | init(uploadPolicy, bound, encoding); |
146 | 16 | } |
147 | ||
148 | // /////////////////////////////////////////////////////////////////////// | |
149 | // //////////////// Public methods /////////////////////////////////////// | |
150 | // /////////////////////////////////////////////////////////////////////// | |
151 | ||
152 | /** | |
153 | * @throws JUploadIOException | |
154 | * @see wjhk.jupload2.upload.helper.ByteArrayEncoder#close() | |
155 | */ | |
156 | synchronized public void close() throws JUploadIOException { | |
157 | 12 | if (isClosed()) { |
158 | 0 | throw new JUploadIOException( |
159 | "Trying to close an already closed ByteArrayEncoded"); | |
160 | } | |
161 | try { | |
162 | 12 | this.writer.close(); |
163 | 0 | } catch (IOException e) { |
164 | 0 | throw new JUploadIOException(e); |
165 | 12 | } |
166 | 12 | this.encodedByteArray = this.baos.toByteArray(); |
167 | 12 | this.encodedLength = this.encodedByteArray.length; |
168 | 12 | this.closed = true; |
169 | 12 | } |
170 | ||
171 | /** {@inheritDoc} */ | |
172 | public ByteArrayEncoder append(String str) throws JUploadIOException { | |
173 | try { | |
174 | 19 | this.writer.append(str); |
175 | 0 | } catch (IOException e) { |
176 | 0 | throw new JUploadIOException(e); |
177 | 19 | } |
178 | // Returning the encoder allows calls like: | |
179 | // bae.append("qdqd").append("qsldqd"); (like StringBuffer) | |
180 | 19 | return this; |
181 | } | |
182 | ||
183 | /** {@inheritDoc} */ | |
184 | public ByteArrayEncoder append(int b) throws JUploadIOException { | |
185 | try { | |
186 | 1 | this.writer.flush(); |
187 | 1 | this.baos.write(b); |
188 | 0 | } catch (IOException e) { |
189 | 0 | throw new JUploadIOException(e); |
190 | 1 | } |
191 | // Returning the encoder allows calls like: | |
192 | // bae.append("qdqd").append("qsldqd"); (like StringBuffer) | |
193 | 1 | return this; |
194 | } | |
195 | ||
196 | /** {@inheritDoc} */ | |
197 | public ByteArrayEncoder append(byte[] b) throws JUploadIOException { | |
198 | try { | |
199 | 2 | this.writer.flush(); |
200 | 2 | this.baos.write(b); |
201 | 0 | } catch (IOException e) { |
202 | 0 | throw new JUploadIOException(e); |
203 | 2 | } |
204 | // Returning the encoder allows calls like: | |
205 | // bae.append("qdqd").append("qsldqd"); (like StringBuffer) | |
206 | 2 | return this; |
207 | } | |
208 | ||
209 | /** {@inheritDoc} */ | |
210 | public ByteArrayEncoder append(ByteArrayEncoder bae) | |
211 | throws JUploadIOException { | |
212 | 1 | this.append(bae.getEncodedByteArray()); |
213 | ||
214 | 1 | return this; |
215 | } | |
216 | ||
217 | /** {@inheritDoc} */ | |
218 | public ByteArrayEncoder appendTextProperty(String name, String value, | |
219 | int index) throws JUploadIOException { | |
220 | 1 | String propertySuffix = ""; |
221 | // if an index is given, we may have to suffix the name of the upload | |
222 | // parameter, depending on the value of httpUploadParameterType | |
223 | 1 | if (index >= 0) { |
224 | 0 | if (this.uploadPolicy.getHttpUploadParameterType().equals( |
225 | UploadPolicy.HTTPUPLOADPARAMETERTYPE_ARRAY)) { | |
226 | 0 | propertySuffix = "[]"; |
227 | 0 | } else if (this.uploadPolicy.getHttpUploadParameterType().equals( |
228 | UploadPolicy.HTTPUPLOADPARAMETERTYPE_ITERATION)) { | |
229 | 0 | propertySuffix = Integer.toString(index); |
230 | } | |
231 | } | |
232 | 1 | this.append(this.bound).append("\r\n"); |
233 | 1 | this.append("Content-Disposition: form-data; name=\"").append(name) |
234 | 1 | .append(propertySuffix).append("\"\r\n"); |
235 | 1 | this.append("Content-Transfer-Encoding: 8bit\r\n"); |
236 | 1 | this.append("Content-Type: text/plain; charset=").append(this.getEncoding()) |
237 | 1 | .append("\r\n"); |
238 | // An empty line before the actual value. | |
239 | 1 | this.append("\r\n"); |
240 | // And then, the value! | |
241 | 1 | this.append(value).append("\r\n"); |
242 | ||
243 | 1 | return this; |
244 | } | |
245 | ||
246 | /** {@inheritDoc} */ | |
247 | public ByteArrayEncoder appendEndPropertyList() throws JUploadIOException { | |
248 | 1 | this.append(this.bound).append("--\r\n"); |
249 | 1 | return this; |
250 | } | |
251 | ||
252 | /** {@inheritDoc} */ | |
253 | @SuppressWarnings("restriction") | |
254 | public ByteArrayEncoder appendFormVariables(String formname) | |
255 | throws JUploadIOException { | |
256 | 0 | String action = "Entering ByteArrayEncoderHTTP.appendFormVariables() [html form: " |
257 | + formname + "]"; | |
258 | try { | |
259 | 0 | this.uploadPolicy.displayDebug(action, 80); |
260 | // TODO Do not use getApplet() anymore, here. | |
261 | 0 | action = "win = netscape.javascript.JSObject.getWindow"; |
262 | 0 | this.uploadPolicy.displayDebug("Before " + action + |
263 | " (this.uploadPolicy.getContext().getApplet(): " | |
264 | 0 | + this.uploadPolicy.getContext().getApplet() + ")" |
265 | , 80); | |
266 | 0 | netscape.javascript.JSObject win = netscape.javascript.JSObject |
267 | 0 | .getWindow(this.uploadPolicy.getContext().getApplet()); |
268 | 0 | this.uploadPolicy.displayDebug("After " + action + |
269 | " (win: " + win + ")", 80); | |
270 | ||
271 | 0 | action = "o = win.eval"; |
272 | 0 | Object o = win.eval("document.forms[\"" + formname |
273 | + "\"].elements.length"); | |
274 | 0 | if (o instanceof Number) { |
275 | 0 | int len = ((Number) o).intValue(); |
276 | 0 | if (len <= 0) { |
277 | 0 | this.uploadPolicy.displayWarn("The specified form \"" |
278 | + formname + "\" does not contain any elements."); | |
279 | } | |
280 | int i; | |
281 | 0 | for (i = 0; i < len; i++) { |
282 | try { | |
283 | 0 | action = "name = win.eval"; |
284 | 0 | Object name = win.eval("document.forms[\"" + formname |
285 | + "\"][" + i + "].name"); | |
286 | 0 | action = "value = win.eval"; |
287 | 0 | Object value = win.eval("document.forms[\"" + formname |
288 | + "\"][" + i + "].value"); | |
289 | 0 | action = "elementType = win.eval"; |
290 | 0 | Object elementType = win.eval("document.forms[\"" |
291 | + formname + "\"][" + i + "].type"); | |
292 | 0 | action = "elementClass = win.eval"; |
293 | // elementClass seems to be not supported by IE7 | |
294 | // The next line prevents formData to be sent to the | |
295 | // server, when formData is used on IE7. Works fine with | |
296 | // firefox. | |
297 | // Object elementClass = win.eval("document." + formname | |
298 | // + "[" + i + "].class"); | |
299 | 0 | action = "etype = win.eval"; |
300 | 0 | Object etype = win.eval("document.forms[\"" + formname |
301 | + "\"][" + i + "].type"); | |
302 | 0 | if (etype instanceof String) { |
303 | 0 | String t = (String) etype; |
304 | 0 | if (t.equals("checkbox") || t.equals("radio")) { |
305 | 0 | action = "on = win.eval"; |
306 | 0 | Object on = win.eval("document.forms[\"" |
307 | + formname + "\"][" + i + "].checked"); | |
308 | 0 | if (on instanceof Boolean) { |
309 | // Skip unchecked checkboxes and | |
310 | // radiobuttons | |
311 | 0 | if (!((Boolean) on).booleanValue()) { |
312 | 0 | this.uploadPolicy |
313 | 0 | .displayDebug( |
314 | " [ByteArrayEncoder.appendFormVariables] Skipping unchecked checkboxes and radiobuttons", | |
315 | 80); | |
316 | // TODO Do not use getApplet() anymore, | |
317 | // here. | |
318 | 0 | continue; |
319 | } | |
320 | } | |
321 | } | |
322 | } | |
323 | 0 | if (name instanceof String |
324 | 0 | && ((String) name).length() > 0) { |
325 | 0 | if (value instanceof String) { |
326 | 0 | this.uploadPolicy.displayDebug( |
327 | " [ByteArrayEncoder.appendFormVariables] Adding formdata element num " | |
328 | + i + " (name: " + name | |
329 | + ", value: " + value | |
330 | + ", type: " + elementType | |
331 | /* + ", class: " + elementClass */ | |
332 | + ")", 80); | |
333 | 0 | this.appendTextProperty((String) name, |
334 | (String) value, -1); | |
335 | } else { | |
336 | 0 | this.uploadPolicy |
337 | 0 | .displayWarn(" [ByteArrayEncoder.appendFormVariables] Value must be an instance of String (name: " |
338 | + name | |
339 | + ", value: )" | |
340 | + value | |
341 | + ")"); | |
342 | } | |
343 | } else { | |
344 | 0 | this.uploadPolicy |
345 | 0 | .displayWarn(" [ByteArrayEncoder.appendFormVariables] Name must be an instance of String (name: " |
346 | + name + ", value: )" + value + ")"); | |
347 | } | |
348 | 0 | } catch (netscape.javascript.JSException e1) { |
349 | 0 | this.uploadPolicy |
350 | 0 | .displayWarn(e1.getStackTrace()[1] |
351 | + ": got JSException in ByteArrayEncoderHTTP.appendFormVariables() [html form: " | |
352 | + formname | |
353 | + "] - bailing out (action: " + action | |
354 | + ")"); | |
355 | 0 | i = len; |
356 | 0 | } |
357 | } | |
358 | 0 | } else { |
359 | 0 | this.uploadPolicy |
360 | 0 | .displayWarn(" [ByteArrayEncoder.appendFormVariables] The specified form \"" |
361 | + formname | |
362 | + "\" could not be found (or has no element)."); | |
363 | } | |
364 | 0 | } catch (netscape.javascript.JSException e) { |
365 | 0 | this.uploadPolicy.displayDebug(e.getStackTrace()[1] |
366 | + ": No JavaScript available (action: " + action + ")", 10); | |
367 | 0 | } |
368 | ||
369 | 0 | this.uploadPolicy |
370 | 0 | .displayDebug( |
371 | " [ByteArrayEncoder.appendFormVariables] End of ByteArrayEncoderHTTP.appendFormVariables()", | |
372 | 80); | |
373 | 0 | return this; |
374 | } | |
375 | ||
376 | /** {@inheritDoc} */ | |
377 | public String getBoundary() { | |
378 | 3 | return this.bound; |
379 | } | |
380 | ||
381 | /** | |
382 | * @return value of the DEFAULT_ENCODING constant. | |
383 | */ | |
384 | public static String getDefaultEncoding() { | |
385 | 1 | return DEFAULT_ENCODING; |
386 | } | |
387 | ||
388 | /** {@inheritDoc} */ | |
389 | public boolean isClosed() { | |
390 | 23 | return this.closed; |
391 | } | |
392 | ||
393 | /** {@inheritDoc} */ | |
394 | public String getEncoding() { | |
395 | 17 | return this.encoding; |
396 | } | |
397 | ||
398 | /** {@inheritDoc} */ | |
399 | public int getEncodedLength() throws JUploadIOException { | |
400 | 1 | if (!isClosed()) { |
401 | 0 | throw new JUploadIOException( |
402 | "Trying to get length of a on non-closed ByteArrayEncoded"); | |
403 | } | |
404 | 1 | return this.encodedLength; |
405 | } | |
406 | ||
407 | /** {@inheritDoc} */ | |
408 | public byte[] getEncodedByteArray() throws JUploadIOException { | |
409 | 9 | if (!isClosed()) { |
410 | 1 | throw new JUploadIOException( |
411 | "Trying to get the byte array of a on non-closed ByteArrayEncoded"); | |
412 | } | |
413 | 8 | return this.encodedByteArray; |
414 | } | |
415 | ||
416 | /** {@inheritDoc} */ | |
417 | public String getString() throws JUploadIOException { | |
418 | 0 | if (!isClosed()) { |
419 | 0 | throw new JUploadIOException( |
420 | "Trying to get the byte array of a on non-closed ByteArrayEncoded"); | |
421 | } | |
422 | try { | |
423 | 0 | return new String(this.encodedByteArray, getEncoding()); |
424 | 0 | } catch (UnsupportedEncodingException e) { |
425 | 0 | throw new JUploadIOException(e); |
426 | } | |
427 | } | |
428 | ||
429 | // /////////////////////////////////////////////////////////////////////// | |
430 | // //////////////// Private methods ////////////////////////////////////// | |
431 | // /////////////////////////////////////////////////////////////////////// | |
432 | ||
433 | /** | |
434 | * Initialization: called by the constructors. | |
435 | * | |
436 | * @throws JUploadIOException | |
437 | */ | |
438 | private void init(UploadPolicy uploadPolicy, String bound, String encoding) | |
439 | throws JUploadIOException { | |
440 | 22 | this.uploadPolicy = uploadPolicy; |
441 | 22 | this.encoding = encoding; |
442 | 22 | this.bound = bound; |
443 | ||
444 | try { | |
445 | 22 | this.writer = new OutputStreamWriter(this.baos, encoding); |
446 | 0 | } catch (UnsupportedEncodingException e) { |
447 | 0 | throw new JUploadIOException(e); |
448 | 22 | } |
449 | 22 | } |
450 | } |