KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > MultipartStream


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source 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 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source 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, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.vfs;
30
31 import com.caucho.util.ByteBuffer;
32 import com.caucho.util.CharBuffer;
33
34 import java.io.IOException JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.Iterator JavaDoc;
37
38 /*
39  * A stream for reading multipart/mime files.
40  *
41  * <p/>Each call to openRead() will return the next part and return null
42  * when complete.
43  *
44  * <pre><code>
45  * MultipartStream multi = new MultipartStream(rawIs, boundary);
46  * ReadStream is;
47  *
48  * while ((is = multi.openRead()) != null) {
49  * // read from is as a normal stream
50  * }
51  */

52 public class MultipartStream extends StreamImpl {
53   private ByteBuffer _boundary = new ByteBuffer();
54   private byte []_boundaryBuffer;
55   private int _boundaryLength;
56
57   private ByteBuffer _peekBuffer = new ByteBuffer();
58   private byte []_peek;
59   private int _peekOffset;
60   private int _peekLength;
61
62   private byte []_dummyBuffer = new byte[32];
63   
64   private ReadStream _is;
65   private ReadStream _readStream;
66   private boolean _isPartDone;
67   private boolean _isDone;
68   private boolean _isComplete;
69   private HashMap JavaDoc<String JavaDoc,String JavaDoc> _headers = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
70   private CharBuffer _line = new CharBuffer();
71
72   private String JavaDoc _defaultEncoding;
73
74   public MultipartStream()
75     throws IOException JavaDoc
76   {
77     _boundary = new ByteBuffer();
78   }
79
80   public MultipartStream(ReadStream is, String JavaDoc boundary)
81     throws IOException JavaDoc
82   {
83     this();
84
85     init(is, boundary);
86   }
87
88   /**
89    * Returns the default encoding.
90    */

91   public String JavaDoc getEncoding()
92   {
93     return _defaultEncoding;
94   }
95
96   /**
97    * Sets the default encoding.
98    */

99   public void setEncoding(String JavaDoc encoding)
100   {
101     _defaultEncoding = encoding;
102   }
103
104   /**
105    * Initialize the multipart stream with a given boundary. The boundary
106    * passed to <code>init</code> will have "--" prefixed.
107    *
108    * @param is the underlying stream
109    * @param headerBoundary the multipart/mime boundary.
110    */

111   public void init(ReadStream is, String JavaDoc headerBoundary)
112     throws IOException JavaDoc
113   {
114     _is = is;
115     
116     _boundary.clear();
117     _boundary.add("--");
118     _boundary.add(headerBoundary);
119
120     _boundaryBuffer = _boundary.getBuffer();
121     _boundaryLength = _boundary.getLength();
122
123     _peekBuffer.setLength(_boundaryLength + 5);
124     _peek = _peekBuffer.getBuffer();
125     _peekOffset = 0;
126     _peekLength = 0;
127     _peek[_peekLength++] = (byte) '\n';
128
129     _isPartDone = false;
130     _isDone = false;
131     _isComplete = false;
132     
133     while (read(_dummyBuffer, 0, _dummyBuffer.length) >= 0) {
134     }
135     
136     _isPartDone = true;
137   }
138
139   /**
140    * Returns true if complete.
141    */

142   public boolean isComplete()
143   {
144     return _isComplete;
145   }
146
147   /**
148    * Opens the next part of the multipart/mime stream for reading. Returns
149    * null when the last section is read.
150    */

151   public ReadStream openRead()
152     throws IOException JavaDoc
153   {
154     if (_isDone)
155       return null;
156     else if (_readStream == null)
157       _readStream = new ReadStream(this, null);
158     else if (! _isPartDone) {
159       int len;
160       while ((len = read(_dummyBuffer, 0, _dummyBuffer.length)) >= 0) {
161       }
162
163       if (_isDone)
164         return null;
165     }
166     
167     _readStream.init(this, null);
168
169     _isPartDone = false;
170     
171     if (scanHeaders()) {
172       String JavaDoc contentType = (String JavaDoc) getAttribute("content-type");
173       
174       String JavaDoc charset = getAttributePart(contentType, "charset");
175       
176       if (charset != null)
177         _readStream.setEncoding(charset);
178       else if (_defaultEncoding != null)
179         _readStream.setEncoding(_defaultEncoding);
180       
181       return _readStream;
182     }
183     else {
184       _isDone = true;
185       _readStream.close();
186       return null;
187     }
188   }
189
190   /**
191    * Returns a read attribute from the multipart mime.
192    */

193   public Object JavaDoc getAttribute(String JavaDoc key)
194   {
195     return _headers.get(key.toLowerCase());
196   }
197
198   /**
199    * Returns the headers from the mime.
200    */

201   public Iterator JavaDoc getAttributeNames()
202   {
203     return _headers.keySet().iterator();
204   }
205
206   /**
207    * Scans the mime headers. The mime headers are in standard mail/http
208    * header format: "key: value".
209    */

210   private boolean scanHeaders()
211     throws IOException JavaDoc
212   {
213     int ch = read() ;
214
215     _headers.clear();
216     while (ch > 0 && ch != '\n' && ch != '\r') {
217       _line.clear();
218
219       _line.append((char) ch);
220       for (ch = read();
221            ch >= 0 && ch != '\n' && ch != '\r';
222            ch = read()) {
223         _line.append((char) ch);
224       }
225
226       if (ch == '\r') {
227         if ((ch = read()) == '\n')
228           ch = read();
229       } else if (ch == '\n')
230         ch = read();
231
232       int i = 0;
233       for (; i < _line.length() && _line.charAt(i) != ':'; i++) {
234       }
235
236       String JavaDoc key = null;
237       String JavaDoc value = null;
238       if (i < _line.length()) {
239         key = _line.substring(0, i).trim().toLowerCase();
240         value = _line.substring(i + 1).trim();
241
242         _headers.put(key, value);
243       }
244     }
245     
246     if (ch == '\r') {
247       if ((ch = read()) != '\n') {
248         _peek[0] = (byte) ch;
249         _peekOffset = 0;
250         _peekLength = 1;
251       }
252     }
253
254     return true;
255   }
256   
257   public boolean canRead()
258   {
259     return true;
260   }
261
262   /**
263    * Returns the number of available bytes.
264    */

265   public int getAvailable()
266     throws IOException JavaDoc
267   {
268     if (_isPartDone)
269       return 0;
270     else if (_peekOffset < _peekLength)
271       return _peekLength - _peekOffset;
272     else {
273       int ch = read();
274       if (ch < 0)
275         return 0;
276       _peekOffset = 0;
277       _peekLength = 1;
278       _peek[0] = (byte) ch;
279
280       return 1;
281     }
282   }
283
284   /**
285    * Reads from the multipart mime buffer.
286    */

287   public int read(byte []buffer, int offset, int length) throws IOException JavaDoc
288   {
289     int b = -1;
290
291     if (_isPartDone)
292       return -1;
293
294     int i = 0;
295     // Need the last peek or would miss the initial '\n'
296
while (_peekOffset + 1 < _peekLength && length > 0) {
297       buffer[offset + i++] = _peek[_peekOffset++];
298       length--;
299     }
300
301     while (i < length && (b = read()) >= 0) {
302       boolean hasCr = false;
303
304       if (b == '\r') {
305         hasCr = true;
306         b = read();
307
308         // XXX: Macintosh?
309
if (b != '\n') {
310           buffer[offset + i++] = (byte) '\r';
311           _peek[0] = (byte) b;
312           _peekOffset = 0;
313           _peekLength = 1;
314           continue;
315         }
316       }
317       else if (b != '\n') {
318         buffer[offset + i++] = (byte) b;
319         continue;
320       }
321
322       int j;
323       for (j = 0;
324            j < _boundaryLength && (b = read()) >= 0 && _boundaryBuffer[j] == b;
325            j++) {
326       }
327
328       if (j == _boundaryLength) {
329         _isPartDone = true;
330         if ((b = read()) == '-') {
331           if ((b = read()) == '-') {
332             _isDone = true;
333         _isComplete = true;
334       }
335     }
336     
337         for (; b > 0 && b != '\r' && b != '\n'; b = read()) {
338         }
339         if (b == '\r' && (b = read()) != '\n') {
340           _peek[0] = (byte) b;
341           _peekOffset = 0;
342           _peekLength = 1;
343         }
344
345         return i > 0 ? i : -1;
346       }
347
348       _peekLength = 0;
349       if (hasCr && i + 1 < length) {
350         buffer[offset + i++] = (byte) '\r';
351         buffer[offset + i++] = (byte) '\n';
352       }
353       else if (hasCr) {
354         buffer[offset + i++] = (byte) '\r';
355         _peek[_peekLength++] = (byte) '\n';
356       }
357       else {
358         buffer[offset + i++] = (byte) '\n';
359       }
360
361       int k = 0;
362       while (k < j && i + 1 < length)
363         buffer[offset + i++] = _boundaryBuffer[k++];
364
365       while (k < j)
366         _peek[_peekLength++] = _boundaryBuffer[k++];
367
368       _peek[_peekLength++] = (byte) b;
369       _peekOffset = 0;
370     }
371
372     if (i <= 0) {
373       _isPartDone = true;
374       if (b < 0)
375         _isDone = true;
376       return -1;
377     }
378     else {
379       return i;
380     }
381   }
382
383   /**
384    * Read the next byte from the peek or from the underlying stream.
385    */

386   private int read()
387     throws IOException JavaDoc
388   {
389     if (_peekOffset < _peekLength)
390       return _peek[_peekOffset++] & 0xff;
391     else
392       return _is.read();
393   }
394
395   private static String JavaDoc getAttributePart(String JavaDoc attr, String JavaDoc name)
396   {
397     if (attr == null)
398       return null;
399     
400     int length = attr.length();
401     int i = attr.indexOf(name);
402     if (i < 0)
403       return null;
404
405     for (i += name.length(); i < length && attr.charAt(i) != '='; i++) {
406     }
407     
408     for (i++; i < length && attr.charAt(i) == ' '; i++) {
409     }
410
411     CharBuffer value = CharBuffer.allocate();
412     if (i < length && attr.charAt(i) == '\'') {
413       for (i++; i < length && attr.charAt(i) != '\''; i++)
414         value.append(attr.charAt(i));
415     }
416     else if (i < length && attr.charAt(i) == '"') {
417       for (i++; i < length && attr.charAt(i) != '"'; i++)
418         value.append(attr.charAt(i));
419     }
420     else if (i < length) {
421       char ch;
422       for (; i < length && (ch = attr.charAt(i)) != ' ' && ch != ';'; i++)
423         value.append(ch);
424     }
425
426     return value.close();
427   }
428 }
429
Popular Tags