1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. 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 package org.apache.commons.fileupload;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.UnsupportedEncodingException;
24
25 import org.apache.commons.fileupload.util.Closeable;
26 import org.apache.commons.fileupload.util.Streams;
27
28 /**
29 * <p> Low level API for processing file uploads.
30 *
31 * <p> This class can be used to process data streams conforming to MIME
32 * 'multipart' format as defined in
33 * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
34 * large amounts of data in the stream can be processed under constant
35 * memory usage.
36 *
37 * <p> The format of the stream is defined in the following way:<br>
38 *
39 * <code>
40 * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
41 * encapsulation := delimiter body CRLF<br>
42 * delimiter := "--" boundary CRLF<br>
43 * close-delimiter := "--" boudary "--"<br>
44 * preamble := <ignore><br>
45 * epilogue := <ignore><br>
46 * body := header-part CRLF body-part<br>
47 * header-part := 1*header CRLF<br>
48 * header := header-name ":" header-value<br>
49 * header-name := <printable ascii characters except ":"><br>
50 * header-value := <any ascii characters except CR & LF><br>
51 * body-data := <arbitrary data><br>
52 * </code>
53 *
54 * <p>Note that body-data can contain another mulipart entity. There
55 * is limited support for single pass processing of such nested
56 * streams. The nested stream is <strong>required</strong> to have a
57 * boundary token of the same length as the parent stream (see {@link
58 * #setBoundary(byte[])}).
59 *
60 * <p>Here is an example of usage of this class.<br>
61 *
62 * <pre>
63 * try {
64 * MultipartStream multipartStream = new MultipartStream(input,
65 * boundary);
66 * boolean nextPart = multipartStream.skipPreamble();
67 * OutputStream output;
68 * while(nextPart) {
69 * header = chunks.readHeader();
70 * // process headers
71 * // create some output stream
72 * multipartStream.readBodyPart(output);
73 * nextPart = multipartStream.readBoundary();
74 * }
75 * } catch(MultipartStream.MalformedStreamException e) {
76 * // the stream failed to follow required syntax
77 * } catch(IOException) {
78 * // a read or write error occurred
79 * }
80 *
81 * </pre>
82 *
83 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
84 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
85 * @author Sean C. Sullivan
86 *
87 * @version $Id: MultipartStream.java 607869 2008-01-01 16:42:17Z jochen $
88 */
89 public class MultipartStream {
90 /**
91 * Internal class, which is used to invoke the
92 * {@link ProgressListener}.
93 */
94 static class ProgressNotifier {
95 /** The listener to invoke.
96 */
97 private final ProgressListener listener;
98 /** Number of expected bytes, if known, or -1.
99 */
100 private final long contentLength;
101 /** Number of bytes, which have been read so far.
102 */
103 private long bytesRead;
104 /** Number of items, which have been read so far.
105 */
106 private int items;
107 /** Creates a new instance with the given listener
108 * and content length.
109 * @param pListener The listener to invoke.
110 * @param pContentLength The expected content length.
111 */
112 ProgressNotifier(ProgressListener pListener, long pContentLength) {
113 listener = pListener;
114 contentLength = pContentLength;
115 }
116 /** Called to indicate that bytes have been read.
117 * @param pBytes Number of bytes, which have been read.
118 */
119 void noteBytesRead(int pBytes) {
120 /* Indicates, that the given number of bytes have been read from
121 * the input stream.
122 */
123 bytesRead += pBytes;
124 notifyListener();
125 }
126 /** Called to indicate, that a new file item has been detected.
127 */
128 void noteItem() {
129 ++items;
130 }
131 /** Called for notifying the listener.
132 */
133 private void notifyListener() {
134 if (listener != null) {
135 listener.update(bytesRead, contentLength, items);
136 }
137 }
138 }
139
140 // ----------------------------------------------------- Manifest constants
141
142
143 /**
144 * The Carriage Return ASCII character value.
145 */
146 public static final byte CR = 0x0D;
147
148
149 /**
150 * The Line Feed ASCII character value.
151 */
152 public static final byte LF = 0x0A;
153
154
155 /**
156 * The dash (-) ASCII character value.
157 */
158 public static final byte DASH = 0x2D;
159
160
161 /**
162 * The maximum length of <code>header-part</code> that will be
163 * processed (10 kilobytes = 10240 bytes.).
164 */
165 public static final int HEADER_PART_SIZE_MAX = 10240;
166
167
168 /**
169 * The default length of the buffer used for processing a request.
170 */
171 protected static final int DEFAULT_BUFSIZE = 4096;
172
173
174 /**
175 * A byte sequence that marks the end of <code>header-part</code>
176 * (<code>CRLFCRLF</code>).
177 */
178 protected static final byte[] HEADER_SEPARATOR = {
179 CR, LF, CR, LF };
180
181
182 /**
183 * A byte sequence that that follows a delimiter that will be
184 * followed by an encapsulation (<code>CRLF</code>).
185 */
186 protected static final byte[] FIELD_SEPARATOR = {
187 CR, LF};
188
189
190 /**
191 * A byte sequence that that follows a delimiter of the last
192 * encapsulation in the stream (<code>--</code>).
193 */
194 protected static final byte[] STREAM_TERMINATOR = {
195 DASH, DASH};
196
197
198 /**
199 * A byte sequence that precedes a boundary (<code>CRLF--</code>).
200 */
201 protected static final byte[] BOUNDARY_PREFIX = {
202 CR, LF, DASH, DASH};
203
204
205 // ----------------------------------------------------------- Data members
206
207
208 /**
209 * The input stream from which data is read.
210 */
211 private final InputStream input;
212
213
214 /**
215 * The length of the boundary token plus the leading <code>CRLF--</code>.
216 */
217 private int boundaryLength;
218
219
220 /**
221 * The amount of data, in bytes, that must be kept in the buffer in order
222 * to detect delimiters reliably.
223 */
224 private int keepRegion;
225
226
227 /**
228 * The byte sequence that partitions the stream.
229 */
230 private byte[] boundary;
231
232
233 /**
234 * The length of the buffer used for processing the request.
235 */
236 private final int bufSize;
237
238
239 /**
240 * The buffer used for processing the request.
241 */
242 private final byte[] buffer;
243
244
245 /**
246 * The index of first valid character in the buffer.
247 * <br>
248 * 0 <= head < bufSize
249 */
250 private int head;
251
252
253 /**
254 * The index of last valid characer in the buffer + 1.
255 * <br>
256 * 0 <= tail <= bufSize
257 */
258 private int tail;
259
260
261 /**
262 * The content encoding to use when reading headers.
263 */
264 private String headerEncoding;
265
266
267 /**
268 * The progress notifier, if any, or null.
269 */
270 private final ProgressNotifier notifier;
271
272 // ----------------------------------------------------------- Constructors
273
274 /**
275 * Creates a new instance.
276 * @deprecated Use {@link #MultipartStream(InputStream, byte[],
277 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)},
278 * or {@link #MultipartStream(InputStream, byte[], int,
279 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}
280 */
281 public MultipartStream() {
282 this(null, null, null);
283 }
284
285 /**
286 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer
287 * and no progress notifier.
288 *
289 * <p> Note that the buffer must be at least big enough to contain the
290 * boundary string, plus 4 characters for CR/LF and double dash, plus at
291 * least one byte of data. Too small a buffer size setting will degrade
292 * performance.
293 *
294 * @param input The <code>InputStream</code> to serve as a data source.
295 * @param boundary The token used for dividing the stream into
296 * <code>encapsulations</code>.
297 * @param bufSize The size of the buffer to be used, in bytes.
298 *
299 * @see #MultipartStream(InputStream, byte[],
300 * MultipartStream.ProgressNotifier)
301 * @deprecated Use {@link #MultipartStream(InputStream, byte[], int,
302 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}.
303 */
304 public MultipartStream(InputStream input, byte[] boundary, int bufSize) {
305 this(input, boundary, bufSize, null);
306 }
307
308 /**
309 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
310 *
311 * <p> Note that the buffer must be at least big enough to contain the
312 * boundary string, plus 4 characters for CR/LF and double dash, plus at
313 * least one byte of data. Too small a buffer size setting will degrade
314 * performance.
315 *
316 * @param input The <code>InputStream</code> to serve as a data source.
317 * @param boundary The token used for dividing the stream into
318 * <code>encapsulations</code>.
319 * @param bufSize The size of the buffer to be used, in bytes.
320 * @param pNotifier The notifier, which is used for calling the
321 * progress listener, if any.
322 *
323 * @see #MultipartStream(InputStream, byte[],
324 * MultipartStream.ProgressNotifier)
325 */
326 MultipartStream(InputStream input,
327 byte[] boundary,
328 int bufSize,
329 ProgressNotifier pNotifier) {
330 this.input = input;
331 this.bufSize = bufSize;
332 this.buffer = new byte[bufSize];
333 this.notifier = pNotifier;
334
335 // We prepend CR/LF to the boundary to chop trailng CR/LF from
336 // body-data tokens.
337 this.boundary = new byte[boundary.length + BOUNDARY_PREFIX.length];
338 this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
339 this.keepRegion = this.boundary.length;
340 System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
341 BOUNDARY_PREFIX.length);
342 System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
343 boundary.length);
344
345 head = 0;
346 tail = 0;
347 }
348
349
350 /**
351 * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
352 *
353 * @param input The <code>InputStream</code> to serve as a data source.
354 * @param boundary The token used for dividing the stream into
355 * <code>encapsulations</code>.
356 * @param pNotifier An object for calling the progress listener, if any.
357 *
358 *
359 * @see #MultipartStream(InputStream, byte[], int,
360 * MultipartStream.ProgressNotifier)
361 */
362 MultipartStream(InputStream input,
363 byte[] boundary,
364 ProgressNotifier pNotifier) {
365 this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
366 }
367
368 /**
369 * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
370 *
371 * @param input The <code>InputStream</code> to serve as a data source.
372 * @param boundary The token used for dividing the stream into
373 * <code>encapsulations</code>.
374 *
375 * @deprecated Use {@link #MultipartStream(InputStream, byte[],
376 * MultipartStream.ProgressNotifier)}.
377 * @see #MultipartStream(InputStream, byte[], int,
378 * MultipartStream.ProgressNotifier)
379 */
380 public MultipartStream(InputStream input,
381 byte[] boundary) {
382 this(input, boundary, DEFAULT_BUFSIZE, null);
383 }
384
385 // --------------------------------------------------------- Public methods
386
387
388 /**
389 * Retrieves the character encoding used when reading the headers of an
390 * individual part. When not specified, or <code>null</code>, the platform
391 * default encoding is used.
392
393 *
394 * @return The encoding used to read part headers.
395 */
396 public String getHeaderEncoding() {
397 return headerEncoding;
398 }
399
400
401 /**
402 * Specifies the character encoding to be used when reading the headers of
403 * individual parts. When not specified, or <code>null</code>, the platform
404 * default encoding is used.
405 *
406 * @param encoding The encoding used to read part headers.
407 */
408 public void setHeaderEncoding(String encoding) {
409 headerEncoding = encoding;
410 }
411
412
413 /**
414 * Reads a byte from the <code>buffer</code>, and refills it as
415 * necessary.
416 *
417 * @return The next byte from the input stream.
418 *
419 * @throws IOException if there is no more data available.
420 */
421 public byte readByte() throws IOException {
422 // Buffer depleted ?
423 if (head == tail) {
424 head = 0;
425 // Refill.
426 tail = input.read(buffer, head, bufSize);
427 if (tail == -1) {
428 // No more data available.
429 throw new IOException("No more data is available");
430 }
431 if (notifier != null) {
432 notifier.noteBytesRead(tail);
433 }
434 }
435 return buffer[head++];
436 }
437
438
439 /**
440 * Skips a <code>boundary</code> token, and checks whether more
441 * <code>encapsulations</code> are contained in the stream.
442 *
443 * @return <code>true</code> if there are more encapsulations in
444 * this stream; <code>false</code> otherwise.
445 *
446 * @throws MalformedStreamException if the stream ends unexpecetedly or
447 * fails to follow required syntax.
448 */
449 public boolean readBoundary()
450 throws MalformedStreamException {
451 byte[] marker = new byte[2];
452 boolean nextChunk = false;
453
454 head += boundaryLength;
455 try {
456 marker[0] = readByte();
457 if (marker[0] == LF) {
458 // Work around IE5 Mac bug with input type=image.
459 // Because the boundary delimiter, not including the trailing
460 // CRLF, must not appear within any file (RFC 2046, section
461 // 5.1.1), we know the missing CR is due to a buggy browser
462 // rather than a file containing something similar to a
463 // boundary.
464 return true;
465 }
466
467 marker[1] = readByte();
468 if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
469 nextChunk = false;
470 } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
471 nextChunk = true;
472 } else {
473 throw new MalformedStreamException(
474 "Unexpected characters follow a boundary");
475 }
476 } catch (IOException e) {
477 throw new MalformedStreamException("Stream ended unexpectedly");
478 }
479 return nextChunk;
480 }
481
482
483 /**
484 * <p>Changes the boundary token used for partitioning the stream.
485 *
486 * <p>This method allows single pass processing of nested multipart
487 * streams.
488 *
489 * <p>The boundary token of the nested stream is <code>required</code>
490 * to be of the same length as the boundary token in parent stream.
491 *
492 * <p>Restoring the parent stream boundary token after processing of a
493 * nested stream is left to the application.
494 *
495 * @param boundary The boundary to be used for parsing of the nested
496 * stream.
497 *
498 * @throws IllegalBoundaryException if the <code>boundary</code>
499 * has a different length than the one
500 * being currently parsed.
501 */
502 public void setBoundary(byte[] boundary)
503 throws IllegalBoundaryException {
504 if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
505 throw new IllegalBoundaryException(
506 "The length of a boundary token can not be changed");
507 }
508 System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
509 boundary.length);
510 }
511
512
513 /**
514 * <p>Reads the <code>header-part</code> of the current
515 * <code>encapsulation</code>.
516 *
517 * <p>Headers are returned verbatim to the input stream, including the
518 * trailing <code>CRLF</code> marker. Parsing is left to the
519 * application.
520 *
521 * <p><strong>TODO</strong> allow limiting maximum header size to
522 * protect against abuse.
523 *
524 * @return The <code>header-part</code> of the current encapsulation.
525 *
526 * @throws MalformedStreamException if the stream ends unexpecetedly.
527 */
528 public String readHeaders()
529 throws MalformedStreamException {
530 int i = 0;
531 byte b;
532 // to support multi-byte characters
533 ByteArrayOutputStream baos = new ByteArrayOutputStream();
534 int size = 0;
535 while (i < HEADER_SEPARATOR.length) {
536 try {
537 b = readByte();
538 } catch (IOException e) {
539 throw new MalformedStreamException("Stream ended unexpectedly");
540 }
541 if (++size > HEADER_PART_SIZE_MAX) {
542 throw new MalformedStreamException(
543 "Header section has more than " + HEADER_PART_SIZE_MAX
544 + " bytes (maybe it is not properly terminated)");
545 }
546 if (b == HEADER_SEPARATOR[i]) {
547 i++;
548 } else {
549 i = 0;
550 }
551 baos.write(b);
552 }
553
554 String headers = null;
555 if (headerEncoding != null) {
556 try {
557 headers = baos.toString(headerEncoding);
558 } catch (UnsupportedEncodingException e) {
559 // Fall back to platform default if specified encoding is not
560 // supported.
561 headers = baos.toString();
562 }
563 } else {
564 headers = baos.toString();
565 }
566
567 return headers;
568 }
569
570
571 /**
572 * <p>Reads <code>body-data</code> from the current
573 * <code>encapsulation</code> and writes its contents into the
574 * output <code>Stream</code>.
575 *
576 * <p>Arbitrary large amounts of data can be processed by this
577 * method using a constant size buffer. (see {@link
578 * #MultipartStream(InputStream,byte[],int,
579 * MultipartStream.ProgressNotifier) constructor}).
580 *
581 * @param output The <code>Stream</code> to write data into. May
582 * be null, in which case this method is equivalent
583 * to {@link #discardBodyData()}.
584 *
585 * @return the amount of data written.
586 *
587 * @throws MalformedStreamException if the stream ends unexpectedly.
588 * @throws IOException if an i/o error occurs.
589 */
590 public int readBodyData(OutputStream output)
591 throws MalformedStreamException, IOException {
592 final InputStream istream = newInputStream();
593 return (int) Streams.copy(istream, output, false);
594 }
595
596 /**
597 * Creates a new {@link ItemInputStream}.
598 * @return A new instance of {@link ItemInputStream}.
599 */
600 ItemInputStream newInputStream() {
601 return new ItemInputStream();
602 }
603
604 /**
605 * <p> Reads <code>body-data</code> from the current
606 * <code>encapsulation</code> and discards it.
607 *
608 * <p>Use this method to skip encapsulations you don't need or don't
609 * understand.
610 *
611 * @return The amount of data discarded.
612 *
613 * @throws MalformedStreamException if the stream ends unexpectedly.
614 * @throws IOException if an i/o error occurs.
615 */
616 public int discardBodyData()
617 throws MalformedStreamException,
618 IOException {
619 return readBodyData(null);
620 }
621
622
623 /**
624 * Finds the beginning of the first <code>encapsulation</code>.
625 *
626 * @return <code>true</code> if an <code>encapsulation</code> was found in
627 * the stream.
628 *
629 * @throws IOException if an i/o error occurs.
630 */
631 public boolean skipPreamble()
632 throws IOException {
633 // First delimiter may be not preceeded with a CRLF.
634 System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
635 boundaryLength = boundary.length - 2;
636 try {
637 // Discard all data up to the delimiter.
638 discardBodyData();
639
640 // Read boundary - if succeded, the stream contains an
641 // encapsulation.
642 return readBoundary();
643 } catch (MalformedStreamException e) {
644 return false;
645 } finally {
646 // Restore delimiter.
647 System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
648 boundaryLength = boundary.length;
649 boundary[0] = CR;
650 boundary[1] = LF;
651 }
652 }
653
654
655 /**
656 * Compares <code>count</code> first bytes in the arrays
657 * <code>a</code> and <code>b</code>.
658 *
659 * @param a The first array to compare.
660 * @param b The second array to compare.
661 * @param count How many bytes should be compared.
662 *
663 * @return <code>true</code> if <code>count</code> first bytes in arrays
664 * <code>a</code> and <code>b</code> are equal.
665 */
666 public static boolean arrayequals(byte[] a,
667 byte[] b,
668 int count) {
669 for (int i = 0; i < count; i++) {
670 if (a[i] != b[i]) {
671 return false;
672 }
673 }
674 return true;
675 }
676
677
678 /**
679 * Searches for a byte of specified value in the <code>buffer</code>,
680 * starting at the specified <code>position</code>.
681 *
682 * @param value The value to find.
683 * @param pos The starting position for searching.
684 *
685 * @return The position of byte found, counting from beginning of the
686 * <code>buffer</code>, or <code>-1</code> if not found.
687 */
688 protected int findByte(byte value,
689 int pos) {
690 for (int i = pos; i < tail; i++) {
691 if (buffer[i] == value) {
692 return i;
693 }
694 }
695
696 return -1;
697 }
698
699
700 /**
701 * Searches for the <code>boundary</code> in the <code>buffer</code>
702 * region delimited by <code>head</code> and <code>tail</code>.
703 *
704 * @return The position of the boundary found, counting from the
705 * beginning of the <code>buffer</code>, or <code>-1</code> if
706 * not found.
707 */
708 protected int findSeparator() {
709 int first;
710 int match = 0;
711 int maxpos = tail - boundaryLength;
712 for (first = head;
713 (first <= maxpos) && (match != boundaryLength);
714 first++) {
715 first = findByte(boundary[0], first);
716 if (first == -1 || (first > maxpos)) {
717 return -1;
718 }
719 for (match = 1; match < boundaryLength; match++) {
720 if (buffer[first + match] != boundary[match]) {
721 break;
722 }
723 }
724 }
725 if (match == boundaryLength) {
726 return first - 1;
727 }
728 return -1;
729 }
730
731 /**
732 * Thrown to indicate that the input stream fails to follow the
733 * required syntax.
734 */
735 public static class MalformedStreamException
736 extends IOException {
737 /**
738 * Constructs a <code>MalformedStreamException</code> with no
739 * detail message.
740 */
741 public MalformedStreamException() {
742 super();
743 }
744
745 /**
746 * Constructs an <code>MalformedStreamException</code> with
747 * the specified detail message.
748 *
749 * @param message The detail message.
750 */
751 public MalformedStreamException(String message) {
752 super(message);
753 }
754 }
755
756
757 /**
758 * Thrown upon attempt of setting an invalid boundary token.
759 */
760 public static class IllegalBoundaryException
761 extends IOException {
762 /**
763 * Constructs an <code>IllegalBoundaryException</code> with no
764 * detail message.
765 */
766 public IllegalBoundaryException() {
767 super();
768 }
769
770 /**
771 * Constructs an <code>IllegalBoundaryException</code> with
772 * the specified detail message.
773 *
774 * @param message The detail message.
775 */
776 public IllegalBoundaryException(String message) {
777 super(message);
778 }
779 }
780
781 /**
782 * An {@link InputStream} for reading an items contents.
783 */
784 public class ItemInputStream extends InputStream implements Closeable {
785 /** The number of bytes, which have been read so far.
786 */
787 private long total;
788 /** The number of bytes, which must be hold, because
789 * they might be a part of the boundary.
790 */
791 private int pad;
792 /** The current offset in the buffer.
793 */
794 private int pos;
795 /** Whether the stream is already closed.
796 */
797 private boolean closed;
798
799 /**
800 * Creates a new instance.
801 */
802 ItemInputStream() {
803 findSeparator();
804 }
805
806 /**
807 * Called for finding the separator.
808 */
809 private void findSeparator() {
810 pos = MultipartStream.this.findSeparator();
811 if (pos == -1) {
812 if (tail - head > keepRegion) {
813 pad = keepRegion;
814 } else {
815 pad = tail - head;
816 }
817 }
818 }
819
820 /**
821 * Returns the number of bytes, which have been read
822 * by the stream.
823 * @return Number of bytes, which have been read so far.
824 */
825 public long getBytesRead() {
826 return total;
827 }
828
829 /**
830 * Returns the number of bytes, which are currently
831 * available, without blocking.
832 * @throws IOException An I/O error occurs.
833 * @return Number of bytes in the buffer.
834 */
835 public int available() throws IOException {
836 if (pos == -1) {
837 return tail - head - pad;
838 }
839 return pos - head;
840 }
841
842 /** Offset when converting negative bytes to integers.
843 */
844 private static final int BYTE_POSITIVE_OFFSET = 256;
845
846 /**
847 * Returns the next byte in the stream.
848 * @return The next byte in the stream, as a non-negative
849 * integer, or -1 for EOF.
850 * @throws IOException An I/O error occurred.
851 */
852 public int read() throws IOException {
853 if (closed) {
854 throw new FileItemStream.ItemSkippedException();
855 }
856 if (available() == 0) {
857 if (makeAvailable() == 0) {
858 return -1;
859 }
860 }
861 ++total;
862 int b = buffer[head++];
863 if (b >= 0) {
864 return b;
865 }
866 return b + BYTE_POSITIVE_OFFSET;
867 }
868
869 /**
870 * Reads bytes into the given buffer.
871 * @param b The destination buffer, where to write to.
872 * @param off Offset of the first byte in the buffer.
873 * @param len Maximum number of bytes to read.
874 * @return Number of bytes, which have been actually read,
875 * or -1 for EOF.
876 * @throws IOException An I/O error occurred.
877 */
878 public int read(byte[] b, int off, int len) throws IOException {
879 if (closed) {
880 throw new FileItemStream.ItemSkippedException();
881 }
882 if (len == 0) {
883 return 0;
884 }
885 int res = available();
886 if (res == 0) {
887 res = makeAvailable();
888 if (res == 0) {
889 return -1;
890 }
891 }
892 res = Math.min(res, len);
893 System.arraycopy(buffer, head, b, off, res);
894 head += res;
895 total += res;
896 return res;
897 }
898
899 /**
900 * Closes the input stream.
901 * @throws IOException An I/O error occurred.
902 */
903 public void close() throws IOException {
904 close(false);
905 }
906
907 /**
908 * Closes the input stream.
909 * @param pCloseUnderlying Whether to close the underlying stream
910 * (hard close)
911 * @throws IOException An I/O error occurred.
912 */
913 public void close(boolean pCloseUnderlying) throws IOException {
914 if (closed) {
915 return;
916 }
917 if (pCloseUnderlying) {
918 closed = true;
919 input.close();
920 } else {
921 for (;;) {
922 int av = available();
923 if (av == 0) {
924 av = makeAvailable();
925 if (av == 0) {
926 break;
927 }
928 }
929 skip(av);
930 }
931 }
932 closed = true;
933 }
934
935 /**
936 * Skips the given number of bytes.
937 * @param bytes Number of bytes to skip.
938 * @return The number of bytes, which have actually been
939 * skipped.
940 * @throws IOException An I/O error occurred.
941 */
942 public long skip(long bytes) throws IOException {
943 if (closed) {
944 throw new FileItemStream.ItemSkippedException();
945 }
946 int av = available();
947 if (av == 0) {
948 av = makeAvailable();
949 if (av == 0) {
950 return 0;
951 }
952 }
953 long res = Math.min(av, bytes);
954 head += res;
955 return res;
956 }
957
958 /**
959 * Attempts to read more data.
960 * @return Number of available bytes
961 * @throws IOException An I/O error occurred.
962 */
963 private int makeAvailable() throws IOException {
964 if (pos != -1) {
965 return 0;
966 }
967
968 // Move the data to the beginning of the buffer.
969 total += tail - head - pad;
970 System.arraycopy(buffer, tail - pad, buffer, 0, pad);
971
972 // Refill buffer with new data.
973 head = 0;
974 tail = pad;
975
976 for (;;) {
977 int bytesRead = input.read(buffer, tail, bufSize - tail);
978 if (bytesRead == -1) {
979 // The last pad amount is left in the buffer.
980 // Boundary can't be in there so signal an error
981 // condition.
982 final String msg = "Stream ended unexpectedly";
983 throw new MalformedStreamException(msg);
984 }
985 if (notifier != null) {
986 notifier.noteBytesRead(bytesRead);
987 }
988 tail += bytesRead;
989
990 findSeparator();
991 int av = available();
992
993 if (av > 0 || pos != -1) {
994 return av;
995 }
996 }
997 }
998
999 /**
1000 * Returns, whether the stream is closed.
1001 * @return True, if the stream is closed, otherwise false.
1002 */
1003 public boolean isClosed() {
1004 return closed;
1005 }
1006 }
1007
1008 // ------------------------------------------------------ Debugging methods
1009
1010
1011 // These are the methods that were used to debug this stuff.
1012 /*
1013
1014 // Dump data.
1015 protected void dump()
1016 {
1017 System.out.println("01234567890");
1018 byte[] temp = new byte[buffer.length];
1019 for(int i=0; i<buffer.length; i++)
1020 {
1021 if (buffer[i] == 0x0D || buffer[i] == 0x0A)
1022 {
1023 temp[i] = 0x21;
1024 }
1025 else
1026 {
1027 temp[i] = buffer[i];
1028 }
1029 }
1030 System.out.println(new String(temp));
1031 int i;
1032 for (i=0; i<head; i++)
1033 System.out.print(" ");
1034 System.out.println("h");
1035 for (i=0; i<tail; i++)
1036 System.out.print(" ");
1037 System.out.println("t");
1038 System.out.flush();
1039 }
1040
1041 // Main routine, for testing purposes only.
1042 //
1043 // @param args A String[] with the command line arguments.
1044 // @throws Exception, a generic exception.
1045 public static void main( String[] args )
1046 throws Exception
1047 {
1048 File boundaryFile = new File("boundary.dat");
1049 int boundarySize = (int)boundaryFile.length();
1050 byte[] boundary = new byte[boundarySize];
1051 FileInputStream input = new FileInputStream(boundaryFile);
1052 input.read(boundary,0,boundarySize);
1053
1054 input = new FileInputStream("multipart.dat");
1055 MultipartStream chunks = new MultipartStream(input, boundary);
1056
1057 int i = 0;
1058 String header;
1059 OutputStream output;
1060 boolean nextChunk = chunks.skipPreamble();
1061 while (nextChunk)
1062 {
1063 header = chunks.readHeaders();
1064 System.out.println("!"+header+"!");
1065 System.out.println("wrote part"+i+".dat");
1066 output = new FileOutputStream("part"+(i++)+".dat");
1067 chunks.readBodyData(output);
1068 nextChunk = chunks.readBoundary();
1069 }
1070 }
1071
1072 */
1073 }