/*
* RHQ Management Platform
* Copyright (C) 2005-2012 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.coregui.server.gwt;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
import org.rhq.enterprise.server.util.LookupUtil;
/**
* @author Heiko W. Rupp
*/
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/* The number of seconds the session can remain inactive. The default is 600 (10 minutes).
* It may take longer than 10 minutes to upload a large file, so we'll increase...
*/
private static final int MAX_INACTIVE_INTERVAL = 60 * 60;
private File tmpDir;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.setMaxInactiveInterval(MAX_INACTIVE_INTERVAL);
if (ServletFileUpload.isMultipartContent(req)) {
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
if (tmpDir == null) {
tmpDir = LookupUtil.getCoreServer().getJBossServerTempDir();
}
fileItemFactory.setRepository(tmpDir);
//fileItemFactory.setSizeThreshold(0);
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
List<FileItem> fileItemsList;
try {
fileItemsList = servletFileUpload.parseRequest(req);
} catch (FileUploadException e) {
writeExceptionResponse(resp, "File upload failed", e);
return;
}
List<FileItem> actualFiles = new ArrayList<FileItem>();
Map<String, String> formFields = new HashMap<String, String>();
boolean retrieve = false;
boolean obfuscate = false;
Subject authenticatedSubject = null;
for (FileItem fileItem : fileItemsList) {
if (fileItem.isFormField()) {
if (fileItem.getFieldName() != null) {
formFields.put(fileItem.getFieldName(), fileItem.getString());
}
if ("retrieve".equals(fileItem.getFieldName())) {
retrieve = true;
} else if ("obfuscate".equals(fileItem.getFieldName())) {
obfuscate = Boolean.parseBoolean(fileItem.getString());
} else if ("sessionid".equals(fileItem.getFieldName())) {
int sessionid = Integer.parseInt(fileItem.getString());
SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
try {
authenticatedSubject = subjectManager.getSubjectBySessionId(sessionid);
} catch (Exception e) {
throw new ServletException("Cannot authenticate request", e);
}
}
fileItem.delete();
} else {
// file item contains an actual uploaded file
actualFiles.add(fileItem);
log("file was uploaded: " + fileItem.getName());
}
}
if (authenticatedSubject == null) {
for (FileItem fileItem : actualFiles) {
fileItem.delete();
}
throw new ServletException("Cannot process unauthenticated request");
}
if (retrieve && actualFiles.size() == 1) {
// sending in "retrieve" form element with a single file means the client just wants the content echoed back
resp.setContentType("text/html");
FileItem fileItem = actualFiles.get(0);
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.print("<html>");
InputStream inputStream = fileItem.getInputStream();
try {
// we have to HTML escape inputStream before writing it to outputStream
StreamUtil.copy(inputStream, outputStream, false, true);
} finally {
inputStream.close();
}
outputStream.print("</html>");
outputStream.flush();
fileItem.delete();
} else {
Map<String, File> allUploadedFiles = new HashMap<String, File>(); // maps form field name to the actual file
Map<String, String> allUploadedFileNames = new HashMap<String, String>(); // maps form field name to upload file name
for (FileItem fileItem : actualFiles) {
File theFile = forceToFile(fileItem);
if (obfuscate) {
// The commons fileupload API has a file tracker that deletes the file when the File object is garbage collected (huh?).
// Because we will be using these files later, and because they are going to be obfuscated, we don't want this to happen,
// so just rename the file to move it away from the file tracker and thus won't get
// prematurely deleted before we get a chance to use it.
File movedFile = new File(theFile.getAbsolutePath() + ".temp");
if (theFile.renameTo(movedFile)) {
theFile = movedFile;
}
try {
FileUtil.compressFile(theFile); // we really just compress it with our special compressor since its faster than obsfucation
} catch (Exception e) {
throw new ServletException("Cannot obfuscate uploaded files", e);
}
}
allUploadedFiles.put(fileItem.getFieldName(), theFile);
allUploadedFileNames.put(fileItem.getFieldName(), (fileItem.getName() != null) ? fileItem.getName()
: theFile.getName());
}
processUploadedFiles(authenticatedSubject, allUploadedFiles, allUploadedFileNames, formFields, req,
resp);
}
}
}
protected void writeExceptionResponse(HttpServletResponse resp, String msg, Exception e) throws IOException {
resp.setStatus(500);
PrintWriter writer = resp.getWriter();
writer.write("<html><head></head><body><strong>" + msg + "</strong><br/>\n");
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
writer.write(sw.toString().replace("\n", "<br/>\n"));
writer.write("</body></html>");
writer.flush();
}
/**
* This method will write the names of all files (the local file system location where the file was stored)
* to the response - each local filename on its own line.
* Subclasses are free to override this to process the files however they need.
*
* @param subject
* @param files maps form field name to the actual File
* @param fileNames maps form field name to the name of the file, as told to us by the client
* @param formFields
* @param request
* @param response
* @throws IOException
*/
protected void processUploadedFiles(Subject subject, Map<String, File> files, Map<String, String> fileNames,
Map<String, String> formFields, HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
writer.println("<html>");
for (File file : files.values()) {
String absolutePath = file.getAbsolutePath();
writer.println(absolutePath);
}
writer.println("</html>");
writer.flush();
return;
}
protected File forceToFile(FileItem fileItem) throws IOException, ServletException {
if (fileItem.isInMemory()) {
String name = fileItem.getName();
if (null == name) {
throw new IllegalArgumentException("FileItem has null name");
}
// some browsers (IE, Chrome) pass an absolute filename, we just want the name of the file, no paths
name = name.replace('\\', '/');
if (name.length() > 2 && name.charAt(1) == ':') {
name = name.substring(2);
}
name = new File(name).getName();
File tmpFile = File.createTempFile(name, null, LookupUtil.getCoreServer().getJBossServerTempDir());
try {
fileItem.write(tmpFile);
return tmpFile;
} catch (Exception e) {
throw new ServletException("Failed to persist uploaded file to disk", e);
}
} else {
return ((DiskFileItem) fileItem).getStoreLocation();
}
}
}