/* * 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.jackrabbit.server.util; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * <code>HttpMultipartPost</code>... */ class HttpMultipartPost { /** * logger instance */ private static final Logger log = LoggerFactory.getLogger(HttpMultipartPost.class); private final Map<String, List<FileItem>> nameToItems = new LinkedHashMap<String, List<FileItem>>(); private final Set<String> fileParamNames = new HashSet<String>(); private boolean initialized; HttpMultipartPost(HttpServletRequest request, File tmpDir) throws IOException { extractMultipart(request, tmpDir); initialized = true; } /** * * @param tmpDir * @return */ private static FileItemFactory getFileItemFactory(File tmpDir) { DiskFileItemFactory fiFactory = new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, tmpDir); return fiFactory; } /** * * @param request * @param tmpDir * @throws IOException */ private void extractMultipart(HttpServletRequest request, File tmpDir) throws IOException { if (!ServletFileUpload.isMultipartContent(request)) { log.debug("Request does not contain multipart content -> ignoring."); return; } ServletFileUpload upload = new ServletFileUpload(getFileItemFactory(tmpDir)); // make sure the content disposition headers are read with the charset // specified in the request content type (or UTF-8 if no charset is specified). // see JCR if (request.getCharacterEncoding() == null) { upload.setHeaderEncoding("UTF-8"); } try { @SuppressWarnings("unchecked") List<FileItem> fileItems = upload.parseRequest(request); for (FileItem fileItem : fileItems) { addItem(fileItem); } } catch (FileUploadException e) { log.error("Error while processing multipart.", e); throw new IOException(e.toString()); } } /** * Add the given file item to the list defined for its name and make the * list is present in the map. If the item does not represent a simple * form field its name is also added to the <code>fileParamNames</code> set. * * @param item The {@link FileItem} to add. */ private void addItem(FileItem item) { String name = item.getFieldName(); List<FileItem> l = nameToItems.get(item.getFieldName()); if (l == null) { l = new ArrayList<FileItem>(); nameToItems.put(name, l); } l.add(item); // if file parameter, add name to the set of file parameters in order to // be able to extract the file parameter values later on without iterating // over all keys. if (!item.isFormField()) { fileParamNames.add(name); } } private void checkInitialized() { if (!initialized) { throw new IllegalStateException("HttpMultipartPost not initialized (or already disposed)."); } } /** * Release all file items hold with the name-to-items map. specially those * having a tmp-file associated with. * * @see FileItem#delete() */ synchronized void dispose() { checkInitialized(); for (List<FileItem> fileItems : nameToItems.values()) { for (FileItem fileItem : fileItems) { fileItem.delete(); } } nameToItems.clear(); fileParamNames.clear(); initialized = false; } /** * Returns an iterator over all file item names. * * @return a set of strings. */ Set<String> getParameterNames() { checkInitialized(); return nameToItems.keySet(); } /** * Returns the content types of the paramaters with the given name. If * the parameter does not exist <code>null</code> is returned. If the content * type of any of the parameter values is not known, the corresponding entry * in the array returned is <code>null</code>. * <p> * The content type of a paramater is only known here if the information * has been sent by the client browser. This is generally only the case * for file upload fields of HTML forms which have been posted using the * HTTP <em>POST</em> with <em>multipart/form-data</em> encoding. * <p> * Example : For the form * <pre> <form name="Upload" method="POST" ENCTYPE="multipart/form-data"> <input type="file" name="Upload"><br> <input type="text" name="Upload"><br> <input type="submit"> </form> * </pre> * this method will return an array of two entries when called for the * <em>Upload</em> parameter. The first entry will contain the content * type (if transmitted by the client) of the file uploaded. The second * entry will be <code>null</code> because the content type of the text * input field will generally not be sent by the client. * * @param name The name of the paramater whose content type is to be * returned. * @return The content types of the file items with the specified name. */ String[] getParameterTypes(String name) { checkInitialized(); String[] cts = null; List<FileItem> l = nameToItems.get(name); if (l != null && !l.isEmpty()) { cts = new String[l.size()]; for (int i = 0; i < cts.length; i++) { cts[i] = l.get(i).getContentType(); } } return cts; } /** * Returns the first value of the file items with the given <code>name</code>. * The byte to string converstion is done using either the contenttype of * the file items or the <code>formEncoding</code>. * <p> * Please note that if the addressed parameter is an uploaded file rather * than a simple form entry, the name of the original file is returned * instead of the content. * * @param name the name of the parameter * @return the string of the first value or <code>null</code> if the * parameter does not exist */ String getParameter(String name) { checkInitialized(); List<FileItem> l = nameToItems.get(name); if (l == null || l.isEmpty()) { return null; } else { FileItem item = l.get(0); if (item.isFormField()) { return item.getString(); } else { return item.getName(); } } } /** * Returns an array of Strings with all values of the parameter addressed * by <code>name</code>. the byte to string conversion is done using either * the content type of the multipart body or the <code>formEncoding</code>. * <p> * Please note that if the addressed parameter is an uploaded file rather * than a simple form entry, the name of the original file is returned * instead of the content. * * @param name the name of the parameter * @return a string array of values or <code>null</code> if no entry with the * given name exists. */ String[] getParameterValues(String name) { checkInitialized(); List<FileItem> l = nameToItems.get(name); if (l == null || l.isEmpty()) { return null; } else { String[] values = new String[l.size()]; for (int i = 0; i < values.length; i++) { FileItem item = l.get(i); if (item.isFormField()) { values[i] = item.getString(); } else { values[i] = item.getName(); } } return values; } } /** * Returns a set of the file parameter names. An empty set if * no file parameters were present in the request. * * @return an set of file item names representing the file * parameters available with the request. */ Set<String> getFileParameterNames() { checkInitialized(); return fileParamNames; } /** * Returns an array of input streams for uploaded file parameters. * * @param name the name of the file parameter(s) * @return an array of input streams or <code>null</code> if no file params * with the given name exist. * @throws IOException if an I/O error occurs */ InputStream[] getFileParameterValues(String name) throws IOException { checkInitialized(); InputStream[] values = null; if (fileParamNames.contains(name)) { List<FileItem> l = nameToItems.get(name); if (l != null && !l.isEmpty()) { List<InputStream> ins = new ArrayList<InputStream>(l.size()); for (FileItem item : l) { if (!item.isFormField()) { ins.add(item.getInputStream()); } } values = ins.toArray(new InputStream[ins.size()]); } } return values; } }