/*******************************************************************************
* Copyright 2015 xWic group (http://www.xwic.de)
*
* Licensed under the Apache License, Version 2.0 (the "License").
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*******************************************************************************/
package de.jwic.upload;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletInputStream;
/**
* Parst den Inhalt einer Multipart / Data Form
*/
public class ContentParser {
private ServletInputStream in;
private String boundary;
private byte[] buf = new byte[8 * 1024];
private Content content;
/**
* Parst den Inhalt einer Multipart/Data Form
*/
public ContentParser(HttpServletRequest req, long maxSize) throws IOException {
// uberprufe Inhalt auf enctype="multipart/form-data"
// Zugriff erfolgt auf zwei Wegen
String sType = null;
String sType1 = req.getHeader("Content-Type");
String sType2 = req.getContentType();
// Wenn einer von beiden Werten NULL ist, einfach den anderen nehmen.
if (sType1 == null && sType2 != null) {
sType = sType2;
}
else if (sType2 == null && sType1 != null) {
sType = sType1;
}
// Wenn keiner von beiden NULL ist, den langeren nehmen.
else if (sType1 != null && sType2 != null) {
sType = (sType1.length() > sType2.length() ? sType1 : sType2);
}
// Wenn beide NULL sind handelt es sich vermutlich um kein enctype="multipart/form-date"
if (sType == null ||
!sType.toLowerCase().startsWith("multipart/form-data")) {
throw new IOException("Posted content type isn't multipart/form-data");
}
// uberprufen der Grosse des Inhaltes.
int length = req.getContentLength();
if (length > maxSize) {
throw new IOException("Posted content length of " + length +
" exceeds limit of " + maxSize);
}
// Ermitteln der Grenzlinie
boundary = extractBoundary(sType);
if (boundary == null) {
throw new IOException("Separation boundary was not specified");
}
in = req.getInputStream();
// Erste Zeile einlesen
String line = readLine();
if (line == null) {
throw new IOException("Corrupt form data: premature ending");
}
// Ergebnis uberprufen ob boundary = Erstezeile
if (!line.startsWith(boundary)) {
throw new IOException("Corrupt form data: no leading boundary: " +
line + " != " + boundary);
}
}
/**
* Ermittelt die Grenzlinie die die einzelnen Felder begrenzt
* Erstellungsdatum: (08.03.01 19:41:46)
* @param line java.lang.String
*/
private String extractBoundary(String line) {
int index = line.lastIndexOf("boundary=");
if (index == -1) {
return null;
}
String bound = line.substring(index + 9);
if (bound.charAt(0) == '"') {
index = bound.lastIndexOf('"');
bound = bound.substring(1, index);
}
bound = "--" + bound;
return bound;
}
/**
* Gibt den ContentType zuruck
* Erstellungsdatum: (08.03.01 19:41:46)
* @param line java.lang.String
*/
private String extractContentType(String line) throws IOException {
String contentType = null;
String origline = line;
line = origline.toLowerCase();
if (line.startsWith("content-type")) {
int start = line.indexOf(" ");
if (start == -1) {
throw new IOException("Content type corrupt: " + origline);
}
contentType = line.substring(start + 1);
}
else if (line.length() != 0) {
throw new IOException("Malformed line after disposition: " + origline);
}
return contentType;
}
/**
* Gibt die Inhaltsangaben zuruck
* Erstellungsdatum: (08.03.01 19:41:46)
* @param line java.lang.String
*/
private String[] extractDispositionInfo(String line) throws IOException {
String[] retval = new String[4];
String origline = line;
line = origline.toLowerCase();
// Lese "content disposition" und "form-data" ein.
int start = line.indexOf("content-disposition: ");
int end = line.indexOf(";");
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String disposition = line.substring(start + 21, end);
if (!disposition.equals("form-data")) {
throw new IOException("Invalid content disposition: " + disposition);
}
// Lese "name" ein.
start = line.indexOf("name=\"", end);
end = line.indexOf("\"", start + 7);
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String name = origline.substring(start + 6, end);
// Lese "filename" ein, wenn vorhanden.
String filename = null;
String origname = null;
start = line.indexOf("filename=\"", end + 2);
end = line.indexOf("\"", start + 10);
if (start != -1 && end != -1) {
filename = origline.substring(start + 10, end);
origname = filename;
int slash =
Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
if (slash > -1) {
filename = filename.substring(slash + 1);
}
}
//Bef�lle R�ckgabe Array
retval[0] = disposition;
retval[1] = name;
retval[2] = filename;
retval[3] = origname;
return retval;
}
/**
* Liest die nachste Zeile ein
* Erstellungsdatum: (08.03.01 19:41:46)
* @return java.lang.String
*/
private String readLine() throws IOException {
StringBuffer sbuf = new StringBuffer();
int result;
//String line;
do {
result = in.readLine(buf, 0, buf.length);
if (result != -1) {
sbuf.append(new String(buf, 0, result, "ISO-8859-1"));
}
} while (result == buf.length);
if (sbuf.length() == 0) {
return null;
}
sbuf.setLength(sbuf.length() - 2); // die letzten beiden Zeichen abschneiden \r\n
return sbuf.toString();
}
/**
* Gibt den nachsten Inhalt / Feld zuruck
* Erstellungsdatum: (08.03.01 19:41:46)
* @exception java.io.IOException
*/
public Content readNextContent() throws IOException {
if (content != null) {
content.close();
content = null;
}
Vector<String> headers = new Vector<String>();
String line = readLine();
if (line == null) {
return null;
}else if (line.length() == 0) {
return null;
}
headers.addElement(line);
while ((line = readLine()) != null && (line.length() > 0)) {
headers.addElement(line);
}
if (line == null) {
return null;
}
String name = null;
String filename = null;
//String origname = null;
//String contentType = "text/plain"; // rfc1867 says this is the default
//Content-Disposition und Content-Type auswerten
Enumeration<String> en = headers.elements();
while (en.hasMoreElements()) {
String headerline = en.nextElement();
if (headerline.toLowerCase().startsWith("content-disposition:")) {
String[] dispInfo = extractDispositionInfo(headerline);
name = dispInfo[1];
filename = dispInfo[2];
//origname = dispInfo[3];
}
else if (headerline.toLowerCase().startsWith("content-type:")) {
String type = extractContentType(headerline);
if (type != null) {
//contentType = type;
}
}
}
// Inhalt einlesen
if (filename == null) {
content = new Content(in, boundary, filename, name);
}else {
if (filename.equals("")) filename = null;
content = new Content(in, boundary, filename, name);
}
return content;
}
}