james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject [05/12] james-project git commit: JAMES-2560 Introduce a mailet for sanitizing header only ICS
Date Fri, 12 Oct 2018 08:28:56 GMT
JAMES-2560 Introduce a mailet for sanitizing header only ICS


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ac82719f
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ac82719f
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ac82719f

Branch: refs/heads/master
Commit: ac82719fd460f5bc8a7306fb944cb4d7715b9dac
Parents: c7b1600
Author: Benoit Tellier <btellier@linagora.com>
Authored: Thu Oct 11 11:38:34 2018 +0700
Committer: Benoit Tellier <btellier@linagora.com>
Committed: Fri Oct 12 15:26:53 2018 +0700

----------------------------------------------------------------------
 mailet/icalendar/pom.xml                        |  20 +++-
 .../james/transport/mailets/ICSSanitizer.java   | 110 +++++++++++++++++
 .../transport/mailets/ICSSanitizerTest.java     | 118 +++++++++++++++++++
 .../src/test/resources/ics_in_header.eml        |  46 ++++++++
 4 files changed, 289 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ac82719f/mailet/icalendar/pom.xml
----------------------------------------------------------------------
diff --git a/mailet/icalendar/pom.xml b/mailet/icalendar/pom.xml
index 78cddde..a7d03f4 100644
--- a/mailet/icalendar/pom.xml
+++ b/mailet/icalendar/pom.xml
@@ -80,11 +80,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>net.javacrumbs.json-unit</groupId>
             <artifactId>json-unit-assertj</artifactId>
             <scope>test</scope>
@@ -104,6 +99,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.mnode.ical4j</groupId>
             <artifactId>ical4j</artifactId>
             <version>3.0.1</version>

