Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItem.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItem.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItem.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.File; +import org.apache.commons.fileupload.disk.DiskFileItem; + +/** + *

The default implementation of the + * {@link org.apache.commons.fileupload.FileItem FileItem} interface. + * + *

After retrieving an instance of this class from a {@link + * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see + * {@link org.apache.commons.fileupload.DiskFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + * @version $Id$ + * + * @deprecated 1.1 Use DiskFileItem instead. + */ +@Deprecated +public class DefaultFileItem + extends DiskFileItem { + + // ----------------------------------------------------------- Constructors + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = 4088572813833518255L; + + /** + * Constructs a new DefaultFileItem instance. + * + * @param fieldName The name of the form field. + * @param contentType The content type passed by the browser or + * null if not specified. + * @param isFormField Whether or not this item is a plain form field, as + * opposed to a file upload. + * @param fileName The original filename in the user's filesystem, or + * null if not specified. + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + * + * @deprecated 1.1 Use DiskFileItem instead. + */ + @Deprecated + public DefaultFileItem(String fieldName, String contentType, + boolean isFormField, String fileName, int sizeThreshold, + File repository) { + super(fieldName, contentType, isFormField, fileName, sizeThreshold, + repository); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItemFactory.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItemFactory.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DefaultFileItemFactory.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.File; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; + +/** + *

The default {@link org.apache.commons.fileupload.FileItemFactory} + * implementation. This implementation creates + * {@link org.apache.commons.fileupload.FileItem} instances which keep their + * content either in memory, for smaller items, or in a temporary file on disk, + * for larger items. The size threshold, above which content will be stored on + * disk, is configurable, as is the directory in which temporary files will be + * created.

+ * + * If not otherwise configured, the default configuration values are as + * follows: + * + * + * @version $Id$ + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ +@Deprecated +public class DefaultFileItemFactory extends DiskFileItemFactory { + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an unconfigured instance of this class. The resulting factory + * may be configured by calling the appropriate setter methods. + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public DefaultFileItemFactory() { + super(); + } + + /** + * Constructs a preconfigured instance of this class. + * + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public DefaultFileItemFactory(int sizeThreshold, File repository) { + super(sizeThreshold, repository); + } + + // --------------------------------------------------------- Public Methods + + /** + * Create a new {@link org.apache.commons.fileupload.DefaultFileItem} + * instance from the supplied parameters and the local factory + * configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Override + @Deprecated + public FileItem createItem( + String fieldName, + String contentType, + boolean isFormField, + String fileName + ) { + return new DefaultFileItem(fieldName, contentType, + isFormField, fileName, getSizeThreshold(), getRepository()); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DiskFileUpload.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DiskFileUpload.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/DiskFileUpload.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.File; +import java.util.List; +import javax.servlet.http.HttpServletRequest; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(HttpServletRequest)} to acquire a list of {@link + * org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

Individual parts will be stored in temporary disk storage or in memory, + * depending on their size, and will be available as {@link + * org.apache.commons.fileupload.FileItem}s.

+ * + * @version $Id$ + * + * @deprecated 1.1 Use ServletFileUpload together with + * DiskFileItemFactory instead. + */ +@Deprecated +public class DiskFileUpload + extends FileUploadBase { + + // ----------------------------------------------------------- Data members + + /** + * The factory to use to create new form items. + */ + private DefaultFileItemFactory fileItemFactory; + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an instance of this class which uses the default factory to + * create FileItem instances. + * + * @see #DiskFileUpload(DefaultFileItemFactory fileItemFactory) + * + * @deprecated 1.1 Use FileUpload instead. + */ + @Deprecated + public DiskFileUpload() { + super(); + this.fileItemFactory = new DefaultFileItemFactory(); + } + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see #DiskFileUpload() + * @param fileItemFactory The file item factory to use. + * + * @deprecated 1.1 Use FileUpload instead. + */ + @Deprecated + public DiskFileUpload(DefaultFileItemFactory fileItemFactory) { + super(); + this.fileItemFactory = fileItemFactory; + } + + // ----------------------------------------------------- Property accessors + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + * + * @deprecated 1.1 Use FileUpload instead. + */ + @Override + @Deprecated + public FileItemFactory getFileItemFactory() { + return fileItemFactory; + } + + /** + * Sets the factory class to use when creating file items. The factory must + * be an instance of DefaultFileItemFactory or a subclass + * thereof, or else a ClassCastException will be thrown. + * + * @param factory The factory class for new file items. + * + * @deprecated 1.1 Use FileUpload instead. + */ + @Override + @Deprecated + public void setFileItemFactory(FileItemFactory factory) { + this.fileItemFactory = (DefaultFileItemFactory) factory; + } + + /** + * Returns the size threshold beyond which files are written directly to + * disk. + * + * @return The size threshold, in bytes. + * + * @see #setSizeThreshold(int) + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public int getSizeThreshold() { + return fileItemFactory.getSizeThreshold(); + } + + /** + * Sets the size threshold beyond which files are written directly to disk. + * + * @param sizeThreshold The size threshold, in bytes. + * + * @see #getSizeThreshold() + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public void setSizeThreshold(int sizeThreshold) { + fileItemFactory.setSizeThreshold(sizeThreshold); + } + + /** + * Returns the location used to temporarily store files that are larger + * than the configured size threshold. + * + * @return The path to the temporary file location. + * + * @see #setRepositoryPath(String) + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public String getRepositoryPath() { + return fileItemFactory.getRepository().getPath(); + } + + /** + * Sets the location used to temporarily store files that are larger + * than the configured size threshold. + * + * @param repositoryPath The path to the temporary file location. + * + * @see #getRepositoryPath() + * + * @deprecated 1.1 Use DiskFileItemFactory instead. + */ + @Deprecated + public void setRepositoryPath(String repositoryPath) { + fileItemFactory.setRepository(new File(repositoryPath)); + } + + // --------------------------------------------------------- Public methods + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. If files are stored + * on disk, the path is given by getRepository(). + * + * @param req The servlet request to be parsed. Must be non-null. + * @param sizeThreshold The max size in bytes to be stored in memory. + * @param sizeMax The maximum allowed upload size, in bytes. + * @param path The location where the files should be stored. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @deprecated 1.1 Use ServletFileUpload instead. + */ + @Deprecated + public List parseRequest(HttpServletRequest req, + int sizeThreshold, + long sizeMax, String path) + throws FileUploadException { + setSizeThreshold(sizeThreshold); + setSizeMax(sizeMax); + setRepositoryPath(path); + return parseRequest(req); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItem.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItem.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItem.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; + +/** + *

This class represents a file or form item that was received within a + * multipart/form-data POST request. + * + *

After retrieving an instance of this class from a {@link + * org.apache.commons.fileupload.FileUpload FileUpload} instance (see + * {@link org.apache.commons.fileupload.servlet.ServletFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of the file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + *

While this interface does not extend + * javax.activation.DataSource per se (to avoid a seldom used + * dependency), several of the defined methods are specifically defined with + * the same signatures as methods in that interface. This allows an + * implementation of this interface to also implement + * javax.activation.DataSource with minimal additional work. + * + * @version $Id$ + * @since 1.3 additionally implements FileItemHeadersSupport + */ +public interface FileItem extends Serializable, FileItemHeadersSupport { + + // ------------------------------- Methods from javax.activation.DataSource + + /** + * Returns an {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @return An {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @throws IOException if an error occurs. + */ + InputStream getInputStream() throws IOException; + + /** + * Returns the content type passed by the browser or null if + * not defined. + * + * @return The content type passed by the browser or null if + * not defined. + */ + String getContentType(); + + /** + * Returns the original filename in the client's filesystem, as provided by + * the browser (or other client software). In most cases, this will be the + * base file name, without path information. However, some clients, such as + * the Opera browser, do include path information. + * + * @return The original filename in the client's filesystem. + * @throws InvalidFileNameException The file name contains a NUL character, + * which might be an indicator of a security attack. If you intend to + * use the file name anyways, catch the exception and use + * InvalidFileNameException#getName(). + */ + String getName(); + + // ------------------------------------------------------- FileItem methods + + /** + * Provides a hint as to whether or not the file contents will be read + * from memory. + * + * @return true if the file contents will be read from memory; + * false otherwise. + */ + boolean isInMemory(); + + /** + * Returns the size of the file item. + * + * @return The size of the file item, in bytes. + */ + long getSize(); + + /** + * Returns the contents of the file item as an array of bytes. + * + * @return The contents of the file item as an array of bytes. + */ + byte[] get(); + + /** + * Returns the contents of the file item as a String, using the specified + * encoding. This method uses {@link #get()} to retrieve the + * contents of the item. + * + * @param encoding The character encoding to use. + * + * @return The contents of the item, as a string. + * + * @throws UnsupportedEncodingException if the requested character + * encoding is not available. + */ + String getString(String encoding) throws UnsupportedEncodingException; + + /** + * Returns the contents of the file item as a String, using the default + * character encoding. This method uses {@link #get()} to retrieve the + * contents of the item. + * + * @return The contents of the item, as a string. + */ + String getString(); + + /** + * A convenience method to write an uploaded item to disk. The client code + * is not concerned with whether or not the item is stored in memory, or on + * disk in a temporary location. They just want to write the uploaded item + * to a file. + *

+ * This method is not guaranteed to succeed if called more than once for + * the same item. This allows a particular implementation to use, for + * example, file renaming, where possible, rather than copying all of the + * underlying data, thus gaining a significant performance benefit. + * + * @param file The File into which the uploaded item should + * be stored. + * + * @throws Exception if an error occurs. + */ + void write(File file) throws Exception; + + /** + * Deletes the underlying storage for a file item, including deleting any + * associated temporary disk file. Although this storage will be deleted + * automatically when the FileItem instance is garbage + * collected, this method can be used to ensure that this is done at an + * earlier time, thus preserving system resources. + */ + void delete(); + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + /** + * Sets the field name used to reference this file item. + * + * @param name The name of the form field. + */ + void setFieldName(String name); + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + boolean isFormField(); + + /** + * Specifies whether or not a FileItem instance represents + * a simple form field. + * + * @param state true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + void setFormField(boolean state); + + /** + * Returns an {@link java.io.OutputStream OutputStream} that can + * be used for storing the contents of the file. + * + * @return An {@link java.io.OutputStream OutputStream} that can be used + * for storing the contensts of the file. + * + * @throws IOException if an error occurs. + */ + OutputStream getOutputStream() throws IOException; + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemFactory.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemFactory.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemFactory.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + *

A factory interface for creating {@link FileItem} instances. Factories + * can provide their own custom configuration, over and above that provided + * by the default file upload implementation.

+ * + * @version $Id$ + */ +public interface FileItemFactory { + + /** + * Create a new {@link FileItem} instance from the supplied parameters and + * any local factory configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + */ + FileItem createItem( + String fieldName, + String contentType, + boolean isFormField, + String fileName + ); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeaders.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeaders.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeaders.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.util.Iterator; + +/** + *

This class provides support for accessing the headers for a file or form + * item that was received within a multipart/form-data POST + * request.

+ * + * @since 1.2.1 + * + * @version $Id$ + */ +public interface FileItemHeaders { + + /** + * Returns the value of the specified part header as a String. + * + * If the part did not include a header of the specified name, this method + * return null. If there are multiple headers with the same + * name, this method returns the first header in the item. The header + * name is case insensitive. + * + * @param name a String specifying the header name + * @return a String containing the value of the requested + * header, or null if the item does not have a header + * of that name + */ + String getHeader(String name); + + /** + *

+ * Returns all the values of the specified item header as an + * Iterator of String objects. + *

+ *

+ * If the item did not include any headers of the specified name, this + * method returns an empty Iterator. The header name is + * case insensitive. + *

+ * + * @param name a String specifying the header name + * @return an Iterator containing the values of the + * requested header. If the item does not have any headers of + * that name, return an empty Iterator + */ + Iterator getHeaders(String name); + + /** + *

+ * Returns an Iterator of all the header names. + *

+ * + * @return an Iterator containing all of the names of + * headers provided with this file item. If the item does not have + * any headers return an empty Iterator + */ + Iterator getHeaderNames(); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeadersSupport.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeadersSupport.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemHeadersSupport.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + * Interface that will indicate that {@link FileItem} or {@link FileItemStream} + * implementations will accept the headers read for the item. + * + * @since 1.2.1 + * + * @see FileItem + * @see FileItemStream + * + * @version $Id$ + */ +public interface FileItemHeadersSupport { + + /** + * Returns the collection of headers defined locally within this item. + * + * @return the {@link FileItemHeaders} present for this item. + */ + FileItemHeaders getHeaders(); + + /** + * Sets the headers read from within an item. Implementations of + * {@link FileItem} or {@link FileItemStream} should implement this + * interface to be able to get the raw headers found within the item + * header block. + * + * @param headers the instance that holds onto the headers + * for this instance. + */ + void setHeaders(FileItemHeaders headers); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemIterator.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemIterator.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemIterator.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.IOException; + +/** + * An iterator, as returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}. + * + * @version $Id$ + */ +public interface FileItemIterator { + + /** + * Returns, whether another instance of {@link FileItemStream} + * is available. + * + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return True, if one or more additional file items + * are available, otherwise false. + */ + boolean hasNext() throws FileUploadException, IOException; + + /** + * Returns the next available {@link FileItemStream}. + * + * @throws java.util.NoSuchElementException No more items are available. Use + * {@link #hasNext()} to prevent this exception. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return FileItemStream instance, which provides + * access to the next file item. + */ + FileItemStream next() throws FileUploadException, IOException; + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemStream.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemStream.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileItemStream.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.IOException; +import java.io.InputStream; + +/** + *

This interface provides access to a file or form item that was + * received within a multipart/form-data POST request. + * The items contents are retrieved by calling {@link #openStream()}.

+ *

Instances of this class are created by accessing the + * iterator, returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}.

+ *

Note: There is an interaction between the iterator and + * its associated instances of {@link FileItemStream}: By invoking + * {@link java.util.Iterator#hasNext()} on the iterator, you discard all data, + * which hasn't been read so far from the previous data.

+ * + * @version $Id$ + */ +public interface FileItemStream extends FileItemHeadersSupport { + + /** + * This exception is thrown, if an attempt is made to read + * data from the {@link InputStream}, which has been returned + * by {@link FileItemStream#openStream()}, after + * {@link java.util.Iterator#hasNext()} has been invoked on the + * iterator, which created the {@link FileItemStream}. + */ + public static class ItemSkippedException extends IOException { + + /** + * The exceptions serial version UID, which is being used + * when serializing an exception instance. + */ + private static final long serialVersionUID = -7280778431581963740L; + + } + + /** + * Creates an {@link InputStream}, which allows to read the + * items contents. + * + * @return The input stream, from which the items data may + * be read. + * @throws IllegalStateException The method was already invoked on + * this item. It is not possible to recreate the data stream. + * @throws IOException An I/O error occurred. + * @see ItemSkippedException + */ + InputStream openStream() throws IOException; + + /** + * Returns the content type passed by the browser or null if + * not defined. + * + * @return The content type passed by the browser or null if + * not defined. + */ + String getContentType(); + + /** + * Returns the original filename in the client's filesystem, as provided by + * the browser (or other client software). In most cases, this will be the + * base file name, without path information. However, some clients, such as + * the Opera browser, do include path information. + * + * @return The original filename in the client's filesystem. + */ + String getName(); + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + boolean isFormField(); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUpload.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUpload.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUpload.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(RequestContext)} to acquire a list + * of {@link org.apache.commons.fileupload.FileItem FileItems} associated + * with a given HTML widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @version $Id$ + */ +public class FileUpload + extends FileUploadBase { + + // ----------------------------------------------------------- Data members + + /** + * The factory to use to create new form items. + */ + private FileItemFactory fileItemFactory; + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an uninitialised instance of this class. + * + * A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see #FileUpload(FileItemFactory) + */ + public FileUpload() { + super(); + } + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see #FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public FileUpload(FileItemFactory fileItemFactory) { + super(); + this.fileItemFactory = fileItemFactory; + } + + // ----------------------------------------------------- Property accessors + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + */ + @Override + public FileItemFactory getFileItemFactory() { + return fileItemFactory; + } + + /** + * Sets the factory class to use when creating file items. + * + * @param factory The factory class for new file items. + */ + @Override + public void setFileItemFactory(FileItemFactory factory) { + this.fileItemFactory = factory; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadBase.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadBase.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadBase.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,1491 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.fileupload.MultipartStream.ItemInputStream; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.fileupload.servlet.ServletRequestContext; +import org.apache.commons.fileupload.util.Closeable; +import org.apache.commons.fileupload.util.FileItemHeadersImpl; +import org.apache.commons.fileupload.util.LimitedInputStream; +import org.apache.commons.fileupload.util.Streams; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(RequestContext)} to acquire a list of {@link + * org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @version $Id$ + */ +public abstract class FileUploadBase { + + // ---------------------------------------------------------- Class methods + + /** + *

Utility method that determines whether the request contains multipart + * content.

+ * + *

NOTE:This method will be moved to the + * ServletFileUpload class after the FileUpload 1.1 release. + * Unfortunately, since this method is static, it is not possible to + * provide its replacement until this method is removed.

+ * + * @param ctx The request context to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent(RequestContext ctx) { + String contentType = ctx.getContentType(); + if (contentType == null) { + return false; + } + if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) { + return true; + } + return false; + } + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param req The servlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + * + * @deprecated 1.1 Use the method on ServletFileUpload instead. + */ + @Deprecated + public static boolean isMultipartContent(HttpServletRequest req) { + return ServletFileUpload.isMultipartContent(req); + } + + // ----------------------------------------------------- Manifest constants + + /** + * HTTP content type header name. + */ + public static final String CONTENT_TYPE = "Content-type"; + + /** + * HTTP content disposition header name. + */ + public static final String CONTENT_DISPOSITION = "Content-disposition"; + + /** + * HTTP content length header name. + */ + public static final String CONTENT_LENGTH = "Content-length"; + + /** + * Content-disposition value for form data. + */ + public static final String FORM_DATA = "form-data"; + + /** + * Content-disposition value for file attachment. + */ + public static final String ATTACHMENT = "attachment"; + + /** + * Part of HTTP content type header. + */ + public static final String MULTIPART = "multipart/"; + + /** + * HTTP content type header for multipart forms. + */ + public static final String MULTIPART_FORM_DATA = "multipart/form-data"; + + /** + * HTTP content type header for multiple uploads. + */ + public static final String MULTIPART_MIXED = "multipart/mixed"; + + /** + * The maximum length of a single header line that will be parsed + * (1024 bytes). + * @deprecated This constant is no longer used. As of commons-fileupload + * 1.2, the only applicable limit is the total size of a parts headers, + * {@link MultipartStream#HEADER_PART_SIZE_MAX}. + */ + @Deprecated + public static final int MAX_HEADER_SIZE = 1024; + + // ----------------------------------------------------------- Data members + + /** + * The maximum size permitted for the complete request, as opposed to + * {@link #fileSizeMax}. A value of -1 indicates no maximum. + */ + private long sizeMax = -1; + + /** + * The maximum size permitted for a single uploaded file, as opposed + * to {@link #sizeMax}. A value of -1 indicates no maximum. + */ + private long fileSizeMax = -1; + + /** + * The content encoding to use when reading part headers. + */ + private String headerEncoding; + + /** + * The progress listener. + */ + private ProgressListener listener; + + // ----------------------------------------------------- Property accessors + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + */ + public abstract FileItemFactory getFileItemFactory(); + + /** + * Sets the factory class to use when creating file items. + * + * @param factory The factory class for new file items. + */ + public abstract void setFileItemFactory(FileItemFactory factory); + + /** + * Returns the maximum allowed size of a complete request, as opposed + * to {@link #getFileSizeMax()}. + * + * @return The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #setSizeMax(long) + * + */ + public long getSizeMax() { + return sizeMax; + } + + /** + * Sets the maximum allowed size of a complete request, as opposed + * to {@link #setFileSizeMax(long)}. + * + * @param sizeMax The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #getSizeMax() + * + */ + public void setSizeMax(long sizeMax) { + this.sizeMax = sizeMax; + } + + /** + * Returns the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. + * + * @see #setFileSizeMax(long) + * @return Maximum size of a single uploaded file. + */ + public long getFileSizeMax() { + return fileSizeMax; + } + + /** + * Sets the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. + * + * @see #getFileSizeMax() + * @param fileSizeMax Maximum size of a single uploaded file. + */ + public void setFileSizeMax(long fileSizeMax) { + this.fileSizeMax = fileSizeMax; + } + + /** + * Retrieves the character encoding used when reading the headers of an + * individual part. When not specified, or null, the request + * encoding is used. If that is also not specified, or null, + * the platform default encoding is used. + * + * @return The encoding used to read part headers. + */ + public String getHeaderEncoding() { + return headerEncoding; + } + + /** + * Specifies the character encoding to be used when reading the headers of + * individual part. When not specified, or null, the request + * encoding is used. If that is also not specified, or null, + * the platform default encoding is used. + * + * @param encoding The encoding used to read part headers. + */ + public void setHeaderEncoding(String encoding) { + headerEncoding = encoding; + } + + // --------------------------------------------------------- Public methods + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param req The servlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @deprecated 1.1 Use {@link ServletFileUpload#parseRequest(HttpServletRequest)} instead. + */ + @Deprecated + public List parseRequest(HttpServletRequest req) + throws FileUploadException { + return parseRequest(new ServletRequestContext(req)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(RequestContext ctx) + throws FileUploadException, IOException { + try { + return new FileItemIteratorImpl(ctx); + } catch (FileUploadIOException e) { + // unwrap encapsulated SizeException + throw (FileUploadException) e.getCause(); + } + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public List parseRequest(RequestContext ctx) + throws FileUploadException { + List items = new ArrayList(); + boolean successful = false; + try { + FileItemIterator iter = getItemIterator(ctx); + FileItemFactory fac = getFileItemFactory(); + if (fac == null) { + throw new NullPointerException("No FileItemFactory has been set."); + } + while (iter.hasNext()) { + final FileItemStream item = iter.next(); + // Don't use getName() here to prevent an InvalidFileNameException. + final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name; + FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), + item.isFormField(), fileName); + items.add(fileItem); + try { + Streams.copy(item.openStream(), fileItem.getOutputStream(), true); + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new IOFileUploadException(format("Processing of %s request failed. %s", + MULTIPART_FORM_DATA, e.getMessage()), e); + } + final FileItemHeaders fih = item.getHeaders(); + fileItem.setHeaders(fih); + } + successful = true; + return items; + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new FileUploadException(e.getMessage(), e); + } finally { + if (!successful) { + for (FileItem fileItem : items) { + try { + fileItem.delete(); + } catch (Throwable e) { + // ignore it + } + } + } + } + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * + * @return A map of FileItem instances parsed from the request. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @since 1.3 + */ + public Map> parseParameterMap(RequestContext ctx) + throws FileUploadException { + final List items = parseRequest(ctx); + final Map> itemsMap = new HashMap>(items.size()); + + for (FileItem fileItem : items) { + String fieldName = fileItem.getFieldName(); + List mappedItems = itemsMap.get(fieldName); + + if (mappedItems == null) { + mappedItems = new ArrayList(); + itemsMap.put(fieldName, mappedItems); + } + + mappedItems.add(fileItem); + } + + return itemsMap; + } + + // ------------------------------------------------------ Protected methods + + /** + * Retrieves the boundary from the Content-type header. + * + * @param contentType The value of the content type header from which to + * extract the boundary value. + * + * @return The boundary, as a byte array. + */ + protected byte[] getBoundary(String contentType) { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(contentType, new char[] {';', ','}); + String boundaryStr = params.get("boundary"); + + if (boundaryStr == null) { + return null; + } + byte[] boundary; + try { + boundary = boundaryStr.getBytes("ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + boundary = boundaryStr.getBytes(); // Intentionally falls back to default charset + } + return boundary; + } + + /** + * Retrieves the file name from the Content-disposition + * header. + * + * @param headers A Map containing the HTTP request headers. + * + * @return The file name for the current encapsulation. + * @deprecated 1.2.1 Use {@link #getFileName(FileItemHeaders)}. + */ + @Deprecated + protected String getFileName(Map headers) { + return getFileName(getHeader(headers, CONTENT_DISPOSITION)); + } + + /** + * Retrieves the file name from the Content-disposition + * header. + * + * @param headers The HTTP headers object. + * + * @return The file name for the current encapsulation. + */ + protected String getFileName(FileItemHeaders headers) { + return getFileName(headers.getHeader(CONTENT_DISPOSITION)); + } + + /** + * Returns the given content-disposition headers file name. + * @param pContentDisposition The content-disposition headers value. + * @return The file name + */ + private String getFileName(String pContentDisposition) { + String fileName = null; + if (pContentDisposition != null) { + String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH); + if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(pContentDisposition, ';'); + if (params.containsKey("filename")) { + fileName = params.get("filename"); + if (fileName != null) { + fileName = fileName.trim(); + } else { + // Even if there is no value, the parameter is present, + // so we return an empty file name rather than no file + // name. + fileName = ""; + } + } + } + } + return fileName; + } + + /** + * Retrieves the field name from the Content-disposition + * header. + * + * @param headers A Map containing the HTTP request headers. + * + * @return The field name for the current encapsulation. + */ + protected String getFieldName(FileItemHeaders headers) { + return getFieldName(headers.getHeader(CONTENT_DISPOSITION)); + } + + /** + * Returns the field name, which is given by the content-disposition + * header. + * @param pContentDisposition The content-dispositions header value. + * @return The field jake + */ + private String getFieldName(String pContentDisposition) { + String fieldName = null; + if (pContentDisposition != null + && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(pContentDisposition, ';'); + fieldName = params.get("name"); + if (fieldName != null) { + fieldName = fieldName.trim(); + } + } + return fieldName; + } + + /** + * Retrieves the field name from the Content-disposition + * header. + * + * @param headers A Map containing the HTTP request headers. + * + * @return The field name for the current encapsulation. + * @deprecated 1.2.1 Use {@link #getFieldName(FileItemHeaders)}. + */ + @Deprecated + protected String getFieldName(Map headers) { + return getFieldName(getHeader(headers, CONTENT_DISPOSITION)); + } + + /** + * Creates a new {@link FileItem} instance. + * + * @param headers A Map containing the HTTP request + * headers. + * @param isFormField Whether or not this item is a form field, as + * opposed to a file. + * + * @return A newly created FileItem instance. + * + * @throws FileUploadException if an error occurs. + * @deprecated 1.2 This method is no longer used in favour of + * internally created instances of {@link FileItem}. + */ + @Deprecated + protected FileItem createItem(Map headers, + boolean isFormField) + throws FileUploadException { + return getFileItemFactory().createItem(getFieldName(headers), + getHeader(headers, CONTENT_TYPE), + isFormField, + getFileName(headers)); + } + + /** + *

Parses the header-part and returns as key/value + * pairs. + * + *

If there are multiple headers of the same names, the name + * will map to a comma-separated list containing the values. + * + * @param headerPart The header-part of the current + * encapsulation. + * + * @return A Map containing the parsed HTTP request headers. + */ + protected FileItemHeaders getParsedHeaders(String headerPart) { + final int len = headerPart.length(); + FileItemHeadersImpl headers = newFileItemHeaders(); + int start = 0; + for (;;) { + int end = parseEndOfLine(headerPart, start); + if (start == end) { + break; + } + StringBuilder header = new StringBuilder(headerPart.substring(start, end)); + start = end + 2; + while (start < len) { + int nonWs = start; + while (nonWs < len) { + char c = headerPart.charAt(nonWs); + if (c != ' ' && c != '\t') { + break; + } + ++nonWs; + } + if (nonWs == start) { + break; + } + // Continuation line found + end = parseEndOfLine(headerPart, nonWs); + header.append(" ").append(headerPart.substring(nonWs, end)); + start = end + 2; + } + parseHeaderLine(headers, header.toString()); + } + return headers; + } + + /** + * Creates a new instance of {@link FileItemHeaders}. + * @return The new instance. + */ + protected FileItemHeadersImpl newFileItemHeaders() { + return new FileItemHeadersImpl(); + } + + /** + *

Parses the header-part and returns as key/value + * pairs. + * + *

If there are multiple headers of the same names, the name + * will map to a comma-separated list containing the values. + * + * @param headerPart The header-part of the current + * encapsulation. + * + * @return A Map containing the parsed HTTP request headers. + * @deprecated 1.2.1 Use {@link #getParsedHeaders(String)} + */ + @Deprecated + protected Map parseHeaders(String headerPart) { + FileItemHeaders headers = getParsedHeaders(headerPart); + Map result = new HashMap(); + for (Iterator iter = headers.getHeaderNames(); iter.hasNext();) { + String headerName = iter.next(); + Iterator iter2 = headers.getHeaders(headerName); + StringBuilder headerValue = new StringBuilder(iter2.next()); + while (iter2.hasNext()) { + headerValue.append(",").append(iter2.next()); + } + result.put(headerName, headerValue.toString()); + } + return result; + } + + /** + * Skips bytes until the end of the current line. + * @param headerPart The headers, which are being parsed. + * @param end Index of the last byte, which has yet been + * processed. + * @return Index of the \r\n sequence, which indicates + * end of line. + */ + private int parseEndOfLine(String headerPart, int end) { + int index = end; + for (;;) { + int offset = headerPart.indexOf('\r', index); + if (offset == -1 || offset + 1 >= headerPart.length()) { + throw new IllegalStateException( + "Expected headers to be terminated by an empty line."); + } + if (headerPart.charAt(offset + 1) == '\n') { + return offset; + } + index = offset + 1; + } + } + + /** + * Reads the next header line. + * @param headers String with all headers. + * @param header Map where to store the current header. + */ + private void parseHeaderLine(FileItemHeadersImpl headers, String header) { + final int colonOffset = header.indexOf(':'); + if (colonOffset == -1) { + // This header line is malformed, skip it. + return; + } + String headerName = header.substring(0, colonOffset).trim(); + String headerValue = + header.substring(header.indexOf(':') + 1).trim(); + headers.addHeader(headerName, headerValue); + } + + /** + * Returns the header with the specified name from the supplied map. The + * header lookup is case-insensitive. + * + * @param headers A Map containing the HTTP request headers. + * @param name The name of the header to return. + * + * @return The value of specified header, or a comma-separated list if + * there were multiple headers of that name. + * @deprecated 1.2.1 Use {@link FileItemHeaders#getHeader(String)}. + */ + @Deprecated + protected final String getHeader(Map headers, + String name) { + return headers.get(name.toLowerCase(Locale.ENGLISH)); + } + + /** + * The iterator, which is returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}. + */ + private class FileItemIteratorImpl implements FileItemIterator { + + /** + * Default implementation of {@link FileItemStream}. + */ + class FileItemStreamImpl implements FileItemStream { + + /** + * The file items content type. + */ + private final String contentType; + + /** + * The file items field name. + */ + private final String fieldName; + + /** + * The file items file name. + */ + private final String name; + + /** + * Whether the file item is a form field. + */ + private final boolean formField; + + /** + * The file items input stream. + */ + private final InputStream stream; + + /** + * Whether the file item was already opened. + */ + private boolean opened; + + /** + * The headers, if any. + */ + private FileItemHeaders headers; + + /** + * Creates a new instance. + * + * @param pName The items file name, or null. + * @param pFieldName The items field name. + * @param pContentType The items content type, or null. + * @param pFormField Whether the item is a form field. + * @param pContentLength The items content length, if known, or -1 + * @throws IOException Creating the file item failed. + */ + FileItemStreamImpl(String pName, String pFieldName, + String pContentType, boolean pFormField, + long pContentLength) throws IOException { + name = pName; + fieldName = pFieldName; + contentType = pContentType; + formField = pFormField; + final ItemInputStream itemStream = multi.newInputStream(); + InputStream istream = itemStream; + if (fileSizeMax != -1) { + if (pContentLength != -1 + && pContentLength > fileSizeMax) { + FileSizeLimitExceededException e = + new FileSizeLimitExceededException( + format("The field %s exceeds its maximum permitted size of %s bytes.", + fieldName, Long.valueOf(fileSizeMax)), + pContentLength, fileSizeMax); + e.setFileName(pName); + e.setFieldName(pFieldName); + throw new FileUploadIOException(e); + } + istream = new LimitedInputStream(istream, fileSizeMax) { + @Override + protected void raiseError(long pSizeMax, long pCount) + throws IOException { + itemStream.close(true); + FileSizeLimitExceededException e = + new FileSizeLimitExceededException( + format("The field %s exceeds its maximum permitted size of %s bytes.", + fieldName, Long.valueOf(pSizeMax)), + pCount, pSizeMax); + e.setFieldName(fieldName); + e.setFileName(name); + throw new FileUploadIOException(e); + } + }; + } + stream = istream; + } + + /** + * Returns the items content type, or null. + * + * @return Content type, if known, or null. + */ + public String getContentType() { + return contentType; + } + + /** + * Returns the items field name. + * + * @return Field name. + */ + public String getFieldName() { + return fieldName; + } + + /** + * Returns the items file name. + * + * @return File name, if known, or null. + * @throws InvalidFileNameException The file name contains a NUL character, + * which might be an indicator of a security attack. If you intend to + * use the file name anyways, catch the exception and use + * InvalidFileNameException#getName(). + */ + public String getName() { + return Streams.checkFileName(name); + } + + /** + * Returns, whether this is a form field. + * + * @return True, if the item is a form field, + * otherwise false. + */ + public boolean isFormField() { + return formField; + } + + /** + * Returns an input stream, which may be used to + * read the items contents. + * + * @return Opened input stream. + * @throws IOException An I/O error occurred. + */ + public InputStream openStream() throws IOException { + if (opened) { + throw new IllegalStateException( + "The stream was already opened."); + } + if (((Closeable) stream).isClosed()) { + throw new FileItemStream.ItemSkippedException(); + } + return stream; + } + + /** + * Closes the file item. + * + * @throws IOException An I/O error occurred. + */ + void close() throws IOException { + stream.close(); + } + + /** + * Returns the file item headers. + * + * @return The items header object + */ + public FileItemHeaders getHeaders() { + return headers; + } + + /** + * Sets the file item headers. + * + * @param pHeaders The items header object + */ + public void setHeaders(FileItemHeaders pHeaders) { + headers = pHeaders; + } + + } + + /** + * The multi part stream to process. + */ + private final MultipartStream multi; + + /** + * The notifier, which used for triggering the + * {@link ProgressListener}. + */ + private final MultipartStream.ProgressNotifier notifier; + + /** + * The boundary, which separates the various parts. + */ + private final byte[] boundary; + + /** + * The item, which we currently process. + */ + private FileItemStreamImpl currentItem; + + /** + * The current items field name. + */ + private String currentFieldName; + + /** + * Whether we are currently skipping the preamble. + */ + private boolean skipPreamble; + + /** + * Whether the current item may still be read. + */ + private boolean itemValid; + + /** + * Whether we have seen the end of the file. + */ + private boolean eof; + + /** + * Creates a new instance. + * + * @param ctx The request context. + * @throws FileUploadException An error occurred while + * parsing the request. + * @throws IOException An I/O error occurred. + */ + FileItemIteratorImpl(RequestContext ctx) + throws FileUploadException, IOException { + if (ctx == null) { + throw new NullPointerException("ctx parameter"); + } + + String contentType = ctx.getContentType(); + if ((null == contentType) + || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) { + throw new InvalidContentTypeException( + format("the request doesn't contain a %s or %s stream, content type header is %s", + MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType)); + } + + InputStream input = ctx.getInputStream(); + + @SuppressWarnings("deprecation") // still has to be backward compatible + final int contentLengthInt = ctx.getContentLength(); + + final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass()) + // Inline conditional is OK here CHECKSTYLE:OFF + ? ((UploadContext) ctx).contentLength() + : contentLengthInt; + // CHECKSTYLE:ON + + if (sizeMax >= 0) { + if (requestSize != -1 && requestSize > sizeMax) { + throw new SizeLimitExceededException( + format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", + Long.valueOf(requestSize), Long.valueOf(sizeMax)), + requestSize, sizeMax); + } + input = new LimitedInputStream(input, sizeMax) { + @Override + protected void raiseError(long pSizeMax, long pCount) + throws IOException { + FileUploadException ex = new SizeLimitExceededException( + format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", + Long.valueOf(pCount), Long.valueOf(pSizeMax)), + pCount, pSizeMax); + throw new FileUploadIOException(ex); + } + }; + } + + String charEncoding = headerEncoding; + if (charEncoding == null) { + charEncoding = ctx.getCharacterEncoding(); + } + + boundary = getBoundary(contentType); + if (boundary == null) { + throw new FileUploadException("the request was rejected because no multipart boundary was found"); + } + + notifier = new MultipartStream.ProgressNotifier(listener, requestSize); + try { + multi = new MultipartStream(input, boundary, notifier); + } catch (IllegalArgumentException iae) { + throw new InvalidContentTypeException( + format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae); + } + multi.setHeaderEncoding(charEncoding); + + skipPreamble = true; + findNextItem(); + } + + /** + * Called for finding the next item, if any. + * + * @return True, if an next item was found, otherwise false. + * @throws IOException An I/O error occurred. + */ + private boolean findNextItem() throws IOException { + if (eof) { + return false; + } + if (currentItem != null) { + currentItem.close(); + currentItem = null; + } + for (;;) { + boolean nextPart; + if (skipPreamble) { + nextPart = multi.skipPreamble(); + } else { + nextPart = multi.readBoundary(); + } + if (!nextPart) { + if (currentFieldName == null) { + // Outer multipart terminated -> No more data + eof = true; + return false; + } + // Inner multipart terminated -> Return to parsing the outer + multi.setBoundary(boundary); + currentFieldName = null; + continue; + } + FileItemHeaders headers = getParsedHeaders(multi.readHeaders()); + if (currentFieldName == null) { + // We're parsing the outer multipart + String fieldName = getFieldName(headers); + if (fieldName != null) { + String subContentType = headers.getHeader(CONTENT_TYPE); + if (subContentType != null + && subContentType.toLowerCase(Locale.ENGLISH) + .startsWith(MULTIPART_MIXED)) { + currentFieldName = fieldName; + // Multiple files associated with this field name + byte[] subBoundary = getBoundary(subContentType); + multi.setBoundary(subBoundary); + skipPreamble = true; + continue; + } + String fileName = getFileName(headers); + currentItem = new FileItemStreamImpl(fileName, + fieldName, headers.getHeader(CONTENT_TYPE), + fileName == null, getContentLength(headers)); + currentItem.setHeaders(headers); + notifier.noteItem(); + itemValid = true; + return true; + } + } else { + String fileName = getFileName(headers); + if (fileName != null) { + currentItem = new FileItemStreamImpl(fileName, + currentFieldName, + headers.getHeader(CONTENT_TYPE), + false, getContentLength(headers)); + currentItem.setHeaders(headers); + notifier.noteItem(); + itemValid = true; + return true; + } + } + multi.discardBodyData(); + } + } + + private long getContentLength(FileItemHeaders pHeaders) { + try { + return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH)); + } catch (Exception e) { + return -1; + } + } + + /** + * Returns, whether another instance of {@link FileItemStream} + * is available. + * + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return True, if one or more additional file items + * are available, otherwise false. + */ + public boolean hasNext() throws FileUploadException, IOException { + if (eof) { + return false; + } + if (itemValid) { + return true; + } + try { + return findNextItem(); + } catch (FileUploadIOException e) { + // unwrap encapsulated SizeException + throw (FileUploadException) e.getCause(); + } + } + + /** + * Returns the next available {@link FileItemStream}. + * + * @throws java.util.NoSuchElementException No more items are + * available. Use {@link #hasNext()} to prevent this exception. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return FileItemStream instance, which provides + * access to the next file item. + */ + public FileItemStream next() throws FileUploadException, IOException { + if (eof || (!itemValid && !hasNext())) { + throw new NoSuchElementException(); + } + itemValid = false; + return currentItem; + } + + } + + /** + * This exception is thrown for hiding an inner + * {@link FileUploadException} in an {@link IOException}. + */ + public static class FileUploadIOException extends IOException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -7047616958165584154L; + + /** + * The exceptions cause; we overwrite the parent + * classes field, which is available since Java + * 1.4 only. + */ + private final FileUploadException cause; + + /** + * Creates a FileUploadIOException with the + * given cause. + * + * @param pCause The exceptions cause, if any, or null. + */ + public FileUploadIOException(FileUploadException pCause) { + // We're not doing super(pCause) cause of 1.3 compatibility. + cause = pCause; + } + + /** + * Returns the exceptions cause. + * + * @return The exceptions cause, if any, or null. + */ + @Override + public Throwable getCause() { + return cause; + } + + } + + /** + * Thrown to indicate that the request is not a multipart request. + */ + public static class InvalidContentTypeException + extends FileUploadException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -9073026332015646668L; + + /** + * Constructs a InvalidContentTypeException with no + * detail message. + */ + public InvalidContentTypeException() { + super(); + } + + /** + * Constructs an InvalidContentTypeException with + * the specified detail message. + * + * @param message The detail message. + */ + public InvalidContentTypeException(String message) { + super(message); + } + + /** + * Constructs an InvalidContentTypeException with + * the specified detail message and cause. + * + * @param msg The detail message. + * @param cause the original cause + * + * @since 1.3.1 + */ + public InvalidContentTypeException(String msg, Throwable cause) { + super(msg, cause); + } + } + + /** + * Thrown to indicate an IOException. + */ + public static class IOFileUploadException extends FileUploadException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 1749796615868477269L; + + /** + * The exceptions cause; we overwrite the parent + * classes field, which is available since Java + * 1.4 only. + */ + private final IOException cause; + + /** + * Creates a new instance with the given cause. + * + * @param pMsg The detail message. + * @param pException The exceptions cause. + */ + public IOFileUploadException(String pMsg, IOException pException) { + super(pMsg); + cause = pException; + } + + /** + * Returns the exceptions cause. + * + * @return The exceptions cause, if any, or null. + */ + @Override + public Throwable getCause() { + return cause; + } + + } + + /** + * This exception is thrown, if a requests permitted size + * is exceeded. + */ + protected abstract static class SizeException extends FileUploadException { + + /** + * Serial version UID, being used, if serialized. + */ + private static final long serialVersionUID = -8776225574705254126L; + + /** + * The actual size of the request. + */ + private final long actual; + + /** + * The maximum permitted size of the request. + */ + private final long permitted; + + /** + * Creates a new instance. + * + * @param message The detail message. + * @param actual The actual number of bytes in the request. + * @param permitted The requests size limit, in bytes. + */ + protected SizeException(String message, long actual, long permitted) { + super(message); + this.actual = actual; + this.permitted = permitted; + } + + /** + * Retrieves the actual size of the request. + * + * @return The actual size of the request. + * @since 1.3 + */ + public long getActualSize() { + return actual; + } + + /** + * Retrieves the permitted size of the request. + * + * @return The permitted size of the request. + * @since 1.3 + */ + public long getPermittedSize() { + return permitted; + } + + } + + /** + * Thrown to indicate that the request size is not specified. In other + * words, it is thrown, if the content-length header is missing or + * contains the value -1. + * + * @deprecated 1.2 As of commons-fileupload 1.2, the presence of a + * content-length header is no longer required. + */ + @Deprecated + public static class UnknownSizeException + extends FileUploadException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 7062279004812015273L; + + /** + * Constructs a UnknownSizeException with no + * detail message. + */ + public UnknownSizeException() { + super(); + } + + /** + * Constructs an UnknownSizeException with + * the specified detail message. + * + * @param message The detail message. + */ + public UnknownSizeException(String message) { + super(message); + } + + } + + /** + * Thrown to indicate that the request size exceeds the configured maximum. + */ + public static class SizeLimitExceededException + extends SizeException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -2474893167098052828L; + + /** + * @deprecated 1.2 Replaced by + * {@code SizeLimitExceededException(String, long, long)} + */ + @Deprecated + public SizeLimitExceededException() { + this(null, 0, 0); + } + + /** + * @deprecated 1.2 Replaced by + * {@code #SizeLimitExceededException(String, long, long)} + * @param message The exceptions detail message. + */ + @Deprecated + public SizeLimitExceededException(String message) { + this(message, 0, 0); + } + + /** + * Constructs a SizeExceededException with + * the specified detail message, and actual and permitted sizes. + * + * @param message The detail message. + * @param actual The actual request size. + * @param permitted The maximum permitted request size. + */ + public SizeLimitExceededException(String message, long actual, + long permitted) { + super(message, actual, permitted); + } + + } + + /** + * Thrown to indicate that A files size exceeds the configured maximum. + */ + public static class FileSizeLimitExceededException + extends SizeException { + + /** + * The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 8150776562029630058L; + + /** + * File name of the item, which caused the exception. + */ + private String fileName; + + /** + * Field name of the item, which caused the exception. + */ + private String fieldName; + + /** + * Constructs a SizeExceededException with + * the specified detail message, and actual and permitted sizes. + * + * @param message The detail message. + * @param actual The actual request size. + * @param permitted The maximum permitted request size. + */ + public FileSizeLimitExceededException(String message, long actual, + long permitted) { + super(message, actual, permitted); + } + + /** + * Returns the file name of the item, which caused the + * exception. + * + * @return File name, if known, or null. + */ + public String getFileName() { + return fileName; + } + + /** + * Sets the file name of the item, which caused the + * exception. + * + * @param pFileName the file name of the item, which caused the exception. + */ + public void setFileName(String pFileName) { + fileName = pFileName; + } + + /** + * Returns the field name of the item, which caused the + * exception. + * + * @return Field name, if known, or null. + */ + public String getFieldName() { + return fieldName; + } + + /** + * Sets the field name of the item, which caused the + * exception. + * + * @param pFieldName the field name of the item, + * which caused the exception. + */ + public void setFieldName(String pFieldName) { + fieldName = pFieldName; + } + + } + + /** + * Returns the progress listener. + * + * @return The progress listener, if any, or null. + */ + public ProgressListener getProgressListener() { + return listener; + } + + /** + * Sets the progress listener. + * + * @param pListener The progress listener, if any. Defaults to null. + */ + public void setProgressListener(ProgressListener pListener) { + listener = pListener; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadException.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadException.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/FileUploadException.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * Exception for errors encountered while processing the request. + * + * @version $Id$ + */ +public class FileUploadException extends Exception { + + /** + * Serial version UID, being used, if the exception + * is serialized. + */ + private static final long serialVersionUID = 8881893724388807504L; + + /** + * The exceptions cause. We overwrite the cause of + * the super class, which isn't available in Java 1.3. + */ + private final Throwable cause; + + /** + * Constructs a new FileUploadException without message. + */ + public FileUploadException() { + this(null, null); + } + + /** + * Constructs a new FileUploadException with specified detail + * message. + * + * @param msg the error message. + */ + public FileUploadException(final String msg) { + this(msg, null); + } + + /** + * Creates a new FileUploadException with the given + * detail message and cause. + * + * @param msg The exceptions detail message. + * @param cause The exceptions cause. + */ + public FileUploadException(String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + /** + * Prints this throwable and its backtrace to the specified print stream. + * + * @param stream PrintStream to use for output + */ + @Override + public void printStackTrace(PrintStream stream) { + super.printStackTrace(stream); + if (cause != null) { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + /** + * Prints this throwable and its backtrace to the specified + * print writer. + * + * @param writer PrintWriter to use for output + */ + @Override + public void printStackTrace(PrintWriter writer) { + super.printStackTrace(writer); + if (cause != null) { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Throwable getCause() { + return cause; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/InvalidFileNameException.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/InvalidFileNameException.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/InvalidFileNameException.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + * This exception is thrown in case of an invalid file name. + * A file name is invalid, if it contains a NUL character. + * Attackers might use this to circumvent security checks: + * For example, a malicious user might upload a file with the name + * "foo.exe\0.png". This file name might pass security checks (i.e. + * checks for the extension ".png"), while, depending on the underlying + * C library, it might create a file named "foo.exe", as the NUL + * character is the string terminator in C. + * + * @version $Id$ + */ +public class InvalidFileNameException extends RuntimeException { + + /** + * Serial version UID, being used, if the exception + * is serialized. + */ + private static final long serialVersionUID = 7922042602454350470L; + + /** + * The file name causing the exception. + */ + private final String name; + + /** + * Creates a new instance. + * + * @param pName The file name causing the exception. + * @param pMessage A human readable error message. + */ + public InvalidFileNameException(String pName, String pMessage) { + super(pMessage); + name = pName; + } + + /** + * Returns the invalid file name. + * + * @return the invalid file name. + */ + public String getName() { + return name; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/MultipartStream.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/MultipartStream.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/MultipartStream.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,1032 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import static java.lang.String.format; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException; +import org.apache.commons.fileupload.util.Closeable; +import org.apache.commons.fileupload.util.Streams; + +/** + *

Low level API for processing file uploads. + * + *

This class can be used to process data streams conforming to MIME + * 'multipart' format as defined in + * RFC 1867. Arbitrarily + * large amounts of data in the stream can be processed under constant + * memory usage. + * + *

The format of the stream is defined in the following way:
+ * + * + * multipart-body := preamble 1*encapsulation close-delimiter epilogue
+ * encapsulation := delimiter body CRLF
+ * delimiter := "--" boundary CRLF
+ * close-delimiter := "--" boundary "--"
+ * preamble := <ignore>
+ * epilogue := <ignore>
+ * body := header-part CRLF body-part
+ * header-part := 1*header CRLF
+ * header := header-name ":" header-value
+ * header-name := <printable ascii characters except ":">
+ * header-value := <any ascii characters except CR & LF>
+ * body-data := <arbitrary data>
+ *
+ * + *

Note that body-data can contain another mulipart entity. There + * is limited support for single pass processing of such nested + * streams. The nested stream is required to have a + * boundary token of the same length as the parent stream (see {@link + * #setBoundary(byte[])}). + * + *

Here is an example of usage of this class.
+ * + *

+ *   try {
+ *     MultipartStream multipartStream = new MultipartStream(input, boundary);
+ *     boolean nextPart = multipartStream.skipPreamble();
+ *     OutputStream output;
+ *     while(nextPart) {
+ *       String header = multipartStream.readHeaders();
+ *       // process headers
+ *       // create some output stream
+ *       multipartStream.readBodyData(output);
+ *       nextPart = multipartStream.readBoundary();
+ *     }
+ *   } catch(MultipartStream.MalformedStreamException e) {
+ *     // the stream failed to follow required syntax
+ *   } catch(IOException e) {
+ *     // a read or write error occurred
+ *   }
+ * 
+ * + * @version $Id$ + */ +public class MultipartStream { + + /** + * Internal class, which is used to invoke the + * {@link ProgressListener}. + */ + public static class ProgressNotifier { + + /** + * The listener to invoke. + */ + private final ProgressListener listener; + + /** + * Number of expected bytes, if known, or -1. + */ + private final long contentLength; + + /** + * Number of bytes, which have been read so far. + */ + private long bytesRead; + + /** + * Number of items, which have been read so far. + */ + private int items; + + /** + * Creates a new instance with the given listener + * and content length. + * + * @param pListener The listener to invoke. + * @param pContentLength The expected content length. + */ + ProgressNotifier(ProgressListener pListener, long pContentLength) { + listener = pListener; + contentLength = pContentLength; + } + + /** + * Called to indicate that bytes have been read. + * + * @param pBytes Number of bytes, which have been read. + */ + void noteBytesRead(int pBytes) { + /* Indicates, that the given number of bytes have been read from + * the input stream. + */ + bytesRead += pBytes; + notifyListener(); + } + + /** + * Called to indicate, that a new file item has been detected. + */ + void noteItem() { + ++items; + notifyListener(); + } + + /** + * Called for notifying the listener. + */ + private void notifyListener() { + if (listener != null) { + listener.update(bytesRead, contentLength, items); + } + } + + } + + // ----------------------------------------------------- Manifest constants + + /** + * The Carriage Return ASCII character value. + */ + public static final byte CR = 0x0D; + + /** + * The Line Feed ASCII character value. + */ + public static final byte LF = 0x0A; + + /** + * The dash (-) ASCII character value. + */ + public static final byte DASH = 0x2D; + + /** + * The maximum length of header-part that will be + * processed (10 kilobytes = 10240 bytes.). + */ + public static final int HEADER_PART_SIZE_MAX = 10240; + + /** + * The default length of the buffer used for processing a request. + */ + protected static final int DEFAULT_BUFSIZE = 4096; + + /** + * A byte sequence that marks the end of header-part + * (CRLFCRLF). + */ + protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF}; + + /** + * A byte sequence that that follows a delimiter that will be + * followed by an encapsulation (CRLF). + */ + protected static final byte[] FIELD_SEPARATOR = {CR, LF}; + + /** + * A byte sequence that that follows a delimiter of the last + * encapsulation in the stream (--). + */ + protected static final byte[] STREAM_TERMINATOR = {DASH, DASH}; + + /** + * A byte sequence that precedes a boundary (CRLF--). + */ + protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH}; + + // ----------------------------------------------------------- Data members + + /** + * The input stream from which data is read. + */ + private final InputStream input; + + /** + * The length of the boundary token plus the leading CRLF--. + */ + private int boundaryLength; + + /** + * The amount of data, in bytes, that must be kept in the buffer in order + * to detect delimiters reliably. + */ + private int keepRegion; + + /** + * The byte sequence that partitions the stream. + */ + private byte[] boundary; + + /** + * The length of the buffer used for processing the request. + */ + private final int bufSize; + + /** + * The buffer used for processing the request. + */ + private final byte[] buffer; + + /** + * The index of first valid character in the buffer. + *
+ * 0 <= head < bufSize + */ + private int head; + + /** + * The index of last valid character in the buffer + 1. + *
+ * 0 <= tail <= bufSize + */ + private int tail; + + /** + * The content encoding to use when reading headers. + */ + private String headerEncoding; + + /** + * The progress notifier, if any, or null. + */ + private final ProgressNotifier notifier; + + // ----------------------------------------------------------- Constructors + + /** + * Creates a new instance. + * + * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, + * ProgressNotifier)} + */ + @Deprecated + public MultipartStream() { + this(null, null, null); + } + + /** + *

Constructs a MultipartStream with a custom size buffer + * and no progress notifier. + * + *

Note that the buffer must be at least big enough to contain the + * boundary string, plus 4 characters for CR/LF and double dash, plus at + * least one byte of data. Too small a buffer size setting will degrade + * performance. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param bufSize The size of the buffer to be used, in bytes. + * + * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, + * ProgressNotifier)}. + */ + @Deprecated + public MultipartStream(InputStream input, byte[] boundary, int bufSize) { + this(input, boundary, bufSize, null); + } + + /** + *

Constructs a MultipartStream with a custom size buffer. + * + *

Note that the buffer must be at least big enough to contain the + * boundary string, plus 4 characters for CR/LF and double dash, plus at + * least one byte of data. Too small a buffer size setting will degrade + * performance. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param bufSize The size of the buffer to be used, in bytes. + * @param pNotifier The notifier, which is used for calling the + * progress listener, if any. + * + * @throws IllegalArgumentException If the buffer size is too small + * + * @since 1.3.1 + */ + public MultipartStream(InputStream input, + byte[] boundary, + int bufSize, + ProgressNotifier pNotifier) { + + if (boundary == null) { + throw new IllegalArgumentException("boundary may not be null"); + } + // We prepend CR/LF to the boundary to chop trailing CR/LF from + // body-data tokens. + this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; + if (bufSize < this.boundaryLength + 1) { + throw new IllegalArgumentException( + "The buffer size specified for the MultipartStream is too small"); + } + + this.input = input; + this.bufSize = Math.max(bufSize, boundaryLength * 2); + this.buffer = new byte[this.bufSize]; + this.notifier = pNotifier; + + this.boundary = new byte[this.boundaryLength]; + this.keepRegion = this.boundary.length; + + System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, + BOUNDARY_PREFIX.length); + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, + boundary.length); + + head = 0; + tail = 0; + } + + /** + *

Constructs a MultipartStream with a default size buffer. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param pNotifier An object for calling the progress listener, if any. + * + * + * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) + */ + MultipartStream(InputStream input, + byte[] boundary, + ProgressNotifier pNotifier) { + this(input, boundary, DEFAULT_BUFSIZE, pNotifier); + } + + /** + *

Constructs a MultipartStream with a default size buffer. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * + * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, + * ProgressNotifier)}. + */ + @Deprecated + public MultipartStream(InputStream input, + byte[] boundary) { + this(input, boundary, DEFAULT_BUFSIZE, null); + } + + // --------------------------------------------------------- Public methods + + /** + * Retrieves the character encoding used when reading the headers of an + * individual part. When not specified, or null, the platform + * default encoding is used. + * + * @return The encoding used to read part headers. + */ + public String getHeaderEncoding() { + return headerEncoding; + } + + /** + * Specifies the character encoding to be used when reading the headers of + * individual parts. When not specified, or null, the platform + * default encoding is used. + * + * @param encoding The encoding used to read part headers. + */ + public void setHeaderEncoding(String encoding) { + headerEncoding = encoding; + } + + /** + * Reads a byte from the buffer, and refills it as + * necessary. + * + * @return The next byte from the input stream. + * + * @throws IOException if there is no more data available. + */ + public byte readByte() throws IOException { + // Buffer depleted ? + if (head == tail) { + head = 0; + // Refill. + tail = input.read(buffer, head, bufSize); + if (tail == -1) { + // No more data available. + throw new IOException("No more data is available"); + } + if (notifier != null) { + notifier.noteBytesRead(tail); + } + } + return buffer[head++]; + } + + /** + * Skips a boundary token, and checks whether more + * encapsulations are contained in the stream. + * + * @return true if there are more encapsulations in + * this stream; false otherwise. + * + * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits + * @throws MalformedStreamException if the stream ends unexpectedly or + * fails to follow required syntax. + */ + public boolean readBoundary() + throws FileUploadIOException, MalformedStreamException { + byte[] marker = new byte[2]; + boolean nextChunk = false; + + head += boundaryLength; + try { + marker[0] = readByte(); + if (marker[0] == LF) { + // Work around IE5 Mac bug with input type=image. + // Because the boundary delimiter, not including the trailing + // CRLF, must not appear within any file (RFC 2046, section + // 5.1.1), we know the missing CR is due to a buggy browser + // rather than a file containing something similar to a + // boundary. + return true; + } + + marker[1] = readByte(); + if (arrayequals(marker, STREAM_TERMINATOR, 2)) { + nextChunk = false; + } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { + nextChunk = true; + } else { + throw new MalformedStreamException( + "Unexpected characters follow a boundary"); + } + } catch (FileUploadIOException e) { + // wraps a SizeException, re-throw as it will be unwrapped later + throw e; + } catch (IOException e) { + throw new MalformedStreamException("Stream ended unexpectedly"); + } + return nextChunk; + } + + /** + *

Changes the boundary token used for partitioning the stream. + * + *

This method allows single pass processing of nested multipart + * streams. + * + *

The boundary token of the nested stream is required + * to be of the same length as the boundary token in parent stream. + * + *

Restoring the parent stream boundary token after processing of a + * nested stream is left to the application. + * + * @param boundary The boundary to be used for parsing of the nested + * stream. + * + * @throws IllegalBoundaryException if the boundary + * has a different length than the one + * being currently parsed. + */ + public void setBoundary(byte[] boundary) + throws IllegalBoundaryException { + if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { + throw new IllegalBoundaryException( + "The length of a boundary token can not be changed"); + } + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, + boundary.length); + } + + /** + *

Reads the header-part of the current + * encapsulation. + * + *

Headers are returned verbatim to the input stream, including the + * trailing CRLF marker. Parsing is left to the + * application. + * + *

TODO allow limiting maximum header size to + * protect against abuse. + * + * @return The header-part of the current encapsulation. + * + * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits. + * @throws MalformedStreamException if the stream ends unexpectedly. + */ + public String readHeaders() throws FileUploadIOException, MalformedStreamException { + int i = 0; + byte b; + // to support multi-byte characters + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int size = 0; + while (i < HEADER_SEPARATOR.length) { + try { + b = readByte(); + } catch (FileUploadIOException e) { + // wraps a SizeException, re-throw as it will be unwrapped later + throw e; + } catch (IOException e) { + throw new MalformedStreamException("Stream ended unexpectedly"); + } + if (++size > HEADER_PART_SIZE_MAX) { + throw new MalformedStreamException( + format("Header section has more than %s bytes (maybe it is not properly terminated)", + Integer.valueOf(HEADER_PART_SIZE_MAX))); + } + if (b == HEADER_SEPARATOR[i]) { + i++; + } else { + i = 0; + } + baos.write(b); + } + + String headers = null; + if (headerEncoding != null) { + try { + headers = baos.toString(headerEncoding); + } catch (UnsupportedEncodingException e) { + // Fall back to platform default if specified encoding is not + // supported. + headers = baos.toString(); + } + } else { + headers = baos.toString(); + } + + return headers; + } + + /** + *

Reads body-data from the current + * encapsulation and writes its contents into the + * output Stream. + * + *

Arbitrary large amounts of data can be processed by this + * method using a constant size buffer. (see {@link + * #MultipartStream(InputStream,byte[],int, + * MultipartStream.ProgressNotifier) constructor}). + * + * @param output The Stream to write data into. May + * be null, in which case this method is equivalent + * to {@link #discardBodyData()}. + * + * @return the amount of data written. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. + */ + public int readBodyData(OutputStream output) + throws MalformedStreamException, IOException { + final InputStream istream = newInputStream(); + return (int) Streams.copy(istream, output, false); + } + + /** + * Creates a new {@link ItemInputStream}. + * @return A new instance of {@link ItemInputStream}. + */ + ItemInputStream newInputStream() { + return new ItemInputStream(); + } + + /** + *

Reads body-data from the current + * encapsulation and discards it. + * + *

Use this method to skip encapsulations you don't need or don't + * understand. + * + * @return The amount of data discarded. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. + */ + public int discardBodyData() throws MalformedStreamException, IOException { + return readBodyData(null); + } + + /** + * Finds the beginning of the first encapsulation. + * + * @return true if an encapsulation was found in + * the stream. + * + * @throws IOException if an i/o error occurs. + */ + public boolean skipPreamble() throws IOException { + // First delimiter may be not preceeded with a CRLF. + System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); + boundaryLength = boundary.length - 2; + try { + // Discard all data up to the delimiter. + discardBodyData(); + + // Read boundary - if succeeded, the stream contains an + // encapsulation. + return readBoundary(); + } catch (MalformedStreamException e) { + return false; + } finally { + // Restore delimiter. + System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); + boundaryLength = boundary.length; + boundary[0] = CR; + boundary[1] = LF; + } + } + + /** + * Compares count first bytes in the arrays + * a and b. + * + * @param a The first array to compare. + * @param b The second array to compare. + * @param count How many bytes should be compared. + * + * @return true if count first bytes in arrays + * a and b are equal. + */ + public static boolean arrayequals(byte[] a, + byte[] b, + int count) { + for (int i = 0; i < count; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + /** + * Searches for a byte of specified value in the buffer, + * starting at the specified position. + * + * @param value The value to find. + * @param pos The starting position for searching. + * + * @return The position of byte found, counting from beginning of the + * buffer, or -1 if not found. + */ + protected int findByte(byte value, + int pos) { + for (int i = pos; i < tail; i++) { + if (buffer[i] == value) { + return i; + } + } + + return -1; + } + + /** + * Searches for the boundary in the buffer + * region delimited by head and tail. + * + * @return The position of the boundary found, counting from the + * beginning of the buffer, or -1 if + * not found. + */ + protected int findSeparator() { + int first; + int match = 0; + int maxpos = tail - boundaryLength; + for (first = head; first <= maxpos && match != boundaryLength; first++) { + first = findByte(boundary[0], first); + if (first == -1 || first > maxpos) { + return -1; + } + for (match = 1; match < boundaryLength; match++) { + if (buffer[first + match] != boundary[match]) { + break; + } + } + } + if (match == boundaryLength) { + return first - 1; + } + return -1; + } + + /** + * Thrown to indicate that the input stream fails to follow the + * required syntax. + */ + public static class MalformedStreamException extends IOException { + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = 6466926458059796677L; + + /** + * Constructs a MalformedStreamException with no + * detail message. + */ + public MalformedStreamException() { + super(); + } + + /** + * Constructs an MalformedStreamException with + * the specified detail message. + * + * @param message The detail message. + */ + public MalformedStreamException(String message) { + super(message); + } + + } + + /** + * Thrown upon attempt of setting an invalid boundary token. + */ + public static class IllegalBoundaryException extends IOException { + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = -161533165102632918L; + + /** + * Constructs an IllegalBoundaryException with no + * detail message. + */ + public IllegalBoundaryException() { + super(); + } + + /** + * Constructs an IllegalBoundaryException with + * the specified detail message. + * + * @param message The detail message. + */ + public IllegalBoundaryException(String message) { + super(message); + } + + } + + /** + * An {@link InputStream} for reading an items contents. + */ + public class ItemInputStream extends InputStream implements Closeable { + + /** + * The number of bytes, which have been read so far. + */ + private long total; + + /** + * The number of bytes, which must be hold, because + * they might be a part of the boundary. + */ + private int pad; + + /** + * The current offset in the buffer. + */ + private int pos; + + /** + * Whether the stream is already closed. + */ + private boolean closed; + + /** + * Creates a new instance. + */ + ItemInputStream() { + findSeparator(); + } + + /** + * Called for finding the separator. + */ + private void findSeparator() { + pos = MultipartStream.this.findSeparator(); + if (pos == -1) { + if (tail - head > keepRegion) { + pad = keepRegion; + } else { + pad = tail - head; + } + } + } + + /** + * Returns the number of bytes, which have been read + * by the stream. + * + * @return Number of bytes, which have been read so far. + */ + public long getBytesRead() { + return total; + } + + /** + * Returns the number of bytes, which are currently + * available, without blocking. + * + * @throws IOException An I/O error occurs. + * @return Number of bytes in the buffer. + */ + @Override + public int available() throws IOException { + if (pos == -1) { + return tail - head - pad; + } + return pos - head; + } + + /** + * Offset when converting negative bytes to integers. + */ + private static final int BYTE_POSITIVE_OFFSET = 256; + + /** + * Returns the next byte in the stream. + * + * @return The next byte in the stream, as a non-negative + * integer, or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + @Override + public int read() throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (available() == 0 && makeAvailable() == 0) { + return -1; + } + ++total; + int b = buffer[head++]; + if (b >= 0) { + return b; + } + return b + BYTE_POSITIVE_OFFSET; + } + + /** + * Reads bytes into the given buffer. + * + * @param b The destination buffer, where to write to. + * @param off Offset of the first byte in the buffer. + * @param len Maximum number of bytes to read. + * @return Number of bytes, which have been actually read, + * or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (len == 0) { + return 0; + } + int res = available(); + if (res == 0) { + res = makeAvailable(); + if (res == 0) { + return -1; + } + } + res = Math.min(res, len); + System.arraycopy(buffer, head, b, off, res); + head += res; + total += res; + return res; + } + + /** + * Closes the input stream. + * + * @throws IOException An I/O error occurred. + */ + @Override + public void close() throws IOException { + close(false); + } + + /** + * Closes the input stream. + * + * @param pCloseUnderlying Whether to close the underlying stream + * (hard close) + * @throws IOException An I/O error occurred. + */ + public void close(boolean pCloseUnderlying) throws IOException { + if (closed) { + return; + } + if (pCloseUnderlying) { + closed = true; + input.close(); + } else { + for (;;) { + int av = available(); + if (av == 0) { + av = makeAvailable(); + if (av == 0) { + break; + } + } + skip(av); + } + } + closed = true; + } + + /** + * Skips the given number of bytes. + * + * @param bytes Number of bytes to skip. + * @return The number of bytes, which have actually been + * skipped. + * @throws IOException An I/O error occurred. + */ + @Override + public long skip(long bytes) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + int av = available(); + if (av == 0) { + av = makeAvailable(); + if (av == 0) { + return 0; + } + } + long res = Math.min(av, bytes); + head += res; + return res; + } + + /** + * Attempts to read more data. + * + * @return Number of available bytes + * @throws IOException An I/O error occurred. + */ + private int makeAvailable() throws IOException { + if (pos != -1) { + return 0; + } + + // Move the data to the beginning of the buffer. + total += tail - head - pad; + System.arraycopy(buffer, tail - pad, buffer, 0, pad); + + // Refill buffer with new data. + head = 0; + tail = pad; + + for (;;) { + int bytesRead = input.read(buffer, tail, bufSize - tail); + if (bytesRead == -1) { + // The last pad amount is left in the buffer. + // Boundary can't be in there so signal an error + // condition. + final String msg = "Stream ended unexpectedly"; + throw new MalformedStreamException(msg); + } + if (notifier != null) { + notifier.noteBytesRead(bytesRead); + } + tail += bytesRead; + + findSeparator(); + int av = available(); + + if (av > 0 || pos != -1) { + return av; + } + } + } + + /** + * Returns, whether the stream is closed. + * + * @return True, if the stream is closed, otherwise false. + */ + public boolean isClosed() { + return closed; + } + + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ParameterParser.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ParameterParser.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ParameterParser.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,341 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.fileupload.util.mime.MimeUtility; + +/** + * A simple parser intended to parse sequences of name/value pairs. + * + * Parameter values are expected to be enclosed in quotes if they + * contain unsafe characters, such as '=' characters or separators. + * Parameter values are optional and can be omitted. + * + *

+ * param1 = value; param2 = "anything goes; really"; param3 + *

+ * + * @version $Id$ + */ +public class ParameterParser { + + /** + * String to be parsed. + */ + private char[] chars = null; + + /** + * Current position in the string. + */ + private int pos = 0; + + /** + * Maximum position in the string. + */ + private int len = 0; + + /** + * Start of a token. + */ + private int i1 = 0; + + /** + * End of a token. + */ + private int i2 = 0; + + /** + * Whether names stored in the map should be converted to lower case. + */ + private boolean lowerCaseNames = false; + + /** + * Default ParameterParser constructor. + */ + public ParameterParser() { + super(); + } + + /** + * Are there any characters left to parse? + * + * @return true if there are unparsed characters, + * false otherwise. + */ + private boolean hasChar() { + return this.pos < this.len; + } + + /** + * A helper method to process the parsed token. This method removes + * leading and trailing blanks as well as enclosing quotation marks, + * when necessary. + * + * @param quoted true if quotation marks are expected, + * false otherwise. + * @return the token + */ + private String getToken(boolean quoted) { + // Trim leading white spaces + while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) { + i1++; + } + // Trim trailing white spaces + while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) { + i2--; + } + // Strip away quotation marks if necessary + if (quoted + && ((i2 - i1) >= 2) + && (chars[i1] == '"') + && (chars[i2 - 1] == '"')) { + i1++; + i2--; + } + String result = null; + if (i2 > i1) { + result = new String(chars, i1, i2 - i1); + } + return result; + } + + /** + * Tests if the given character is present in the array of characters. + * + * @param ch the character to test for presense in the array of characters + * @param charray the array of characters to test against + * + * @return true if the character is present in the array of + * characters, false otherwise. + */ + private boolean isOneOf(char ch, final char[] charray) { + boolean result = false; + for (char element : charray) { + if (ch == element) { + result = true; + break; + } + } + return result; + } + + /** + * Parses out a token until any of the given terminators + * is encountered. + * + * @param terminators the array of terminating characters. Any of these + * characters when encountered signify the end of the token + * + * @return the token + */ + private String parseToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + while (hasChar()) { + ch = chars[pos]; + if (isOneOf(ch, terminators)) { + break; + } + i2++; + pos++; + } + return getToken(false); + } + + /** + * Parses out a token until any of the given terminators + * is encountered outside the quotation marks. + * + * @param terminators the array of terminating characters. Any of these + * characters when encountered outside the quotation marks signify the end + * of the token + * + * @return the token + */ + private String parseQuotedToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + boolean quoted = false; + boolean charEscaped = false; + while (hasChar()) { + ch = chars[pos]; + if (!quoted && isOneOf(ch, terminators)) { + break; + } + if (!charEscaped && ch == '"') { + quoted = !quoted; + } + charEscaped = (!charEscaped && ch == '\\'); + i2++; + pos++; + + } + return getToken(true); + } + + /** + * Returns true if parameter names are to be converted to lower + * case when name/value pairs are parsed. + * + * @return true if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * Otherwise returns false + */ + public boolean isLowerCaseNames() { + return this.lowerCaseNames; + } + + /** + * Sets the flag if parameter names are to be converted to lower case when + * name/value pairs are parsed. + * + * @param b true if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * false otherwise. + */ + public void setLowerCaseNames(boolean b) { + this.lowerCaseNames = b; + } + + /** + * Extracts a map of name/value pairs from the given string. Names are + * expected to be unique. Multiple separators may be specified and + * the earliest found in the input string is used. + * + * @param str the string that contains a sequence of name/value pairs + * @param separators the name/value pairs separators + * + * @return a map of name/value pairs + */ + public Map parse(final String str, char[] separators) { + if (separators == null || separators.length == 0) { + return new HashMap(); + } + char separator = separators[0]; + if (str != null) { + int idx = str.length(); + for (char separator2 : separators) { + int tmp = str.indexOf(separator2); + if (tmp != -1 && tmp < idx) { + idx = tmp; + separator = separator2; + } + } + } + return parse(str, separator); + } + + /** + * Extracts a map of name/value pairs from the given string. Names are + * expected to be unique. + * + * @param str the string that contains a sequence of name/value pairs + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse(final String str, char separator) { + if (str == null) { + return new HashMap(); + } + return parse(str.toCharArray(), separator); + } + + /** + * Extracts a map of name/value pairs from the given array of + * characters. Names are expected to be unique. + * + * @param charArray the array of characters that contains a sequence of + * name/value pairs + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse(final char[] charArray, char separator) { + if (charArray == null) { + return new HashMap(); + } + return parse(charArray, 0, charArray.length, separator); + } + + /** + * Extracts a map of name/value pairs from the given array of + * characters. Names are expected to be unique. + * + * @param charArray the array of characters that contains a sequence of + * name/value pairs + * @param offset - the initial offset. + * @param length - the length. + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse( + final char[] charArray, + int offset, + int length, + char separator) { + + if (charArray == null) { + return new HashMap(); + } + HashMap params = new HashMap(); + this.chars = charArray; + this.pos = offset; + this.len = length; + + String paramName = null; + String paramValue = null; + while (hasChar()) { + paramName = parseToken(new char[] { + '=', separator }); + paramValue = null; + if (hasChar() && (charArray[pos] == '=')) { + pos++; // skip '=' + paramValue = parseQuotedToken(new char[] { + separator }); + + if (paramValue != null) { + try { + paramValue = MimeUtility.decodeText(paramValue); + } catch (UnsupportedEncodingException e) { + // let's keep the original value in this case + } + } + } + if (hasChar() && (charArray[pos] == separator)) { + pos++; // skip separator + } + if ((paramName != null) && (paramName.length() > 0)) { + if (this.lowerCaseNames) { + paramName = paramName.toLowerCase(Locale.ENGLISH); + } + + params.put(paramName, paramValue); + } + } + return params; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ProgressListener.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ProgressListener.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/ProgressListener.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + * The {@link ProgressListener} may be used to display a progress bar + * or do stuff like that. + * + * @version $Id$ + */ +public interface ProgressListener { + + /** + * Updates the listeners status information. + * + * @param pBytesRead The total number of bytes, which have been read + * so far. + * @param pContentLength The total number of bytes, which are being + * read. May be -1, if this number is unknown. + * @param pItems The number of the field, which is currently being + * read. (0 = no item so far, 1 = first item is being read, ...) + */ + void update(long pBytesRead, long pContentLength, int pItems); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/RequestContext.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/RequestContext.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/RequestContext.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +import java.io.InputStream; +import java.io.IOException; + +/** + *

Abstracts access to the request information needed for file uploads. This + * interface should be implemented for each type of request that may be + * handled by FileUpload, such as servlets and portlets.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public interface RequestContext { + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + String getCharacterEncoding(); + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + String getContentType(); + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @deprecated 1.3 Use {@link UploadContext#contentLength()} instead + */ + @Deprecated + int getContentLength(); + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + InputStream getInputStream() throws IOException; + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/UploadContext.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/UploadContext.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/UploadContext.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload; + +/** + * Enhanced access to the request information needed for file uploads, + * which fixes the Content Length data access in {@link RequestContext}. + * + * The reason of introducing this new interface is just for backward compatibility + * and it might vanish for a refactored 2.x version moving the new method into + * RequestContext again. + * + * @since 1.3 + */ +public interface UploadContext extends RequestContext { + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @since 1.3 + */ + long contentLength(); + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItem.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItem.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItem.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,720 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.disk; + +import static java.lang.String.format; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemHeaders; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.ParameterParser; +import org.apache.commons.fileupload.util.Streams; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.DeferredFileOutputStream; + +/** + *

The default implementation of the + * {@link org.apache.commons.fileupload.FileItem FileItem} interface. + * + *

After retrieving an instance of this class from a {@link + * DiskFileItemFactory} instance (see + * {@link org.apache.commons.fileupload.servlet.ServletFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + *

Temporary files, which are created for file items, should be + * deleted later on. The best way to do this is using a + * {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the + * {@link DiskFileItemFactory}. However, if you do use such a tracker, + * then you must consider the following: Temporary files are automatically + * deleted as soon as they are no longer needed. (More precisely, when the + * corresponding instance of {@link java.io.File} is garbage collected.) + * This is done by the so-called reaper thread, which is started and stopped + * automatically by the {@link org.apache.commons.io.FileCleaningTracker} when + * there are files to be tracked. + * It might make sense to terminate that thread, for example, if + * your web application ends. See the section on "Resource cleanup" + * in the users guide of commons-fileupload.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class DiskFileItem + implements FileItem { + + /** + * Although it implements {@link java.io.Serializable}, a DiskFileItem can actually only be deserialized, + * if this System property is true. + */ + public static final String SERIALIZABLE_PROPERTY = DiskFileItem.class.getName() + ".serializable"; + + // ----------------------------------------------------- Manifest constants + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = 2237570099615271025L; + + /** + * Default content charset to be used when no explicit charset + * parameter is provided by the sender. Media subtypes of the + * "text" type are defined to have a default charset value of + * "ISO-8859-1" when received via HTTP. + */ + public static final String DEFAULT_CHARSET = "ISO-8859-1"; + + // ----------------------------------------------------------- Data members + + /** + * UID used in unique file name generation. + */ + private static final String UID = + UUID.randomUUID().toString().replace('-', '_'); + + /** + * Counter used in unique identifier generation. + */ + private static final AtomicInteger COUNTER = new AtomicInteger(0); + + /** + * The name of the form field as provided by the browser. + */ + private String fieldName; + + /** + * The content type passed by the browser, or null if + * not defined. + */ + private final String contentType; + + /** + * Whether or not this item is a simple form field. + */ + private boolean isFormField; + + /** + * The original filename in the user's filesystem. + */ + private final String fileName; + + /** + * The size of the item, in bytes. This is used to cache the size when a + * file item is moved from its original location. + */ + private long size = -1; + + + /** + * The threshold above which uploads will be stored on disk. + */ + private final int sizeThreshold; + + /** + * The directory in which uploaded files will be stored, if stored on disk. + */ + private final File repository; + + /** + * Cached contents of the file. + */ + private byte[] cachedContent; + + /** + * Output stream for this item. + */ + private transient DeferredFileOutputStream dfos; + + /** + * The temporary file to use. + */ + private transient File tempFile; + + /** + * File to allow for serialization of the content of this item. + */ + private File dfosFile; + + /** + * The file items headers. + */ + private FileItemHeaders headers; + + // ----------------------------------------------------------- Constructors + + /** + * Constructs a new DiskFileItem instance. + * + * @param fieldName The name of the form field. + * @param contentType The content type passed by the browser or + * null if not specified. + * @param isFormField Whether or not this item is a plain form field, as + * opposed to a file upload. + * @param fileName The original filename in the user's filesystem, or + * null if not specified. + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + */ + public DiskFileItem(String fieldName, + String contentType, boolean isFormField, String fileName, + int sizeThreshold, File repository) { + this.fieldName = fieldName; + this.contentType = contentType; + this.isFormField = isFormField; + this.fileName = fileName; + this.sizeThreshold = sizeThreshold; + this.repository = repository; + } + + // ------------------------------- Methods from javax.activation.DataSource + + /** + * Returns an {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @return An {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @throws IOException if an error occurs. + */ + public InputStream getInputStream() + throws IOException { + if (!isInMemory()) { + return new FileInputStream(dfos.getFile()); + } + + if (cachedContent == null) { + cachedContent = dfos.getData(); + } + return new ByteArrayInputStream(cachedContent); + } + + /** + * Returns the content type passed by the agent or null if + * not defined. + * + * @return The content type passed by the agent or null if + * not defined. + */ + public String getContentType() { + return contentType; + } + + /** + * Returns the content charset passed by the agent or null if + * not defined. + * + * @return The content charset passed by the agent or null if + * not defined. + */ + public String getCharSet() { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(getContentType(), ';'); + return params.get("charset"); + } + + /** + * Returns the original filename in the client's filesystem. + * + * @return The original filename in the client's filesystem. + * @throws org.apache.commons.fileupload.InvalidFileNameException The file name contains a NUL character, + * which might be an indicator of a security attack. If you intend to + * use the file name anyways, catch the exception and use + * {@link org.apache.commons.fileupload.InvalidFileNameException#getName()}. + */ + public String getName() { + return Streams.checkFileName(fileName); + } + + // ------------------------------------------------------- FileItem methods + + /** + * Provides a hint as to whether or not the file contents will be read + * from memory. + * + * @return true if the file contents will be read + * from memory; false otherwise. + */ + public boolean isInMemory() { + if (cachedContent != null) { + return true; + } + return dfos.isInMemory(); + } + + /** + * Returns the size of the file. + * + * @return The size of the file, in bytes. + */ + public long getSize() { + if (size >= 0) { + return size; + } else if (cachedContent != null) { + return cachedContent.length; + } else if (dfos.isInMemory()) { + return dfos.getData().length; + } else { + return dfos.getFile().length(); + } + } + + /** + * Returns the contents of the file as an array of bytes. If the + * contents of the file were not yet cached in memory, they will be + * loaded from the disk storage and cached. + * + * @return The contents of the file as an array of bytes. + */ + public byte[] get() { + if (isInMemory()) { + if (cachedContent == null) { + cachedContent = dfos.getData(); + } + return cachedContent; + } + + byte[] fileData = new byte[(int) getSize()]; + InputStream fis = null; + + try { + fis = new BufferedInputStream(new FileInputStream(dfos.getFile())); + fis.read(fileData); + } catch (IOException e) { + fileData = null; + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // ignore + } + } + } + + return fileData; + } + + /** + * Returns the contents of the file as a String, using the specified + * encoding. This method uses {@link #get()} to retrieve the + * contents of the file. + * + * @param charset The charset to use. + * + * @return The contents of the file, as a string. + * + * @throws UnsupportedEncodingException if the requested character + * encoding is not available. + */ + public String getString(final String charset) + throws UnsupportedEncodingException { + return new String(get(), charset); + } + + /** + * Returns the contents of the file as a String, using the default + * character encoding. This method uses {@link #get()} to retrieve the + * contents of the file. + * + * TODO Consider making this method throw UnsupportedEncodingException. + * + * @return The contents of the file, as a string. + */ + public String getString() { + byte[] rawdata = get(); + String charset = getCharSet(); + if (charset == null) { + charset = DEFAULT_CHARSET; + } + try { + return new String(rawdata, charset); + } catch (UnsupportedEncodingException e) { + return new String(rawdata); + } + } + + /** + * A convenience method to write an uploaded item to disk. The client code + * is not concerned with whether or not the item is stored in memory, or on + * disk in a temporary location. They just want to write the uploaded item + * to a file. + *

+ * This implementation first attempts to rename the uploaded item to the + * specified destination file, if the item was originally written to disk. + * Otherwise, the data will be copied to the specified file. + *

+ * This method is only guaranteed to work once, the first time it + * is invoked for a particular item. This is because, in the event that the + * method renames a temporary file, that file will no longer be available + * to copy or rename again at a later time. + * + * @param file The File into which the uploaded item should + * be stored. + * + * @throws Exception if an error occurs. + */ + public void write(File file) throws Exception { + if (isInMemory()) { + FileOutputStream fout = null; + try { + fout = new FileOutputStream(file); + fout.write(get()); + } finally { + if (fout != null) { + fout.close(); + } + } + } else { + File outputFile = getStoreLocation(); + if (outputFile != null) { + // Save the length of the file + size = outputFile.length(); + /* + * The uploaded file is being stored on disk + * in a temporary location so move it to the + * desired file. + */ + if (!outputFile.renameTo(file)) { + BufferedInputStream in = null; + BufferedOutputStream out = null; + try { + in = new BufferedInputStream( + new FileInputStream(outputFile)); + out = new BufferedOutputStream( + new FileOutputStream(file)); + IOUtils.copy(in, out); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // ignore + } + } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + } + } else { + /* + * For whatever reason we cannot write the + * file to disk. + */ + throw new FileUploadException( + "Cannot write uploaded file to disk!"); + } + } + } + + /** + * Deletes the underlying storage for a file item, including deleting any + * associated temporary disk file. Although this storage will be deleted + * automatically when the FileItem instance is garbage + * collected, this method can be used to ensure that this is done at an + * earlier time, thus preserving system resources. + */ + public void delete() { + cachedContent = null; + File outputFile = getStoreLocation(); + if (outputFile != null && outputFile.exists()) { + outputFile.delete(); + } + } + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + * + * @see #setFieldName(java.lang.String) + * + */ + public String getFieldName() { + return fieldName; + } + + /** + * Sets the field name used to reference this file item. + * + * @param fieldName The name of the form field. + * + * @see #getFieldName() + * + */ + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + * + * @see #setFormField(boolean) + * + */ + public boolean isFormField() { + return isFormField; + } + + /** + * Specifies whether or not a FileItem instance represents + * a simple form field. + * + * @param state true if the instance represents a simple form + * field; false if it represents an uploaded file. + * + * @see #isFormField() + * + */ + public void setFormField(boolean state) { + isFormField = state; + } + + /** + * Returns an {@link java.io.OutputStream OutputStream} that can + * be used for storing the contents of the file. + * + * @return An {@link java.io.OutputStream OutputStream} that can be used + * for storing the contensts of the file. + * + * @throws IOException if an error occurs. + */ + public OutputStream getOutputStream() + throws IOException { + if (dfos == null) { + File outputFile = getTempFile(); + dfos = new DeferredFileOutputStream(sizeThreshold, outputFile); + } + return dfos; + } + + // --------------------------------------------------------- Public methods + + /** + * Returns the {@link java.io.File} object for the FileItem's + * data's temporary location on the disk. Note that for + * FileItems that have their data stored in memory, + * this method will return null. When handling large + * files, you can use {@link java.io.File#renameTo(java.io.File)} to + * move the file to new location without copying the data, if the + * source and destination locations reside within the same logical + * volume. + * + * @return The data file, or null if the data is stored in + * memory. + */ + public File getStoreLocation() { + if (dfos == null) { + return null; + } + return dfos.getFile(); + } + + // ------------------------------------------------------ Protected methods + + /** + * Removes the file contents from the temporary storage. + */ + @Override + protected void finalize() { + File outputFile = dfos.getFile(); + + if (outputFile != null && outputFile.exists()) { + outputFile.delete(); + } + } + + /** + * Creates and returns a {@link java.io.File File} representing a uniquely + * named temporary file in the configured repository path. The lifetime of + * the file is tied to the lifetime of the FileItem instance; + * the file will be deleted when the instance is garbage collected. + * + * @return The {@link java.io.File File} to be used for temporary storage. + */ + protected File getTempFile() { + if (tempFile == null) { + File tempDir = repository; + if (tempDir == null) { + tempDir = new File(System.getProperty("java.io.tmpdir")); + } + + String tempFileName = format("upload_%s_%s.tmp", UID, getUniqueId()); + + tempFile = new File(tempDir, tempFileName); + } + return tempFile; + } + + // -------------------------------------------------------- Private methods + + /** + * Returns an identifier that is unique within the class loader used to + * load this class, but does not have random-like apearance. + * + * @return A String with the non-random looking instance identifier. + */ + private static String getUniqueId() { + final int limit = 100000000; + int current = COUNTER.getAndIncrement(); + String id = Integer.toString(current); + + // If you manage to get more than 100 million of ids, you'll + // start getting ids longer than 8 characters. + if (current < limit) { + id = ("00000000" + id).substring(id.length()); + } + return id; + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + @Override + public String toString() { + return format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s", + getName(), getStoreLocation(), Long.valueOf(getSize()), + Boolean.valueOf(isFormField()), getFieldName()); + } + + // -------------------------------------------------- Serialization methods + + /** + * Writes the state of this object during serialization. + * + * @param out The stream to which the state should be written. + * + * @throws IOException if an error occurs. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + // Read the data + if (dfos.isInMemory()) { + cachedContent = get(); + } else { + cachedContent = null; + dfosFile = dfos.getFile(); + } + + // write out values + out.defaultWriteObject(); + } + + /** + * Reads the state of this object during deserialization. + * + * @param in The stream from which the state should be read. + * + * @throws IOException if an error occurs. + * @throws ClassNotFoundException if class cannot be found. + */ + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + if (!Boolean.getBoolean(SERIALIZABLE_PROPERTY)) { + throw new IllegalStateException("Property " + SERIALIZABLE_PROPERTY + + " is not true, rejecting to deserialize a DiskFileItem."); + } + // read values + in.defaultReadObject(); + + /* One expected use of serialization is to migrate HTTP sessions + * containing a DiskFileItem between JVMs. Particularly if the JVMs are + * on different machines It is possible that the repository location is + * not valid so validate it. + */ + if (repository != null) { + if (repository.isDirectory()) { + // Check path for nulls + if (repository.getPath().contains("\0")) { + throw new IOException(format( + "The repository [%s] contains a null character", + repository.getPath())); + } + } else { + throw new IOException(format( + "The repository [%s] is not a directory", + repository.getAbsolutePath())); + } + } + + OutputStream output = getOutputStream(); + if (cachedContent != null) { + output.write(cachedContent); + } else { + FileInputStream input = new FileInputStream(dfosFile); + IOUtils.copy(input, output); + dfosFile.delete(); + dfosFile = null; + } + output.close(); + + cachedContent = null; + } + + /** + * Returns the file item headers. + * @return The file items headers. + */ + public FileItemHeaders getHeaders() { + return headers; + } + + /** + * Sets the file item headers. + * @param pHeaders The file items headers. + */ + public void setHeaders(FileItemHeaders pHeaders) { + headers = pHeaders; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItemFactory.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItemFactory.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/DiskFileItemFactory.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.disk; + +import java.io.File; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.io.FileCleaningTracker; + +/** + *

The default {@link org.apache.commons.fileupload.FileItemFactory} + * implementation. This implementation creates + * {@link org.apache.commons.fileupload.FileItem} instances which keep their + * content either in memory, for smaller items, or in a temporary file on disk, + * for larger items. The size threshold, above which content will be stored on + * disk, is configurable, as is the directory in which temporary files will be + * created.

+ * + *

If not otherwise configured, the default configuration values are as + * follows:

+ *
    + *
  • Size threshold is 10KB.
  • + *
  • Repository is the system default temp directory, as returned by + * System.getProperty("java.io.tmpdir").
  • + *
+ *

+ * NOTE: Files are created in the system default temp directory with + * predictable names. This means that a local attacker with write access to that + * directory can perform a TOUTOC attack to replace any uploaded file with a + * file of the attackers choice. The implications of this will depend on how the + * uploaded file is used but could be significant. When using this + * implementation in an environment with local, untrusted users, + * {@link #setRepository(File)} MUST be used to configure a repository location + * that is not publicly writable. In a Servlet container the location identified + * by the ServletContext attribute javax.servlet.context.tempdir + * may be used. + *

+ * + *

Temporary files, which are created for file items, should be + * deleted later on. The best way to do this is using a + * {@link FileCleaningTracker}, which you can set on the + * {@link DiskFileItemFactory}. However, if you do use such a tracker, + * then you must consider the following: Temporary files are automatically + * deleted as soon as they are no longer needed. (More precisely, when the + * corresponding instance of {@link java.io.File} is garbage collected.) + * This is done by the so-called reaper thread, which is started and stopped + * automatically by the {@link FileCleaningTracker} when there are files to be + * tracked. + * It might make sense to terminate that thread, for example, if + * your web application ends. See the section on "Resource cleanup" + * in the users guide of commons-fileupload.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class DiskFileItemFactory implements FileItemFactory { + + // ----------------------------------------------------- Manifest constants + + /** + * The default threshold above which uploads will be stored on disk. + */ + public static final int DEFAULT_SIZE_THRESHOLD = 10240; + + // ----------------------------------------------------- Instance Variables + + /** + * The directory in which uploaded files will be stored, if stored on disk. + */ + private File repository; + + /** + * The threshold above which uploads will be stored on disk. + */ + private int sizeThreshold = DEFAULT_SIZE_THRESHOLD; + + /** + *

The instance of {@link FileCleaningTracker}, which is responsible + * for deleting temporary files.

+ *

May be null, if tracking files is not required.

+ */ + private FileCleaningTracker fileCleaningTracker; + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an unconfigured instance of this class. The resulting factory + * may be configured by calling the appropriate setter methods. + */ + public DiskFileItemFactory() { + this(DEFAULT_SIZE_THRESHOLD, null); + } + + /** + * Constructs a preconfigured instance of this class. + * + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + */ + public DiskFileItemFactory(int sizeThreshold, File repository) { + this.sizeThreshold = sizeThreshold; + this.repository = repository; + } + + // ------------------------------------------------------------- Properties + + /** + * Returns the directory used to temporarily store files that are larger + * than the configured size threshold. + * + * @return The directory in which temporary files will be located. + * + * @see #setRepository(java.io.File) + * + */ + public File getRepository() { + return repository; + } + + /** + * Sets the directory used to temporarily store files that are larger + * than the configured size threshold. + * + * @param repository The directory in which temporary files will be located. + * + * @see #getRepository() + * + */ + public void setRepository(File repository) { + this.repository = repository; + } + + /** + * Returns the size threshold beyond which files are written directly to + * disk. The default value is 10240 bytes. + * + * @return The size threshold, in bytes. + * + * @see #setSizeThreshold(int) + */ + public int getSizeThreshold() { + return sizeThreshold; + } + + /** + * Sets the size threshold beyond which files are written directly to disk. + * + * @param sizeThreshold The size threshold, in bytes. + * + * @see #getSizeThreshold() + * + */ + public void setSizeThreshold(int sizeThreshold) { + this.sizeThreshold = sizeThreshold; + } + + // --------------------------------------------------------- Public Methods + + /** + * Create a new {@link org.apache.commons.fileupload.disk.DiskFileItem} + * instance from the supplied parameters and the local factory + * configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + */ + public FileItem createItem(String fieldName, String contentType, + boolean isFormField, String fileName) { + DiskFileItem result = new DiskFileItem(fieldName, contentType, + isFormField, fileName, sizeThreshold, repository); + FileCleaningTracker tracker = getFileCleaningTracker(); + if (tracker != null) { + tracker.track(result.getTempFile(), result); + } + return result; + } + + /** + * Returns the tracker, which is responsible for deleting temporary + * files. + * + * @return An instance of {@link FileCleaningTracker}, or null + * (default), if temporary files aren't tracked. + */ + public FileCleaningTracker getFileCleaningTracker() { + return fileCleaningTracker; + } + + /** + * Sets the tracker, which is responsible for deleting temporary + * files. + * + * @param pTracker An instance of {@link FileCleaningTracker}, + * which will from now on track the created files, or null + * (default), to disable tracking. + */ + public void setFileCleaningTracker(FileCleaningTracker pTracker) { + fileCleaningTracker = pTracker; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/disk/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + *

+ * A disk-based implementation of the + * {@link org.apache.commons.fileupload.FileItem FileItem} + * interface. This implementation retains smaller items in memory, while + * writing larger ones to disk. The threshold between these two is + * configurable, as is the location of files that are written to disk. + *

+ *

+ * In typical usage, an instance of + * {@link org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory} + * would be created, configured, and then passed to a + * {@link org.apache.commons.fileupload.FileUpload FileUpload} + * implementation such as + * {@link org.apache.commons.fileupload.servlet.ServletFileUpload ServletFileUpload} + * or + * {@link org.apache.commons.fileupload.portlet.PortletFileUpload PortletFileUpload}. + *

+ *

+ * The following code fragment demonstrates this usage. + *

+ *
+ *        DiskFileItemFactory factory = new DiskFileItemFactory();
+ *        // maximum size that will be stored in memory
+ *        factory.setSizeThreshold(4096);
+ *        // the location for saving data that is larger than getSizeThreshold()
+ *        factory.setRepository(new File("/tmp"));
+ *
+ *        ServletFileUpload upload = new ServletFileUpload(factory);
+ * 
+ *

+ * Please see the FileUpload + * User Guide + * for further details and examples of how to use this package. + *

+ */ +package org.apache.commons.fileupload.disk; Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + *

+ * A component for handling HTML file uploads as specified by + * RFC 1867. + * This component provides support for uploads within both servlets (JSR 53) + * and portlets (JSR 168). + *

+ *

+ * While this package provides the generic functionality for file uploads, + * these classes are not typically used directly. Instead, normal usage + * involves one of the provided extensions of + * {@link org.apache.commons.fileupload.FileUpload FileUpload} such as + * {@link org.apache.commons.fileupload.servlet.ServletFileUpload ServletFileUpload} + * or + * {@link org.apache.commons.fileupload.portlet.PortletFileUpload PortletFileUpload}, + * together with a factory for + * {@link org.apache.commons.fileupload.FileItem FileItem} instances, + * such as + * {@link org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. + *

+ *

+ * The following is a brief example of typical usage in a servlet, storing + * the uploaded files on disk. + *

+ *
public void doPost(HttpServletRequest req, HttpServletResponse res) {
+ *   DiskFileItemFactory factory = new DiskFileItemFactory();
+ *   // maximum size that will be stored in memory
+ *   factory.setSizeThreshold(4096);
+ *   // the location for saving data that is larger than getSizeThreshold()
+ *   factory.setRepository(new File("/tmp"));
+ *
+ *   ServletFileUpload upload = new ServletFileUpload(factory);
+ *   // maximum size before a FileUploadException will be thrown
+ *   upload.setSizeMax(1000000);
+ *
+ *   List fileItems = upload.parseRequest(req);
+ *   // assume we know there are two files. The first file is a small
+ *   // text file, the second is unknown and is written to a file on
+ *   // the server
+ *   Iterator i = fileItems.iterator();
+ *   String comment = ((FileItem)i.next()).getString();
+ *   FileItem fi = (FileItem)i.next();
+ *   // filename on the client
+ *   String fileName = fi.getName();
+ *   // save comment and filename to database
+ *   ...
+ *   // write the file
+ *   fi.write(new File("/www/uploads/", fileName));
+ * }
+ * 
+ *

+ * In the example above, the first file is loaded into memory as a + * String. Before calling the getString method, + * the data may have been in memory or on disk depending on its size. The + * second file we assume it will be large and therefore never explicitly + * load it into memory, though if it is less than 4096 bytes it will be + * in memory before it is written to its final location. When writing to + * the final location, if the data is larger than the threshold, an attempt + * is made to rename the temporary file to the given location. If it cannot + * be renamed, it is streamed to the new location. + *

+ *

+ * Please see the FileUpload + * User Guide + * for further details and examples of how to use this package. + *

+ */ +package org.apache.commons.fileupload; Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletFileUpload.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletFileUpload.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletFileUpload.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.portlet; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import javax.portlet.ActionRequest; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileItemIterator; +import org.apache.commons.fileupload.FileUpload; +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.FileUploadException; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use + * {@link org.apache.commons.fileupload.servlet.ServletFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)} to acquire a list + * of {@link org.apache.commons.fileupload.FileItem FileItems} associated + * with a given HTML widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class PortletFileUpload extends FileUpload { + + // ---------------------------------------------------------- Class methods + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param request The portlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent(ActionRequest request) { + return FileUploadBase.isMultipartContent( + new PortletRequestContext(request)); + } + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an uninitialised instance of this class. A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see FileUpload#FileUpload(FileItemFactory) + */ + public PortletFileUpload() { + super(); + } + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see FileUpload#FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public PortletFileUpload(FileItemFactory fileItemFactory) { + super(fileItemFactory); + } + + // --------------------------------------------------------- Public methods + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public List parseRequest(ActionRequest request) + throws FileUploadException { + return parseRequest(new PortletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return A map of FileItem instances parsed from the request. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @since 1.3 + */ + public Map> parseParameterMap(ActionRequest request) + throws FileUploadException { + return parseParameterMap(new PortletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(ActionRequest request) + throws FileUploadException, IOException { + return super.getItemIterator(new PortletRequestContext(request)); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletRequestContext.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletRequestContext.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/PortletRequestContext.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.portlet; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.InputStream; + +import javax.portlet.ActionRequest; + +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.UploadContext; + +/** + *

Provides access to the request information needed for a request made to + * a portlet.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class PortletRequestContext implements UploadContext { + + // ----------------------------------------------------- Instance Variables + + /** + * The request for which the context is being provided. + */ + private final ActionRequest request; + + + // ----------------------------------------------------------- Constructors + + /** + * Construct a context for this request. + * + * @param request The request to which this context applies. + */ + public PortletRequestContext(ActionRequest request) { + this.request = request; + } + + + // --------------------------------------------------------- Public Methods + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + public String getCharacterEncoding() { + return request.getCharacterEncoding(); + } + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + public String getContentType() { + return request.getContentType(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @deprecated 1.3 Use {@link #contentLength()} instead + */ + @Deprecated + public int getContentLength() { + return request.getContentLength(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @since 1.3 + */ + public long contentLength() { + long size; + try { + size = Long.parseLong(request.getProperty(FileUploadBase.CONTENT_LENGTH)); + } catch (NumberFormatException e) { + size = request.getContentLength(); + } + return size; + } + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + public InputStream getInputStream() throws IOException { + return request.getPortletInputStream(); + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + @Override + public String toString() { + return format("ContentLength=%s, ContentType=%s", + Long.valueOf(this.contentLength()), + this.getContentType()); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/portlet/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + *

+ * An implementation of + * {@link org.apache.commons.fileupload.FileUpload FileUpload} + * for use in portlets conforming to JSR 168. This implementation requires + * only access to the portlet's current ActionRequest instance, + * and a suitable + * {@link org.apache.commons.fileupload.FileItemFactory FileItemFactory} + * implementation, such as + * {@link org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. + *

+ *

+ * The following code fragment demonstrates typical usage. + *

+ *
+ *       DiskFileItemFactory factory = new DiskFileItemFactory();
+ *        // Configure the factory here, if desired.
+ *        PortletFileUpload upload = new PortletFileUpload(factory);
+ *        // Configure the uploader here, if desired.
+ *        List fileItems = upload.parseRequest(request);
+ * 
+ *

+ * Please see the FileUpload + * User Guide + * for further details and examples of how to use this package. + *

+ */ +package org.apache.commons.fileupload.portlet; Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.servlet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletContextEvent; + +import org.apache.commons.io.FileCleaningTracker; + +/** + * A servlet context listener, which ensures that the + * {@link FileCleaningTracker}'s reaper thread is terminated, + * when the web application is destroyed. + * + * @version $Id$ + */ +public class FileCleanerCleanup implements ServletContextListener { + + /** + * Attribute name, which is used for storing an instance of + * {@link FileCleaningTracker} in the web application. + */ + public static final String FILE_CLEANING_TRACKER_ATTRIBUTE + = FileCleanerCleanup.class.getName() + ".FileCleaningTracker"; + + /** + * Returns the instance of {@link FileCleaningTracker}, which is + * associated with the given {@link ServletContext}. + * + * @param pServletContext The servlet context to query + * @return The contexts tracker + */ + public static FileCleaningTracker + getFileCleaningTracker(ServletContext pServletContext) { + return (FileCleaningTracker) + pServletContext.getAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE); + } + + /** + * Sets the instance of {@link FileCleaningTracker}, which is + * associated with the given {@link ServletContext}. + * + * @param pServletContext The servlet context to modify + * @param pTracker The tracker to set + */ + public static void setFileCleaningTracker(ServletContext pServletContext, + FileCleaningTracker pTracker) { + pServletContext.setAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE, pTracker); + } + + /** + * Called when the web application is initialized. Does + * nothing. + * + * @param sce The servlet context, used for calling + * {@link #setFileCleaningTracker(ServletContext, FileCleaningTracker)}. + */ + public void contextInitialized(ServletContextEvent sce) { + setFileCleaningTracker(sce.getServletContext(), + new FileCleaningTracker()); + } + + /** + * Called when the web application is being destroyed. + * Calls {@link FileCleaningTracker#exitWhenFinished()}. + * + * @param sce The servlet context, used for calling + * {@link #getFileCleaningTracker(ServletContext)}. + */ + public void contextDestroyed(ServletContextEvent sce) { + getFileCleaningTracker(sce.getServletContext()).exitWhenFinished(); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletFileUpload.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletFileUpload.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletFileUpload.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.servlet; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileItemIterator; +import org.apache.commons.fileupload.FileUpload; +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.FileUploadException; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(HttpServletRequest)} to acquire a list of {@link + * org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @version $Id$ + */ +public class ServletFileUpload extends FileUpload { + + /** + * Constant for HTTP POST method. + */ + private static final String POST_METHOD = "POST"; + + // ---------------------------------------------------------- Class methods + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param request The servlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent( + HttpServletRequest request) { + if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) { + return false; + } + return FileUploadBase.isMultipartContent(new ServletRequestContext(request)); + } + + // ----------------------------------------------------------- Constructors + + /** + * Constructs an uninitialised instance of this class. A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see FileUpload#FileUpload(FileItemFactory) + */ + public ServletFileUpload() { + super(); + } + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see FileUpload#FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public ServletFileUpload(FileItemFactory fileItemFactory) { + super(fileItemFactory); + } + + // --------------------------------------------------------- Public methods + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + @Override + public List parseRequest(HttpServletRequest request) + throws FileUploadException { + return parseRequest(new ServletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return A map of FileItem instances parsed from the request. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @since 1.3 + */ + public Map> parseParameterMap(HttpServletRequest request) + throws FileUploadException { + return parseParameterMap(new ServletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(HttpServletRequest request) + throws FileUploadException, IOException { + return super.getItemIterator(new ServletRequestContext(request)); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletRequestContext.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletRequestContext.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/ServletRequestContext.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.servlet; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.UploadContext; + +/** + *

Provides access to the request information needed for a request made to + * an HTTP servlet.

+ * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class ServletRequestContext implements UploadContext { + + // ----------------------------------------------------- Instance Variables + + /** + * The request for which the context is being provided. + */ + private final HttpServletRequest request; + + // ----------------------------------------------------------- Constructors + + /** + * Construct a context for this request. + * + * @param request The request to which this context applies. + */ + public ServletRequestContext(HttpServletRequest request) { + this.request = request; + } + + // --------------------------------------------------------- Public Methods + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + public String getCharacterEncoding() { + return request.getCharacterEncoding(); + } + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + public String getContentType() { + return request.getContentType(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @deprecated 1.3 Use {@link #contentLength()} instead + */ + @Deprecated + public int getContentLength() { + return request.getContentLength(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @since 1.3 + */ + public long contentLength() { + long size; + try { + size = Long.parseLong(request.getHeader(FileUploadBase.CONTENT_LENGTH)); + } catch (NumberFormatException e) { + size = request.getContentLength(); + } + return size; + } + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + public InputStream getInputStream() throws IOException { + return request.getInputStream(); + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + @Override + public String toString() { + return format("ContentLength=%s, ContentType=%s", + Long.valueOf(this.contentLength()), + this.getContentType()); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/servlet/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + *

+ * An implementation of + * {@link org.apache.commons.fileupload.FileUpload FileUpload} + * for use in servlets conforming to JSR 53. This implementation requires + * only access to the servlet's current HttpServletRequest + * instance, and a suitable + * {@link org.apache.commons.fileupload.FileItemFactory FileItemFactory} + * implementation, such as + * {@link org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. + *

+ *

+ * The following code fragment demonstrates typical usage. + *

+ *
+ *        DiskFileItemFactory factory = new DiskFileItemFactory();
+ *        // Configure the factory here, if desired.
+ *        ServletFileUpload upload = new ServletFileUpload(factory);
+ *        // Configure the uploader here, if desired.
+ *        List fileItems = upload.parseRequest(request);
+ * 
+ *

+ * Please see the FileUpload + * User Guide + * for further details and examples of how to use this package. + *

+ */ +package org.apache.commons.fileupload.servlet; Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Closeable.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Closeable.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Closeable.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util; + +import java.io.IOException; + +/** + * Interface of an object, which may be closed. + * + * @version $Id$ + */ +public interface Closeable { + + /** + * Closes the object. + * + * @throws IOException An I/O error occurred. + */ + void close() throws IOException; + + /** + * Returns, whether the object is already closed. + * + * @return True, if the object is closed, otherwise false. + * @throws IOException An I/O error occurred. + */ + boolean isClosed() throws IOException; + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/FileItemHeadersImpl.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/FileItemHeadersImpl.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/FileItemHeadersImpl.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.fileupload.FileItemHeaders; + +/** + * Default implementation of the {@link FileItemHeaders} interface. + * + * @since 1.2.1 + * + * @version $Id$ + */ +public class FileItemHeadersImpl implements FileItemHeaders, Serializable { + + /** + * Serial version UID, being used, if serialized. + */ + private static final long serialVersionUID = -4455695752627032559L; + + /** + * Map of String keys to a List of + * String instances. + */ + private final Map> headerNameToValueListMap = new LinkedHashMap>(); + + /** + * {@inheritDoc} + */ + public String getHeader(String name) { + String nameLower = name.toLowerCase(Locale.ENGLISH); + List headerValueList = headerNameToValueListMap.get(nameLower); + if (null == headerValueList) { + return null; + } + return headerValueList.get(0); + } + + /** + * {@inheritDoc} + */ + public Iterator getHeaderNames() { + return headerNameToValueListMap.keySet().iterator(); + } + + /** + * {@inheritDoc} + */ + public Iterator getHeaders(String name) { + String nameLower = name.toLowerCase(Locale.ENGLISH); + List headerValueList = headerNameToValueListMap.get(nameLower); + if (null == headerValueList) { + headerValueList = Collections.emptyList(); + } + return headerValueList.iterator(); + } + + /** + * Method to add header values to this instance. + * + * @param name name of this header + * @param value value of this header + */ + public synchronized void addHeader(String name, String value) { + String nameLower = name.toLowerCase(Locale.ENGLISH); + List headerValueList = headerNameToValueListMap.get(nameLower); + if (null == headerValueList) { + headerValueList = new ArrayList(); + headerNameToValueListMap.put(nameLower, headerValueList); + } + headerValueList.add(value); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/LimitedInputStream.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/LimitedInputStream.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/LimitedInputStream.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream, which limits its data size. This stream is + * used, if the content length is unknown. + * + * @version $Id$ + */ +public abstract class LimitedInputStream extends FilterInputStream implements Closeable { + + /** + * The maximum size of an item, in bytes. + */ + private final long sizeMax; + + /** + * The current number of bytes. + */ + private long count; + + /** + * Whether this stream is already closed. + */ + private boolean closed; + + /** + * Creates a new instance. + * + * @param inputStream The input stream, which shall be limited. + * @param pSizeMax The limit; no more than this number of bytes + * shall be returned by the source stream. + */ + public LimitedInputStream(InputStream inputStream, long pSizeMax) { + super(inputStream); + sizeMax = pSizeMax; + } + + /** + * Called to indicate, that the input streams limit has + * been exceeded. + * + * @param pSizeMax The input streams limit, in bytes. + * @param pCount The actual number of bytes. + * @throws IOException The called method is expected + * to raise an IOException. + */ + protected abstract void raiseError(long pSizeMax, long pCount) + throws IOException; + + /** + * Called to check, whether the input streams + * limit is reached. + * + * @throws IOException The given limit is exceeded. + */ + private void checkLimit() throws IOException { + if (count > sizeMax) { + raiseError(sizeMax, count); + } + } + + /** + * Reads the next byte of data from this input stream. The value + * byte is returned as an int in the range + * 0 to 255. If no byte is available + * because the end of the stream has been reached, the value + * -1 is returned. This method blocks until input data + * is available, the end of the stream is detected, or an exception + * is thrown. + *

+ * This method + * simply performs in.read() and returns the result. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public int read() throws IOException { + int res = super.read(); + if (res != -1) { + count++; + checkLimit(); + } + return res; + } + + /** + * Reads up to len bytes of data from this input stream + * into an array of bytes. If len is not zero, the method + * blocks until some input is available; otherwise, no + * bytes are read and 0 is returned. + *

+ * This method simply performs in.read(b, off, len) + * and returns the result. + * + * @param b the buffer into which the data is read. + * @param off The start offset in the destination array + * b. + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end of + * the stream has been reached. + * @exception NullPointerException If b is null. + * @exception IndexOutOfBoundsException If off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + int res = super.read(b, off, len); + if (res > 0) { + count += res; + checkLimit(); + } + return res; + } + + /** + * Returns, whether this stream is already closed. + * + * @return True, if the stream is closed, otherwise false. + * @throws IOException An I/O error occurred. + */ + public boolean isClosed() throws IOException { + return closed; + } + + /** + * Closes this input stream and releases any system resources + * associated with the stream. + * This + * method simply performs in.close(). + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Streams.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Streams.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/Streams.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.fileupload.InvalidFileNameException; +import org.apache.commons.io.IOUtils; + +/** + * Utility class for working with streams. + * + * @version $Id$ + */ +public final class Streams { + + /** + * Private constructor, to prevent instantiation. + * This class has only static methods. + */ + private Streams() { + // Does nothing + } + + /** + * Default buffer size for use in + * {@link #copy(InputStream, OutputStream, boolean)}. + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Copies the contents of the given {@link InputStream} + * to the given {@link OutputStream}. Shortcut for + *

+     *   copy(pInputStream, pOutputStream, new byte[8192]);
+     * 
+ * + * @param inputStream The input stream, which is being read. + * It is guaranteed, that {@link InputStream#close()} is called + * on the stream. + * @param outputStream The output stream, to which data should + * be written. May be null, in which case the input streams + * contents are simply discarded. + * @param closeOutputStream True guarantees, that {@link OutputStream#close()} + * is called on the stream. False indicates, that only + * {@link OutputStream#flush()} should be called finally. + * + * @return Number of bytes, which have been copied. + * @throws IOException An I/O error occurred. + */ + public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream) + throws IOException { + return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copies the contents of the given {@link InputStream} + * to the given {@link OutputStream}. + * + * @param inputStream The input stream, which is being read. + * It is guaranteed, that {@link InputStream#close()} is called + * on the stream. + * @param outputStream The output stream, to which data should + * be written. May be null, in which case the input streams + * contents are simply discarded. + * @param closeOutputStream True guarantees, that {@link OutputStream#close()} + * is called on the stream. False indicates, that only + * {@link OutputStream#flush()} should be called finally. + * @param buffer Temporary buffer, which is to be used for + * copying data. + * @return Number of bytes, which have been copied. + * @throws IOException An I/O error occurred. + */ + public static long copy(InputStream inputStream, + OutputStream outputStream, boolean closeOutputStream, + byte[] buffer) + throws IOException { + OutputStream out = outputStream; + InputStream in = inputStream; + try { + long total = 0; + for (;;) { + int res = in.read(buffer); + if (res == -1) { + break; + } + if (res > 0) { + total += res; + if (out != null) { + out.write(buffer, 0, res); + } + } + } + if (out != null) { + if (closeOutputStream) { + out.close(); + } else { + out.flush(); + } + out = null; + } + in.close(); + in = null; + return total; + } finally { + IOUtils.closeQuietly(in); + if (closeOutputStream) { + IOUtils.closeQuietly(out); + } + } + } + + /** + * This convenience method allows to read a + * {@link org.apache.commons.fileupload.FileItemStream}'s + * content into a string. The platform's default character encoding + * is used for converting bytes into characters. + * + * @param inputStream The input stream to read. + * @see #asString(InputStream, String) + * @return The streams contents, as a string. + * @throws IOException An I/O error occurred. + */ + public static String asString(InputStream inputStream) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copy(inputStream, baos, true); + return baos.toString(); + } + + /** + * This convenience method allows to read a + * {@link org.apache.commons.fileupload.FileItemStream}'s + * content into a string, using the given character encoding. + * + * @param inputStream The input stream to read. + * @param encoding The character encoding, typically "UTF-8". + * @see #asString(InputStream) + * @return The streams contents, as a string. + * @throws IOException An I/O error occurred. + */ + public static String asString(InputStream inputStream, String encoding) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copy(inputStream, baos, true); + return baos.toString(encoding); + } + + /** + * Checks, whether the given file name is valid in the sense, + * that it doesn't contain any NUL characters. If the file name + * is valid, it will be returned without any modifications. Otherwise, + * an {@link InvalidFileNameException} is raised. + * + * @param fileName The file name to check + * @return Unmodified file name, if valid. + * @throws InvalidFileNameException The file name was found to be invalid. + */ + public static String checkFileName(String fileName) { + if (fileName != null && fileName.indexOf('\u0000') != -1) { + // pFileName.replace("\u0000", "\\0") + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fileName.length(); i++) { + char c = fileName.charAt(i); + switch (c) { + case 0: + sb.append("\\0"); + break; + default: + sb.append(c); + break; + } + } + throw new InvalidFileNameException(fileName, + "Invalid file name: " + sb); + } + return fileName; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/Base64Decoder.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/Base64Decoder.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/Base64Decoder.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util.mime; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @since 1.3 + */ +final class Base64Decoder { + + /** + * Decoding table value for invalid bytes. + */ + private static final int INVALID_BYTE = -1; // must be outside range 0-63 + + /** + * Decoding table value for padding bytes, so can detect PAD afer conversion. + */ + private static final int PAD_BYTE = -2; // must be outside range 0-63 + + /** + * Mask to treat byte as unsigned integer. + */ + private static final int MASK_BYTE_UNSIGNED = 0xFF; + + /** + * Number of bytes per encoded chunk - 4 6bit bytes produce 3 8bit bytes on output. + */ + private static final int INPUT_BYTES_PER_CHUNK = 4; + + /** + * Set up the encoding table. + */ + private static final byte[] ENCODING_TABLE = { + (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', + (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', + (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', + (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', + (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', + (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', + (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', + (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', + (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', + (byte) '7', (byte) '8', (byte) '9', + (byte) '+', (byte) '/' + }; + + /** + * The padding byte. + */ + private static final byte PADDING = (byte) '='; + + /** + * Set up the decoding table; this is indexed by a byte converted to an unsigned int, + * so must be at least as large as the number of different byte values, + * positive and negative and zero. + */ + private static final byte[] DECODING_TABLE = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + + static { + // Initialise as all invalid characters + for (int i = 0; i < DECODING_TABLE.length; i++) { + DECODING_TABLE[i] = INVALID_BYTE; + } + // set up valid characters + for (int i = 0; i < ENCODING_TABLE.length; i++) { + DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i; + } + // Allow pad byte to be easily detected after conversion + DECODING_TABLE[PADDING] = PAD_BYTE; + } + + /** + * Hidden constructor, this class must not be instantiated. + */ + private Base64Decoder() { + // do nothing + } + + /** + * Decode the base 64 encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @param data the buffer containing the Base64-encoded data + * @param out the output stream to hold the decoded bytes + * + * @return the number of bytes produced. + * @throws IOException thrown when the padding is incorrect or the input is truncated. + */ + public static int decode(byte[] data, OutputStream out) throws IOException { + int outLen = 0; + byte [] cache = new byte[INPUT_BYTES_PER_CHUNK]; + int cachedBytes = 0; + + for (byte b : data) { + final byte d = DECODING_TABLE[MASK_BYTE_UNSIGNED & b]; + if (d == INVALID_BYTE) { + continue; // Ignore invalid bytes + } + cache[cachedBytes++] = d; + if (cachedBytes == INPUT_BYTES_PER_CHUNK) { + // CHECKSTYLE IGNORE MagicNumber FOR NEXT 4 LINES + final byte b1 = cache[0]; + final byte b2 = cache[1]; + final byte b3 = cache[2]; + final byte b4 = cache[3]; + if (b1 == PAD_BYTE || b2 == PAD_BYTE) { + throw new IOException("Invalid Base64 input: incorrect padding, first two bytes cannot be padding"); + } + // Convert 4 6-bit bytes to 3 8-bit bytes + // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE + out.write((b1 << 2) | (b2 >> 4)); // 6 bits of b1 plus 2 bits of b2 + outLen++; + if (b3 != PAD_BYTE) { + // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE + out.write((b2 << 4) | (b3 >> 2)); // 4 bits of b2 plus 4 bits of b3 + outLen++; + if (b4 != PAD_BYTE) { + // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE + out.write((b3 << 6) | b4); // 2 bits of b3 plus 6 bits of b4 + outLen++; + } + } else if (b4 != PAD_BYTE) { // if byte 3 is pad, byte 4 must be pad too + throw new // line wrap to avoid 120 char limit + IOException("Invalid Base64 input: incorrect padding, 4th byte must be padding if 3rd byte is"); + } + cachedBytes = 0; + } + } + // Check for anything left over + if (cachedBytes != 0) { + throw new IOException("Invalid Base64 input: truncated"); + } + return outLen; + } +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/MimeUtility.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/MimeUtility.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/MimeUtility.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util.mime; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * Utility class to decode MIME texts. + * + * @since 1.3 + */ +public final class MimeUtility { + + /** + * The {@code US-ASCII} charset identifier constant. + */ + private static final String US_ASCII_CHARSET = "US-ASCII"; + + /** + * The marker to indicate text is encoded with BASE64 algorithm. + */ + private static final String BASE64_ENCODING_MARKER = "B"; + + /** + * The marker to indicate text is encoded with QuotedPrintable algorithm. + */ + private static final String QUOTEDPRINTABLE_ENCODING_MARKER = "Q"; + + /** + * If the text contains any encoded tokens, those tokens will be marked with "=?". + */ + private static final String ENCODED_TOKEN_MARKER = "=?"; + + /** + * If the text contains any encoded tokens, those tokens will terminate with "=?". + */ + private static final String ENCODED_TOKEN_FINISHER = "?="; + + /** + * The linear whitespace chars sequence. + */ + private static final String LINEAR_WHITESPACE = " \t\r\n"; + + /** + * Mappings between MIME and Java charset. + */ + private static final Map MIME2JAVA = new HashMap(); + + static { + MIME2JAVA.put("iso-2022-cn", "ISO2022CN"); + MIME2JAVA.put("iso-2022-kr", "ISO2022KR"); + MIME2JAVA.put("utf-8", "UTF8"); + MIME2JAVA.put("utf8", "UTF8"); + MIME2JAVA.put("ja_jp.iso2022-7", "ISO2022JP"); + MIME2JAVA.put("ja_jp.eucjp", "EUCJIS"); + MIME2JAVA.put("euc-kr", "KSC5601"); + MIME2JAVA.put("euckr", "KSC5601"); + MIME2JAVA.put("us-ascii", "ISO-8859-1"); + MIME2JAVA.put("x-us-ascii", "ISO-8859-1"); + } + + /** + * Hidden constructor, this class must not be instantiated. + */ + private MimeUtility() { + // do nothing + } + + /** + * Decode a string of text obtained from a mail header into + * its proper form. The text generally will consist of a + * string of tokens, some of which may be encoded using + * base64 encoding. + * + * @param text The text to decode. + * + * @return The decoded text string. + * @throws UnsupportedEncodingException if the detected encoding in the input text is not supported. + */ + public static String decodeText(String text) throws UnsupportedEncodingException { + // if the text contains any encoded tokens, those tokens will be marked with "=?". If the + // source string doesn't contain that sequent, no decoding is required. + if (text.indexOf(ENCODED_TOKEN_MARKER) < 0) { + return text; + } + + int offset = 0; + int endOffset = text.length(); + + int startWhiteSpace = -1; + int endWhiteSpace = -1; + + StringBuilder decodedText = new StringBuilder(text.length()); + + boolean previousTokenEncoded = false; + + while (offset < endOffset) { + char ch = text.charAt(offset); + + // is this a whitespace character? + if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found + startWhiteSpace = offset; + while (offset < endOffset) { + // step over the white space characters. + ch = text.charAt(offset); + if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found + offset++; + } else { + // record the location of the first non lwsp and drop down to process the + // token characters. + endWhiteSpace = offset; + break; + } + } + } else { + // we have a word token. We need to scan over the word and then try to parse it. + int wordStart = offset; + + while (offset < endOffset) { + // step over the non white space characters. + ch = text.charAt(offset); + if (LINEAR_WHITESPACE.indexOf(ch) == -1) { // not white space + offset++; + } else { + break; + } + + //NB: Trailing whitespace on these header strings will just be discarded. + } + // pull out the word token. + String word = text.substring(wordStart, offset); + // is the token encoded? decode the word + if (word.startsWith(ENCODED_TOKEN_MARKER)) { + try { + // if this gives a parsing failure, treat it like a non-encoded word. + String decodedWord = decodeWord(word); + + // are any whitespace characters significant? Append 'em if we've got 'em. + if (!previousTokenEncoded && startWhiteSpace != -1) { + decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); + startWhiteSpace = -1; + } + // this is definitely a decoded token. + previousTokenEncoded = true; + // and add this to the text. + decodedText.append(decodedWord); + // we continue parsing from here...we allow parsing errors to fall through + // and get handled as normal text. + continue; + + } catch (ParseException e) { + // just ignore it, skip to next word + } + } + // this is a normal token, so it doesn't matter what the previous token was. Add the white space + // if we have it. + if (startWhiteSpace != -1) { + decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); + startWhiteSpace = -1; + } + // this is not a decoded token. + previousTokenEncoded = false; + decodedText.append(word); + } + } + + return decodedText.toString(); + } + + /** + * Parse a string using the RFC 2047 rules for an "encoded-word" + * type. This encoding has the syntax: + * + * encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" + * + * @param word The possibly encoded word value. + * + * @return The decoded word. + * @throws ParseException + * @throws UnsupportedEncodingException + */ + private static String decodeWord(String word) throws ParseException, UnsupportedEncodingException { + // encoded words start with the characters "=?". If this not an encoded word, we throw a + // ParseException for the caller. + + if (!word.startsWith(ENCODED_TOKEN_MARKER)) { + throw new ParseException("Invalid RFC 2047 encoded-word: " + word); + } + + int charsetPos = word.indexOf('?', 2); + if (charsetPos == -1) { + throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word); + } + + // pull out the character set information (this is the MIME name at this point). + String charset = word.substring(2, charsetPos).toLowerCase(); + + // now pull out the encoding token the same way. + int encodingPos = word.indexOf('?', charsetPos + 1); + if (encodingPos == -1) { + throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word); + } + + String encoding = word.substring(charsetPos + 1, encodingPos); + + // and finally the encoded text. + int encodedTextPos = word.indexOf(ENCODED_TOKEN_FINISHER, encodingPos + 1); + if (encodedTextPos == -1) { + throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word); + } + + String encodedText = word.substring(encodingPos + 1, encodedTextPos); + + // seems a bit silly to encode a null string, but easy to deal with. + if (encodedText.length() == 0) { + return ""; + } + + try { + // the decoder writes directly to an output stream. + ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length()); + + byte[] encodedData = encodedText.getBytes(US_ASCII_CHARSET); + + // Base64 encoded? + if (encoding.equals(BASE64_ENCODING_MARKER)) { + Base64Decoder.decode(encodedData, out); + } else if (encoding.equals(QUOTEDPRINTABLE_ENCODING_MARKER)) { // maybe quoted printable. + QuotedPrintableDecoder.decode(encodedData, out); + } else { + throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding); + } + // get the decoded byte data and convert into a string. + byte[] decodedData = out.toByteArray(); + return new String(decodedData, javaCharset(charset)); + } catch (IOException e) { + throw new UnsupportedEncodingException("Invalid RFC 2047 encoding"); + } + } + + /** + * Translate a MIME standard character set name into the Java + * equivalent. + * + * @param charset The MIME standard name. + * + * @return The Java equivalent for this name. + */ + private static String javaCharset(String charset) { + // nothing in, nothing out. + if (charset == null) { + return null; + } + + String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ENGLISH)); + // if there is no mapping, then the original name is used. Many of the MIME character set + // names map directly back into Java. The reverse isn't necessarily true. + if (mappedCharset == null) { + return charset; + } + return mappedCharset; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/ParseException.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/ParseException.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/ParseException.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util.mime; + +/** + * @since 1.3 + */ +final class ParseException extends Exception { + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = 5355281266579392077L; + + /** + * Constructs a new exception with the specified detail message. + * + * @param message the detail message. + */ + public ParseException(String message) { + super(message); + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/QuotedPrintableDecoder.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/QuotedPrintableDecoder.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/QuotedPrintableDecoder.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.fileupload.util.mime; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @since 1.3 + */ +final class QuotedPrintableDecoder { + + /** + * The shift value required to create the upper nibble + * from the first of 2 byte values converted from ascii hex. + */ + private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2; + + /** + * Hidden constructor, this class must not be instantiated. + */ + private QuotedPrintableDecoder() { + // do nothing + } + + /** + * Decode the encoded byte data writing it to the given output stream. + * + * @param data The array of byte data to decode. + * @param out The output stream used to return the decoded data. + * + * @return the number of bytes produced. + * @exception IOException + */ + public static int decode(byte[] data, OutputStream out) throws IOException { + int off = 0; + int length = data.length; + int endOffset = off + length; + int bytesWritten = 0; + + while (off < endOffset) { + byte ch = data[off++]; + + // space characters were translated to '_' on encode, so we need to translate them back. + if (ch == '_') { + out.write(' '); + } else if (ch == '=') { + // we found an encoded character. Reduce the 3 char sequence to one. + // but first, make sure we have two characters to work with. + if (off + 1 >= endOffset) { + throw new IOException("Invalid quoted printable encoding; truncated escape sequence"); + } + + byte b1 = data[off++]; + byte b2 = data[off++]; + + // we've found an encoded carriage return. The next char needs to be a newline + if (b1 == '\r') { + if (b2 != '\n') { + throw new IOException("Invalid quoted printable encoding; CR must be followed by LF"); + } + // this was a soft linebreak inserted by the encoding. We just toss this away + // on decode. + } else { + // this is a hex pair we need to convert back to a single byte. + int c1 = hexToBinary(b1); + int c2 = hexToBinary(b2); + out.write((c1 << UPPER_NIBBLE_SHIFT) | c2); + // 3 bytes in, one byte out + bytesWritten++; + } + } else { + // simple character, just write it out. + out.write(ch); + bytesWritten++; + } + } + + return bytesWritten; + } + + /** + * Convert a hex digit to the binary value it represents. + * + * @param b the ascii hex byte to convert (0-0, A-F, a-f) + * @return the int value of the hex byte, 0-15 + * @throws IOException if the byte is not a valid hex digit. + */ + private static int hexToBinary(final byte b) throws IOException { + // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE + final int i = Character.digit((char) b, 16); + if (i == -1) { + throw new IOException("Invalid quoted printable encoding: not a valid hex digit: " + b); + } + return i; + } + +} Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/mime/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + * MIME decoder implementation, imported and retailed from + * Apache Geronimo. + */ +package org.apache.commons.fileupload.util.mime; Index: 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/package-info.java =================================================================== diff -u --- 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/package-info.java (revision 0) +++ 3rdParty_sources/commons-fileupload/org/apache/commons/fileupload/util/package-info.java (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + * This package contains various IO related utility classes + * or methods, which are basically reusable and not necessarily + * restricted to the scope of a file upload. + */ +package org.apache.commons.fileupload.util; Index: 3rdParty_sources/versions.txt =================================================================== diff -u -rae060b7e61e715d667e47c7ee61f04fa2f2a0fbc -r2e874fcc582c774efc0294796c69bad5ddc46fcf --- 3rdParty_sources/versions.txt (.../versions.txt) (revision ae060b7e61e715d667e47c7ee61f04fa2f2a0fbc) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision 2e874fcc582c774efc0294796c69bad5ddc46fcf) @@ -17,6 +17,8 @@ Commons Digester 2.1 +Commons Fileupload 1.3.3 + Commons Lang 2.6 Commons IO 2.4