Author: hermanns
Date: Fri Jun 13 14:14:44 2008
New Revision: 667656

URL: http://svn.apache.org/viewvc?rev=667656&view=rev
Log:
WW-2557 FileUploadInterceptor allows forbidden files when passed with allowed 
files
o applied slightly modified patch submitted by Stephan Schroeder

Modified:
    
struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
    
struts/struts2/trunk/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java

Modified: 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java?rev=667656&r1=667655&r2=667656&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
 (original)
+++ 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
 Fri Jun 13 14:14:44 2008
@@ -22,15 +22,7 @@
 package org.apache.struts2.interceptor;
 
 import java.io.File;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
+import java.util.*;
 
 import javax.servlet.http.HttpServletRequest;
 
@@ -48,73 +40,73 @@
 
 /**
  * <!-- START SNIPPET: description -->
- *
+ * <p/>
  * Interceptor that is based off of [EMAIL PROTECTED] 
MultiPartRequestWrapper}, which is automatically applied for any request that
  * includes a file. It adds the following parameters, where [File Name] is the 
name given to the file uploaded by the
  * HTML form:
- *
+ * <p/>
  * <ul>
- *
+ * <p/>
  * <li>[File Name] : File - the actual File</li>
- *
+ * <p/>
  * <li>[File Name]ContentType : String - the content type of the file</li>
- *
+ * <p/>
  * <li>[File Name]FileName : String - the actual name of the file uploaded 
(not the HTML name)</li>
- *
+ * <p/>
  * </ul>
- *
+ * <p/>
  * <p/> You can get access to these files by merely providing setters in your 
action that correspond to any of the three
  * patterns above, such as setDocument(File document), 
setDocumentContentType(String contentType), etc.
  * <br/>See the example code section.
- *
+ * <p/>
  * <p/> This interceptor will add several field errors, assuming that the 
action implements [EMAIL PROTECTED] ValidationAware}.
  * These error messages are based on several i18n values stored in 
struts-messages.properties, a default i18n file
  * processed for all i18n requests. You can override the text of these 
messages by providing text for the following
  * keys:
- *
+ * <p/>
  * <ul>
- *
+ * <p/>
  * <li>struts.messages.error.uploading - a general error that occurs when the 
file could not be uploaded</li>
- *
+ * <p/>
  * <li>struts.messages.error.file.too.large - occurs when the uploaded file is 
too large</li>
- *
+ * <p/>
  * <li>struts.messages.error.content.type.not.allowed - occurs when the 
uploaded file does not match the expected
  * content types specified</li>
- *
+ * <p/>
  * </ul>
- *
+ * <p/>
  * <!-- END SNIPPET: description -->
- *
+ * <p/>
  * <p/> <u>Interceptor parameters:</u>
- *
+ * <p/>
  * <!-- START SNIPPET: parameters -->
- *
+ * <p/>
  * <ul>
- *
+ * <p/>
  * <li>maximumSize (optional) - the maximum size (in bytes) that the 
interceptor will allow a file reference to be set
  * on the action. Note, this is <b>not</b> related to the various properties 
found in struts.properties.
  * Default to approximately 2MB.</li>
- *
+ * <p/>
  * <li>allowedTypes (optional) - a comma separated list of content types (ie: 
text/html) that the interceptor will allow
  * a file reference to be set on the action. If none is specified allow all 
types to be uploaded.</li>
- *
+ * <p/>
  * </ul>
- *
+ * <p/>
  * <!-- END SNIPPET: parameters -->
- *
+ * <p/>
  * <p/> <u>Extending the interceptor:</u>
- *
  * <p/>
- *
+ * <p/>
+ * <p/>
  * <!-- START SNIPPET: extending -->
- *
+ * <p/>
  * You can extend this interceptor and override the acceptFile method to 
provide more control over which files
  * are supported and which are not.
- *
+ * <p/>
  * <!-- END SNIPPET: extending -->
- *
+ * <p/>
  * <p/> <u>Example code:</u>
- *
+ * <p/>
  * <pre>
  * <!-- START SNIPPET: example-configuration -->
  * &lt;action name="doUpload" class="com.example.UploadAction"&gt;
@@ -124,13 +116,13 @@
  * &lt;/action&gt;
  * <!-- END SNIPPET: example-configuration -->
  * </pre>
- *
+ * <p/>
  * <!-- START SNIPPET: multipart-note -->
- *
+ * <p/>
  * You must set the encoding to <code>multipart/form-data</code> in the form 
where the user selects the file to upload.
- *
+ * <p/>
  * <!-- END SNIPPET: multipart-note -->
- *
+ * <p/>
  * <pre>
  * <!-- START SNIPPET: example-form -->
  *   &lt;s:form action="doUpload" method="post" 
enctype="multipart/form-data"&gt;
@@ -139,34 +131,34 @@
  *   &lt;/s:form&gt;
  * <!-- END SNIPPET: example-form -->
  * </pre>
- *
+ * <p/>
  * And then in your action code you'll have access to the File object if you 
provide setters according to the
  * naming convention documented in the start.
- *
+ * <p/>
  * <pre>
  * <!-- START SNIPPET: example-action -->
  *    package com.example;
- *
+ * <p/>
  *    import java.io.File;
  *    import com.opensymphony.xwork2.ActionSupport;
- *
+ * <p/>
  *    public UploadAction extends ActionSupport {
  *       private File file;
  *       private String contentType;
  *       private String filename;
- *
+ * <p/>
  *       public void setUpload(File file) {
  *          this.file = file;
  *       }
- *
+ * <p/>
  *       public void setUploadContentType(String contentType) {
  *          this.contentType = contentType;
  *       }
- *
+ * <p/>
  *       public void setUploadFileName(String filename) {
  *          this.filename = filename;
  *       }
- *
+ * <p/>
  *       public String execute() {
  *          //...
  *          return SUCCESS;
@@ -174,7 +166,6 @@
  *  }
  * <!-- END SNIPPET: example-action -->
  * </pre>
- *
  */
 public class FileUploadInterceptor extends AbstractInterceptor {
 
@@ -264,15 +255,24 @@
                 if (isNonEmpty(fileName)) {
                     // Get a File object for the uploaded File
                     File[] files = multiWrapper.getFiles(inputName);
-                    if (files != null) {
+                    if (files != null && files.length > 0) {
+                        ArrayList<File> acceptedFiles = new 
ArrayList<File>(files.length);
+                        ArrayList<String> acceptedContentTypes = new 
ArrayList<String>(files.length);
+                        ArrayList<String> acceptedFileNames = new 
ArrayList<String>(files.length);
+                        String contentTypeName = inputName + "ContentType";
+                        String fileNameName = inputName + "FileName";
                         for (int index = 0; index < files.length; index++) {
-
                             if (acceptFile(files[index], contentType[index], 
inputName, validation, ac.getLocale())) {
-                                parameters.put(inputName, files);
-                                parameters.put(inputName + "ContentType", 
contentType);
-                                parameters.put(inputName + "FileName", 
fileName);
+                                acceptedFiles.add(files[index]);
+                                acceptedContentTypes.add(contentType[index]);
+                                acceptedFileNames.add(fileName[index]);
                             }
                         }
+                        if (acceptedFiles.size() != 0) {
+                            parameters.put(inputName, 
acceptedFiles.toArray(new File[acceptedFiles.size()]));
+                            parameters.put(contentTypeName, 
acceptedContentTypes.toArray(new String[acceptedContentTypes.size()]));
+                            parameters.put(fileNameName, 
acceptedFileNames.toArray(new String[acceptedFileNames.size()]));
+                        }
                     }
                 } else {
                     LOG.error(getTextMessage("struts.messages.invalid.file", 
new Object[]{inputName}, ActionContext.getContext().getLocale()));
@@ -292,8 +292,8 @@
             File[] file = multiWrapper.getFiles(inputValue);
             for (int index = 0; index < file.length; index++) {
                 File currentFile = file[index];
-                if(LOG.isInfoEnabled()) {
-                       
LOG.info(getTextMessage("struts.messages.removing.file", new 
Object[]{inputValue, currentFile}, ActionContext.getContext().getLocale()));
+                if (LOG.isInfoEnabled()) {
+                    LOG.info(getTextMessage("struts.messages.removing.file", 
new Object[]{inputValue, currentFile}, ActionContext.getContext().getLocale()));
                 }
                 if ((currentFile != null) && currentFile.isFile()) {
                     currentFile.delete();
@@ -333,7 +333,7 @@
             }
 
             LOG.error(errMsg);
-        } else if ((! allowedTypesSet.isEmpty()) && 
(!containsItem(allowedTypesSet, contentType))) {
+        } else if ((!allowedTypesSet.isEmpty()) && 
(!containsItem(allowedTypesSet, contentType))) {
             String errMsg = 
getTextMessage("struts.messages.error.content.type.not.allowed", new 
Object[]{inputName, file.getName(), contentType}, locale);
             if (validation != null) {
                 validation.addFieldError(inputName, errMsg);

Modified: 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java?rev=667656&r1=667655&r2=667656&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
 (original)
+++ 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
 Fri Jun 13 14:14:44 2008
@@ -37,6 +37,7 @@
 import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
 import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
 import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import org.springframework.mock.web.MockHttpServletRequest;
 
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
@@ -48,7 +49,6 @@
 
 /**
  * Test case for FileUploadInterceptor.
- *
  */
 public class FileUploadInterceptorTest extends StrutsTestCase {
 
@@ -191,12 +191,12 @@
 
         // inspired by the unit tests for jakarta commons fileupload
         String content = ("-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
-            "Content-Type: text/html\r\n" +
-            "\r\n" +
-            "Unit test of FileUploadInterceptor" +
-            "\r\n" +
-            "-----1234--\r\n");
+                "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
+                "Content-Type: text/html\r\n" +
+                "\r\n" +
+                "Unit test of FileUploadInterceptor" +
+                "\r\n" +
+                "-----1234--\r\n");
         req.setContent(content.getBytes("US-ASCII"));
 
         MyFileupAction action = new MyFileupAction();
@@ -205,15 +205,16 @@
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map param = new HashMap();
+        Map<String, Object[]> param = new HashMap<String, Object[]>();
         ActionContext.getContext().setParameters(param);
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequest((HttpServletRequest) req, 2000));
 
         interceptor.intercept(mai);
 
-        assertTrue(! action.hasErrors());
+        assertTrue(!action.hasErrors());
 
         assertTrue(param.size() == 3);
+        System.err.println("param.get(\"file\"): " + 
param.get("file").getClass());
         File[] files = (File[]) param.get("file");
         String[] fileContentTypes = (String[]) param.get("fileContentType");
         String[] fileRealFilenames = (String[]) param.get("fileFileName");
@@ -228,10 +229,89 @@
         assertNotNull("deleteme.txt", fileRealFilenames[0]);
     }
 
+    /**
+     * tests whether with multiple files sent with the same name, the ones 
with forbiddenTypes (see
+     * FileUploadInterceptor.setAllowedTypes(...) ) are sorted out.
+     *
+     * @throws Exception
+     */
+    public void testMultipleAccept() throws Exception {
+        final String htmlContent = "<html><head></head><body>html 
content</body></html>";
+        final String plainContent = "plain content";
+        final String bondary = "simple boundary";
+        final String endline = "\r\n";
+
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        req.setCharacterEncoding("text/html");
+        req.setMethod("POST");
+        req.setContentType("multipart/form-data; boundary=" + bondary);
+        req.addHeader("Content-type", "multipart/form-data");
+        StringBuilder content = new StringBuilder(128);
+        content.append(encodeTextFile(bondary, endline, "file", "test.html", 
"text/plain", plainContent));
+        content.append(encodeTextFile(bondary, endline, "file", "test1.html", 
"text/html", htmlContent));
+        content.append(encodeTextFile(bondary, endline, "file", "test2.html", 
"text/html", htmlContent));
+        content.append(endline);
+        content.append(endline);
+        content.append(endline);
+        content.append("--");
+        content.append(bondary);
+        content.append("--");
+        content.append(endline);
+        req.setContent(content.toString().getBytes());
+
+        assertTrue(ServletFileUpload.isMultipartContent(req));
+
+        MyFileupAction action = new MyFileupAction();
+        MockActionInvocation mai = new MockActionInvocation();
+        mai.setAction(action);
+        mai.setResultCode("success");
+        mai.setInvocationContext(ActionContext.getContext());
+        Map<String, Object[]> param = new HashMap<String, Object[]>();
+        ActionContext.getContext().setParameters(param);
+        ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequest(req, 2000));
+
+        interceptor.setAllowedTypes("text/html");
+        interceptor.intercept(mai);
+
+        assertEquals(3, param.size());
+        File[] files = (File[]) param.get("file");
+        String[] fileContentTypes = (String[]) param.get("fileContentType");
+        String[] fileRealFilenames = (String[]) param.get("fileFileName");
+
+        assertNotNull(files);
+        assertNotNull(fileContentTypes);
+        assertNotNull(fileRealFilenames);
+        assertEquals("files accepted ", 2, files.length);
+        assertEquals(2, fileContentTypes.length);
+        assertEquals(2, fileRealFilenames.length);
+        assertEquals("text/html", fileContentTypes[0]);
+        assertNotNull("test1.html", fileRealFilenames[0]);
+    }
+
+    private String encodeTextFile(String bondary, String endline, String name, 
String filename, String contentType, String content) {
+        final StringBuilder sb = new StringBuilder(64);
+        sb.append(endline);
+        sb.append("--");
+        sb.append(bondary);
+        sb.append(endline);
+        sb.append("Content-Disposition: form-data; name=\"");
+        sb.append(name);
+        sb.append("\"; filename=\"");
+        sb.append(filename);
+        sb.append(endline);
+        sb.append("Content-Type: ");
+        sb.append(contentType);
+        sb.append(endline);
+        sb.append(endline);
+        sb.append(content);
+
+        return sb.toString();
+    }
+
     private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest 
req, int maxsize) throws IOException {
-       JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
-       jak.setMaxSize(String.valueOf(maxsize));
-       return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath());
+        JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
+        jak.setMaxSize(String.valueOf(maxsize));
+        return new MultiPartRequestWrapper(jak, req, 
tempDir.getAbsolutePath());
     }
 
     protected void setUp() throws Exception {


Reply via email to