MultipartWrapper.java
package sk.iway.iwcm.system.stripes;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemHeaders;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import net.sourceforge.stripes.action.FileBean;
import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.FileTools;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.SetCharacterEncodingFilter;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.users.UsersDB;
/**
* MultipartWrapper.java - multipart pri stripes nie je mozne pouzit, potom by nefungovali veci v admin casti WebJETu (pouzivajuce struts)
*
*@Title webjet5
*@Company Interway s.r.o. (www.interway.sk)
*@Copyright Interway s.r.o. (c) 2001-2007
*@author $Author: jeeff $
*@version $Revision: 1.7 $
*@created Date: 26.3.2007 9:52:29
*@modified $Date: 2009/09/11 06:54:19 $
*/
public class MultipartWrapper implements net.sourceforge.stripes.controller.multipart.MultipartWrapper //NOSONAR
{
private HttpServletRequest request;
private boolean isParsed = false;
private Map<String,FileItem> files = new HashMap<>();
private Map<String,String[]> parameters = new HashMap<>();
private Map<String, List<String>> params;
private List<String> uploadPaths = new ArrayList<>();
@Override
public void build(HttpServletRequest request, File tempDir, long maxPostSize) throws IOException, FileUploadLimitExceededException
{
Logger.debug(MultipartWrapper.class, "build...");
String path = request.getRequestURI();
String queryString = request.getQueryString();
if (queryString==null) queryString = "";
if (path.contains("upload") || path.endsWith(".jsp") || path.endsWith(".action") || path.contains("stripes") ||
queryString.contains("__sfu=1") || path.contains("/admin/elfinder-connector/") || queryString.contains("__forceParse=1")) {
boolean processUpload = true;
//multiplefileupload.do aj ine Struts action sa spracovavaju napriamo
if (path.contains(".do")) processUpload = false;
//wj9 spring admin apps su springove, upload spracuju same
if (path.startsWith("/apps/") && path.contains("/admin/")) processUpload = false;
if (path.contains("spring") || path.contains("rest") || path.startsWith("/admin/v9/") || queryString.contains("__sfu=0") ) {
//do not process this URLs, it's spring upload
processUpload = false;
}
//moznost ovplyvnit spracovanie uploadu pomocou URL parametra bez ohladu na dalsie parametre
if (queryString.contains("__forceParse=1")) processUpload = true;
if (queryString.contains("__forceParse=0")) processUpload = false;
if (processUpload)
{
buildImpl(request, tempDir, maxPostSize, queryString);
if (Constants.getBoolean("alwaysStoreUploadedFileToRequest") || queryString.indexOf("__setf=1")!=-1 || path.contains("/admin/elfinder-connector/"))
{
Logger.debug(MultipartWrapper.class, "Storing files to request");
request.setAttribute("MultipartWrapper.files", files);
}
}
}
this.request = request;
}
private boolean isFileAllowedForUpload(Identity user, FileItem file)
{
//zmenene z false na true pretoze potom sa zle plnili polia so subormi a padalo to dalej na NPE
if (file == null || Tools.isEmpty(file.getName())) return true;
return FileTools.isFileAllowedForUpload(user, file.getName());
}
private void buildImpl(HttpServletRequest request, File tempDir, long maxPostSize, String queryString) throws IOException, FileUploadLimitExceededException
{
//list nazvov suborov ktore sa neuploadli kvoli pravam usera (napr. prekrocena max velkost suboru), ziska sa nasledne v Elfinderi pre vypis error hlasky
List<String> notUploaded = new ArrayList<>();
try
{
Logger.debug(MultipartWrapper.class, "Build IMPL");
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(tempDir);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding(SetCharacterEncodingFilter.getEncoding());
// MBO FIX: po upgrade Stripes niekedy davno:) prestali ist uploady
// velkych suborov, maxPostSize sa ktovie odkial bral a mal hodnotu
// 947912704
// vytiahneme z konfiguracie maxPostSize pre Stripes, konvertneme na
// long a nastavime pre upload
long maxPostSizeOveride = Tools.getLongValue(Tools.replace(Constants.getString("stripes.FileUpload.MaximumPostSize"), "m", "000000"), 5000000000L);
upload.setSizeMax(maxPostSizeOveride);
List<FileItem> items = upload.parseRequest(request);
params = new HashMap<>();
Identity user = UsersDB.getCurrentUser(request.getSession());
for (FileItem item : items) {
// If it's a form field, add the string value to the list
if (item.isFormField()) {
List<String> values = params.get(item.getFieldName());
if (values == null) {
values = new ArrayList<>();
params.put(item.getFieldName(), values);
}
values.add(item.getString(SetCharacterEncodingFilter.getEncoding()));
if (item.getFieldName().equalsIgnoreCase("upload_path[]")) {
uploadPaths.add(item.getString(SetCharacterEncodingFilter.getEncoding()));
}
}
}
//default true, kedze nie vsade je CSRF token
boolean csrfOK = true;
//CSRF ochrana
if (queryString.indexOf("__sfu=1")!=-1)
{
//ak nepride token, default je false
csrfOK = false;
//skonvertuj queryString hodnoty do parametrov, nech sa to da pouzit standardizovane
String[] queryParamValues = Tools.getTokens(queryString, "&");
for (String paramValue : queryParamValues)
{
int index = paramValue.indexOf("=");
if (index>0 && index<paramValue.length())
{
String name = paramValue.substring(0, index);
List<String> values = params.get(name);
if(values==null) values = new ArrayList<>();
values.add(paramValue.substring(index+1));
this.params.put(name, values);
}
}
List<String> values = this.params.get("__token");
if (values!=null && values.size()>0)
{
String token = values.get(0);
if (request.getRequestURI().contains("/rest/datatables") || request.getParameter("csrfKeepToken")!=null)
{
//datatables ma vynimku, token po nahrati suboru nemazeme, pretoze sa nemeni datatable save URL
csrfOK = CSRF.verifyTokenAjax(request.getSession(), token);
}
else
{
csrfOK = CSRF.verifyTokenAndDeleteIt(request.getSession(), token);
}
}
}
if (csrfOK)
{
for (FileItem item : items) {
if (!item.isFormField()) {
String fileName = clearFileName(item.getName());
String directory = getDirectory(fileName); //NOSONAR
FileItem fi = new RenamedFileItem(item, fileName);
boolean isAllowedForUpload = isFileAllowedForUpload(user, fi);
Logger.debug(MultipartWrapper.class, "Storing file: " + fi.getFieldName() + ", File name: " + fi.getName() + ", File type: " + fi.getContentType()
+ ", File size: " + fi.getSize() + ", allowed=" + isAllowedForUpload);
if (isAllowedForUpload) {
String key = fi.getFieldName();
//toto je pre elfinder pre multiupload, vtedy subory odkladame do mapy pod rozumnymi nazvami a nie original field name
//cid sa posiela pri chunked uploade a vtedy mame nazvy ulozene inde
if (key.equalsIgnoreCase("upload[]") && params.get("cid") == null) {
key = Tools.isNotEmpty(directory) ? directory + "/" + fileName : fileName;
}
Logger.debug(MultipartWrapper.class, "Storing file " + key);
files.put(key, fi);
} else {
notUploaded.add(fi.getName());
}
}
}
}
else
{
Logger.error(MultipartWrapper.class, "CHYBA: nesedi CSRF token, pre upload je povinny");
}
fixDocIdQueryString(request);
convertParams();
slowdownUpload();
isParsed = true;
}
catch (FileUploadBase.SizeLimitExceededException slee)
{
throw new FileUploadLimitExceededException(maxPostSize, maxPostSize);
}
catch (FileUploadException fue)
{
IOException ioe = new IOException("Could not parse and cache file upload data.");
ioe.initCause(fue);
throw ioe;
}
if (notUploaded.size()>0)
{
request.setAttribute("MultipartWrapper.notUploaded", notUploaded);
}
}
/**
* Riesenie problemu s chybou 429 Too Many Requests
*/
public static void slowdownUpload()
{
int uploadProtectionInterval = Constants.getInt("uploadProtectionInterval");
if (uploadProtectionInterval > 0)
{
Logger.debug(MultipartWrapper.class, "Waiting for "+uploadProtectionInterval+" ms");
try
{
Thread.sleep(uploadProtectionInterval);
}
catch (Exception e)
{
sk.iway.iwcm.Logger.error(e);
}
Logger.debug(MultipartWrapper.class, "Waiting done");
}
}
private String getDirectory(String fileName) {
if (uploadPaths != null && uploadPaths.size() > 0) {
Iterator<String> i = uploadPaths.iterator();
while (i.hasNext()) {
String uploadPath = i.next();
if (uploadPath.endsWith(fileName)) {
fileName = uploadPath;
i.remove();
break;
}
}
}
int lomka = fileName.lastIndexOf("/");
if (lomka > 0) {
return fileName.substring(0, lomka);
}
return "";
}
private String clearFileName(String fileName) {
if (fileName == null) return null;
// MBO oprava IE bugu, kedy IE posiela pri uploade celu
if (fileName.contains("\\"))
{
fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
Logger.debug(MultipartWrapper.class, "IE poslalo celu cestu k suboru, orezavam ho iba nan nazov: " + fileName);
}
int lomka = fileName.lastIndexOf("/");
if (lomka > -1) {
return fileName.substring(lomka + 1);
}
return fileName;
}
private void fixDocIdQueryString(HttpServletRequest request) {
try
{
// fix na docid v query stringu
String queryString = request.getQueryString();
if (queryString != null && params.get("docid") == null)
{
int i = queryString.indexOf("docid=");
int j = queryString.indexOf('&');
if (i != -1 && j > i)
{
String value = queryString.substring(i + 6, j);
List<String> values = new ArrayList<>();
values.add(value);
Logger.debug(MultipartWrapper.class, "Adding docid: " + value);
params.put("docid", values);
}
}
}
catch (Exception ex)
{
sk.iway.iwcm.Logger.error(ex);
}
}
private void convertParams() {
// Now convert them down into the usual map of String->String[]
for (Map.Entry<String, List<String>> entry : params.entrySet())
{
List<String> values = entry.getValue();
this.parameters.put(entry.getKey(), values.toArray(new String[values.size()]));
}
}
@Override
public Enumeration<String> getParameterNames() {
if (isParsed == false) return this.request.getParameterNames();
return new IteratorEnumeration(this.parameters.keySet().iterator());
}
@Override
public String[] getParameterValues(String name) {
if (isParsed == false) return this.request.getParameterValues(name);
return this.parameters.get(name);
}
@Override
public Enumeration<String> getFileParameterNames() {
if (isParsed == false) return(null);
return new IteratorEnumeration(this.files.keySet().iterator());
}
@Override
public FileBean getFileParameterValue(String name) {
if (isParsed == false) return null;
final FileItem item = this.files.get(name);
if (item == null) {
return null;
}
else {
// Use an subclass of FileBean that overrides all the
// methods that rely on having a File present, to use the FileItem
// created by commons upload instead.
return new IwayFileBean(null, item);
}
}
/** Little helper class to create an enumeration as per the interface. */
private static class IteratorEnumeration implements Enumeration<String> { //NOSONAR
Iterator<String> iterator;
/** Constructs an enumeration that consumes from the underlying iterator. */
IteratorEnumeration(Iterator<String> iterator) { this.iterator = iterator; }
/** Returns true if more elements can be consumed, false otherwise. */
@Override
public boolean hasMoreElements() { return this.iterator.hasNext(); }
/** Gets the next element out of the iterator. */
@Override
public String nextElement() { return this.iterator.next(); }
}
/**
* FileItem so zmenenym nazvom - umoznuje odstranovat celu cestu k suboru
* @author MBO
*
*/
public static class RenamedFileItem implements FileItem
{
private FileItem item;
private String name;
public RenamedFileItem(FileItem item, String name) {
this.item = item;
this.name = name;
}
@Override
public InputStream getInputStream() throws IOException {
return item.getInputStream();
}
@Override
public String getContentType() {
return item.getContentType();
}
@Override
public String getName() {
return name;
}
@Override
public boolean isInMemory() {
return item.isInMemory();
}
@Override
public long getSize() {
return item.getSize();
}
@Override
public byte[] get() {
return item.get();
}
@Override
public String getString(String paramString) throws UnsupportedEncodingException {
return item.getString(paramString);
}
@Override
public String getString() {
return item.getString();
}
@Override
public void write(File paramFile) throws Exception {
item.write(paramFile);
}
@Override
public void delete() {
item.delete();
}
@Override
public String getFieldName() {
return item.getFieldName();
}
@Override
public void setFieldName(String paramString) {
item.setFieldName(paramString);
}
@Override
public boolean isFormField() {
return item.isFormField();
}
@Override
public void setFormField(boolean paramBoolean) {
item.setFormField(paramBoolean);
}
@Override
public OutputStream getOutputStream() throws IOException {
return item.getOutputStream();
}
@Override
public FileItemHeaders getHeaders() {
return item.getHeaders();
}
@Override
public void setHeaders(FileItemHeaders arg0) {
item.setHeaders(arg0);
}
}
}