You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2013/11/11 17:16:40 UTC

svn commit: r1540761 - in /cxf/trunk: core/src/main/java/org/apache/cxf/attachment/ core/src/test/java/org/apache/cxf/attachment/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/e...

Author: sergeyb
Date: Mon Nov 11 16:16:39 2013
New Revision: 1540761

URL: http://svn.apache.org/r1540761
Log:
[CXF-5368] Using a dedicated Content-Disposition handler to avoid issues with filename parameters containing ';'

Added:
    cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java   (with props)
    cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java   (with props)
Modified:
    cxf/trunk/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java

Modified: cxf/trunk/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
URL: http://svn.apache.org/viewvc/cxf/trunk/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java?rev=1540761&r1=1540760&r2=1540761&view=diff
==============================================================================
--- cxf/trunk/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java (original)
+++ cxf/trunk/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java Mon Nov 11 16:16:39 2013
@@ -37,7 +37,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.UUID;
 
 import javax.activation.CommandInfo;
@@ -318,23 +317,7 @@ public final class AttachmentUtil {
         
         final String ct = getHeader(headers, "Content-Type");
         String cd = getHeader(headers, "Content-Disposition");
-        String fileName = null;
-        if (!StringUtils.isEmpty(cd)) {
-            StringTokenizer token = new StringTokenizer(cd, ";");
-            while (token.hasMoreElements()) {
-                fileName = token.nextToken();
-                if (fileName.startsWith("name=")) {
-                    break;
-                }
-            }
-            if (!StringUtils.isEmpty(fileName)) {
-                if (fileName.contains("\"")) {
-                    fileName = fileName.substring(fileName.indexOf("\"") + 1, fileName.lastIndexOf("\""));
-                } else {
-                    fileName = fileName.substring(fileName.indexOf("=") + 1);
-                }
-            }
-        }
+        String fileName = getContentDispositionFileName(cd);
         
         String encoding = null;
         
@@ -360,6 +343,13 @@ public final class AttachmentUtil {
         return att;
     }
     
+    static String getContentDispositionFileName(String cd) {
+        if (StringUtils.isEmpty(cd)) {
+            return null;
+        }
+        //TODO: save ContentDisposition directly 
+        return new ContentDisposition(cd).getParameter("filename");
+    }
     
     public static InputStream decode(InputStream in, String encoding) throws IOException {
         encoding = encoding.toLowerCase();

Added: cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java?rev=1540761&view=auto
==============================================================================
--- cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java (added)
+++ cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java Mon Nov 11 16:16:39 2013
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.attachment;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ContentDisposition {
+    private static final String CD_HEADER_PARAMS_EXPRESSION = 
+        "(([\\w]+( )?=( )?\"[^\"]+\")|([\\w]+( )?=( )?[^;]+))";
+    private static final Pattern CD_HEADER_PARAMS_PATTERN =
+        Pattern.compile(CD_HEADER_PARAMS_EXPRESSION);
+    private String value;
+    private String type;
+    private Map<String, String> params = new LinkedHashMap<String, String>();
+    
+    public ContentDisposition(String value) {
+        this.value = value;
+        
+        String tempValue = value;
+        
+        int index = tempValue.indexOf(';');
+        if (index > 0 && !(tempValue.indexOf('=') < index)) {
+            type = tempValue.substring(0, index).trim();
+            tempValue = tempValue.substring(index + 1);
+        }
+        
+        Matcher m = CD_HEADER_PARAMS_PATTERN.matcher(tempValue);
+        while (m.find()) {
+            String[] pair = m.group().trim().split("=");
+            params.put(pair[0].trim(), 
+                       pair.length == 2 ? pair[1].trim().replace("\"", "") : "");
+        }
+    }
+    
+    public String getType() {
+        return type;
+    }
+    
+    public String getParameter(String name) {
+        return params.get(name);
+    }
+    
+    public Map<String, String> getParameters() {
+        return Collections.unmodifiableMap(params);
+    }
+    
+    public String toString() {
+        return value;
+    }
+}

Propchange: cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java?rev=1540761&view=auto
==============================================================================
--- cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java (added)
+++ cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java Mon Nov 11 16:16:39 2013
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.attachment;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AttachmentUtilTest extends Assert {
+    
+    @Test
+    public void testContendDispositionFileNameNoQuotes() {
+        assertEquals("a.txt", 
+                     AttachmentUtil.getContentDispositionFileName("form-data; filename=a.txt"));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameNoQuotesAndType() {
+        assertEquals("a.txt", 
+                     AttachmentUtil.getContentDispositionFileName("filename=a.txt"));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameNoQuotesAndType2() {
+        assertEquals("a.txt", 
+                     AttachmentUtil.getContentDispositionFileName("name=files; filename=a.txt"));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameSpacesNoQuotes() {
+        assertEquals("a.txt", 
+                     AttachmentUtil.getContentDispositionFileName("form-data; filename = a.txt"));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameWithQuotes() {
+        assertEquals("a.txt", 
+                     AttachmentUtil.getContentDispositionFileName("form-data; filename=\"a.txt\""));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameWithQuotesAndSemicolon() {
+        assertEquals("a;txt", 
+                     AttachmentUtil.getContentDispositionFileName("form-data; filename=\"a;txt\""));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameWithQuotesAndSemicolon2() {
+        assertEquals("a;txt", 
+                     AttachmentUtil.getContentDispositionFileName("filename=\"a;txt\""));
+    }
+    
+    @Test
+    public void testContendDispositionFileNameWithQuotesAndSemicolon3() {
+        assertEquals("a;txt", 
+                     AttachmentUtil.getContentDispositionFileName("name=\"a\";filename=\"a;txt\""));
+    }
+    
+    
+}
+

Propchange: cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java?rev=1540761&r1=1540760&r2=1540761&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java Mon Nov 11 16:16:39 2013
@@ -19,57 +19,10 @@
 
 package org.apache.cxf.jaxrs.ext.multipart;
 
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
 
-import org.apache.cxf.common.util.StringUtils;
-
-public class ContentDisposition {
-
-    private List<String> values;
+public class ContentDisposition extends org.apache.cxf.attachment.ContentDisposition {
     
     public ContentDisposition(String value) {
-        values = Arrays.asList(StringUtils.split(value, ";"));
-    }
-    
-    public String getType() {
-        return values.get(0).trim();
-    }
-    
-    public String getParameter(String name) {
-        for (int i = 1; i < values.size(); i++) {
-            String v = values.get(i).trim();
-            if (v.startsWith(name)) {
-                String[] parts = StringUtils.split(v, "=");
-                return parts.length == 2 ? parts[1].trim().replace("\"", "").replace("'", "") : ""; 
-            }
-        }
-        return null;
-    }
-    
-    public Map<String, String> getParameters() {
-        Map<String, String> map = new LinkedHashMap<String, String>();
-        for (int i = 1; i < values.size(); i++) {
-            String[] parts = StringUtils.split(values.get(i), "=");
-            map.put(parts[0].trim(), parts.length == 2 
-                    ? parts[1].trim().replace("\"", "").replace("'", "") : ""); 
-        }
-        return map;
-    }
-    
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.size(); i++) {
-            if (values.get(i).length() == 0) {
-                continue;
-            }
-            sb.append(values.get(i));
-            if (i + 1 < values.size()) {
-                sb.append(';');
-            }
-        }
-        return sb.toString();
+        super(value);
     }
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java?rev=1540761&r1=1540760&r2=1540761&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java Mon Nov 11 16:16:39 2013
@@ -32,5 +32,19 @@ public class ContentDispositionTest exte
         assertEquals("foo", cd.getParameter("bar"));
         assertEquals("baz1", cd.getParameter("baz"));
     }
+    @Test
+    public void testContentDispositionWithQuotes() {
+        ContentDisposition cd = new ContentDisposition(" attachment ; bar=\"foo.txt\" ; baz = baz1");
+        assertEquals("attachment", cd.getType());
+        assertEquals("foo.txt", cd.getParameter("bar"));
+        assertEquals("baz1", cd.getParameter("baz"));
+    }
+    @Test
+    public void testContentDispositionWithQuotesAndSemicolon() {
+        ContentDisposition cd = new ContentDisposition(" attachment ; bar=\"foo;txt\" ; baz = baz1");
+        assertEquals("attachment", cd.getType());
+        assertEquals("foo;txt", cd.getParameter("bar"));
+        assertEquals("baz1", cd.getParameter("baz"));
+    }
     
 }

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java?rev=1540761&r1=1540760&r2=1540761&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java Mon Nov 11 16:16:39 2013
@@ -20,6 +20,7 @@
 package org.apache.cxf.systest.jaxrs;
 
 import java.awt.Image;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
@@ -669,6 +670,23 @@ public class JAXRSMultipartTest extends 
     }
     
     @Test
+    public void testUploadFileWithSemicolonName() throws Exception {
+        String address = "http://localhost:" + PORT + "/bookstore/books/file/semicolon";
+        WebClient client = WebClient.create(address);
+        client.type("multipart/form-data").accept("text/plain");
+        
+        ContentDisposition cd = new ContentDisposition("attachment;name=\"a\";filename=\"a;txt\"");
+        MultivaluedMap<String, String> headers = new MetadataMap<String, String>();
+        headers.putSingle("Content-Disposition", cd.toString());
+        Attachment att = new Attachment(new ByteArrayInputStream("file name with semicolon".getBytes()), 
+                                        headers);
+        
+        MultipartBody body = new MultipartBody(att);
+        String partContent = client.post(body, String.class);
+        assertEquals("file name with semicolon, filename:" + "a;txt", partContent);
+    }
+    
+    @Test
     public void testUploadImageFromForm2() throws Exception {
         File file = 
             new File(getClass().getResource("/org/apache/cxf/systest/jaxrs/resources/java.jpg")

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java?rev=1540761&r1=1540760&r2=1540761&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java Mon Nov 11 16:16:39 2013
@@ -142,6 +142,15 @@ public class MultipartStore {
         return image;
     }
     
+    @Path("/books/file/semicolon")
+    @Consumes("multipart/form-data")
+    @Produces("text/plain")
+    @POST
+    public String addBookFileNameSemicolon(@Multipart("a") Attachment att) {
+        return att.getObject(String.class) 
+            + ", filename:" + att.getContentDisposition().getParameter("filename");
+    }
+    
     @POST
     @Path("/books/formimage")
     @Consumes("multipart/form-data")