// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.servlet.upload; import jodd.core.JoddCore; import jodd.servlet.ServletUtil; import jodd.upload.FileUploadFactory; import jodd.upload.MultipartStreamParser; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Enumeration; /** * Handles multi-part requests and extract uploaded files and parameters from * it. Multi-part forms should be defined as: * <p> * * <code> * <form method="post" enctype="multipart/form-data" accept-charset="<i>charset</i>"... * </code> * * <p> * "accept-charset" may be used in case when jsp page uses specific * encoding. If default encoding is used, this attribute is not required. * * <p> * MultipleRequest class may be created in two ways:<br> * 1) with the constructors, when user must prevent instantiating more than once;<br> * 2) using static factory methods, which always return valid MultipleRequest instance. * * <p> * This class loads complete request. To prevent big uploads (and potential * DoS attacks) check content length <b>before</b> loading. */ public class MultipartRequest extends MultipartStreamParser { // ---------------------------------------------------------------- properties private HttpServletRequest request; private String characterEncoding; /** * Returns actual http servlet request instance. */ public HttpServletRequest getServletRequest() { return request; } /** * Returns request content length. Usually used before loading, to check the upload size. */ public int getContentLength() { return request.getContentLength(); } /** * Returns current encoding. */ public String getCharacterEncoding() { return characterEncoding; } // ---------------------------------------------------------------- constructors /** * Creates new multi-part request with form encoding and file upload factory. * After construction stream is <b>not</b> yet parsed! Use {@link #parseMultipartRequest()} or * {@link #parseRequest()} to parse before further usage. * * <p> * If not specified, character encoding is read from the request. If not specified there, * default Jodd encoding is used. * * <p> * Multiple instantiation doesn't work, since input stream can be parsed just once. * Still, if it is needed, use {@link #getInstance(javax.servlet.http.HttpServletRequest, FileUploadFactory, String)} * instead. * * @param request http request * @param encoding form encoding or <code>null</code> * @param fileUploadFactory file factory, or <code>null</code> for default factory */ public MultipartRequest(HttpServletRequest request, FileUploadFactory fileUploadFactory, String encoding) { super(fileUploadFactory); this.request = request; if (encoding != null) { this.characterEncoding = encoding; } else { this.characterEncoding = request.getCharacterEncoding(); } if (this.characterEncoding == null) { this.characterEncoding = JoddCore.encoding; } } // ---------------------------------------------------------------- factories private static final String MREQ_ATTR_NAME = MultipartRequest.class.getName(); /** * Returns new or existing instance of <code>MultipartRequest</code>. */ public static MultipartRequest getInstance(HttpServletRequest request, FileUploadFactory fileUploadFactory, String encoding) throws IOException { MultipartRequest mreq = (MultipartRequest) request.getAttribute(MREQ_ATTR_NAME); if (mreq == null) { mreq = new MultipartRequest(request, fileUploadFactory, encoding); request.setAttribute(MREQ_ATTR_NAME, mreq); } if (!mreq.isParsed()) { mreq.parseRequest(); } return mreq; } /** * Returns new or existing instance of <code>MultipartRequest</code>. */ public static MultipartRequest getInstance(HttpServletRequest request) throws IOException { return getInstance(request, null, null); } // ---------------------------------------------------------------- load /** * Loads and parse multi-part request. It <b>doesn't</b> check if request is multi-part. * Must be called on same request at most <b>once</b>. */ public void parseMultipartRequest() throws IOException { parseRequestStream(request.getInputStream(), characterEncoding); } /** * Checks if request if multi-part and parse it. If request is not multi-part it * copies all parameters, to make usage the same in both cases. * * @see MultipartRequestWrapper */ public void parseRequest() throws IOException { if (ServletUtil.isMultipartRequest(request)) { parseRequestStream(request.getInputStream(), characterEncoding); } else { Enumeration names = request.getParameterNames(); while (names.hasMoreElements()) { String paramName = (String) names.nextElement(); String[] values = request.getParameterValues(paramName); putParameters(paramName, values); } } } }