001: /*
002: * $Header: /cvsroot/webman-cms/source/webman/com/teamkonzept/web/TKHttpMultipartBuffer.java,v 1.8 2001/06/11 09:14:11 alex Exp $
003: *
004: */
005: package com.teamkonzept.web;
006:
007: import java.io.*;
008:
009: import com.teamkonzept.lib.*;
010:
011: public class TKHttpMultipartBuffer {
012: public static final byte[] END_OF_HEADER = { 13, 10, 13, 10 };
013: public static final String CRLF = "\015\012";
014: //public static final String END_OF_HEADER = CRLF+CRLF;
015:
016: public int maxBufferSize = 5000;
017: public byte[] buffer; // hier werden bis zu maxBufferSize Bytes gespeichert
018: public int bufferEntries; // soviel Bytes sind gerade in buffer noch drin
019: public InputStream dataStream; // Hierher werden die Daten gelesen
020: public int contentLength; // soviele Daten m¸ssen noch gelesen werden
021: public byte[] boundary; // das ist der Trenncode zwischen den Parametern
022: public int boundaryLength; // das ist die L‰nge des Trenncodes
023: public int boundaryStart; // an dieser Stelle in buffer beginnt der naechste Trenncode
024:
025: // -1 := neuer Parameter; -2 := Folge-Parameter
026:
027: public TKHttpMultipartBuffer(InputStream is, int length,
028: String boundary) throws IOException {
029: this .dataStream = is;
030: this .contentLength = length;
031: if (boundary != null) {
032: this .boundaryLength = boundary.length() + 2;
033: this .boundary = new byte[this .boundaryLength];
034: this .boundary[0] = (byte) '-';
035: this .boundary[1] = (byte) '-';
036: boundary.getBytes(0, this .boundaryLength - 2,
037: this .boundary, 2);
038: }
039: this .bufferEntries = 0;
040: this .boundaryStart = -1;
041:
042: init();
043: }
044:
045: public void init() throws IOException {
046: if (boundary != null) {
047: dataStream.skip(boundary.length + 2);
048: } else {
049: // Netscape-Bug mit fehlenden Boundaries umgehen
050: StringBuffer boundBuf = new StringBuffer();
051: int lastRead = dataStream.read();
052: int currChar;
053: while (!(((currChar = dataStream.read()) == 10 && (lastRead == 13)))) {
054: boundBuf.append((char) lastRead);
055: lastRead = currChar;
056: }
057: boundaryLength = boundBuf.length();
058: boundBuf.toString()
059: .getBytes(0, boundaryLength, boundary, 0);
060: }
061:
062: // boundary + CRLF ueberspringen
063: contentLength -= boundaryLength + 2;
064:
065: if (maxBufferSize < boundary.length) {
066: maxBufferSize = boundary.length * 2;
067: }
068:
069: buffer = new byte[maxBufferSize];
070: }
071:
072: /* public int getRequestData( StringBuffer dest, byte[] delimiter )
073: throws IOException
074: {
075: boolean done = false;
076: int delimiterPos = 0;
077: int delimiterLength = delimiter.length;
078: int read = 0;
079: dest.setLength(0);
080: while( true ) {
081: int nextChar = dataStream.read();
082: if( nextChar < 0 ) throw new Error("error in multipart form");
083: read++;
084: dest.append( (char) nextChar );
085: if( nextChar == delimiter[ delimiterPos ] ) {
086: delimiterPos++;
087: if( delimiterPos >= delimiterLength ) {
088: read -= delimiterLength;
089: dest.setLength( read );
090: return read-1;
091: }
092: }
093: else {
094: delimiterPos = 0;
095: }
096: }
097: return -1;
098: }
099:
100: public TKHashtable getMultipartParams( String boundary, int length )
101: {
102: if( boundary == null ) {
103: final byte[] crlf = { 13, 10 };
104: StringBuffer boundaryBuf = new StringBuffer();
105: length -= appendRequestData( boundaryBuf, crlf );
106: boundary = boundaryBuf.toString();
107: }
108: else {
109: boundary = "--"+boundary;
110: length -= append;
111: }
112: }
113: */
114: public boolean hasMoreHeaders() {
115: return (bufferEntries > 0) || (contentLength > 0);
116: }
117:
118: public int arrayIndexOf(byte[] buf, byte[] pat) {
119: return arrayIndexOf(buf, buf.length, pat, 0);
120: }
121:
122: public int arrayIndexOf(byte[] buf, int length, byte[] pat) {
123: return arrayIndexOf(buf, length, pat, 0);
124: }
125:
126: public int arrayIndexOf(byte[] buf, byte[] pat, int startIdx) {
127: return arrayIndexOf(buf, buf.length, pat, startIdx);
128: }
129:
130: public int arrayIndexOf(byte[] buf, int length, byte[] pat,
131: int startIdx) {
132: return (new String(buf, 0, startIdx, length - startIdx))
133: .indexOf(new String(pat, 0), 0)
134: + startIdx;
135: }
136:
137: public void expandBuffer(int newSize) {
138: byte[] newBuffer = new byte[newSize];
139: System.arraycopy(buffer, 0, newBuffer, 0, bufferEntries);
140: buffer = newBuffer;
141: maxBufferSize = newSize;
142: }
143:
144: public void stripBuffer(int firstEntries) {
145: if ((bufferEntries -= firstEntries) > 0) {
146: System.arraycopy(buffer, firstEntries, buffer, 0,
147: bufferEntries);
148: }
149: }
150:
151: /**
152: liest den naechsten Parameter-Header aus dem Puffer
153: */
154:
155: public TKHashtable nextHeader() throws IOException {
156: boolean ok = false;
157: boolean bad = false;
158: int endIdx = -1;
159:
160: int nextStart = 0;
161: do {
162: fillBuffer(maxBufferSize);
163: //bufferStr = new String( buffer, 0, 0, bufferEntries );
164: //ok = ( (endIdx = bufferStr.indexOf( END_OF_HEADER )) >= 0 )
165: ok = ((endIdx = arrayIndexOf(buffer, END_OF_HEADER,
166: nextStart)) >= 0)
167: || (bufferEntries == 0);
168: if (!ok) {
169: bad = (contentLength <= 0);
170: if (bufferEntries == maxBufferSize) {
171: expandBuffer(maxBufferSize * 2);
172: nextStart = bufferEntries - END_OF_HEADER.length
173: + 1;
174: }
175: }
176: } while (!(ok || bad));
177:
178: if (bad)
179: throw new Error("Format-error in multipart-encoded data");
180:
181: String bufferStr = new String(buffer, 0, 0, endIdx + 2);
182: TKHashtable result = new TKHashtable();
183: if (endIdx > 0) {
184: int startPos = 0;
185: while (startPos < endIdx + 2) {
186: int colonPos = bufferStr.indexOf(':', startPos);
187: int endOfLinePos = bufferStr
188: .indexOf(CRLF, colonPos + 1);
189: result.put(bufferStr.substring(startPos, colonPos)
190: .toLowerCase(), bufferStr.substring(
191: colonPos + 2, endOfLinePos));
192: startPos = endOfLinePos + 2;
193: }
194: }
195: stripBuffer(endIdx + 4);
196: return result;
197: }
198:
199: public String nextBody() throws IOException {
200: String body = "";
201: int bodySize = 0;
202: while ((bodySize = nextBodyChunk()) != -1) {
203: body += new String(buffer, 0, 0, bodySize);
204: }
205: return body;
206: }
207:
208: public int nextBodyChunk() throws IOException {
209: if (boundaryStart != -1) {
210: // zuletzt ausgelesenen Puffer loeschen
211: if (boundaryStart >= 0) {
212: // beim letzten Aufruf wurde das Ende des Bodies erreicht?
213: if ((buffer[boundaryStart + boundaryLength] == (byte) '-')
214: && (buffer[boundaryStart + boundaryLength + 1] == (byte) '-')) {
215: // Ende-Markierung erreicht!
216: bufferEntries = 0;
217: contentLength = 0;
218: boundaryStart = -1;
219: return -1;
220: }
221: // letzten body, boundary + CRLF rausnehmen
222: stripBuffer(boundaryStart + boundaryLength + 2);
223: boundaryStart = -1;
224: return -1;
225: } else {
226: // aktueller Body passte nicht vollstaendig in Buffer
227: stripBuffer(bufferEntries - boundaryLength - 1);
228: }
229: }
230:
231: // Puffer auffuellen
232: fillBuffer(maxBufferSize);
233:
234: // Boundary suchen
235: boundaryStart = arrayIndexOf(buffer, boundary);
236:
237: // Input komplett ausgelesen und keine Boundary gefunden?
238: if ((boundaryStart < 0) && (contentLength <= 0)) {
239: throw new Error("malformed multipart POST");
240: }
241:
242: if (boundaryStart > 0)
243: // alles bis zum CRLF vor Boundary auslesen lassen
244: return boundaryStart - 2;
245:
246: // markieren, daš Body ueber mehrere Puffereintraege laueft
247: boundaryStart = -2;
248:
249: // darauf achten, dass eventuell nur das letzte
250: // Zeichen in der Boundary gefehlt hat, und deshalb
251: // muessen CRLF und Boundary-Length-1 Zeichen
252: // unausgelesen bleiben
253:
254: return bufferEntries - boundaryLength - 1;
255: }
256:
257: /**
258: liest den naechsten Block aus dem Eingabestrom,
259: wobei gew‰hrleistet wird, dass die maximale
260: Puffer maxSize nicht ueberschritten wird.
261: */
262:
263: public void fillBuffer(int maxSize) throws IOException {
264: if (contentLength <= 0)
265: return;
266:
267: int bytesToRead = maxSize - bufferEntries;// + boundaryLength + 2;
268:
269: if (bytesToRead > contentLength)
270: bytesToRead = contentLength;
271:
272: int bytesRead = dataStream.read(buffer, bufferEntries,
273: bytesToRead);
274:
275: contentLength -= bytesRead;
276: bufferEntries += bytesRead;
277: }
278:
279: public TKHashtable getParams() throws IOException {
280: TKHashtable params = new TKHashtable();
281:
282: while (hasMoreHeaders()) {
283: TKHashtable header = nextHeader();
284: String disposition = (String) header
285: .get("content-disposition");
286:
287: int nameStart = disposition.indexOf(" name=\"") + 7;
288: int nameEnd = disposition.indexOf('"', nameStart);
289: String name = disposition.substring(nameStart, nameEnd);
290:
291: String filename = null;
292: int fileStart = disposition.indexOf(" filename=\"") + 11;
293: if (fileStart >= 11) {
294: filename = disposition.substring(fileStart, disposition
295: .length() - 1);
296: }
297:
298: if (filename == null) {
299: // normaler Parameter
300: params.extend(name, nextBody());
301: } else {
302: // hochgeladenes File
303: File tempFile = TKTemporaryFile.newTempFile();
304: OutputStream temp = new FileOutputStream(tempFile);
305: int chunkSize;
306: while ((chunkSize = nextBodyChunk()) != -1) {
307: temp.write(buffer, 0, chunkSize);
308: }
309: temp.close();
310: params.extend(name, new TKUploadFileInputStream(
311: tempFile, filename, header));
312: }
313: }
314: return params;
315: }
316: }
|