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