You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2017/11/16 14:19:57 UTC
[10/18] james-project git commit: JAMES-2220 Implement sending draft
with move to Outbox
JAMES-2220 Implement sending draft with move to Outbox
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/29d233cc
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/29d233cc
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/29d233cc
Branch: refs/heads/master
Commit: 29d233cc497e6e7a6f62f38285e751940fae5e48
Parents: 85ad874
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 21:40:30 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100
----------------------------------------------------------------------
.../org/apache/james/server/core/MailImpl.java | 29 +++
.../integration/SetMessagesMethodTest.java | 214 ++++++++++++++++++-
.../exceptions/InvalidOutboxMoveException.java | 23 ++
.../james/jmap/methods/MessageSender.java | 19 +-
.../methods/SetMessagesUpdateProcessor.java | 102 +++++++--
.../org/apache/james/jmap/send/MailFactory.java | 2 +
.../methods/SetMessagesUpdateProcessorTest.java | 16 +-
7 files changed, 386 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index c5c7f9b..af9020a 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -28,6 +28,7 @@ import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.OutputStream;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@@ -35,7 +36,10 @@ import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
+import javax.mail.Address;
import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.ParseException;
@@ -49,6 +53,8 @@ import org.apache.mailet.PerRecipientHeaders.Header;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -84,6 +90,29 @@ public class MailImpl implements Disposable, Mail {
return new MailImpl(mail, deriveNewName(mail.getName()));
}
+ public static MailImpl fromMimeMessage(String name, MimeMessage mimeMessage) throws MessagingException {
+ MailAddress sender = getSender(mimeMessage);
+ ImmutableList<MailAddress> recipients = getRecipients(mimeMessage);
+ return new MailImpl(name, sender, recipients, mimeMessage);
+ }
+
+ private static ImmutableList<MailAddress> getRecipients(MimeMessage mimeMessage) throws MessagingException {
+ return Arrays.stream(mimeMessage.getAllRecipients())
+ .map(Throwing.function(MailImpl::castToMailAddress).sneakyThrow())
+ .collect(Guavate.toImmutableList());
+ }
+
+ private static MailAddress getSender(MimeMessage mimeMessage) throws MessagingException {
+ Address[] sender = mimeMessage.getFrom();
+ Preconditions.checkArgument(sender.length == 1);
+ return castToMailAddress(sender[0]);
+ }
+
+ private static MailAddress castToMailAddress(Address address) throws AddressException {
+ Preconditions.checkArgument(address instanceof InternetAddress);
+ return new MailAddress((InternetAddress) address);
+ }
+
/**
* Create a unique new primary key name for the given MailObject.
* Detect if this has been called more than 8 times recursively
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index c29302b..924c4a2 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -198,6 +198,11 @@ public abstract class SetMessagesMethodTest {
return getMailboxId(accessToken, Role.DRAFTS);
}
+ private String getInboxId(AccessToken accessToken) {
+ return getMailboxId(accessToken, Role.INBOX);
+ }
+
+
private String getMailboxId(AccessToken accessToken, Role role) {
return getAllMailboxesIds(accessToken).stream()
.filter(x -> x.get("role").equalsIgnoreCase(role.serialize()))
@@ -564,7 +569,7 @@ public abstract class SetMessagesMethodTest {
}
@Test
- @Ignore("gitlab-446 should allowed in drafts mailbox, rejected outside")
+ @Ignore("JAMES-2220 should allowed in drafts mailbox, rejected outside")
public void setMessagesShouldReturnAnErrorWhenKeywordsWithAddingDraftArePassed() throws MailboxException {
mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
@@ -700,7 +705,7 @@ public abstract class SetMessagesMethodTest {
}
@Test
- @Ignore("gitlab-446 should allowed outside drafts mailbox, rejected inside")
+ @Ignore("JAMES-2220 should allowed outside drafts mailbox, rejected inside")
public void setMessagesShouldReturnAnErrorWhenKeywordsWithRemoveDraftArePassed() throws MailboxException {
mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
@@ -1445,7 +1450,7 @@ public abstract class SetMessagesMethodTest {
}
@Test
- public void setMessageShouldAllowDraftCreationWhenUsingIsDraftProperty() {
+ public void setMessagesShouldAllowDraftCreationWhenUsingIsDraftProperty() {
String messageCreationId = "creationId1337";
String fromAddress = USERNAME;
String requestBody = "[" +
@@ -1894,6 +1899,209 @@ public abstract class SetMessagesMethodTest {
}
@Test
+ public void setMessagesShouldSendMessageByMovingDraftToOutbox() {
+ String draftCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String createDraft = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + draftCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String draftId =
+ with()
+ .header("Authorization", accessToken.serialize())
+ .body(createDraft)
+ .post("/jmap")
+ .then()
+ .extract()
+ .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+ String moveDraftToOutBox = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"update\": { \"" + draftId + "\" : {" +
+ " \"keywords\": {}," +
+ " \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ with()
+ .header("Authorization", accessToken.serialize())
+ .body(moveDraftToOutBox)
+ .post("/jmap");
+
+ calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> isAnyMessageFoundInRecipientsMailboxes(bobAccessToken));
+ }
+
+ @Test
+ @Ignore("WIP")
+ public void setMessagesShouldRejectDraftCopyToOutbox() {
+ String draftCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String createDraft = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + draftCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String draftId =
+ with()
+ .header("Authorization", accessToken.serialize())
+ .body(createDraft)
+ .post("/jmap")
+ .then()
+ .extract()
+ .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+ String copyDraftToOutBox = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"update\": { \"" + draftId + "\" : {" +
+ " \"keywords\": {}," +
+ " \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\",\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(copyDraftToOutBox)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notUpdated", hasKey(draftId))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].type", equalTo("invalidArguments"))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].description", endsWith("One can not have a message in mailboxes that don't have all the `draft` role"))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", hasSize(1))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", contains("mailboxIds"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
+ }
+
+ @Test
+ public void setMessagesShouldRejectMovingMessageToOutboxWhenNotInDraft() throws MailboxException {
+ ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, MailboxPath.forUser(USERNAME, MailboxConstants.INBOX),
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
+ await();
+
+ String messageId = message.getMessageId().serialize();
+ String moveMessageToOutBox = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"update\": { \"" + messageId + "\" : {" +
+ " \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(moveMessageToOutBox)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notUpdated", hasKey(messageId))
+ .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].description", endsWith("only drafts can be moved to Outbox"))
+ .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].properties", hasSize(1))
+ .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].properties", contains("mailboxIds"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
+ }
+
+ @Test
+ public void setMessagesShouldRejectMovingMessageToOutboxWhenDraftKeyworkSet() throws MailboxException {
+ String draftCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String createDraft = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + draftCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String draftId =
+ with()
+ .header("Authorization", accessToken.serialize())
+ .body(createDraft)
+ .post("/jmap")
+ .then()
+ .extract()
+ .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+ String moveDraftToOutBox = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"update\": { \"" + draftId + "\" : {" +
+ " \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(moveDraftToOutBox)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notUpdated", hasKey(draftId))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].description", endsWith("message with $Draft keyword can't be moved outside outbox"))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", hasSize(1))
+ .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", contains("keywords"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
+ }
+
+
+ @Test
public void setMessagesShouldSupportArbitraryMessageId() {
String messageCreationId = "1717fcd1-603e-44a5-b2a6-1234dbcd5723";
String fromAddress = USERNAME;
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
new file mode 100644
index 0000000..a70a750
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * 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.jmap.exceptions;
+
+public class InvalidOutboxMoveException extends RuntimeException {
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
index 3ce4086..d6835c9 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
@@ -22,6 +22,7 @@ package org.apache.james.jmap.methods;
import javax.inject.Inject;
import javax.mail.MessagingException;
+import org.apache.james.core.MailAddress;
import org.apache.james.jmap.model.Envelope;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.send.MailFactory;
@@ -30,6 +31,7 @@ import org.apache.james.jmap.send.MailSpool;
import org.apache.james.lifecycle.api.LifecycleUtil;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MessageId;
import org.apache.mailet.Mail;
public class MessageSender {
@@ -55,9 +57,22 @@ public class MessageSender {
}
}
+ public void sendMessage(MessageId messageId,
+ Mail mail,
+ MailboxSession session) throws MailboxException, MessagingException {
+ assertUserIsSender(session, mail.getSender());
+ MailMetadata metadata = new MailMetadata(messageId, session.getUser().getUserName());
+ mailSpool.send(mail, metadata);
+ }
+
private void assertUserIsInSenders(Envelope envelope, MailboxSession session) throws MailboxSendingNotAllowedException {
- String allowedSender = session.getUser().getUserName();
- if (!session.getUser().isSameUser(envelope.getFrom().asString())) {
+ MailAddress sender = envelope.getFrom();
+ assertUserIsSender(session, sender);
+ }
+
+ private void assertUserIsSender(MailboxSession session, MailAddress sender) throws MailboxSendingNotAllowedException {
+ if (!session.getUser().isSameUser(sender.asString())) {
+ String allowedSender = session.getUser().getUserName();
throw new MailboxSendingNotAllowedException(allowedSender);
}
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
index c54deec..e3865c0 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
@@ -21,16 +21,23 @@ package org.apache.james.jmap.methods;
import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
+import java.io.IOException;
+import java.util.Collection;
import java.util.List;
import java.util.Optional;
+import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.mail.Flags;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
import org.apache.james.jmap.exceptions.DraftMessageMailboxUpdateException;
+import org.apache.james.jmap.exceptions.InvalidOutboxMoveException;
import org.apache.james.jmap.model.MessageProperties;
import org.apache.james.jmap.model.SetError;
import org.apache.james.jmap.model.SetMessagesRequest;
@@ -42,6 +49,7 @@ import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageIdManager;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.model.FetchGroupImpl;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxId.Factory;
@@ -49,6 +57,8 @@ import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageResult;
import org.apache.james.metrics.api.MetricFactory;
import org.apache.james.metrics.api.TimeMetric;
+import org.apache.james.server.core.MailImpl;
+import org.apache.james.util.OptionalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,17 +77,22 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
private final SystemMailboxesProvider systemMailboxesProvider;
private final Factory mailboxIdFactory;
private final MetricFactory metricFactory;
+ private final MessageSender messageSender;
@Inject
@VisibleForTesting SetMessagesUpdateProcessor(
- UpdateMessagePatchConverter updatePatchConverter,
- MessageIdManager messageIdManager,
- SystemMailboxesProvider systemMailboxesProvider, Factory mailboxIdFactory, MetricFactory metricFactory) {
+ UpdateMessagePatchConverter updatePatchConverter,
+ MessageIdManager messageIdManager,
+ SystemMailboxesProvider systemMailboxesProvider,
+ Factory mailboxIdFactory,
+ MessageSender messageSender,
+ MetricFactory metricFactory) {
this.updatePatchConverter = updatePatchConverter;
this.messageIdManager = messageIdManager;
this.systemMailboxesProvider = systemMailboxesProvider;
this.mailboxIdFactory = mailboxIdFactory;
this.metricFactory = metricFactory;
+ this.messageSender = messageSender;
}
@Override
@@ -116,10 +131,18 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
} else {
builder.updated(ImmutableList.of(messageId));
}
+ sendMessageWhenOutboxInTargetMailboxIds(messageId, updateMessagePatch, mailboxSession, builder);
}
} catch (DraftMessageMailboxUpdateException e) {
handleDraftMessageMailboxUpdateException(messageId, builder, e);
- } catch (MailboxException e) {
+ } catch (InvalidOutboxMoveException e) {
+ ValidationResult invalidPropertyMailboxIds = ValidationResult.builder()
+ .property(MessageProperties.MessageProperty.mailboxIds.asFieldName())
+ .message("only drafts can be moved to Outbox")
+ .build();
+
+ handleInvalidRequest(builder, messageId, ImmutableList.of(invalidPropertyMailboxIds));
+ } catch (MailboxException|IOException|MessagingException e) {
handleMessageUpdateException(messageId, builder, e);
} catch (IllegalArgumentException e) {
ValidationResult invalidPropertyKeywords = ValidationResult.builder()
@@ -129,24 +152,54 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
handleInvalidRequest(builder, messageId, ImmutableList.of(invalidPropertyKeywords));
}
+ }
+ private void sendMessageWhenOutboxInTargetMailboxIds(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, SetMessagesResponse.Builder builder) throws MailboxException, MessagingException, IOException {
+ if (isTargetingOutbox(mailboxSession, listTargetMailboxIds(updateMessagePatch))) {
+ Optional<MessageResult> messagesToSend =
+ messageIdManager.getMessages(
+ ImmutableList.of(messageId), FetchGroupImpl.FULL_CONTENT, mailboxSession)
+ .stream()
+ .findFirst();
+ if (messagesToSend.isPresent()) {
+ MailImpl mail = buildMailFromMessage(messagesToSend.get());
+ messageSender.sendMessage(messageId, mail, mailboxSession);
+ } else {
+ addMessageIdNotFoundToResponse(messageId, builder);
+ }
+ }
}
private void assertValidUpdate(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch, MailboxSession session) throws MailboxException {
List<MailboxId> draftMailboxes = mailboxIdFor(Role.DRAFTS, session);
+ List<MailboxId> outboxMailboxes = mailboxIdFor(Role.OUTBOX, session);
+
+ ImmutableList<MailboxId> previousMailboxes = messagesToBeUpdated.stream()
+ .map(MessageResult::getMailboxId)
+ .collect(Guavate.toImmutableList());
List<Flags> futureFlags = patchFlags(messagesToBeUpdated, updateMessagePatch);
- List<MailboxId> targetMailboxes = getTargetedMailboxes(messagesToBeUpdated, updateMessagePatch);
+ List<MailboxId> targetMailboxes = getTargetedMailboxes(previousMailboxes, updateMessagePatch);
+ boolean originIsDraft = previousMailboxes.stream().allMatch(draftMailboxes::contains);
+ boolean targetIsOutbox = targetMailboxes.stream().anyMatch(outboxMailboxes::contains);
boolean targetHasDraft = targetMailboxes.stream().anyMatch(draftMailboxes::contains);
boolean targetHasNonDraft = targetMailboxes.stream().anyMatch(id -> !draftMailboxes.contains(id));
- assertValidUpdate(futureFlags, targetHasDraft, targetHasNonDraft);
+ assertValidUpdate(futureFlags, targetHasDraft, targetHasNonDraft, targetIsOutbox, originIsDraft);
}
- private void assertValidUpdate(List<Flags> futureFlags, boolean targetHasDraft, boolean targetHasNonDraft) throws DraftMessageMailboxUpdateException {
+ private void assertValidUpdate(List<Flags> futureFlags, boolean targetHasDraft, boolean targetHasNonDraft,
+ boolean targetIsOutbox, boolean originIsDraft) throws DraftMessageMailboxUpdateException {
assertMessageIsNotInDraftAndNonDraftMailboxes(targetHasDraft, targetHasNonDraft);
assertNoNonDraftMessageInsideDraftMailbox(futureFlags, targetHasDraft);
assertNoDraftMessageOutOfDraftMailbox(futureFlags, targetHasNonDraft);
+ assertOutboxMoveOnlyFromDraft(targetIsOutbox, originIsDraft);
+ }
+
+ private void assertOutboxMoveOnlyFromDraft(boolean targetIsOutbox, boolean originIsDraft) {
+ if (targetIsOutbox && !originIsDraft) {
+ throw new InvalidOutboxMoveException();
+ }
}
private void assertMessageIsNotInDraftAndNonDraftMailboxes(boolean targetHasDraft, boolean targetHasNonDraft) throws DraftMessageMailboxUpdateException {
@@ -173,10 +226,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
}
}
- private List<MailboxId> getTargetedMailboxes(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
- ImmutableList<MailboxId> previousMailboxes = messagesToBeUpdated.stream()
- .map(MessageResult::getMailboxId)
- .collect(Guavate.toImmutableList());
+ private List<MailboxId> getTargetedMailboxes(ImmutableList<MailboxId> previousMailboxes, UpdateMessagePatch updateMessagePatch) {
return updateMessagePatch.getMailboxIds()
.map(ids -> ids.stream().map(mailboxIdFactory::fromString).collect(Guavate.toImmutableList()))
.orElse(previousMailboxes);
@@ -195,6 +245,34 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
.collect(Guavate.toImmutableList());
}
+ private MailImpl buildMailFromMessage(MessageResult message) throws MessagingException, IOException, MailboxException {
+ return MailImpl.fromMimeMessage(message.getMessageId().serialize(),
+ new MimeMessage(
+ Session.getDefaultInstance(new Properties()),
+ message.getFullContent().getInputStream()));
+ }
+
+ private Set<MailboxId> listTargetMailboxIds(UpdateMessagePatch updateMessagePatch) {
+ return OptionalUtils.toStream(updateMessagePatch.getMailboxIds())
+ .flatMap(Collection::stream)
+ .map(mailboxIdFactory::fromString)
+ .collect(Guavate.toImmutableSet());
+ }
+
+ private boolean isTargetingOutbox(MailboxSession mailboxSession, Set<MailboxId> targetMailboxIds) throws MailboxException {
+ Set<MailboxId> outboxes = listMailboxIdsForRole(mailboxSession, Role.OUTBOX);
+ if (outboxes.isEmpty()) {
+ throw new MailboxNotFoundException("At least one outbox should be accessible");
+ }
+ return targetMailboxIds.stream().anyMatch(outboxes::contains);
+ }
+
+ private Set<MailboxId> listMailboxIdsForRole(MailboxSession session, Role role) throws MailboxException {
+ return systemMailboxesProvider.getMailboxByRole(role, session)
+ .map(MessageManager::getId)
+ .collect(Guavate.toImmutableSet());
+ }
+
private Stream<MailboxException> updateFlags(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, MessageResult messageResult) {
try {
if (!updateMessagePatch.isFlagsIdentity()) {
@@ -240,7 +318,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
private SetMessagesResponse.Builder handleMessageUpdateException(MessageId messageId,
SetMessagesResponse.Builder builder,
- MailboxException e) {
+ Exception e) {
LOGGER.error("An error occurred when updating a message", e);
return builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()
.type("anErrorOccurred")
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
index 3d156d3..966f659 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
@@ -40,4 +40,6 @@ public class MailFactory {
message.getContent());
}
+
+
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
index de2f809..2264fda 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
@@ -49,10 +49,13 @@ public class SetMessagesUpdateProcessorTest {
MessageIdManager messageIdManager = null;
SystemMailboxesProvider systemMailboxesProvider = null;
MailboxId.Factory mailboxIdFactory = null;
+ MessageSender messageSender = null;
SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(updatePatchConverter,
messageIdManager,
systemMailboxesProvider,
- mailboxIdFactory, new NoopMetricFactory());
+ mailboxIdFactory,
+ messageSender,
+ new NoopMetricFactory());
SetMessagesRequest requestWithEmptyUpdate = SetMessagesRequest.builder().build();
SetMessagesResponse result = sut.process(requestWithEmptyUpdate, null);
@@ -76,7 +79,16 @@ public class SetMessagesUpdateProcessorTest {
when(mockConverter.fromJsonNode(any(ObjectNode.class)))
.thenReturn(mockInvalidPatch);
- SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter, null, null, null, new NoopMetricFactory());
+ MessageIdManager messageIdManager = null;
+ SystemMailboxesProvider systemMailboxesProvider = null;
+ MailboxId.Factory mailboxIdFactory = null;
+ MessageSender messageSender = null;
+ SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter,
+ messageIdManager,
+ systemMailboxesProvider,
+ mailboxIdFactory,
+ messageSender,
+ new NoopMetricFactory());
MessageId requestMessageId = TestMessageId.of(1);
SetMessagesRequest requestWithInvalidUpdate = SetMessagesRequest.builder()
.update(ImmutableMap.of(requestMessageId, JsonNodeFactory.instance.objectNode()))
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org