You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/11/11 15:57:43 UTC

[james-project] branch master updated: JAMES-3850 [JMAP] Configure `urn:ietf:params:jmap:mail` `maxSizeAttac… (#1303)

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

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new bbbd3071ed JAMES-3850 [JMAP] Configure `urn:ietf:params:jmap:mail` `maxSizeAttac… (#1303)
bbbd3071ed is described below

commit bbbd3071eda233f980395c1440aa711b0959547a
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Fri Nov 11 22:57:38 2022 +0700

    JAMES-3850 [JMAP] Configure `urn:ietf:params:jmap:mail` `maxSizeAttac… (#1303)
---
 .../docs/modules/ROOT/pages/configure/jmap.adoc              |  7 +++++++
 .../main/java/org/apache/james/jmap/draft/JMAPModule.java    |  8 ++++++--
 .../main/scala/org/apache/james/jmap/core/Capabilities.scala |  2 +-
 .../main/scala/org/apache/james/jmap/core/Capability.scala   | 12 ++++++++++--
 .../apache/james/jmap/core/JmapRfc8621Configuration.scala    |  9 ++++++++-
 .../apache/james/jmap/method/EmailSetCreatePerformer.scala   |  5 +++--
 src/site/xdoc/server/config-jmap.xml                         |  8 ++++++++
 7 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/jmap.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/jmap.adoc
index 2d0a5531fc..27ce9a0a01 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/jmap.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/jmap.adoc
@@ -40,6 +40,13 @@ Defaults to an empty list.
 | Optional. Configuration max size for message created in both JMAP Draft amd RFC-8621.
 Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).
 
+| max.size.attachments.per.mail
+| Optional. Defaults to 20MB. RFC-8621 `maxSizeAttachmentsPerEmail` advertised to JMAP client as part of the
+`urn:ietf:params:jmap:mail` capability. This needs to be at least 33% lower than `email.send.max.size` property
+(in order to account for text body, headers, base64 encoding and MIME structures).
+JMAP clients would use this property in order not to create too big emails.
+Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).
+
 | upload.max.size
 | Optional. Configuration max size Upload in new JMAP-RFC-8621.
 Default value: 30M. Supported units are B (bytes) K (KB) M (MB) G (GB).
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
index 784d9f937c..9f6dce1161 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
@@ -43,7 +43,7 @@ import org.apache.james.jmap.core.IdentitySortOrderCapabilityFactory$;
 import org.apache.james.jmap.core.JmapQuotaCapabilityFactory$;
 import org.apache.james.jmap.core.JmapRfc8621Configuration;
 import org.apache.james.jmap.core.MDNCapabilityFactory$;
-import org.apache.james.jmap.core.MailCapabilityFactory$;
+import org.apache.james.jmap.core.MailCapabilityFactory;
 import org.apache.james.jmap.core.QuotaCapabilityFactory$;
 import org.apache.james.jmap.core.SharesCapabilityFactory$;
 import org.apache.james.jmap.core.SubmissionCapabilityFactory$;
@@ -144,7 +144,6 @@ public class JMAPModule extends AbstractModule {
         supportedVersions.addBinding().toInstance(Version.RFC8621);
 
         Multibinder<CapabilityFactory> supportedCapabilities = Multibinder.newSetBinder(binder(), CapabilityFactory.class);
-        supportedCapabilities.addBinding().toInstance(MailCapabilityFactory$.MODULE$);
         supportedCapabilities.addBinding().toInstance(QuotaCapabilityFactory$.MODULE$);
         supportedCapabilities.addBinding().toInstance(JmapQuotaCapabilityFactory$.MODULE$);
         supportedCapabilities.addBinding().toInstance(IdentitySortOrderCapabilityFactory$.MODULE$);
@@ -170,6 +169,11 @@ public class JMAPModule extends AbstractModule {
         return ProcessorsCheck.noCheck();
     }
 
+    @ProvidesIntoSet
+    CapabilityFactory vacationMailetCheck(JmapRfc8621Configuration configuration) {
+        return new MailCapabilityFactory(configuration);
+    }
+
     @ProvidesIntoSet
     CapabilityFactory coreCapability(JmapRfc8621Configuration configuration) {
         return new CoreCapabilityFactory(configuration.maxUploadSize());
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
index 43a43190fb..57f1747aa9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
@@ -25,7 +25,7 @@ object DefaultCapabilities {
   @VisibleForTesting
   def supported(configuration: JmapRfc8621Configuration): Set[CapabilityFactory] = Set(
     CoreCapabilityFactory(configuration.maxUploadSize),
-    MailCapabilityFactory,
+    MailCapabilityFactory(configuration),
     QuotaCapabilityFactory,
     JmapQuotaCapabilityFactory,
     IdentitySortOrderCapabilityFactory,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
index 25a715e99e..c3d587d4f4 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
@@ -179,18 +179,26 @@ object MailCapability {
 final case class MailCapability(properties: MailCapabilityProperties,
                                 identifier: CapabilityIdentifier = JMAP_MAIL) extends Capability
 
-case object MailCapabilityFactory extends CapabilityFactory {
+case class MailCapabilityFactory(configuration: JmapRfc8621Configuration) extends CapabilityFactory {
   override def id(): CapabilityIdentifier = JMAP_MAIL
 
   override def create(urlPrefixes: UrlPrefixes): Capability = MailCapability(MailCapabilityProperties(
     MaxMailboxesPerEmail(Some(10_000_000L)),
     MaxMailboxDepth(None),
     MaxSizeMailboxName(200L),
-    MaxSizeAttachmentsPerEmail(20_000_000L),
+    configuration.maxSizeAttachmentsPerEmail,
     emailQuerySortOptions = List("receivedAt", "sentAt", "size", "from", "to", "subject"),
     MayCreateTopLevelMailbox(true)))
 }
 
+
+object MaxSizeAttachmentsPerEmail {
+  def of(size: Size): Try[MaxSizeAttachmentsPerEmail] = refined.refineV[UnsignedIntConstraint](size.asBytes()) match {
+    case Right(value) => Success(MaxSizeAttachmentsPerEmail(value))
+    case Left(error) => Failure(new NumberFormatException(error))
+  }
+}
+
 case class MaxMailboxesPerEmail(value: Option[UnsignedInt])
 case class MaxMailboxDepth(value: Option[UnsignedInt])
 case class MaxSizeMailboxName(value: UnsignedInt)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
index 2ae82d9c66..0a8597264d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
@@ -23,7 +23,7 @@ import java.net.URI
 import java.util.Optional
 
 import org.apache.commons.configuration2.Configuration
-import org.apache.james.jmap.core.JmapRfc8621Configuration.UPLOAD_LIMIT_DEFAULT
+import org.apache.james.jmap.core.JmapRfc8621Configuration.{MAX_SIZE_ATTACHMENTS_PER_MAIL_DEFAULT, UPLOAD_LIMIT_DEFAULT}
 import org.apache.james.jmap.pushsubscription.PushClientConfiguration
 import org.apache.james.util.Size
 
@@ -31,6 +31,7 @@ import scala.jdk.OptionConverters._
 
 object JmapConfigProperties {
   val UPLOAD_LIMIT_PROPERTY: String = "upload.max.size"
+  val MAX_SIZE_ATTACHMENTS_PER_MAIL_PROPERTY: String = "max.size.attachments.per.mail"
   val URL_PREFIX_PROPERTY: String = "url.prefix"
   val WEBSOCKET_URL_PREFIX_PROPERTY: String = "websocket.url.prefix"
   val WEB_PUSH_MAX_TIMEOUT_SECONDS_PROPERTY: String = "webpush.maxTimeoutSeconds"
@@ -45,6 +46,7 @@ object JmapRfc8621Configuration {
   val URL_PREFIX_DEFAULT: String = "http://localhost"
   val WEBSOCKET_URL_PREFIX_DEFAULT: String = "ws://localhost"
   val UPLOAD_LIMIT_DEFAULT: MaxSizeUpload = MaxSizeUpload.of(Size.of(30L, Size.Unit.M)).get
+  val MAX_SIZE_ATTACHMENTS_PER_MAIL_DEFAULT: MaxSizeAttachmentsPerEmail = MaxSizeAttachmentsPerEmail.of(Size.of(20_000_000L, Size.Unit.B)).get
 
   val LOCALHOST_CONFIGURATION: JmapRfc8621Configuration = JmapRfc8621Configuration(
     urlPrefixString = URL_PREFIX_DEFAULT,
@@ -60,6 +62,10 @@ object JmapRfc8621Configuration {
         .map(Size.parse)
         .map(MaxSizeUpload.of(_).get)
         .getOrElse(UPLOAD_LIMIT_DEFAULT),
+      maxSizeAttachmentsPerEmail = Option(configuration.getString(MAX_SIZE_ATTACHMENTS_PER_MAIL_PROPERTY, null))
+        .map(Size.parse)
+        .map(MaxSizeAttachmentsPerEmail.of(_).get)
+        .getOrElse(MAX_SIZE_ATTACHMENTS_PER_MAIL_DEFAULT),
       maxTimeoutSeconds = Optional.ofNullable(configuration.getInteger(WEB_PUSH_MAX_TIMEOUT_SECONDS_PROPERTY, null)).map(Integer2int).toScala,
       maxConnections = Optional.ofNullable(configuration.getInteger(WEB_PUSH_MAX_CONNECTIONS_PROPERTY, null)).map(Integer2int).toScala,
       preventServerSideRequestForgery = Optional.ofNullable(configuration.getBoolean(WEB_PUSH_PREVENT_SERVER_SIDE_REQUEST_FORGERY, null)).orElse(true),
@@ -70,6 +76,7 @@ case class JmapRfc8621Configuration(urlPrefixString: String,
                                     websocketPrefixString: String,
                                     dynamicJmapPrefixResolutionEnabled: Boolean = false,
                                     maxUploadSize: MaxSizeUpload = UPLOAD_LIMIT_DEFAULT,
+                                    maxSizeAttachmentsPerEmail: MaxSizeAttachmentsPerEmail = MAX_SIZE_ATTACHMENTS_PER_MAIL_DEFAULT,
                                     maxTimeoutSeconds: Option[Int] = None,
                                     maxConnections: Option[Int] = None,
                                     authenticationStrategies: Option[java.util.List[String]] = None,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
index 35b25b3588..a9f34ada3e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
@@ -28,7 +28,7 @@ import javax.mail.Flags
 import org.apache.james.jmap.JMAPConfiguration
 import org.apache.james.jmap.api.model.Size.sanitizeSize
 import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.core.{Properties, SetError, UTCDate}
+import org.apache.james.jmap.core.{JmapRfc8621Configuration, Properties, SetError, UTCDate}
 import org.apache.james.jmap.json.EmailSetSerializer
 import org.apache.james.jmap.mail.{BlobId, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, ThreadId}
 import org.apache.james.jmap.method.EmailSetCreatePerformer.{CreationFailure, CreationResult, CreationResults, CreationSuccess}
@@ -82,7 +82,8 @@ class EmailSetCreatePerformer @Inject()(serializer: EmailSetSerializer,
                                         blobResolvers: BlobResolvers,
                                         htmlTextExtractor: HtmlTextExtractor,
                                         mailboxManager: MailboxManager,
-                                        configuration: JMAPConfiguration) {
+                                        configuration: JMAPConfiguration,
+                                        configurationRfc8621: JmapRfc8621Configuration) {
 
   def create(request: EmailSetRequest, mailboxSession: MailboxSession): SMono[CreationResults] =
     SFlux.fromIterable(request.create.getOrElse(Map()))
diff --git a/src/site/xdoc/server/config-jmap.xml b/src/site/xdoc/server/config-jmap.xml
index 7dcbda82b9..9cfc5fa1c7 100644
--- a/src/site/xdoc/server/config-jmap.xml
+++ b/src/site/xdoc/server/config-jmap.xml
@@ -74,6 +74,14 @@
                     <dd>Optional. Configuration max size for message created in both JMAP Draft amd RFC-8621.</dd>
                     <dd>Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).</dd>
 
+                    <dt><strong>max.size.attachments.per.mail</strong></dt>
+                    <dd>Optional. Defaults to 20MB. RFC-8621 <code>maxSizeAttachmentsPerEmail</code> advertised to JMAP
+                        client as part of the <code>urn:ietf:params:jmap:mail</code> capability. This needs to be at
+                        least 33% lower than <code>email.send.max.size</code> property (in order to account for text body,
+                        headers, base64 encoding and MIME structures). JMAP clients would use this property in order not
+                        to create too big emails.</dd>
+                    <dd>Default value: None. Supported units are B (bytes) K (KB) M (MB) G (GB).</dd>
+
                     <dt><strong>view.email.query.enabled</strong></dt>
                     <dd>Optional boolean. Defaults to false.</dd>
                     <dd>Should simple Email/query be resolved against a Cassandra projection, or should we resolve them against OpenSearch?


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