MIMEMessageConverter { @VisibleForTesting Message convertToMime(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<MessageAttachmentMetadata> messageAttachments, MailboxSession session) { if (creationMessageEntry == null || creationMessageEntry.getValue() == null) { throw new IllegalArgumentException("creationMessageEntry is either null or has null message"); } Message.Builder messageBuilder = Message.Builder.of(); if (isMultipart(creationMessageEntry.getValue(), messageAttachments)) { messageBuilder.setBody(createMultipart(creationMessageEntry.getValue(), messageAttachments, session)); } else { messageBuilder.setBody(createTextBody(creationMessageEntry.getValue())) .setContentTransferEncoding(QUOTED_PRINTABLE); } buildMimeHeaders(messageBuilder, creationMessageEntry.getValue(), messageAttachments); return messageBuilder.build(); } @Inject  MIMEMessageConverter(AttachmentContentLoader attachmentContentLoader); byte[] convert(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<MessageAttachmentMetadata> messageAttachments, MailboxSession session); byte[] asBytes(Message message);  }
@Test void convertToMimeShouldAddInReplyToHeaderWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); String matchingMessageId = "unique-message-id"; CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("sender").build()) .inReplyToMessageId(matchingMessageId) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields("In-Reply-To")).extracting(Field::getBody) .containsOnly(matchingMessageId); }
@Test void convertToMimeShouldGenerateMessageId() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage message = CreationMessage.builder() .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of(), session); assertThat(result.getHeader().getFields("Message-ID")).extracting(Field::getBody) .isNotNull(); }
@Test void convertToMimeShouldGenerateMessageIdWhenSenderWithoutDomain() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage message = CreationMessage.builder() .from(DraftEmailer.builder().email("sender").build()) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of(), session); assertThat(result.getHeader().getFields("Message-ID")).extracting(Field::getBody) .isNotNull(); }
@Test void convertToMimeShouldGenerateMessageIdContainingSenderDomain() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage message = CreationMessage.builder() .from(DraftEmailer.builder().email("email@domain.com").build()) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), message), ImmutableList.of(), session); assertThat(result.getHeader().getFields("Message-ID")).hasSize(1); assertThat(result.getHeader().getFields("Message-ID").get(0).getBody()) .contains("@domain.com"); }
@Test void convertToMimeShouldAddHeaderWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("sender").build()) .headers(ImmutableMap.of("FIRST", "first value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody) .containsOnly("first value"); }
@Test void convertToMimeShouldAddHeadersWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("sender").build()) .headers(ImmutableMap.of("FIRST", "first value", "SECOND", "second value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody) .containsOnly("first value"); assertThat(result.getHeader().getFields("SECOND")).extracting(Field::getBody) .containsOnly("second value"); }
@Test void convertToMimeShouldFilterGeneratedHeadersWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); String joesEmail = "joe@example.com"; CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().email(joesEmail).name("joe").build()) .headers(ImmutableMap.of("From", "hacker@example.com", "VALID", "valid header value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getFrom()).extracting(Mailbox::getAddress) .allMatch(f -> f.equals(joesEmail)); assertThat(result.getHeader().getFields("VALID")).extracting(Field::getBody) .containsOnly("valid header value"); assertThat(result.getHeader().getFields("From")).extracting(Field::getBody) .containsOnly("joe <joe@example.com>"); }
@Test void convertToMimeShouldFilterGeneratedHeadersRegardlessOfCaseWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); String joesEmail = "joe@example.com"; CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().email(joesEmail).name("joe").build()) .headers(ImmutableMap.of("frOM", "hacker@example.com", "VALID", "valid header value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getFrom()).extracting(Mailbox::getAddress) .allMatch(f -> f.equals(joesEmail)); assertThat(result.getHeader().getFields("VALID")).extracting(Field::getBody) .containsOnly("valid header value"); assertThat(result.getHeader().getFields("From")).extracting(Field::getBody) .containsOnly("joe <joe@example.com>"); }
@Test void convertToMimeShouldAddMultivaluedHeadersWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("sender").build()) .headers(ImmutableMap.of("FIRST", "first value\nsecond value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields("FIRST")).extracting(Field::getBody) .containsOnly("first value", "second value"); }
@Test void convertToMimeShouldFilterEmptyHeaderNames() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("joe").build()) .headers(ImmutableMap.of("", "empty header name value")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields("")).isEmpty(); }
@Test void convertToMimeShouldFilterWhiteSpacesOnlyHeaderNames() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage messageHavingInReplyTo = CreationMessage.builder() .from(DraftEmailer.builder().name("joe").build()) .headers(ImmutableMap.of(" ", "only spaces header name values")) .mailboxIds(ImmutableList.of("dead-beef-1337")) .subject("subject") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo), ImmutableList.of(), session); assertThat(result.getHeader().getFields(" ")).isEmpty(); assertThat(result.getHeader().getFields("")).isEmpty(); }
@Test void convertToMimeShouldThrowWhenMessageIsNull() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); assertThatThrownBy(() -> sut.convertToMime( new ValueWithId.CreationMessageEntry(CreationMessageId.of("any"), null), ImmutableList.of(), session)) .isInstanceOf(IllegalArgumentException.class); }
@Test void convertToMimeShouldSetBothFromAndSenderHeaders() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); String joesEmail = "joe@example.com"; CreationMessage testMessage = CreationMessage.builder() .mailboxIds(ImmutableList.of("deadbeef-dead-beef-dead-beef")) .subject("subject") .from(DraftEmailer.builder().email(joesEmail).name("joe").build()) .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getFrom()).extracting(Mailbox::getAddress).allMatch(f -> f.equals(joesEmail)); assertThat(result.getSender().getAddress()).isEqualTo(joesEmail); }
@Test void convertToMimeShouldSetCorrectLocalDate() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); Instant now = Instant.now(); ZonedDateTime messageDate = ZonedDateTime.ofInstant(now, ZoneId.systemDefault()); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .date(messageDate) .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getDate()).isEqualToIgnoringMillis(Date.from(now)); }
@Test void convertToMimeShouldSetQuotedPrintableContentTransferEncodingWhenText() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .htmlBody("Hello <b>all</b>!") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getHeader() .getField("Content-Transfer-Encoding") .getBody()) .isEqualTo("quoted-printable"); }
@Test void convertToMimeShouldSetTextBodyWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); TextBody expected = new BasicBodyFactory().textBody("Hello all!", StandardCharsets.UTF_8); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .textBody("Hello all!") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset"); }
@Test void convertToMimeShouldSetEmptyBodyWhenNoBodyProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset"); }
@Test void convertToMimeShouldSetHtmlBodyWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); TextBody expected = new BasicBodyFactory().textBody("Hello <b>all</b>!", StandardCharsets.UTF_8); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .htmlBody("Hello <b>all</b>!") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset"); }
@Test void convertToMimeShouldSetMimeTypeWhenTextBody() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .textBody("Hello all!") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getMimeType()).isEqualTo("text/plain"); }
@Test void convertToMimeShouldSetMimeTypeWhenHtmlBody() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .htmlBody("Hello <b>all<b>!") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getMimeType()).isEqualTo("text/html"); }
@Test void convertToMimeShouldSetEmptyHtmlBodyWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .htmlBody("") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset"); assertThat(result.getMimeType()).isEqualTo("text/html"); }
@Test void convertToMimeShouldSetEmptyTextBodyWhenProvided() { MIMEMessageConverter sut = new MIMEMessageConverter(attachmentContentLoader); TextBody expected = new BasicBodyFactory().textBody("", StandardCharsets.UTF_8); CreationMessage testMessage = CreationMessage.builder() .mailboxId("dead-bada55") .subject("subject") .from(DraftEmailer.builder().name("sender").build()) .textBody("") .build(); Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry( CreationMessageId.of("user|mailbox|1"), testMessage), ImmutableList.of(), session); assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset"); assertThat(result.getMimeType()).isEqualTo("text/plain"); }