diff --git a/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java b/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java index 10f2a7855..3c61d1ec4 100644 --- a/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java +++ b/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java @@ -19,4 +19,16 @@ public class URLDecoderUtil { return UDecoder.URLDecode(sequence, charset); } + /** + * Decodes a x-www-form-urlencoded string. + * @param sequence the String to decode + * @param charset The name of a supported character encoding. + * @param isQueryString whether input is a query string. If true other decoding rules apply. + * @return the newly decoded String + * @exception IllegalArgumentException If the encoding is not valid + */ + public static String decode(String sequence, String charset, boolean isQueryString) { + return UDecoder.URLDecode(sequence, charset, isQueryString); + } + } diff --git a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java index 16739af63..668d1a94e 100644 --- a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java +++ b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java @@ -284,14 +284,25 @@ public class DefaultUrlHelper implements UrlHelper { * @return the encoded string */ public String decode( String input ) { - try { - return URLDecoderUtil.decode(input, encoding); - } catch (Exception e) { - LOG.warn("Could not decode URL parameter '{}', returning value un-decoded", input); - return input; - } + return URLDecoderUtil.decode(input, encoding, false); } + /** + * Decodes the URL using {@link URLDecoderUtil#decode(String, String, boolean)} with the encoding specified in the configuration. + * + * @param input the input to decode + * @param isQueryString whether input is a query string. If true other decoding rules apply. + * @return the encoded string + */ + public String decode( String input, boolean isQueryString ) { + try { + return URLDecoderUtil.decode(input, encoding, isQueryString); + } catch (Exception e) { + LOG.warn("Could not decode URL parameter '{}', returning value un-decoded", input); + return input; + } + } + public Map parseQueryString(String queryString, boolean forceValueArray) { Map queryParams = new LinkedHashMap(); if (queryString != null) { @@ -308,8 +319,8 @@ public class DefaultUrlHelper implements UrlHelper { paramValue = tmpParams[1]; } if (paramName != null) { - paramName = decode(paramName); - String translatedParamValue = decode(paramValue); + paramName = decode(paramName, true); + String translatedParamValue = decode(paramValue, true); if (queryParams.containsKey(paramName) || forceValueArray) { // WW-1619 append new param value to existing value(s) diff --git a/core/src/test/java/org/apache/struts2/util/URLDecoderUtilTest.java b/core/src/test/java/org/apache/struts2/util/URLDecoderUtilTest.java index f21c08fc8..fd274bf34 100644 --- a/core/src/test/java/org/apache/struts2/util/URLDecoderUtilTest.java +++ b/core/src/test/java/org/apache/struts2/util/URLDecoderUtilTest.java @@ -68,4 +68,11 @@ public class URLDecoderUtilTest { assertEquals("xxxx\u00ea", result); } + @Test + public void testURLDecodePlusCharAsSpace() { + + String result = URLDecoderUtil.decode("a+b", "UTF-8", true); + assertEquals("a b", result); + } + } \ No newline at end of file diff --git a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java index 57786c49b..b4a5b26c4 100644 --- a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java +++ b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java @@ -21,22 +21,23 @@ package org.apache.struts2.views.util; -import com.mockobjects.dynamic.Mock; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.inject.Scope.Strategy; -import org.apache.struts2.StrutsConstants; -import org.apache.struts2.StrutsInternalTestCase; -import org.junit.Ignore; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.StrutsInternalTestCase; + +import com.mockobjects.dynamic.Mock; +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.inject.Scope.Strategy; + /** * Test case for DefaultUrlHelper. @@ -129,34 +130,6 @@ public class DefaultUrlHelperTest extends StrutsInternalTestCase { expectedUrl, url.toString()); } - @Ignore - public void ignoreTestBuildUrlWithJavaScriptInjected() throws Exception { - String expectedUrl = "http://localhost:8080/myContext/myPage.jsp?initParam=initValue&param1=value1&param2=value2&param3%22%3Cscript+type%3D%22text%2Fjavascript%22%3Ealert%281%29%3B%3C%2Fscript%3E=value3"; - - // there is explicit escaping for EcmaScript before URL encoding - String expectedUrlBeforeEncoding = "http:\\/\\/localhost:8080\\/myContext\\/myPage.jsp?initParam=initValue&param1=value1&param2=value2&param3\\\"","value3"); - - String result = urlHelper.buildUrl("/myPage.jsp?initParam=initValue", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), parameters, "http", true, true, true); - - assertEquals( - expectedUrl, result); - mockHttpServletRequest.verify(); - } - public void testForceAddNullSchemeHostAndPort() throws Exception { String expectedUrl = "http://localhost/contextPath/path1/path2/myAction.action"; @@ -423,25 +396,11 @@ public class DefaultUrlHelperTest extends StrutsInternalTestCase { assertEquals(result, expectedResult); } - @Ignore - public void ignoreTestDontEncode() throws Exception { - String expectedUrl = "http://localhost/contextPath/myAction.action?param1=value+with+spaces"; + public void testDecodeSpacesInQueryString() throws Exception { + Map queryParameters = urlHelper.parseQueryString("name=value+with+space", false); - Mock mockHttpServletRequest = new Mock(HttpServletRequest.class); - mockHttpServletRequest.expectAndReturn("getScheme", "http"); - mockHttpServletRequest.expectAndReturn("getServerName", "localhost"); - mockHttpServletRequest.expectAndReturn("getContextPath", "/contextPath"); - mockHttpServletRequest.expectAndReturn("getServerPort", 80); - - Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); - - Map parameters = new LinkedHashMap(); - parameters.put("param1", "value+with+spaces"); - - String result = urlHelper.buildUrl("/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), parameters, "http", true, false, true); - - assertEquals( - expectedUrl, result); + assertTrue(queryParameters.containsKey("name")); + assertEquals("value with space", queryParameters.get("name")); }