This is an automated email from the ASF dual-hosted git repository.

jono pushed a commit to branch camel-4.4.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.4.x by this push:
     new abaeeea2cc8 Camel-18017: mdn content corruption (#13719)
abaeeea2cc8 is described below

commit abaeeea2cc82c43dc22a64e97789735a868d19ed
Author: Jono Morris <j...@apache.org>
AuthorDate: Tue Apr 9 21:52:40 2024 +1200

    Camel-18017: mdn content corruption (#13719)
---
 .../AS2MessageDispositionNotificationEntity.java   | 15 +++-
 .../util/DispositionNotificationContentUtils.java  | 10 ++-
 .../component/as2/api/entity/EntityParserTest.java | 85 +++++++++++++++++++---
 3 files changed, 98 insertions(+), 12 deletions(-)

diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
index 24591a849f6..f35260b4714 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
@@ -67,6 +67,7 @@ public class AS2MessageDispositionNotificationEntity extends 
MimeEntity {
     private String[] warningFields;
     private Map<String, String> extensionFields = new HashMap<>();
     private ReceivedContentMic receivedContentMic;
+    private String parsedBodyPartFields;
 
     public AS2MessageDispositionNotificationEntity(HttpEntityEnclosingRequest 
request,
                                                    HttpResponse response,
@@ -120,7 +121,8 @@ public class AS2MessageDispositionNotificationEntity 
extends MimeEntity {
                                                    String[] errorFields,
                                                    String[] warningFields,
                                                    Map<String, String> 
extensionFields,
-                                                   ReceivedContentMic 
receivedContentMic) {
+                                                   ReceivedContentMic 
receivedContentMic,
+                                                   String 
parsedBodyPartFields) {
         this.reportingUA = reportingUA;
         this.mtnName = mtnName;
         this.finalRecipient = finalRecipient;
@@ -133,6 +135,7 @@ public class AS2MessageDispositionNotificationEntity 
extends MimeEntity {
         this.warningFields = warningFields;
         this.extensionFields = extensionFields;
         this.receivedContentMic = receivedContentMic;
+        this.parsedBodyPartFields = parsedBodyPartFields;
     }
 
     public String getReportingUA() {
@@ -201,6 +204,16 @@ public class AS2MessageDispositionNotificationEntity 
extends MimeEntity {
                                              // 5.1.1
             }
 
+            if (parsedBodyPartFields != null) {
+                // The 'writeTo' method is used when verifying the signature 
of the received MDN, and any alteration
+                // to the body part fields would mean that the signature would 
fail verification. Therefor return
+                // the fields parsed from the MDN entity if available so that 
the specific field
+                // ordering/formatting is maintained otherwise fall back to 
recreating each header, e.g. 'Reporting-UA',
+                // in the order prescribed in this method.
+                canonicalOutstream.writeln(parsedBodyPartFields);
+                return;
+            }
+
             if (reportingUA != null) {
                 Header reportingUAField = new BasicHeader(REPORTING_UA, 
reportingUA);
                 canonicalOutstream.writeln(reportingUAField.toString());
diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
index 7350d0af7fc..6fc4613a38e 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
@@ -131,6 +131,7 @@ public final class DispositionNotificationContentUtils {
 
     private static final char PARAM_DELIMITER = ',';
     private static final char ELEM_DELIMITER = ';';
+    private static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
 
     private static final BitSet TOKEN_DELIMS = 
TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER);
 
@@ -152,9 +153,15 @@ public final class DispositionNotificationContentUtils {
         List<String> warnings = new ArrayList<>();
         Map<String, String> extensionFields = new HashMap<>();
         ReceivedContentMic receivedContentMic = null;
+        CharArrayBuffer bodyPartFields = new 
CharArrayBuffer(DEFAULT_BUFFER_SIZE);
 
         for (int i = 0; i < dispositionNotificationFields.size(); i++) {
             final CharArrayBuffer fieldLine = 
dispositionNotificationFields.get(i);
+            bodyPartFields.append(fieldLine);
+            if (i < dispositionNotificationFields.size() - 1) {
+                bodyPartFields.append('\r');
+                bodyPartFields.append('\n');
+            }
             final Field field = parseDispositionField(fieldLine);
             switch (field.getName().toLowerCase()) {
                 case REPORTING_UA: {
@@ -250,7 +257,8 @@ public final class DispositionNotificationContentUtils {
                 errors.toArray(new String[0]),
                 warnings.toArray(new String[0]),
                 extensionFields,
-                receivedContentMic);
+                receivedContentMic,
+                bodyPartFields.toString());
     }
 
     public static Field parseDispositionField(CharArrayBuffer fieldLine) {
diff --git 
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
index 1033e8cb8e2..33b5bab27ec 100644
--- 
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
+++ 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.component.as2.api.entity;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
@@ -43,6 +44,7 @@ import org.apache.http.HttpVersion;
 import org.apache.http.entity.BasicHttpEntity;
 import org.apache.http.impl.EnglishReasonPhraseCatalog;
 import org.apache.http.impl.io.HttpTransportMetricsImpl;
+import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpResponse;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
@@ -108,6 +110,39 @@ public class EntityParserTest {
                                                                          + 
"\r\n"
                                                                          + 
"------=_Part_56_1672293592.1028122454656--\r\n";
 
+    // version of the MDN report without any folded body parts that would be 
unfolded when the entity is parsed
+    // modifying the report
+    public static final String 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_UNFOLDED = "\r\n"
+                                                                               
   + "------=_Part_56_1672293592.1028122454656\r\n"
+                                                                               
   + "Content-Type: text/plain\r\n"
+                                                                               
   + "Content-Transfer-Encoding: 7bit\r\n"
+                                                                               
   + "\r\n"
+                                                                               
   + "MDN for -\r\n"
+                                                                               
   + " Message ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n"
+                                                                               
   + "  From: \"\\\"  as2Name  \\\"\"\r\n"
+                                                                               
   + "  To: \"0123456780000\""
+                                                                               
   + "  Received on: 2002-07-31 at 09:34:14 (EDT)\r\n"
+                                                                               
   + " Status: processed\r\n"
+                                                                               
   + " Comment: This is not a guarantee that the message has\r\n"
+                                                                               
   + "  been completely processed or &understood by the receiving\r\n"
+                                                                               
   + "  translator\r\n" + "\r\n"
+                                                                               
   + "------=_Part_56_1672293592.1028122454656\r\n"
+                                                                               
   + "Content-Type: message/disposition-notification\r\n"
+                                                                               
   + "Content-Transfer-Encoding: 7bit\r\n"
+                                                                               
   + "\r\n"
+                                                                               
   + "Reporting-UA: AS2 Server\r\n"
+                                                                               
   + "MDN-Gateway: dns; example.com\r\n"
+                                                                               
   + "Original-Recipient: rfc822; 0123456780000\r\n"
+                                                                               
   + "Final-Recipient: rfc822; 0123456780000\r\n"
+                                                                               
   + "Original-Message-ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n"
+                                                                               
   + "Disposition: automatic-action/MDN-sent-automatically; rocessed/warning: 
you're awesome\r\n"
+                                                                               
   + "Failure: oops-a-failure\r\n"
+                                                                               
   + "Error: oops-an-error\r\n"
+                                                                               
   + "Warning: oops-a-warning\r\n"
+                                                                               
   + "Received-content-MIC: 7v7F++fQaNB1sVLFtMRp+dF+eG4=, sha1\r\n"
+                                                                               
   + "\r\n"
+                                                                               
   + "------=_Part_56_1672293592.1028122454656--\r\n";
+
     public static final String 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY = 
"----=_Part_56_1672293592.1028122454656";
 
     public static final String 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME = "US-ASCII";
@@ -214,16 +249,8 @@ public class EntityParserTest {
     @Test
     public void parseMessageDispositionNotificationReportBodyTest() throws 
Exception {
 
-        InputStream is = new ByteArrayInputStream(
-                
DISPOSITION_NOTIFICATION_REPORT_CONTENT.getBytes(DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME));
-        AS2SessionInputBuffer inbuffer
-                = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), 
DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, null);
-        inbuffer.bind(is);
-
-        DispositionNotificationMultipartReportEntity 
dispositionNotificationMultipartReportEntity = EntityParser
-                .parseMultipartReportEntityBody(inbuffer, 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY,
-                        DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME,
-                        
DISPOSITION_NOTIFICATION_REPORT_CONTENT_TRANSFER_ENCODING);
+        DispositionNotificationMultipartReportEntity 
dispositionNotificationMultipartReportEntity
+                = createMdnEntity(DISPOSITION_NOTIFICATION_REPORT_CONTENT, 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY);
 
         assertNotNull(dispositionNotificationMultipartReportEntity,
                 "Unexpected Null disposition notification multipart entity");
@@ -235,6 +262,25 @@ public class EntityParserTest {
                 "Unexpected type for second body part");
     }
 
+    // verify that parsing the MDN has made no alteration to the entity's body 
part fields
+    @Test
+    public void messageDispositionNotificationReportBodyContentTest() throws 
Exception {
+
+        DispositionNotificationMultipartReportEntity 
dispositionNotificationMultipartReportEntity
+                = 
createMdnEntity(DISPOSITION_NOTIFICATION_REPORT_CONTENT_UNFOLDED,
+                        DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY);
+
+        String expectedContent = String.format("%s\r\n%s\r\n%s",
+                new BasicHeader(AS2Header.CONTENT_TYPE, 
REPORT_CONTENT_TYPE_VALUE),
+                new BasicHeader(AS2Header.CONTENT_TRANSFER_ENCODING, 
DISPOSITION_NOTIFICATION_REPORT_CONTENT_TRANSFER_ENCODING),
+                DISPOSITION_NOTIFICATION_REPORT_CONTENT_UNFOLDED);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        dispositionNotificationMultipartReportEntity.writeTo(out);
+
+        assertEquals(expectedContent, 
out.toString(DISPOSITION_NOTIFICATION_CONTENT_CHARSET_NAME));
+    }
+
     @Test
     public void parseTextPlainBodyTest() throws Exception {
 
@@ -380,6 +426,25 @@ public class EntityParserTest {
         return utils.createAuthorityKeyIdentifier(info);
     }
 
+    private DispositionNotificationMultipartReportEntity 
createMdnEntity(String reportContent, String boundary)
+            throws Exception {
+        InputStream is = new ByteArrayInputStream(
+                
reportContent.getBytes(DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME));
+        AS2SessionInputBuffer inbuffer
+                = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), 
DEFAULT_BUFFER_SIZE);
+        inbuffer.bind(is);
+
+        DispositionNotificationMultipartReportEntity 
dispositionNotificationMultipartReportEntity = EntityParser
+                .parseMultipartReportEntityBody(inbuffer, boundary,
+                        DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME,
+                        
DISPOSITION_NOTIFICATION_REPORT_CONTENT_TRANSFER_ENCODING);
+
+        assertNotNull(dispositionNotificationMultipartReportEntity,
+                "Unexpected Null disposition notification multipart entity");
+
+        return dispositionNotificationMultipartReportEntity;
+    }
+
     static SubjectKeyIdentifier createSubjectKeyId(PublicKey pub) throws 
IOException {
         SubjectPublicKeyInfo info = 
SubjectPublicKeyInfo.getInstance(pub.getEncoded());
 

Reply via email to