http://git-wip-us.apache.org/repos/asf/james-project/blob/ac82719f/mailet/icalendar/src/main/java/org/apache/james/transport/mailets/ICSSanitizer.java
----------------------------------------------------------------------
diff --git a/mailet/icalendar/src/main/java/org/apache/james/transport/mailets/ICSSanitizer.java
b/mailet/icalendar/src/main/java/org/apache/james/transport/mailets/ICSSanitizer.java
new file mode 100644
index 0000000..a2b268d
--- /dev/null
+++ b/mailet/icalendar/src/main/java/org/apache/james/transport/mailets/ICSSanitizer.java
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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.james.transport.mailets;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.mail.BodyPart;
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMailet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+
+public class ICSSanitizer extends GenericMailet {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ICSSanitizer.class);
+    private static final int TEXT_PREFIX_SIZE = 5;
+
+    @Override
+    public void service(Mail mail) {
+        try {
+            MimeMessage mimeMessage = mail.getMessage();
+
+            if (mimeMessage.getContent() instanceof Multipart) {
+                Multipart multipart = (Multipart) mimeMessage.getContent();
+
+                if (needsSanitizing(multipart)) {
+                    mimeMessage.setContent(sanitize(multipart));
+                    mimeMessage.saveChanges();
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Could not sanitize {}", mail.getName(), e);
+        }
+    }
+
+    private boolean needsSanitizing(Multipart multipart) throws MessagingException {
+        return bodyPartStream(multipart)
+            .anyMatch(Throwing.predicate(this::needsSanitizing));
+    }
+
+    private boolean needsSanitizing(BodyPart bodyPart) throws MessagingException {
+        return bodyPart.isMimeType("text/calendar") && bodyPart.getSize() <= 0;
+    }
+
+    private MimeMultipart sanitize(Multipart multipart) throws MessagingException {
+        MimeMultipart mimeMultipart = new MimeMultipart();
+        bodyPartStream(multipart)
+            .map(Throwing.function(this::sanitize))
+            .forEach(Throwing.consumer(mimeMultipart::addBodyPart));
+        return mimeMultipart;
+    }
+
+    private BodyPart sanitize(BodyPart bodyPart) throws MessagingException {
+        if (needsSanitizing(bodyPart)) {
+            if (bodyPart instanceof MimeBodyPart) {
+                MimeBodyPart mimeBodyPart = (MimeBodyPart) bodyPart;
+                mimeBodyPart.setText(
+                    computeBodyFromOriginalCalendar(bodyPart),
+                    StandardCharsets.UTF_8.name(),
+                    bodyPart.getContentType().substring(TEXT_PREFIX_SIZE));
+            }
+        }
+        return bodyPart;
+    }
+
+    private String computeBodyFromOriginalCalendar(BodyPart bodyPart) throws MessagingException
{
+        return headerStream(bodyPart)
+            .map(header -> header.getName() + ": " + header.getValue())
+            .collect(Collectors.joining("\r\n"));
+    }
+
+    private Stream<Header> headerStream(BodyPart bodyPart) throws MessagingException
{
+        return Collections.list(bodyPart.getAllHeaders()).stream();
+    }
+
+    private Stream<BodyPart> bodyPartStream(Multipart multipart) throws MessagingException
{
+        return IntStream.range(0, multipart.getCount())
+            .boxed()
+            .map(Throwing.function(multipart::getBodyPart));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ac82719f/mailet/icalendar/src/test/java/org/apache/james/transport/mailets/ICSSanitizerTest.java
----------------------------------------------------------------------
diff --git a/mailet/icalendar/src/test/java/org/apache/james/transport/mailets/ICSSanitizerTest.java
b/mailet/icalendar/src/test/java/org/apache/james/transport/mailets/ICSSanitizerTest.java
new file mode 100644
index 0000000..1e39fa2
--- /dev/null
+++ b/mailet/icalendar/src/test/java/org/apache/james/transport/mailets/ICSSanitizerTest.java
@@ -0,0 +1,118 @@
+/****************************************************************
+ * 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.james.transport.mailets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.charset.StandardCharsets;
+
+import javax.mail.Multipart;
+import javax.mail.util.SharedByteArrayInputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.MimeMessageUtil;
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class ICSSanitizerTest {
+    private ICSSanitizer testee;
+
+    @BeforeEach
+    void setUp() {
+        testee = new ICSSanitizer();
+    }
+
+    @Test
+    void serviceShouldEnhanceTextCalendarOnlyHeaders() throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .mimeMessage(MimeMessageUtil.mimeMessageFromStream(ClassLoaderUtils.getSystemResourceAsSharedStream("ics_in_header.eml")))
+            .build();
+
+        testee.service(mail);
+
+        Object content = mail.getMessage().getContent();
+
+        assertThat(content).isInstanceOf(Multipart.class);
+        Multipart multipart = (Multipart) content;
+        assertThat(multipart.getCount()).isEqualTo(1);
+
+        assertThat(multipart.getBodyPart(0).getContent())
+            .isEqualTo("BEGIN: VCALENDAR\r\n" +
+                "PRODID: -//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\r\n" +
+                "VERSION: 2.0\r\n" +
+                "METHOD: REPLY\r\n" +
+                "BEGIN: VEVENT\r\n" +
+                "CREATED: 20180911T144134Z\r\n" +
+                "LAST-MODIFIED: 20180912T085818Z\r\n" +
+                "DTSTAMP: 20180912T085818Z\r\n" +
+                "UID: f1514f44bf39311568d64072945fc3b2973debebb0d550e8c841f3f0604b2481e047fe\r\n"
+
+                " b2aab16e43439a608f28671ab7c10e754cbbe63441a01ba232a553df751eb0931728d67672\r\n"
+
+                " \r\n" +
+                "SUMMARY: Point Produit\r\n" +
+                "PRIORITY: 5\r\n" +
+                "ORGANIZER;CN=Bob;X-OBM-ID=348: mailto:bob@linagora.com\r\n" +
+                "ATTENDEE;CN=Alice;PARTSTAT=ACCEPTED;CUTYPE=INDIVIDUAL;X-OBM-ID=810: mailto:alice@linagora.com\r\n"
+
+                "DTSTART: 20180919T123000Z\r\n" +
+                "DURATION: PT1H\r\n" +
+                "TRANSP: OPAQUE\r\n" +
+                "SEQUENCE: 0\r\n" +
+                "X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR: No value for X property.
Rem\r\n" +
+                " oving entire property:\r\n" +
+                "CLASS: PUBLIC\r\n" +
+                "X-OBM-DOMAIN: linagora.com\r\n" +
+                "X-OBM-DOMAIN-UUID: 02874f7c-d10e-102f-acda-0015176f7922\r\n" +
+                "LOCATION: TÃ\u0083©lÃ\u0083©phone\r\n" +
+                "END: VEVENT\r\n" +
+                "END: VCALENDAR\r\n" +
+                "Content-class: urn:content-classes:calendarmessage\r\n" +
+                "Content-type: text/calendar; method=REPLY; charset=UTF-8\r\n" +
+                "Content-transfer-encoding: 8BIT\r\n" +
+                "Content-Disposition: attachment");
+        assertThat(multipart.getBodyPart(0).getContentType()).startsWith("text/calendar;
method=REPLY; charset=UTF-8");
+    }
+
+    @Test
+    void validTextCalendarShouldNotBeSanitized() throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .mimeMessage(
+                MimeMessageBuilder.mimeMessageBuilder()
+                    .setMultipartWithBodyParts(
+                        MimeMessageBuilder.bodyPartBuilder()
+                            .type("text/calendar")
+                            .data("Not empty")
+                            .addHeader("X-CUSTOM", "Because it is a valid ICS it should not
be pushed in body")))
+            .build();
+
+        testee.service(mail);
+
+        Object content = mail.getMessage().getContent();
+
+        assertThat(content).isInstanceOf(Multipart.class);
+        Multipart multipart = (Multipart) content;
+        assertThat(multipart.getCount()).isEqualTo(1);
+
+        SharedByteArrayInputStream inputStream = (SharedByteArrayInputStream) multipart.getBodyPart(0).getContent();
+        assertThat(IOUtils.toString(inputStream, StandardCharsets.UTF_8))
+            .doesNotContain("X-CUSTOM");
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/ac82719f/mailet/icalendar/src/test/resources/ics_in_header.eml
----------------------------------------------------------------------
diff --git a/mailet/icalendar/src/test/resources/ics_in_header.eml b/mailet/icalendar/src/test/resources/ics_in_header.eml
new file mode 100644
index 0000000..76ca1b7
--- /dev/null
+++ b/mailet/icalendar/src/test/resources/ics_in_header.eml
@@ -0,0 +1,46 @@
+MIME-Version: 1.0
+Content-Type: multipart/mixed; 
+	boundary="----=_Part_80_888179120.1536742699653"
+From: Alice <alice@linagora.com>
+Message-ID: <82eb227b-d3e1-3d7a-3220-8a1b4e96a85b@linagora.com>
+To: Bob <bob@linagora.com>
+Date: Wed, 12 Sep 2018 10:58:18 +0200
+Subject: Event Invitation Reply (Accepted):
+ Point Produit
+
+------=_Part_80_888179120.1536742699653
+BEGIN:VCALENDAR
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+CREATED:20180911T144134Z
+LAST-MODIFIED:20180912T085818Z
+DTSTAMP:20180912T085818Z
+UID:f1514f44bf39311568d64072945fc3b2973debebb0d550e8c841f3f0604b2481e047fe
+ b2aab16e43439a608f28671ab7c10e754cbbe63441a01ba232a553df751eb0931728d67672
+ 
+SUMMARY:Point Produit
+PRIORITY:5
+ORGANIZER;CN=Bob;X-OBM-ID=348:mailto:bob@linagora.com
+ATTENDEE;CN=Alice;PARTSTAT=ACCEPTED;CUTYPE=INDIVIDUAL;X-OBM-ID=810:
+ mailto:alice@linagora.com
+DTSTART:20180919T123000Z
+DURATION:PT1H
+TRANSP:OPAQUE
+SEQUENCE:0
+X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR:No value for X property. Rem
+ oving entire property:
+CLASS:PUBLIC
+X-OBM-DOMAIN:linagora.com
+X-OBM-DOMAIN-UUID:02874f7c-d10e-102f-acda-0015176f7922
+LOCATION:Téléphone
+END:VEVENT
+END:VCALENDAR
+Content-class: urn:content-classes:calendarmessage
+Content-type: text/calendar; method=REPLY; charset=UTF-8
+Content-transfer-encoding: 8BIT
+Content-Disposition: attachment
+
+
+------=_Part_80_888179120.1536742699653--


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message