001: // MultipartInputStream.java
002: // $Id: MultipartInputStream.java,v 1.8 2000/08/16 21:38:01 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.mime;
007:
008: import java.io.BufferedInputStream;
009: import java.io.IOException;
010: import java.io.InputStream;
011:
012: /**
013: * A class to handle multipart MIME input streams. See RC 1521.
014: * This class handles multipart input streams, as defined by the RFC 1521.
015: * It prvides a sequential interface to all MIME parts, and for each part
016: * it delivers a suitable InputStream for getting its body.
017: */
018:
019: public class MultipartInputStream extends InputStream {
020: InputStream in = null;
021: byte boundary[] = null;
022: byte buffer[] = null;
023: boolean partEnd = false;
024: boolean fileEnd = false;
025:
026: // Read boundary bytes of input in buffer
027: // Return true if enough bytes available, false otherwise.
028:
029: private final boolean readBoundaryBytes() throws IOException {
030: int pos = 0;
031: while (pos < buffer.length) {
032: int got = in.read(buffer, pos, buffer.length - pos);
033: if (got < 0)
034: return false;
035: pos += got;
036: }
037: return true;
038: }
039:
040: // Skip to next input boundary, set stream at begining of content:
041: // Returns true if boundary was found, false otherwise.
042:
043: protected boolean skipToBoundary() throws IOException {
044: int ch = in.read();
045: skip: while (ch != -1) {
046: if (ch != '-') {
047: ch = in.read();
048: continue;
049: }
050: if ((ch = in.read()) != '-')
051: continue;
052: in.mark(boundary.length);
053: if (!readBoundaryBytes()) {
054: in.reset();
055: ch = in.read();
056: continue skip;
057: }
058: for (int i = 0; i < boundary.length; i++) {
059: if (buffer[i] != boundary[i]) {
060: in.reset();
061: ch = in.read();
062: continue skip;
063: }
064: }
065: // FIXME: should we check for a properly syntaxed part, which
066: // means that we should expect '\r\n'. For now, we just skip
067: // as much as we can.
068: if ((ch = in.read()) == '\r') {
069: ch = in.read();
070: }
071: in.mark(3);
072: if (in.read() == '-') { // check fileEnd!
073: if (in.read() == '\r' && in.read() == '\n') {
074: fileEnd = true;
075: return false;
076: }
077: }
078: in.reset();
079: return true;
080: }
081: fileEnd = true;
082: return false;
083: }
084:
085: /**
086: * Read one byte of data from the current part.
087: * @return A byte of data, or <strong>-1</strong> if end of file.
088: * @exception IOException If some IO error occured.
089: */
090:
091: public int read() throws IOException {
092: int ch;
093: if (partEnd)
094: return -1;
095: switch (ch = in.read()) {
096: case '\r':
097: // check for a boundary
098: in.mark(boundary.length + 3);
099: int c1 = in.read();
100: int c2 = in.read();
101: int c3 = in.read();
102: if ((c1 == '\n') && (c2 == '-') && (c3 == '-')) {
103: if (!readBoundaryBytes()) {
104: in.reset();
105: return ch;
106: }
107: for (int i = 0; i < boundary.length; i++) {
108: if (buffer[i] != boundary[i]) {
109: in.reset();
110: return ch;
111: }
112: }
113: partEnd = true;
114: if ((ch = in.read()) == '\r') {
115: in.read();
116: } else if (ch == '-') {
117: // FIXME, check the second hyphen
118: if (in.read() == '-')
119: fileEnd = true;
120: } else {
121: fileEnd = (ch == -1);
122: }
123: return -1;
124: } else {
125: in.reset();
126: return ch;
127: }
128: // not reached
129: case -1:
130: fileEnd = true;
131: return -1;
132: default:
133: return ch;
134: }
135: }
136:
137: /**
138: * Read n bytes of data from the current part.
139: * @return the number of bytes data, read or <strong>-1</strong>
140: * if end of file.
141: * @exception IOException If some IO error occured.
142: */
143: public int read(byte b[], int off, int len) throws IOException {
144: int got = 0;
145: int ch;
146:
147: while (got < len) {
148: if ((ch = read()) == -1)
149: return (got == 0) ? -1 : got;
150: b[off + (got++)] = (byte) (ch & 0xFF);
151: }
152: return got;
153: }
154:
155: public long skip(long n) throws IOException {
156: while ((--n >= 0) && (read() != -1))
157: ;
158: return n;
159: }
160:
161: public int available() throws IOException {
162: return in.available();
163: }
164:
165: /**
166: * Switch to the next available part of data.
167: * One can interrupt the current part, and use this method to switch
168: * to next part before current part was totally read.
169: * @return A boolean <strong>true</strong> if there next partis ready,
170: * or <strong>false</strong> if this was the last part.
171: */
172:
173: public boolean nextInputStream() throws IOException {
174: if (fileEnd) {
175: return false;
176: }
177: if (!partEnd) {
178: return skipToBoundary();
179: } else {
180: partEnd = false;
181: return true;
182: }
183: }
184:
185: /**
186: * Construct a new multipart input stream.
187: * @param in The initial (multipart) input stream.
188: * @param boundary The input stream MIME boundary.
189: */
190:
191: public MultipartInputStream(InputStream in, byte boundary[]) {
192: this .in = (in.markSupported() ? in : new BufferedInputStream(
193: in, boundary.length + 4));
194: this .boundary = boundary;
195: this .buffer = new byte[boundary.length];
196: this .partEnd = false;
197: this .fileEnd = false;
198: }
199:
200: }
|