This is an automated email from the ASF dual-hosted git repository. dkulp pushed a commit to branch 3.6.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit a5ad0b1798aac1f5df2f725363f932a808395b3e Author: Andriy Redko <drr...@gmail.com> AuthorDate: Sun Nov 27 11:13:55 2022 -0500 CXF-8799: Unxepected URLEncode in MTOM Content-Id (cherry picked from commit ed102a26c22b5376ace2951d508485457f1f606b) --- .../cxf/attachment/AttachmentSerializer.java | 19 ++++++- .../org/apache/cxf/attachment/AttachmentUtil.java | 63 +++++++--------------- .../cxf/attachment/AttachmentSerializerTest.java | 6 +++ .../apache/cxf/attachment/AttachmentUtilTest.java | 27 ++++------ 4 files changed, 55 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java index bb3dc4e04d..fc56cf2131 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java @@ -241,7 +241,14 @@ public class AttachmentSerializer { // String[] address = attachmentId.split("@", 2); if (address.length == 2) { - writer.write(attachmentId); + // See please AttachmentUtil::createContentID, the domain part is URL encoded + final String decoded = tryDecode(address[1], StandardCharsets.UTF_8); + // If the domain part is encoded, decode it + if (!decoded.equalsIgnoreCase(address[1])) { + writer.write(address[0] + "@" + decoded); + } else { + writer.write(attachmentId); + } } else { writer.write(URLEncoder.encode(attachmentId, StandardCharsets.UTF_8.name())); } @@ -377,4 +384,14 @@ public class AttachmentSerializer { private static String decode(String s, Charset charset) { return URLDecoder.decode(s.replaceAll("([^%])[+]", "$1%2B"), charset); } + + // Try to decode the string assuming the decoding may fail, the original string is going to + // be returned in this case. + private static String tryDecode(String s, Charset charset) { + try { + return decode(s, charset); + } catch (IllegalArgumentException ex) { + return s; + } + } } diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java index 8d0841c5c6..7ff0734645 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -45,7 +45,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; -import java.util.regex.Pattern; import javax.activation.CommandInfo; import javax.activation.CommandMap; @@ -82,15 +81,8 @@ public final class AttachmentUtil { private static final Random BOUND_RANDOM = new Random(); private static final CommandMap DEFAULT_COMMAND_MAP = CommandMap.getDefaultCommandMap(); private static final MailcapCommandMap COMMAND_MAP = new EnhancedMailcapCommandMap(); - - /** - * Yet <a href="https://datatracker.ietf.org/doc/html/rfc822#appendix-D">RFC-822 Appendix D (ALPHABETICAL LISTING OF SYNTAX RULES)</a> - * allows more characters in domain-literal, - * this regex is valid to check that the parsed domain is compliant, - * although it is stricter - */ - private static final Pattern ALPHA_NUMERIC_DOMAIN_PATTERN = Pattern.compile("^\\w+(\\.\\w+)*$"); - + + static final class EnhancedMailcapCommandMap extends MailcapCommandMap { @Override public synchronized DataContentHandler createDataContentHandler( @@ -240,49 +232,24 @@ public final class AttachmentUtil { } } - /** - * Creates Content ID from {@link #ATT_UUID} and given namespace - * <p> - * Example: - * <pre>6976d00d-740c-48ed-b63d-8c56707544f...@example.com</pre> - * <p> - * <a href="https://datatracker.ietf.org/doc/html/rfc2392#section-2">RFC-2392 Section 2 (The MID and CID URL Schemes)</a> - * specifies Content ID as: - * <pre> - * content-id = url-addr-spec - * url-addr-spec = addr-spec ; URL encoding of RFC 822 addr-spec - * </pre> - * <a href="https://datatracker.ietf.org/doc/html/rfc822#appendix-D">RFC-822 Appendix D (ALPHABETICAL LISTING OF SYNTAX RULES)</a>: - * <pre> - * addr-spec = local-part "@" domain ; global address - * </pre> - * - * @param ns namespace. If null, falls back to "cxf.apache.org" - * @return Content ID - */ - public static String createContentID(String ns) { + public static String createContentID(String ns) throws UnsupportedEncodingException { // tend to change String cid = "cxf.apache.org"; if (ns != null && !ns.isEmpty()) { - if (isAlphaNumericDomain(ns)) { - cid = ns; - } try { URI uri = new URI(ns); String host = uri.getHost(); - if (host != null && isAlphaNumericDomain(host)) { + if (host != null) { cid = host; + } else { + cid = ns; } } catch (Exception e) { - // Could not parse domain => use fallback value + cid = ns; } } return ATT_UUID + '-' + Integer.toString(COUNTER.incrementAndGet()) + '@' - + URLEncoder.encode(cid, StandardCharsets.UTF_8); - } - - private static boolean isAlphaNumericDomain(String string) { - return ALPHA_NUMERIC_DOMAIN_PATTERN.matcher(string).matches(); + + URLEncoder.encode(cid, StandardCharsets.UTF_8.name()); } public static String getUniqueBoundaryValue() { @@ -522,7 +489,12 @@ public final class AttachmentUtil { source.setContentType(mimeType); DataHandler handler = new DataHandler(source); - String id = AttachmentUtil.createContentID(elementNS); + String id; + try { + id = AttachmentUtil.createContentID(elementNS); + } catch (UnsupportedEncodingException e) { + throw new Fault(e); + } AttachmentImpl att = new AttachmentImpl(id, handler); att.setXOP(isXop); return att; @@ -557,7 +529,12 @@ public final class AttachmentUtil { // ignore, just do the normal attachment thing } - String id = AttachmentUtil.createContentID(elementNS); + String id; + try { + id = AttachmentUtil.createContentID(elementNS); + } catch (UnsupportedEncodingException e) { + throw new Fault(e); + } AttachmentImpl att = new AttachmentImpl(id, handler); if (!StringUtils.isEmpty(handler.getName())) { //set Content-Disposition attachment header if filename isn't null diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java index 33df34fb3d..4226418d7d 100644 --- a/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerTest.java @@ -183,6 +183,12 @@ public class AttachmentSerializerTest { doTestMessageMTOM("cid:http%3A%2F%2Fcxf.apache.org%2F", "<http://cxf.apache.org/>"); } + @Test + public void testMessageMTOMCidEncoded() throws Exception { + doTestMessageMTOM("cid:cxf@[2001%3A0db8%3A11a3%3A09d7%3A1f34%3A8a2e%3A07a0%3A765d]", + "<cxf@[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]>"); + } + @Test public void testMessageMTOMUrlDecoded() throws Exception { doTestMessageMTOM("test+me.xml", "<test%2Bme.xml>"); diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java index eeebc24e2e..6b3285c810 100644 --- a/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentUtilTest.java @@ -20,13 +20,15 @@ package org.apache.cxf.attachment; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; -import org.junit.Ignore; import org.junit.Test; import static org.easymock.EasyMock.createMock; @@ -34,16 +36,10 @@ import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.matchesPattern; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; public class AttachmentUtilTest { - - // Yet RFC822 allows more characters in domain-literal, - // this regex is enough to check that the fallback domain is compliant - public static final String CONTENT_ID_WITH_ALPHA_NUMERIC_DOMAIN_PATTERN = ".+@\\w+(\\.\\w+)*"; - @Test public void testContendDispositionFileNameNoQuotes() { assertEquals("a.txt", @@ -141,15 +137,15 @@ public class AttachmentUtilTest { assertNotEquals(AttachmentUtil.createContentID(null), AttachmentUtil.createContentID(null)); } + @Test - public void testCreateContentIDWithNullDomainNamePassed() { + public void testCreateContentIDWithNullDomainNamePassed() throws UnsupportedEncodingException { String actual = AttachmentUtil.createContentID(null); - - assertThat(actual, matchesPattern(CONTENT_ID_WITH_ALPHA_NUMERIC_DOMAIN_PATTERN)); + assertThat(actual, endsWith("@cxf.apache.org")); } @Test - public void testCreateContentIDWithDomainNamePassed() { + public void testCreateContentIDWithDomainNamePassed() throws UnsupportedEncodingException { String domain = "subdomain.example.com"; String actual = AttachmentUtil.createContentID(domain); @@ -158,7 +154,7 @@ public class AttachmentUtilTest { } @Test - public void testCreateContentIDWithUrlPassed() { + public void testCreateContentIDWithUrlPassed() throws UnsupportedEncodingException { String domain = "subdomain.example.com"; String url = "https://" + domain + "/a/b/c"; @@ -168,7 +164,7 @@ public class AttachmentUtilTest { } @Test - public void testCreateContentIDWithIPv4BasedUrlPassed() { + public void testCreateContentIDWithIPv4BasedUrlPassed() throws UnsupportedEncodingException { String domain = "127.0.0.1"; String url = "https://" + domain + "/a/b/c"; @@ -178,13 +174,12 @@ public class AttachmentUtilTest { } @Test - public void testCreateContentIDWithIPv6BasedUrlPassed() { + public void testCreateContentIDWithIPv6BasedUrlPassed() throws UnsupportedEncodingException { String domain = "[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"; String url = "http://" + domain + "/a/b/c"; String actual = AttachmentUtil.createContentID(url); - - assertThat(actual, matchesPattern(CONTENT_ID_WITH_ALPHA_NUMERIC_DOMAIN_PATTERN)); + assertThat(actual, endsWith("@" + URLEncoder.encode(domain, StandardCharsets.UTF_8))); } private CachedOutputStream testSetStreamedAttachmentProperties(final String property, final Object value)