KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > mime > MultipartMimeInputStream


1
2 /*
3  * Enhydra Java Application Server Project
4  *
5  * The contents of this file are subject to the Enhydra Public License
6  * Version 1.1 (the "License"); you may not use this file except in
7  * compliance with the License. You may obtain a copy of the License on
8  * the Enhydra web site ( http://www.enhydra.org/ ).
9  *
10  * Software distributed under the License is distributed on an "AS IS"
11  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
12  * the License for the specific terms governing rights and limitations
13  * under the License.
14  *
15  * The Initial Developer of the Enhydra Application Server is Lutris
16  * Technologies, Inc. The Enhydra Application Server and portions created
17  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
18  * All Rights Reserved.
19  *
20  * Contributor(s):
21  *
22  * $Id: MultipartMimeInputStream.java,v 1.4 2005/03/24 10:51:25 slobodan Exp $
23  */

24
25
26
27
28 package com.lutris.mime;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.util.Hashtable JavaDoc;
32
33 import com.lutris.util.BMByteSearch;
34 import com.lutris.util.BMByteSearchStream;
35
36 /**
37  * Presents the current section of a multipart MIME stream as a distinct
38  * InputStream object for which the end of the section is the end of the
39  * stream. MIME headers for the current section are extracted, parsed,
40  * and made available via the <code>getMimeHeaders()<code> method. The
41  * stream itself begins at the first byte following the Mime header
42  * section. Closing an instance of <code>MultipartMimeInputStream</code>
43  * causes it to skip data on the underlying input stream until the next
44  * section is found, or end-of-input is reached.
45  */

