This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.5.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit f92c8278e3bba8c7d900900cff3eb9b0a33d23b6 Author: Andriy Redko <[email protected]> AuthorDate: Fri Jul 15 09:54:42 2022 -0400 CXF-8733: [regression] Content-ID of attachments for outgoing requests is not URL-decoded (#968) (cherry picked from commit 952cd07527d9abb0b6665d7483913afe1c1ae5e7) --- .../org/apache/cxf/attachment/AttachmentUtil.java | 23 +-- .../AttachmentSerializerDeserializerTest.java | 188 +++++++++++++++++++++ 2 files changed, 196 insertions(+), 15 deletions(-) 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 f62ebee76b..ce393ee54d 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -355,21 +355,14 @@ public final class AttachmentUtil { } // strip cid: if (id.startsWith("cid:")) { - // - // RFC-2392 (https://datatracker.ietf.org/doc/html/rfc2392) says: - // - // A "cid" URL is converted to the corresponding Content-ID message - // header [MIME] by removing the "cid:" prefix, converting the % encoded - // character to their equivalent US-ASCII characters, and enclosing the - // remaining parts with an angle bracket pair, "<" and ">". - // - try { - id = id.substring(4); - // urldecode - id = URLDecoder.decode(id, StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - //ignore, keep id as is - } + id = id.substring(4); + } + + try { + // urldecode + id = URLDecoder.decode(id, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore, keep id as is } } if (id == null) { diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerDeserializerTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerDeserializerTest.java new file mode 100644 index 0000000000..99d5c11492 --- /dev/null +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentSerializerDeserializerTest.java @@ -0,0 +1,188 @@ +/** + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import javax.activation.DataHandler; +import javax.mail.MessagingException; +import javax.mail.util.ByteArrayDataSource; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.message.Attachment; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageImpl; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AttachmentSerializerDeserializerTest { + + @Test + public void testMessageWriteXopOn1() throws Exception { + doTestMessageSerde(true, "text/xml"); + } + + @Test + public void testMessageWriteXopOn2() throws Exception { + doTestMessageSerde(true, "application/soap+xml; action=\"urn:foo\""); + } + + @Test + public void testMessageWriteXopOff1() throws Exception { + doTestMessageSerde(false, "text/xml"); + } + + @Test + public void testMessageWriteXopOff2() throws Exception { + doTestMessageSerde(false, "application/soap+xml; action=\"urn:foo\""); + } + + private void doTestMessageSerde(boolean xop, String soapContentType) throws Exception { + MessageImpl in = new MessageImpl(); + + Collection<Attachment> atts = new ArrayList<>(); + AttachmentImpl a = new AttachmentImpl("test.xml"); + + InputStream is = getClass().getResourceAsStream("my.wav"); + ByteArrayDataSource ds = new ByteArrayDataSource(is, "application/octet-stream"); + a.setDataHandler(new DataHandler(ds)); + + atts.add(a); + + in.setAttachments(atts); + + // Set the SOAP content type + in.put(Message.CONTENT_TYPE, soapContentType); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + in.setContent(OutputStream.class, out); + + AttachmentSerializer serializer = new AttachmentSerializer(in); + if (!xop) { + // default is "on" + serializer.setXop(xop); + } + + serializer.writeProlog(); + out.write("<soap:Body/>".getBytes()); + + serializer.writeAttachments(); + out.flush(); + + doTestMessageRead(in, "test.xml"); + is.close(); + } + + private void doTestMessageRead(Message in, String contentId) + throws IOException, MessagingException { + + final MessageImpl msg = new MessageImpl(); + msg.put(Message.CONTENT_TYPE, "multipart/related;"); + + final ByteArrayOutputStream out = (ByteArrayOutputStream)in.getContent(OutputStream.class); + try (ByteArrayInputStream content = new ByteArrayInputStream(out.toByteArray())) { + msg.setContent(InputStream.class, content); + + AttachmentDeserializer deserializer = new AttachmentDeserializer(msg); + deserializer.initializeAttachments(); + + Collection<Attachment> atts = msg.getAttachments(); + assertNotNull(atts); + + Iterator<Attachment> itr = atts.iterator(); + assertTrue(itr.hasNext()); + + Attachment a = itr.next(); + assertNotNull(a); + + assertEquals("binary", a.getHeader("Content-Transfer-Encoding")); + assertEquals(contentId, a.getId()); + + // check the cached output stream + InputStream attBody = msg.getContent(InputStream.class); + try (ByteArrayOutputStream attOut = new ByteArrayOutputStream()) { + IOUtils.copy(attBody, attOut); + assertEquals("<soap:Body/>", attOut.toString()); + } + } + } + + @Test + public void testMessageMTOM() throws Exception { + doTestMessageMTOM("test.xml", "<test.xml>", "test.xml"); + } + + @Test + public void testMessageMTOMCid() throws Exception { + doTestMessageMTOM("cid:http%3A%2F%2Fcxf.apache.org%2F", "<http://cxf.apache.org/>", "http://cxf.apache.org/"); + } + + @Test + public void testMessageMTOMUrlDecoded() throws Exception { + doTestMessageMTOM("test+me.xml", "<test%2Bme.xml>", "test+me.xml"); + } + + private void doTestMessageMTOM(String contentId, String expectedEncocedContentId, + String expectedDecocedContentId) throws Exception { + MessageImpl msg = new MessageImpl(); + + Collection<Attachment> atts = new ArrayList<>(); + AttachmentImpl a = new AttachmentImpl(contentId); + + InputStream is = getClass().getResourceAsStream("my.wav"); + ByteArrayDataSource ds = new ByteArrayDataSource(is, "application/octet-stream"); + a.setDataHandler(new DataHandler(ds)); + + atts.add(a); + + msg.setAttachments(atts); + + // Set the SOAP content type + msg.put(Message.CONTENT_TYPE, "application/soap+xml"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + msg.setContent(OutputStream.class, out); + + AttachmentSerializer serializer = new AttachmentSerializer(msg); + + serializer.writeProlog(); + + String ct = (String) msg.get(Message.CONTENT_TYPE); + assertTrue(ct.indexOf("multipart/related;") == 0); + assertTrue(ct.indexOf("start=\"<[email protected]>\"") > -1); + assertTrue(ct.indexOf("start-info=\"application/soap+xml\"") > -1); + + out.write("<soap:Body/>".getBytes()); + + serializer.writeAttachments(); + out.flush(); + + doTestMessageRead(msg, expectedDecocedContentId); + } +} \ No newline at end of file
