001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019:
020: package de.schlund.pfixxml.multipart;
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.PushbackInputStream;
025: import java.io.UnsupportedEncodingException;
026:
027: import javax.servlet.ServletInputStream;
028:
029: /**
030: * This class wrapes a {@link javax.servlet.ServletInputStream ServletInputStream}
031: * and takes care of boundaries from a multipart message.
032: * <p/>
033: * <b>Attention</b><br/>
034: * This class is not reentrant!
035: *
036: *
037: */
038:
039: public class MultipartStream extends InputStream {
040:
041: public static final int DEF_BUFFER_SIZE = 1024;
042: public static final String DEF_CHAR_ENC = "ISO-8859-1";
043: public static final long DEF_MAX_PART_SIZE = 10 * 1024 * 1024; // 10 MB
044: public static final int CR = 13;
045: public static final int LF = 10;
046: public static final int DASH = 45;
047: public static final int STATE_BEGIN = 0;
048: public static final int STATE_CR = 1;
049: public static final int STATE_LF = 2;
050: public static final int STATE_TEST = 3;
051:
052: private PushbackInputStream in = null;
053: private int[] buf = null;
054: private int[] boundaryArray = null;
055: private int boundaryLength = -1;
056: private int bufSize = DEF_BUFFER_SIZE;
057: private boolean eom = false;
058: private boolean eop = false;
059:
060: private long maxPartSize = DEF_MAX_PART_SIZE;
061: private long partSize = 0;
062:
063: /**
064: * Constructor for MultipartStream.
065: */
066: public MultipartStream() {
067: this (null, -1);
068: }
069:
070: public MultipartStream(ServletInputStream inS) {
071: this (inS, -1);
072: }
073:
074: public MultipartStream(ServletInputStream inS, int bSize) {
075: super ();
076: bufSize = bSize;
077: if (bSize <= 0)
078: bufSize = DEF_BUFFER_SIZE;
079: buf = new int[bufSize];
080: in = new PushbackInputStream(inS, bufSize);
081: eom = false;
082: eop = false;
083: boundaryArray = new int[0];
084: }
085:
086: public void setServletInputStream(ServletInputStream inS) {
087: in = new PushbackInputStream(inS, bufSize);
088: }
089:
090: public void setMaxPartSize(long pSize) {
091: maxPartSize = pSize;
092: }
093:
094: public long getMaxPartSize() {
095: return maxPartSize;
096: }
097:
098: public void setBoundary(String bParam) {
099: if (bParam == null)
100: throw new IllegalArgumentException("Given boundary is null");
101:
102: boundaryLength = bParam.length() + 2;
103: String boundary = "--" + bParam + "--";
104: eom = false;
105: eop = false;
106: partSize = 0;
107: try {
108: byte[] tmpArr = boundary.getBytes("ISO-8859-1");
109: if (boundaryArray == null
110: || boundaryArray.length < (tmpArr.length)) {
111: boundaryArray = new int[tmpArr.length];
112: }
113: for (int i = 0; i < boundaryArray.length; i++) {
114: boundaryArray[i] = tmpArr[i];
115: }
116: if (buf == null || buf.length < (boundaryArray.length + 1)) {
117: buf = new int[boundaryArray.length + 1];
118: }
119: } catch (UnsupportedEncodingException e) {
120: }
121: }
122:
123: public void skipBoundary() {
124: if (boundaryArray == null || boundaryArray.length == 0)
125: throw new IllegalStateException("Have no boundary");
126: if (in == null)
127: throw new IllegalStateException("Have no stream");
128:
129: boolean doLoop = true;
130:
131: int tmpVal = -1;
132: int compIdx = 0;
133: partSize = 0;
134: eop = false;
135: eom = false;
136: while (doLoop) {
137: tmpVal = getByteFromStream();
138: if (tmpVal != -1) {
139: if (boundaryArray[compIdx++] == tmpVal) {
140: if (boundaryLength == compIdx) {
141: eop = true;
142: eom = false;
143: } else if (boundaryArray.length == compIdx) {
144: eop = false;
145: eom = true;
146: doLoop = false;
147: }
148: } else if (eop) {
149: doLoop = false;
150: try {
151: in.unread(tmpVal);
152: } catch (IOException e) {
153: eop = false;
154: eom = true;
155: }
156: } else {
157: gotoStartOfLine();
158: compIdx = 0;
159: }
160: } else {
161: eom = true;
162: eop = false;
163: doLoop = false;
164: }
165: }
166: if (tmpVal != -1) {
167: gotoStartOfLine();
168: eop = false;
169: eom = false;
170: }
171: }
172:
173: protected void gotoStartOfLine() {
174: int tmpVal = -1;
175: boolean doLoop = true;
176: boolean found = false;
177: while (doLoop) {
178: tmpVal = getByteFromStream();
179: if (0 <= tmpVal) {
180: if (tmpVal == CR || tmpVal == LF) {
181: found = true;
182: } else if (found) {
183: doLoop = false;
184: try {
185: in.unread(tmpVal);
186: partSize--;
187: if (partSize < 0)
188: partSize = 0;
189: } catch (IOException e) {
190: eop = false;
191: eom = true;
192: }
193: }
194: } else {
195: doLoop = false;
196: eop = false;
197: eom = true;
198: }
199: }
200: }
201:
202: public boolean isEndOfPart() {
203: return eop;
204: }
205:
206: public boolean isEndOfMultipart() {
207: return eom;
208: }
209:
210: /**
211: * @see InputStream#read()
212: */
213: public int read() throws IOException {
214: int rc = -1;
215: if (isEndOfPart() || isEndOfMultipart())
216: return rc;
217:
218: if (maxPartSize < partSize)
219: throw new PartToLongException("Maximum part size of "
220: + maxPartSize + " exeeded");
221: rc = in.read();
222: partSize++;
223: if (rc == CR || rc == LF) {
224: rc = checkPartBoundary(rc);
225: }
226:
227: return rc;
228: }
229:
230: private int checkPartBoundary(int val) throws IOException {
231: int rc = val;
232: boolean doLoop = (0 <= val);
233: if (doLoop) {
234: in.unread(val);
235: }
236:
237: eop = false;
238: eom = false;
239: int compIdx = 0;
240: int writeIdx = 0;
241: int state = STATE_BEGIN;
242: boolean innerLoop = true;
243: while (doLoop) {
244: val = getByteFromStream();
245: if (0 <= val) {
246: buf[writeIdx++] = val;
247: innerLoop = true;
248: while (innerLoop) {
249: switch (state) {
250: case STATE_BEGIN:
251: if (val == CR) {
252: state = STATE_CR;
253: innerLoop = false;
254: } else if (val == LF) {
255: state = STATE_LF;
256: innerLoop = false;
257: } else {
258: state = STATE_TEST;
259: }
260: break;
261: case STATE_CR:
262: if (val == LF) {
263: state = STATE_LF;
264: innerLoop = false;
265: } else {
266: state = STATE_TEST;
267: }
268: break;
269: case STATE_LF:
270: if (val == CR || val == LF) {
271: innerLoop = false;
272: doLoop = false;
273: } else {
274: state = STATE_TEST;
275: }
276: break;
277: case STATE_TEST:
278: if (boundaryArray[compIdx++] == val) {
279: if (boundaryLength == compIdx) {
280: eop = true;
281: eom = false;
282: } else if (boundaryArray.length == compIdx) {
283: eop = false;
284: eom = true;
285: doLoop = false;
286: }
287: } else {
288: doLoop = false;
289: }
290: innerLoop = false;
291: break;
292: }
293: }
294: } else {
295: doLoop = false;
296: }
297: }
298: if (eop || eom) {
299: pushbackIntArray(buf, 0, writeIdx);
300: rc = -1;
301: } else if (0 < --writeIdx) {
302: pushbackIntArray(buf, 1, writeIdx);
303: }
304: return rc;
305: }
306:
307: /**
308: * @see InputStream#available()
309: */
310: public int available() throws IOException {
311: return in.available();
312: }
313:
314: /**
315: * @see InputStream#close()
316: */
317: public void close() throws IOException {
318: in.close();
319: }
320:
321: protected int getByteFromStream() {
322: int rc = -1;
323: try {
324: rc = in.read();
325: } catch (IOException e) {
326: rc = -1;
327: }
328: return rc;
329: }
330:
331: protected void pushbackIntArray(int[] val, int start, int length)
332: throws IOException {
333:
334: int startLoop = start + length - 1;
335: for (int i = startLoop; start <= i; i--) {
336: in.unread(val[i]);
337: }
338: }
339: }
|