46 public class MultipartMimeInputStream extends InputStream JavaDoc
47 {
48     /**
49      * Boyer-Moore streaming pattern searcher that represents the input
50      * source. It searches its InputStream, up to a specified pattern,
51      * returning the read data until the pattern is reached.
52      */

53     private BMByteSearchStream inputSource;
54
55     /**
56      * The stream is closed if <code>true</code> or not closed if
57      * <code>false</code>.
58      */

59     private boolean closed;
60
61     /**
62      * Precomputed separator pattern search object.
63      */

64     private BMByteSearch searchPattern;
65
66     /**
67      * Precomputed BM search pattern for "\n". This should only
68      * ever be computed once.
69      */

70     private static BMByteSearch newlinePattern = null;
71
72     /**
73      * Byte to read into for single-byte read() operation.
74      */

75     private byte[] readByte = new byte[1];
76
77     /**
78      * EOF indicated to prevent skipping end marker twice.
79      */

80     private boolean atEOF = false;
81
82     /**
83      * Array of raw, unparsed Mime headers.
84      */

85     private String JavaDoc[] rawHeaders = null;
86
87     /**
88      * Array of unparseable headers.
89      */

90     private String JavaDoc[] garbageHeaders = null;
91
92     /**
93      * Table of all parsed headers.
94      */

95     private Hashtable JavaDoc headers = null;
96
97     /**
98      * Boolean that indicates that this input stream was the
99      * last part of the total multipart input. This field is only to
100      * be accessed by the MultipartMimeInput object.
101      */

102     boolean lastPart = false;
103
104     /**
105      * Creates a MultipartFormStream object from the given pattern search
106      * stream object, separator, and end of line pattern. This constructor
107      * may only be called internally from within
108      * <code>MultipartMimeInput</code> or its derived subclasses.
109      * Other packages never create instances of this class directly.
110      *
111      * @param source Input stream that can perform a
112      * pattern serarch on the raw input stream.
113      * @param sep Separator pattern.
114      * @exception IOException If an I/O error occurs.
115      * @exception MimeEOFException If at EOF and the current
116      * section does not exist.
117      */

118     protected
119     MultipartMimeInputStream(BMByteSearchStream source, BMByteSearch sep)
120     throws IOException JavaDoc, MimeEOFException
121     {
122     int rawPos=0, garbagePos=0;
123     int n;
124     byte[] lineBuf = new byte[2048];
125     inputSource = source;
126     searchPattern = sep;
127
128     // Create hash table for headers.
129
headers = new Hashtable JavaDoc();
130
131     // Only create the newline pattern once.
132
if (newlinePattern == null) {
133         newlinePattern = new BMByteSearch("\n");
134     }
135     inputSource.setPattern(newlinePattern);
136
137     //
138
// Read and process the MIME header up to and including
139
// the terminating blank line.
140
//
141
outer: while ((n = readAll(lineBuf)) > 0) {
142         if (lineBuf[n-1] == '\r') --n; // Handle CR+LF case.
143
if (n <= 0) break outer; // ..and recheck length.
144
String JavaDoc line = new String JavaDoc(bytesToChars(lineBuf,0,n));
145         String JavaDoc[] newRaw = new String JavaDoc[rawPos + 1];
146         for (int i=0; i<rawPos; i++) {
147         newRaw[i] = rawHeaders[i];
148         rawHeaders[i] = null;
149         }
150         newRaw[rawPos++] = line;
151         rawHeaders = newRaw; newRaw = null;
152         try {
153         
154         /*
155         * Michael Strapp patch:
156         * Parameters are now being picked up from the headers
157         */

158         //MimeHeader header = new MimeHeader(line);
159
MimeHeader header = new ContentHeader(line);
160         
161         if ((header.getValue() == null)
162             || (header.getHeaderType() == null))
163         {
164             // Not a parseable Mime header. Save under garbage
165
// collection key in case caller wants to try to parse it.
166
String JavaDoc[] newGarbage = new String JavaDoc[garbagePos + 1];
167             for (int i=0; i<garbagePos; i++) {
168             newGarbage[i] = garbageHeaders[i];
169             garbageHeaders[i] = null;
170             }
171             newGarbage[garbagePos++] = line;
172             garbageHeaders = newGarbage; newGarbage = null;
173         } else {
174             String JavaDoc name = header.getHeaderType().toLowerCase();
175             MimeHeader[] hdrs =(MimeHeader[]) headers.get(name);
176             int num = (hdrs == null) ? 0 : hdrs.length;
177             MimeHeader[] newHdrs = new MimeHeader[num+1];
178             for (int i=0; i<num; i++) {
179             newHdrs[i] = hdrs[i];
180             hdrs[i] = null;
181             }
182             newHdrs[num] = header;
183             hdrs = null;
184             headers.put(name, newHdrs);
185         }
186         } catch (Exception JavaDoc e) {
187         throw new IOException JavaDoc("Error while reading " +
188             "MultipartMimeInputStream: " + e.toString());
189         }
190     } /* outer */
191     // Hack: the last part detection code would wrongly detect
192
// this part as the last one if it begins with "--" if we didn't
193
// reset this flag after headers parsing.
194
lastPart = false;
195     // Now, begin reading the stream.
196
inputSource.setPattern(searchPattern);
197     }
198
199     /**
200      * Reads the next byte of data from this input stream. The value byte
201      * is returned as an <code>int</code> in the range 0 to 255. If no
202      * byte is available because the end of the stream has been reached,
203      * the value -1 is returned. This method blocks until input data is
204      * available, the end of the stream is detected, or an exception is
205      * thrown
206      *
207      * @return The next byte of data, or -1 if the end
208      * of stream is reached.
209      * @exception IOException If an I/O error occurs.
210      */

211     public
212     int read()
213     throws IOException JavaDoc
214     {
215     int len = read(readByte, 0, 1);
216     if (len != 1) return -1;
217     return ((int)readByte[0] + 0x100) & 0xff;
218     }
219
220     /**
221      * Reads up to <code>buffer.length</code> bytes of data from this input
222      * stream into an array of bytes. This method blocks until some input
223      * is available
224      *
225      * @param buffer The buffer into which data are read.
226      * @return The number of bytes actually read, or -1
227      * if there are no more bytes because the
228      * end of stream has been reached.
229      * @exception IOException If an I/O error occurs.
230      */

231     public
232     int read(byte[] buffer)
233     throws IOException JavaDoc
234     {
235     return(read(buffer, 0, buffer.length));
236     }
237
238     /**
239      * Reads <code>length</code> bytes of data from this input stream into
240      * an array of bytes. This method blocks until some input is available.
241      *
242      * @param buffer The buffer into which data are read.
243      * @param offset The start offset of the data.
244      * @param length The maximum number of bytes read.
245      * @return The total number of bytes read into the
246      * buffer, or -1 if there are no more bytes
247      * because the end of stream has been reached.
248      * @exception IOException If an I/O error occurs.
249      */

250     public
251     int read(byte[] buffer, int offset, int length)
252     throws IOException JavaDoc
253     {
254     checkOpen();
255     int ret = inputSource.readTo(buffer, offset, length);
256     switch (ret) {
257         case BMByteSearchStream.AT_PATTERN:
258         inputSource.skipPattern();
259         if (inputSource.peekAheadString(2).equals("--")) lastPart=true;
260         inputSource.setPattern(newlinePattern);
261         inputSource.skipPattern();
262         atEOF = true;
263         return(-1);
264         case BMByteSearchStream.EOF:
265         return(-1);
266         default:
267         if (ret < 0) return -1;
268         return(ret);
269     }
270     }
271
272     /**
273      * Skips over and discards <code>n</code> bytes of data from this
274      * input stream. The <code>skip</code> method may, for a variety of
275      * reasons, end up skipping over some smaller number of bytes,
276      * possibly <code>0</code>. The actual number of bytes skipped is
277      * returned.
278      *
279      * @param num The number of bytes to be skipped.
280      * @return The actual number of bytes skipped.
281      *
282      * @exception IOException If an I/O error occurs.
283      */

284     public
285     long skip(long num)
286     throws IOException JavaDoc
287     {
288     checkOpen();
289     byte[] buf = new byte[1024];
290     long count=0, n;
291     while (count < num) {
292         if (num - count > 1024)
293         n = this.read(buf, 0, 1024);
294         else
295         n = this.read(buf, 0, (int)(num - count));
296         if (n < 0) break;
297         count += n;
298     }
299     return count;
300     }
301
302     /**
303      * Returns the number of bytes that can be read from this stream
304      * without blocking.
305      *
306      * @return The number of bytes that can be read
307      * from this stream without blocking.
308      *
309      * @exception IOException If an I/O error occurs.
310      */

311     public
312     int available()
313     throws IOException JavaDoc
314     {
315     checkOpen();
316    return(inputSource.availableTo());
317     //return(inputSource.available());
318
}
319
320     /**
321      * Skips all remaining bytes on this stream and closes it. Further
322      * operations on this stream, other than <code>close()</code> will
323      * cause an <code>IOException</code> to be thrown.
324      *
325      * @exception IOException If an I/O error occurs.
326      */

327     public
328     void close()
329     throws IOException JavaDoc
330     {
331     if (!atEOF) {
332         inputSource.setPattern(searchPattern);
333         inputSource.skipPattern();
334         if (inputSource.peekAheadString(2).equals("--")) lastPart=true;
335         inputSource.setPattern(newlinePattern);
336         inputSource.skipPattern();
337     }
338     atEOF = true;
339     closed = true;
340     }
341
342     /**
343      * Returns a single <code>MimeHeader</code> object associated with
344      * a given Mime header name. If the selected header name is associated
345      * with more than one value, then the last instance received from the input
346      * stream is returned.
347      *
348      * @param headerName The name of the Mime header to return.
349      * @return The value of the header, or <code>null</code> if not found.
350      */

351     public
352     MimeHeader getHeader(String JavaDoc headerName)
353     {
354     MimeHeader[] hdrs =
355         (MimeHeader[]) headers.get(headerName.toLowerCase());
356     if (hdrs == null) return null;
357     if (hdrs.length < 1) return null;
358     return(hdrs[hdrs.length - 1]);
359     }
360
361     /**
362      * Returns a array of type <code>MimeHeader</code> containing all values
363      * associated with a given Mime header name.
364      *
365      * @param headerName The name of the Mime header to return.
366      * @return All values associated with the header, or
367      * <code>null</code> if not found.
368      */

369     public
370     MimeHeader[] getHeaders(String JavaDoc headerName)
371     {
372     MimeHeader[] hdrs =
373         (MimeHeader[]) headers.get(headerName.toLowerCase());
374     if ((hdrs == null) || (hdrs.length < 1)) return null;
375     return hdrs;
376     }
377
378     /**
379      * Returns an array of <code>String</code> containing each Mime
380      * header as it was read from the header of this section. All
381      * whitespace and punctuation is left intact.
382      *
383      * @return Array of raw Mime headers.
384      */

385     public
386     String JavaDoc[] getRawHeaders()
387     {
388     return rawHeaders;
389     }
390
391     /**
392      * Returns an array of all Mime header lines that could not be
393      * parsed in the normal "name: value" fashion.
394      *
395      * @return Array of raw Mime headers.
396      */

397     public
398     String JavaDoc[] getGarbageHeaders()
399     {
400     return garbageHeaders;
401     }
402
403     // ------ Private Utility Routines Below -------
404

405     /**
406      * Reads into a byte array until the array is full, EOF is reached,
407      * or an I/O error occurs. This private method is intended
408      * only for reading individual lines of MIME headers. If the
409      * buffer is not long enough to hold an entire line from the input
410      * then the remainder bytes are skipped.
411      *
412      * @param buffer The buffer to read into.
413      * @return The number of bytes read.
414      * @exception IOException If an I/O error occurs.
415      * @exception MimeEOFException If EOF reached in the MIME header.
416      */

417     private
418     int readAll(byte[] buffer)
419     throws IOException JavaDoc, MimeEOFException
420     {
421     String JavaDoc la;
422     int pos = 0;
423     int len = buffer.length;
424     int n=0;
425
426     while (pos < len) {
427         switch (n = inputSource.readTo(buffer, pos, len - pos)) {
428         case BMByteSearchStream.EOF:
429             throw new MimeEOFException("EOF");
430         case BMByteSearchStream.AT_PATTERN:
431             inputSource.skipPattern();
432             if (inputSource.peekAheadString(2).equals("--"))
433             lastPart=true;
434             return pos;
435         default:
436             pos += n;
437             break;
438         }
439     }
440     //
441
// If the input line overflows the buffer, then the overflow
442
// part is discarded. Care should be taken to use a buffer
443
// large enough to hold any reasonable Mime header line.
444
//
445
inputSource.skipPattern();
446     return pos;
447     }
448
449     /**
450      * Converts a byte array into a raw ASCII character array with
451      * characters in the range to ÿ by twos-complement
452      * signed-to-unsigned conversion.
453      *
454      * @param buffer The byte array to convert.
455      * @param off Offset in buffer where conversion begins.
456      * @param len Number of bytes to convert.
457      * @return New character array containing the
458      * converted bytes
459      */

460     private static final
461     char[] bytesToChars(byte[] buffer, int off, int len)
462     {
463     char[] cbuf = new char[len];
464     int src, dst;
465     for (dst=0,src=off; dst < len; dst++,src++) {
466         cbuf[dst] = (char) (((int)(buffer[src]) + 0x100) & 0xff);
467     }
468     return cbuf;
469     }
470
471     /**
472      * Used by routines that fail after closure to verify that the
473      * current stream is open. If closed, this method throws an
474      * <code>IOException</code> to indicate an operation attempted
475      * on a closed stream.
476      *
477      * @exception IOException Thrown if the stream is closed.
478      */

479     private
480     void checkOpen()
481     throws IOException JavaDoc
482     {
483     if (!closed) return;
484     throw new IOException JavaDoc("Operation on a closed stream.");
485     }
486
487 }
488
Popular Tags