All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.james.jmap.model.MessageFactory Maven / Gradle / Ivy
/****************************************************************
* 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.model;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.mail.Flags;
import javax.mail.internet.SharedInputStream;
import org.apache.james.jmap.utils.HtmlTextExtractor;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.Cid;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageAttachment;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageResult;
import org.apache.james.mime4j.dom.address.AddressList;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.message.MessageBuilder;
import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig;
import org.apache.james.util.mime.MessageContentExtractor;
import org.apache.james.util.mime.MessageContentExtractor.MessageContent;
import com.github.steveash.guavate.Guavate;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
public class MessageFactory {
private static final MimeConfig MIME_ENTITY_CONFIG = MimeConfig.custom()
.setMaxContentLen(-1)
.setMaxHeaderCount(-1)
.setMaxHeaderLen(-1)
.setMaxHeaderCount(-1)
.setMaxLineLen(-1)
.build();
private static final ZoneId UTC_ZONE_ID = ZoneId.of("Z");
private final MessagePreviewGenerator messagePreview;
private final MessageContentExtractor messageContentExtractor;
private final HtmlTextExtractor htmlTextExtractor;
@Inject
public MessageFactory(MessagePreviewGenerator messagePreview, MessageContentExtractor messageContentExtractor, HtmlTextExtractor htmlTextExtractor) {
this.messagePreview = messagePreview;
this.messageContentExtractor = messageContentExtractor;
this.htmlTextExtractor = htmlTextExtractor;
}
public Message fromMetaDataWithContent(MetaDataWithContent message) throws MailboxException {
org.apache.james.mime4j.dom.Message mimeMessage = parse(message);
MessageContent messageContent = extractContent(mimeMessage);
Optional htmlBody = messageContent.getHtmlBody();
Optional mainTextContent = mainTextContent(messageContent);
Optional textBody = computeTextBodyIfNeeded(messageContent, mainTextContent);
String preview = messagePreview.compute(mainTextContent);
return Message.builder()
.id(message.getMessageId())
.blobId(BlobId.of(String.valueOf(message.getUid().asLong())))
.threadId(message.getMessageId().serialize())
.mailboxIds(message.getMailboxIds())
.inReplyToMessageId(getHeader(mimeMessage, "in-reply-to"))
.isUnread(! message.getFlags().contains(Flags.Flag.SEEN))
.isFlagged(message.getFlags().contains(Flags.Flag.FLAGGED))
.isAnswered(message.getFlags().contains(Flags.Flag.ANSWERED))
.isDraft(message.getFlags().contains(Flags.Flag.DRAFT))
.subject(Strings.nullToEmpty(mimeMessage.getSubject()).trim())
.headers(toMap(mimeMessage.getHeader().getFields()))
.from(firstFromMailboxList(mimeMessage.getFrom()))
.to(fromAddressList(mimeMessage.getTo()))
.cc(fromAddressList(mimeMessage.getCc()))
.bcc(fromAddressList(mimeMessage.getBcc()))
.replyTo(fromAddressList(mimeMessage.getReplyTo()))
.size(message.getSize())
.date(message.getInternalDateAsZonedDateTime())
.textBody(textBody)
.htmlBody(htmlBody)
.preview(preview)
.attachments(getAttachments(message.getAttachments()))
.build();
}
private Optional computeTextBodyIfNeeded(MessageContent messageContent, Optional mainTextContent) {
return messageContent.getTextBody()
.map(Optional::of)
.orElse(mainTextContent);
}
private Optional mainTextContent(MessageContent messageContent) {
return messageContent.getHtmlBody()
.map(htmlTextExtractor::toPlainText)
.filter(s -> !Strings.isNullOrEmpty(s))
.map(Optional::of)
.orElse(messageContent.getTextBody());
}
private org.apache.james.mime4j.dom.Message parse(MetaDataWithContent message) throws MailboxException {
try {
return MessageBuilder
.create()
.use(MIME_ENTITY_CONFIG)
.parse(message.getContent())
.setDate(message.getInternalDate(), TimeZone.getTimeZone(UTC_ZONE_ID))
.build();
} catch (IOException e) {
throw new MailboxException("Unable to parse message: " + e.getMessage(), e);
}
}
private MessageContent extractContent(org.apache.james.mime4j.dom.Message mimeMessage) throws MailboxException {
try {
return messageContentExtractor.extract(mimeMessage);
} catch (IOException e) {
throw new MailboxException("Unable to extract content: " + e.getMessage(), e);
}
}
private Emailer firstFromMailboxList(MailboxList list) {
if (list == null) {
return null;
}
return list.stream()
.map(this::fromMailbox)
.findFirst()
.orElse(null);
}
private ImmutableList fromAddressList(AddressList list) {
if (list == null) {
return ImmutableList.of();
}
return list.flatten()
.stream()
.map(this::fromMailbox)
.collect(Guavate.toImmutableList());
}
private Emailer fromMailbox(Mailbox mailbox) {
return Emailer.builder()
.name(getNameOrAddress(mailbox))
.email(mailbox.getAddress())
.allowInvalid()
.build();
}
private String getNameOrAddress(Mailbox mailbox) {
if (mailbox.getName() != null) {
return mailbox.getName();
}
return mailbox.getAddress();
}
private ImmutableMap toMap(List fields) {
Function>, String> bodyConcatenator = fieldListEntry -> fieldListEntry.getValue()
.stream()
.map(Field::getBody)
.collect(Collectors.toList())
.stream()
.collect(Collectors.joining(","));
return Multimaps.index(fields, Field::getName)
.asMap()
.entrySet()
.stream()
.collect(Guavate.toImmutableMap(Map.Entry::getKey, bodyConcatenator));
}
private String getHeader(org.apache.james.mime4j.dom.Message message, String header) {
Field field = message.getHeader().getField(header);
if (field == null) {
return null;
}
return field.getBody();
}
private List getAttachments(List attachments) {
return attachments.stream()
.map(this::fromMailboxAttachment)
.collect(Guavate.toImmutableList());
}
private Attachment fromMailboxAttachment(MessageAttachment attachment) {
return Attachment.builder()
.blobId(BlobId.of(attachment.getAttachmentId().getId()))
.type(attachment.getAttachment().getType())
.size(attachment.getAttachment().getSize())
.name(attachment.getName().orNull())
.cid(attachment.getCid().transform(Cid::getValue).orNull())
.isInline(attachment.isInline())
.build();
}
public static class MetaDataWithContent {
public static Builder builder() {
return new Builder();
}
public static Builder builderFromMessageResult(MessageResult messageResult) throws MailboxException {
Builder builder = builder()
.uid(messageResult.getUid())
.modSeq(messageResult.getModSeq())
.flags(messageResult.getFlags())
.size(messageResult.getSize())
.internalDate(messageResult.getInternalDate())
.attachments(messageResult.getAttachments())
.mailboxId(messageResult.getMailboxId());
try {
return builder.content(messageResult.getFullContent().getInputStream());
} catch (IOException e) {
throw new MailboxException("Can't get message full content: " + e.getMessage(), e);
}
}
public static class Builder {
private MessageUid uid;
private Long modSeq;
private Flags flags;
private Long size;
private Date internalDate;
private InputStream content;
private SharedInputStream sharedContent;
private List attachments;
private Set mailboxIds = Sets.newHashSet();
private MessageId messageId;
public Builder uid(MessageUid uid) {
this.uid = uid;
return this;
}
public Builder modSeq(long modSeq) {
this.modSeq = modSeq;
return this;
}
public Builder flags(Flags flags) {
this.flags = flags;
return this;
}
public Builder size(long size) {
this.size = size;
return this;
}
public Builder internalDate(Date internalDate) {
this.internalDate = internalDate;
return this;
}
public Builder content(InputStream content) {
this.content = content;
return this;
}
public Builder sharedContent(SharedInputStream sharedContent) {
this.sharedContent = sharedContent;
return this;
}
public Builder attachments(List attachments) {
this.attachments = attachments;
return this;
}
public Builder mailboxId(MailboxId mailboxId) {
this.mailboxIds.add(mailboxId);
return this;
}
public Builder mailboxIds(List mailboxIds) {
this.mailboxIds.addAll(mailboxIds);
return this;
}
public Builder messageId(MessageId messageId) {
this.messageId = messageId;
return this;
}
public MetaDataWithContent build() {
Preconditions.checkArgument(uid != null);
if (modSeq == null) {
modSeq = -1L;
}
Preconditions.checkArgument(flags != null);
Preconditions.checkArgument(size != null);
Preconditions.checkArgument(internalDate != null);
Preconditions.checkArgument(content != null ^ sharedContent != null);
Preconditions.checkArgument(attachments != null);
Preconditions.checkArgument(mailboxIds != null);
Preconditions.checkArgument(messageId != null);
return new MetaDataWithContent(uid, modSeq, flags, size, internalDate, content, sharedContent, attachments, mailboxIds, messageId);
}
}
private final MessageUid uid;
private final long modSeq;
private final Flags flags;
private final long size;
private final Date internalDate;
private final InputStream content;
private final SharedInputStream sharedContent;
private final List attachments;
private final Set mailboxIds;
private final MessageId messageId;
private MetaDataWithContent(MessageUid uid, long modSeq, Flags flags, long size, Date internalDate, InputStream content, SharedInputStream sharedContent, List attachments, Set mailboxIds, MessageId messageId) {
this.uid = uid;
this.modSeq = modSeq;
this.flags = flags;
this.size = size;
this.internalDate = internalDate;
this.content = content;
this.sharedContent = sharedContent;
this.attachments = attachments;
this.mailboxIds = mailboxIds;
this.messageId = messageId;
}
public MessageUid getUid() {
return uid;
}
public long getModSeq() {
return modSeq;
}
public Flags getFlags() {
return flags;
}
public long getSize() {
return size;
}
public Date getInternalDate() {
return internalDate;
}
public ZonedDateTime getInternalDateAsZonedDateTime() {
return ZonedDateTime.ofInstant(internalDate.toInstant(), UTC_ZONE_ID);
}
public InputStream getContent() {
if (sharedContent != null) {
long begin = 0;
long allContent = -1;
return sharedContent.newStream(begin, allContent);
}
return content;
}
public List getAttachments() {
return attachments;
}
public Set getMailboxIds() {
return mailboxIds;
}
public MessageId getMessageId() {
return messageId;
}
}
}