mschachter 01/10/11 09:28:17
Modified: src/share/org/apache/struts/upload
BufferedMultipartInputStream.java
MultipartIterator.java
Log:
- port from 1.0 branch to address bugs #3702, #3828, and #2683
Revision Changes Path
1.6 +7 -4
jakarta-struts/src/share/org/apache/struts/upload/BufferedMultipartInputStream.java
Index: BufferedMultipartInputStream.java
===================================================================
RCS file:
/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/BufferedMultipartInputStream.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- BufferedMultipartInputStream.java 2001/09/24 16:41:37 1.5
+++ BufferedMultipartInputStream.java 2001/10/11 16:28:17 1.6
@@ -218,13 +218,16 @@
int read = read();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- while (read != -1) {
- if (read == '\n') {
- return baos.toByteArray();
- }
+
+ // return null if there are no more bytes to read
+ if( -1 == read )
+ return null;
+
+ while ((read != -1) && (read != '\n')) {
baos.write(read);
read = read();
}
+
return baos.toByteArray();
}
1.18 +95 -90
jakarta-struts/src/share/org/apache/struts/upload/MultipartIterator.java
Index: MultipartIterator.java
===================================================================
RCS file:
/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/MultipartIterator.java,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- MultipartIterator.java 2001/09/24 16:41:37 1.17
+++ MultipartIterator.java 2001/10/11 16:28:17 1.18
@@ -21,7 +21,7 @@
* <pre>
* MultipartIterator iterator = new MultipartIterator(request);
* MultipartElement element;
- *
+ *
* while ((element = iterator.getNextElement()) != null) {
* //do something with element
* }
@@ -31,64 +31,64 @@
* @author Mike Schachter
*/
public class MultipartIterator {
-
+
/**
* The maximum size in bytes of the buffer used to read lines [4K]
*/
public static int MAX_LINE_SIZE = 4096;
-
+
/**
* The request instance for this class
*/
protected HttpServletRequest request;
-
+
/**
* The input stream instance for this class
*/
protected BufferedMultipartInputStream inputStream;
-
+
/**
* The boundary for this multipart request
*/
protected String boundary;
-
+
/**
* The byte array representing the boundary for this multipart request
*/
protected byte[] boundaryBytes;
-
+
/**
* Whether or not the input stream is finished
*/
protected boolean contentRead = false;
-
+
/**
* The maximum file size in bytes allowed. Ignored if -1
*/
protected long maxSize = -1;
-
+
/**
* The total bytes read from this request
*/
protected long totalLength = 0;
-
+
/**
* The content length of this request
*/
protected int contentLength;
-
+
/**
* The size in bytes written to the filesystem at a time [20K]
*/
protected int diskBufferSize = 2 * 10240;
-
+
/**
* The amount of data read from a request at a time.
* This also represents the maximum size in bytes of
* a line read from the request [4KB]
*/
protected int bufferSize = 4096;
-
+
/**
* The temporary directory to store files
*/
@@ -97,13 +97,13 @@
/**
* Constructs a MultipartIterator with a default buffer size and no file size
* limit
- *
+ *
* @param request The multipart request to iterate
*/
public MultipartIterator(HttpServletRequest request) throws ServletException{
this(request, -1);
}
-
+
/**
* Constructs a MultipartIterator with the specified buffer size and
* no file size limit
@@ -112,10 +112,10 @@
* @param bufferSize The size in bytes that should be read from the input
* stream at a times
*/
- public MultipartIterator(HttpServletRequest request, int bufferSize) throws
ServletException {
+ public MultipartIterator(HttpServletRequest request, int bufferSize) throws
ServletException {
this (request, bufferSize, -1);
}
-
+
/**
* Constructs a MultipartIterator with the specified buffer size and
* the specified file size limit in bytes
@@ -125,18 +125,18 @@
* stream at a times
* @param maxSize The maximum size in bytes allowed for a multipart element's
data
*/
- public MultipartIterator(HttpServletRequest request, int bufferSize, long
maxSize)
+ public MultipartIterator(HttpServletRequest request, int bufferSize, long
maxSize)
throws
ServletException {
-
- this(request, bufferSize, maxSize, null);
-
+
+ this(request, bufferSize, maxSize, null);
+
}
-
+
public MultipartIterator(HttpServletRequest request,
int bufferSize,
long maxSize,
String tempDir) throws ServletException {
-
+
this.request = request;
this.maxSize = maxSize;
if (bufferSize > -1) {
@@ -151,7 +151,7 @@
}
parseRequest();
}
-
+
/**
* Retrieves the next element in the iterator if one exists.
*
@@ -166,18 +166,18 @@
//retrieve the "Content-Disposition" header
//and parse
String disposition = readLine();
-
-
+
+
if ((disposition != null) &&
(disposition.startsWith("Content-Disposition"))) {
String name = parseDispositionName(disposition);
String filename = parseDispositionFilename(disposition);
-
+
String contentType = null;
boolean isFile = (filename != null);
-
+
if (isFile) {
filename = new File(filename).getName();
-
+
//check for windows filenames,
//from linux jdk's the entire filepath
//isn't parsed correctly from File.getName()
@@ -187,29 +187,29 @@
colonIndex = filename.indexOf("\\\\");
}
int slashIndex = filename.lastIndexOf("\\");
-
+
if ((colonIndex > -1) && (slashIndex > -1)) {
//then consider this filename to be a full
//windows filepath, and parse it accordingly
//to retrieve just the file name
filename = filename.substring(slashIndex+1, filename.length());
}
-
+
//get the content type
contentType = readLine();
contentType = parseContentType(contentType);
}
-
-
-
+
+
+
//ignore next line (whitespace) (unless it's a file
//without content-type)
if (! ((isFile) && contentType == null)) {
readLine();
}
-
+
MultipartElement element = null;
-
+
//process a file element
if (isFile) {
try {
@@ -218,6 +218,7 @@
element = new MultipartElement(name, filename, contentType,
elementFile);
} catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
throw new ServletException("IOException while reading file
element: " + ioe.getMessage(), ioe);
}
}
@@ -240,13 +241,13 @@
textData.setLength(textData.length()-1);
}
}
-
+
//create the element
element = new MultipartElement(name, textData.toString());
}
return element;
- }
-
+ }
+
//reset stream
if (inputStream.markSupported()) {
try {
@@ -257,9 +258,9 @@
ioe.getMessage());
}
}
- return null;
+ return null;
}
-
+
/**
* Set the maximum amount of bytes read from a line at one time
*
@@ -268,7 +269,7 @@
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
-
+
/**
* Get the maximum amount of bytes read from a line at one time
*
@@ -277,7 +278,7 @@
public int getBufferSize() {
return bufferSize;
}
-
+
/**
* Set the maximum post data size allowed for a multipart request
* @param maxSize The maximum post data size in bytes, set to <code>-1</code>
@@ -286,26 +287,26 @@
public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}
-
- /**
+
+ /**
* Get the maximum post data size allowed for a multipart request
* @return The maximum post data size in bytes
*/
public long getMaxSize() {
return maxSize;
}
-
+
/**
* Handles retrieving the boundary and setting the input stream
*/
protected void parseRequest() throws ServletException {
-
+
contentLength = request.getContentLength();
-
+
//set boundary
boundary = parseBoundary(request.getContentType());
boundaryBytes = boundary.getBytes();
-
+
try {
//set the input stream
inputStream = new BufferedMultipartInputStream(request.getInputStream(),
@@ -316,23 +317,23 @@
if (inputStream.markSupported()) {
inputStream.mark(contentLength+1);
}
-
+
}
catch (IOException ioe) {
- throw new ServletException("Problem while reading request: " +
+ throw new ServletException("Problem while reading request: " +
ioe.getMessage(), ioe);
}
-
+
if ((boundary == null) || (boundary.length() < 1)) {
//try retrieving the header through more "normal" means
boundary = parseBoundary(request.getHeader("Content-type"));
}
-
+
if ((boundary == null) || (boundary.length() < 1)) {
throw new ServletException("MultipartIterator: cannot retrieve boundary
" +
"for multipart request");
}
-
+
//read first line
try {
String firstLine = readLine();
@@ -350,37 +351,39 @@
throw new ServletException("MultipartIterator: encoding \"ISO-8859-1\"
not supported");
}
}
-
+
/**
- * Parses a content-type String for the boundary. Appends a
+ * Parses a content-type String for the boundary. Appends a
* "--" to the beginning of the boundary, because thats the
* real boundary as opposed to the shortened one in the
* content type.
*/
public static String parseBoundary(String contentType) {
if (contentType.lastIndexOf("boundary=") != -1) {
- String _boundary = "--" +
+ String _boundary = "--" +
contentType.substring(contentType.lastIndexOf("boundary=")+9);
if (_boundary.endsWith("\n")) {
//strip it off
return _boundary.substring(0, _boundary.length()-1);
}
- return _boundary;
+ return _boundary;
}
- return null;
+ return null;
}
-
+
/**
* Parses the "Content-Type" line of a multipart form for a content type
*
- * @param contentTypeString A String reprsenting the Content-Type line,
+ * @param contentTypeString A String reprsenting the Content-Type line,
* with a trailing "\n"
* @return The content type specified, or <code>null</code> if one can't be
* found.
*/
public static String parseContentType(String contentTypeString) {
int nameIndex = contentTypeString.indexOf("Content-Type: ");
-
+ if (nameIndex == -1)
+ nameIndex = contentTypeString.indexOf("\n");
+
if (nameIndex != -1) {
int endLineIndex = contentTypeString.indexOf("\n");
if (endLineIndex == -1) {
@@ -390,10 +393,10 @@
}
return null;
}
-
+
/**
* Retrieves the "name" attribute from a content disposition line
- *
+ *
* @param dispositionString The entire "Content-disposition" string
* @return <code>null</code> if no name could be found, otherwise,
* returns the name
@@ -402,8 +405,8 @@
public static String parseDispositionName(String dispositionString) {
return parseForAttribute("name", dispositionString);
}
-
- /**
+
+ /**
* Retrieves the "filename" attribute from a content disposition line
*
* @param dispositionString The entire "Content-disposition" string
@@ -414,8 +417,8 @@
public static String parseDispositionFilename(String dispositionString) {
return parseForAttribute("filename", dispositionString);
}
-
-
+
+
/**
* Parses a string looking for a attribute-value pair, and returns the value.
* For example:
@@ -424,7 +427,7 @@
* MultipartIterator.parseForAttribute(parseString, "name");
* </pre>
* That will return "bob".
- *
+ *
* @param attribute The name of the attribute you're trying to get
* @param parseString The string to retrieve the value from
* @return The value of the attribute, or <code>null</code> if none could be
found
@@ -432,17 +435,17 @@
public static String parseForAttribute(String attribute, String parseString) {
int nameIndex = parseString.indexOf(attribute + "=\"");
if (nameIndex != -1) {
-
+
int endQuoteIndex = parseString.indexOf("\"",
nameIndex+attribute.length()+3);
-
+
if (endQuoteIndex != -1) {
return parseString.substring(nameIndex+attribute.length()+2,
endQuoteIndex);
}
return "";
- }
+ }
return null;
}
-
+
/**
* Reads the input stream until it reaches a new line
*/
@@ -450,42 +453,44 @@
byte[] bufferByte;
int bytesRead;
-
+
if (totalLength >= contentLength) {
return null;
}
-
+
try {
bufferByte = inputStream.readLine();
+ if (bufferByte == null)
+ return null;
bytesRead = bufferByte.length;
}
catch (IOException ioe) {
- throw new ServletException("IOException while reading multipart
request: " +
+ throw new ServletException("IOException while reading multipart
request: " +
ioe.getMessage());
}
if (bytesRead == -1) {
return null;
}
-
+
totalLength += bytesRead;
return new String(bufferByte, 0, bytesRead, "ISO-8859-1");
}
-
+
/**
* Creates a file on disk from the current mulitpart element
* @param fileName the name of the multipart file
*/
protected File createLocalFile() throws IOException {
-
+
File tempFile = File.createTempFile("strts", null, new File(tempDir));
BufferedOutputStream fos = new BufferedOutputStream(new
FileOutputStream(tempFile),
diskBufferSize);
byte[] lineBuffer = inputStream.readLine();
int bytesRead = lineBuffer.length;
-
+
boolean cutCarriage = false;
boolean cutNewline = false;
-
+
try {
while ((bytesRead != -1) && (!equals(lineBuffer, 0,
boundaryBytes.length,
boundaryBytes))) {
@@ -514,12 +519,12 @@
tempFile.delete();
throw ioe;
}
-
- fos.flush();
+
+ fos.flush();
fos.close();
return tempFile;
}
-
+
/**
* Checks bytes for equality. Two byte arrays are equal if
* each of their elements are the same. This method checks
@@ -528,21 +533,21 @@
* @param comp The byte to compare to <code>source</code>
* @param offset The offset to start at in <code>comp</code>
* @param length The length of <code>comp</code> to compare to
- * @param source The reference byte to test for equality
+ * @param source The reference byte array to test for equality
*/
public static boolean equals(byte[] comp, int offset, int length,
byte[] source) {
-
- if (length != source.length) {
- return false;
+
+ if ((length != source.length) || (comp.length - offset < length)) {
+ return false;
}
-
+
for (int i = 0; i < length; i++) {
if (comp[offset+i] != source[i]) {
return false;
}
}
- return true;
+ return true;
}
}