1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.mylasta.direction.sponsor;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.Serializable;
22 import java.util.Hashtable;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29
30 import org.apache.commons.fileupload.FileItem;
31 import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
32 import org.apache.commons.fileupload.FileUploadException;
33 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
34 import org.apache.commons.fileupload.servlet.ServletFileUpload;
35 import org.dbflute.helper.message.ExceptionMessageBuilder;
36 import org.lastaflute.core.message.UserMessages;
37 import org.lastaflute.web.LastaWebKey;
38 import org.lastaflute.web.exception.Forced404NotFoundException;
39 import org.lastaflute.web.ruts.config.ModuleConfig;
40 import org.lastaflute.web.ruts.multipart.MultipartFormFile;
41 import org.lastaflute.web.ruts.multipart.MultipartRequestHandler;
42 import org.lastaflute.web.ruts.multipart.MultipartRequestWrapper;
43 import org.lastaflute.web.ruts.multipart.exception.MultipartExceededException;
44 import org.lastaflute.web.util.LaServletContextUtil;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48
49
50
51 public class FessMultipartRequestHandler implements MultipartRequestHandler {
52
53
54
55
56 private static final Logger logger = LoggerFactory.getLogger(FessMultipartRequestHandler.class);
57 public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024L;
58 public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024;
59 protected static final String CONTEXT_TEMPDIR_KEY = "javax.servlet.context.tempdir";
60 protected static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir";
61
62
63
64
65 protected Map<String, Object> elementsAll;
66 protected Map<String, MultipartFormFile> elementsFile;
67 protected Map<String, String[]> elementsText;
68
69
70
71
72 @Override
73 public void handleRequest(final HttpServletRequest request) throws ServletException {
74
75
76
77
78
79 final ServletFileUpload upload = createServletFileUpload(request);
80 prepareElementsHash();
81 try {
82 final List<FileItem> items = parseRequest(request, upload);
83 mappingParameter(request, items);
84 } catch (final SizeLimitExceededException e) {
85 handleSizeLimitExceededException(request, e);
86 } catch (final FileUploadException e) {
87 handleFileUploadException(e);
88 }
89 }
90
91 protected ModuleConfig getModuleConfig(final HttpServletRequest request) {
92 return (ModuleConfig) request.getAttribute(LastaWebKey.MODULE_CONFIG_KEY);
93 }
94
95
96
97
98 protected ServletFileUpload createServletFileUpload(final HttpServletRequest request) {
99 final DiskFileItemFactory fileItemFactory = createDiskFileItemFactory();
100 final ServletFileUpload upload = newServletFileUpload(fileItemFactory);
101 upload.setHeaderEncoding(request.getCharacterEncoding());
102 upload.setSizeMax(getSizeMax());
103 return upload;
104 }
105
106 protected ServletFileUpload newServletFileUpload(final DiskFileItemFactory fileItemFactory) {
107 return new ServletFileUpload(fileItemFactory) {
108 @Override
109 protected byte[] getBoundary(final String contentType) {
110 final byte[] boundary = super.getBoundary(contentType);
111 checkBoundarySize(contentType, boundary);
112 return boundary;
113 }
114 };
115 }
116
117 protected void checkBoundarySize(final String contentType, final byte[] boundary) {
118 final int boundarySize = boundary.length;
119 final int limitSize = getBoundaryLimitSize();
120 if (boundarySize > getBoundaryLimitSize()) {
121 throwTooLongBoundarySizeException(contentType, boundarySize, limitSize);
122 }
123 }
124
125 protected int getBoundaryLimitSize() {
126
127
128 return 2000;
129 }
130
131 protected void throwTooLongBoundarySizeException(final String contentType, final int boundarySize, final int limitSize) {
132 final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
133 br.addNotice("Too long boundary size so treats it as 404.");
134 br.addItem("Advice");
135 br.addElement("Against for JVN14876762.");
136 br.addElement("Boundary size is limited by Framework.");
137 br.addElement("Too long boundary is treated as 404 because it's thought of as attack.");
138 br.addElement("");
139 br.addElement("While, you can override the boundary limit size");
140 br.addElement(" in " + FessMultipartRequestHandler.class.getSimpleName() + ".");
141 br.addItem("Content Type");
142 br.addElement(contentType);
143 br.addItem("Boundary Size");
144 br.addElement(boundarySize);
145 br.addItem("Limit Size");
146 br.addElement(limitSize);
147 final String msg = br.buildExceptionMessage();
148 throw new Forced404NotFoundException(msg, UserMessages.empty());
149 }
150
151 protected DiskFileItemFactory createDiskFileItemFactory() {
152 final File repository = createRepositoryFile();
153 return new DiskFileItemFactory((int) getSizeThreshold(), repository);
154 }
155
156 protected File createRepositoryFile() {
157 return new File(getRepositoryPath());
158 }
159
160
161
162
163 protected void prepareElementsHash() {
164 elementsText = new Hashtable<>();
165 elementsFile = new Hashtable<>();
166 elementsAll = new Hashtable<>();
167 }
168
169 protected List<FileItem> parseRequest(final HttpServletRequest request, final ServletFileUpload upload) throws FileUploadException {
170 return upload.parseRequest(request);
171 }
172
173 protected void mappingParameter(final HttpServletRequest request, final List<FileItem> items) {
174 showFieldLoggingTitle();
175 final Iterator<FileItem> iter = items.iterator();
176 while (iter.hasNext()) {
177 final FileItem item = iter.next();
178 if (item.isFormField()) {
179 showFormFieldParameter(item);
180 addTextParameter(request, item);
181 } else {
182 showFileFieldParameter(item);
183 final String itemName = item.getName();
184 if (itemName != null && !itemName.isEmpty()) {
185 addFileParameter(item);
186 }
187 }
188 }
189 }
190
191 protected void showFieldLoggingTitle() {
192
193 if (logger.isDebugEnabled()) {
194 logger.debug("[Multipart Request Parameter]");
195 }
196 }
197
198 protected void showFormFieldParameter(final FileItem item) {
199 if (logger.isDebugEnabled()) {
200 logger.debug("[param] {}={}", item.getFieldName(), item.getString());
201 }
202 }
203
204 protected void showFileFieldParameter(final FileItem item) {
205 if (logger.isDebugEnabled()) {
206 logger.debug("[param] {}:{name={}, size={}}", item.getFieldName(), item.getName(), item.getSize());
207 }
208 }
209
210 protected void handleSizeLimitExceededException(final HttpServletRequest request, final SizeLimitExceededException e) {
211 final long actual = e.getActualSize();
212 final long permitted = e.getPermittedSize();
213 final String msg = "Exceeded size of the multipart request: actual=" + actual + " permitted=" + permitted;
214 request.setAttribute(MAX_LENGTH_EXCEEDED_KEY, new MultipartExceededException(msg, actual, permitted, e));
215 try {
216 final InputStream is = request.getInputStream();
217 try {
218 final byte[] buf = new byte[1024];
219 while ((is.read(buf)) != -1) {}
220 } catch (final Exception ignored) {} finally {
221 try {
222 is.close();
223 } catch (final Exception ignored) {}
224 }
225 } catch (final Exception ignored) {}
226 }
227
228 protected void handleFileUploadException(final FileUploadException e) throws ServletException {
229
230
231 throw new ServletException("Failed to upload the file.", e);
232 }
233
234
235
236
237 @Override
238 public void rollback() {
239 final Iterator<MultipartFormFile> iter = elementsFile.values().iterator();
240 while (iter.hasNext()) {
241 final MultipartFormFile formFile = iter.next();
242 formFile.destroy();
243 }
244 }
245
246
247
248
249 protected void addTextParameter(final HttpServletRequest request, final FileItem item) {
250 final String name = item.getFieldName();
251 final String encoding = request.getCharacterEncoding();
252 String value = null;
253 boolean haveValue = false;
254 if (encoding != null) {
255 try {
256 value = item.getString(encoding);
257 haveValue = true;
258 } catch (final Exception e) {}
259 }
260 if (!haveValue) {
261 try {
262 value = item.getString("ISO-8859-1");
263 } catch (final java.io.UnsupportedEncodingException uee) {
264 value = item.getString();
265 }
266 haveValue = true;
267 }
268 if (request instanceof MultipartRequestWrapper) {
269 final MultipartRequestWrapper wrapper = (MultipartRequestWrapper) request;
270 wrapper.setParameter(name, value);
271 }
272 final String[] oldArray = elementsText.get(name);
273 final String[] newArray;
274 if (oldArray != null) {
275 newArray = new String[oldArray.length + 1];
276 System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
277 newArray[oldArray.length] = value;
278 } else {
279 newArray = new String[] { value };
280 }
281 elementsText.put(name, newArray);
282 elementsAll.put(name, newArray);
283 }
284
285 protected void addFileParameter(final FileItem item) {
286 final MultipartFormFile formFile = newActionMultipartFormFile(item);
287 elementsFile.put(item.getFieldName(), formFile);
288 elementsAll.put(item.getFieldName(), formFile);
289 }
290
291 protected ActionMultipartFormFile newActionMultipartFormFile(final FileItem item) {
292 return new ActionMultipartFormFile(item);
293 }
294
295
296
297
298 @Override
299 public void finish() {
300 rollback();
301 }
302
303
304
305
306 protected long getSizeMax() {
307 return DEFAULT_SIZE_MAX;
308 }
309
310 protected long getSizeThreshold() {
311 return DEFAULT_SIZE_THRESHOLD;
312 }
313
314 protected String getRepositoryPath() {
315 final File tempDirFile = (File) LaServletContextUtil.getServletContext().getAttribute(CONTEXT_TEMPDIR_KEY);
316 String tempDir = tempDirFile.getAbsolutePath();
317 if (tempDir == null || tempDir.length() == 0) {
318 tempDir = System.getProperty(JAVA_IO_TMPDIR_KEY);
319 }
320 return tempDir;
321 }
322
323
324
325
326 protected static class ActionMultipartFormFile implements MultipartFormFile, Serializable {
327
328 private static final long serialVersionUID = 1L;
329
330 protected final FileItem fileItem;
331
332 public ActionMultipartFormFile(final FileItem fileItem) {
333 this.fileItem = fileItem;
334 }
335
336 @Override
337 public byte[] getFileData() throws IOException {
338 return fileItem.get();
339 }
340
341 @Override
342 public InputStream getInputStream() throws IOException {
343 return fileItem.getInputStream();
344 }
345
346 @Override
347 public String getContentType() {
348 return fileItem.getContentType();
349 }
350
351 @Override
352 public int getFileSize() {
353 return (int) fileItem.getSize();
354 }
355
356 @Override
357 public String getFileName() {
358 return getBaseFileName(fileItem.getName());
359 }
360
361 protected String getBaseFileName(final String filePath) {
362 final String fileName = new File(filePath).getName();
363 int colonIndex = fileName.indexOf(':');
364 if (colonIndex == -1) {
365 colonIndex = fileName.indexOf("\\\\");
366 }
367 final int backslashIndex = fileName.lastIndexOf('\\');
368 if (colonIndex > -1 && backslashIndex > -1) {
369 return fileName.substring(backslashIndex + 1);
370 } else {
371 return fileName;
372 }
373 }
374
375 @Override
376 public void destroy() {
377 fileItem.delete();
378 }
379
380 @Override
381 public String toString() {
382 return "formFile:{" + getFileName() + "}";
383 }
384 }
385
386
387
388
389 @Override
390 public Map<String, Object> getAllElements() {
391 return elementsAll;
392 }
393
394 @Override
395 public Map<String, String[]> getTextElements() {
396 return elementsText;
397 }
398
399 @Override
400 public Map<String, MultipartFormFile> getFileElements() {
401 return elementsFile;
402 }
403 }