This is a cache of https://fess.codelibs.org/xref/org/codelibs/fess/mylasta/direction/sponsor/FessMultipartRequestHandler.html. It is a snapshot of the page at 2020-10-25T01:23:40.873+0000.
FessMultipartRequestHandler xref
View Javadoc
1   /*
2    * Copyright 2012-2020 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
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   * @author modified by jflute (originated in Seasar)
51   */
52  public class FessMultipartRequestHandler implements MultipartRequestHandler {
53  
54      // ===================================================================================
55      //                                                                          Definition
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      //                                                                           Attribute
63      //                                                                           =========
64      protected Map<String, Object> elementsAll;
65      protected Map<String, MultipartFormFile> elementsFile;
66      protected Map<String, String[]> elementsText;
67  
68      // ===================================================================================
69      //                                                                      Handle Request
70      //                                                                      ==============
71      @Override
72      public void handleRequest(final HttpServletRequest request) throws ServletException {
73          // /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
74          // copied from super's method and extends it
75          // basically for JVN#14876762
76          // thought not all problems are resolved however the main case is safety
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      //                                                            Create ServletFileUpload
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) { // for security
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         // one HTTP proxy tool already limits the size (e.g. 3450 bytes)
126         // so specify this size for test
127         return 2000; // you can override as you like it
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()); // heavy attack!? so give no page to tell wasted action
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     //                                                                      Handling Parts
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         // logging filter cannot show the parameters when multi-part so logging here
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         // suppress logging because it can be caught by logging filter
229         //log.error("Failed to parse multipart request", e);
230         throw new ServletException("Failed to upload the file.", e);
231     }
232 
233     // ===================================================================================
234     //                                                                           Roll-back
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     //                                                                            Add Text
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     //                                                                              Finish
296     //                                                                              ======
297     @Override
298     public void finish() {
299         rollback();
300     }
301 
302     // ===================================================================================
303     //                                                                        Small Helper
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     //                                                                           Form File
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("\\\\"); // Windows SMB
365             }
366             final int backslashIndex = fileName.lastIndexOf('\\');
367             if (colonIndex > -1 && backslashIndex > -1) {
368                 return fileName.substring(backslashIndex + 1);
369             } else {
370                 return fileName;
371             }
372         }
373 
374         @Override
375         public void destroy() {
376             fileItem.delete();
377         }
378 
379         @Override
380         public String toString() {
381             return "formFile:{" + getFileName() + "}";
382         }
383     }
384 
385     // ===================================================================================
386     //                                                                            Accessor
387     //                                                                            ========
388     @Override
389     public Map<String, Object> getAllElements() {
390         return elementsAll;
391     }
392 
393     @Override
394     public Map<String, String[]> getTextElements() {
395         return elementsText;
396     }
397 
398     @Override
399     public Map<String, MultipartFormFile> getFileElements() {
400         return elementsFile;
401     }
402 }