123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- // Copyright (c) 2003-present, Jodd Team (http://jodd.org)
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // 1. Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // 2. Redistributions in binary form must reproduce the above copyright
- // notice, this list of conditions and the following disclaimer in the
- // documentation and/or other materials provided with the distribution.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- package com.fjhx.utils.mail;
- import jakarta.mail.*;
- import jakarta.mail.internet.MimeMessage;
- import jakarta.mail.internet.MimePart;
- import jakarta.mail.util.ByteArrayDataSource;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.nio.charset.StandardCharsets;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.List;
- import static jakarta.mail.Flags.Flag;
- /**
- * Received email.
- */
- public class ReceivedEmail extends CommonEmail<ReceivedEmail> {
- public static final ReceivedEmail[] EMPTY_ARRAY = new ReceivedEmail[0];
- /**
- * {@link List} of attached {@link ReceivedEmail}s.
- */
- private final List<ReceivedEmail> attachedMessages = new ArrayList<>();
- private File attachmentStorage;
- /**
- * {@link Message} for this {@link ReceivedEmail}.
- */
- private Message originalMessage;
- /**
- * {@link Flags} for this {@link ReceivedEmail}.
- */
- private Flags flags;
- private int messageNumber;
- private String messageId;
- private Date receivedDate;
- /**
- * Creates an empty {@link ReceivedEmail}.
- */
- private ReceivedEmail() {
- }
- /**
- * Creates a {@link ReceivedEmail} from a given {@link Message}.
- *
- * @param msg {@link Message}
- * @param envelope flag if this is an envelope
- */
- public ReceivedEmail(final Message msg, final boolean envelope, final File attachmentStorage) {
- this.attachmentStorage = attachmentStorage;
- this.originalMessage = msg;
- try {
- parseMessage(msg, envelope);
- } catch (final Exception ex) {
- throw new MailException("Message parsing failed", ex);
- }
- }
- /**
- * Static constructor for fluent interface.
- *
- * @return new {@link ReceivedEmail}.
- */
- public static ReceivedEmail create() {
- return new ReceivedEmail();
- }
- /**
- * Returns the Content-ID of this {@link Part}. Returns {@code null} if none present.
- *
- * @param part {@link Part} the Part to parse.
- * @return String containing content ID.
- * @throws MessagingException if there is a failure.
- * @see MimePart#getContentID()
- */
- protected static String parseContentId(final Part part) throws MessagingException {
- if (part instanceof MimePart) {
- final MimePart mp = (MimePart) part;
- return mp.getContentID();
- } else {
- return null;
- }
- }
- // ---------------------------------------------------------------- original message
- /**
- * Returns {@code true} if the {@link Part} is inline.
- *
- * @param part {@link Part} to parse.
- * @return {@code true} if the {@link Part} is inline.
- * @throws MessagingException if there is a failure.
- */
- protected static boolean parseInline(final Part part) throws MessagingException {
- if (part instanceof MimePart) {
- final String dispositionId = part.getDisposition();
- return dispositionId != null && dispositionId.equalsIgnoreCase("inline");
- }
- return false;
- }
- /**
- * Creates {@link EmailAttachmentBuilder} from {@link Part} and sets Content ID, inline and name.
- *
- * @param part {@link Part}.
- * @return this
- * @see #attachment(EmailAttachment)
- */
- private static EmailAttachmentBuilder addAttachmentInfo(final Part part) throws MessagingException {
- final String fileName = EmailUtil.resolveFileName(part);
- final String contentId = parseContentId(part);
- final boolean isInline = parseInline(part);
- return new EmailAttachmentBuilder()
- .name(fileName)
- .contentId(contentId)
- .inline(isInline);
- }
- @Override
- public ReceivedEmail clone() {
- return create()
- // original message
- .originalMessage(originalMessage())
- // flags
- .flags(flags())
- // message number and id
- .messageNumber(messageNumber())
- .messageId(messageId())
- // from / reply-to
- .from(from())
- .replyTo(replyTo())
- // recipients
- .to(to())
- .cc(cc())
- // subject
- .subject(subject(), subjectEncoding())
- // dates
- .receivedDate(receivedDate())
- .sentDate(sentDate())
- // headers - includes priority
- .headers(headers())
- // content / attachments
- .message(messages())
- .storeAttachments(attachments())
- .attachedMessages(attachedMessages());
- }
- // ---------------------------------------------------------------- flags
- /**
- * Parses {@link Message} and extracts all data for the received message.
- *
- * @param msg {@link Message} to parse.
- * @throws IOException if there is an error with the content
- * @throws MessagingException if there is an error.
- */
- protected void parseMessage(final Message msg, final boolean envelope) throws MessagingException, IOException {
- // flags
- flags(msg.getFlags());
- // message number
- messageNumber(msg.getMessageNumber());
- if (msg instanceof MimeMessage) {
- messageId(((MimeMessage) msg).getMessageID());
- }
- // single from
- final Address[] addresses = msg.getFrom();
- if (addresses != null && addresses.length > 0) {
- from(addresses[0]);
- }
- // reply-to
- replyTo(msg.getReplyTo());
- // recipients
- to(msg.getRecipients(Message.RecipientType.TO));
- cc(msg.getRecipients(Message.RecipientType.CC));
- // no BCC because this will always be empty
- // subject
- subject(msg.getSubject());
- // dates
- receivedDate(msg.getReceivedDate());
- sentDate(msg.getSentDate());
- // headers
- headers(msg.getAllHeaders());
- // content
- if (!envelope) {
- processPart(msg);
- }
- }
- /**
- * Process part of the received message. All parts are simply added to the {@link ReceivedEmail},
- * i.e. hierarchy is not saved.
- *
- * @param part {@link Part} of received message
- * @throws IOException if there is an error with the content.
- * @throws MessagingException if there is an error.
- */
- protected void processPart(final Part part) throws MessagingException, IOException {
- final Object content = part.getContent();
- if (content instanceof String) {
- addStringContent(part, (String) content);
- } else if (content instanceof Multipart) {
- processMultipart((Multipart) content);
- } else if (content instanceof InputStream) {
- addAttachment(part, (InputStream) content, attachmentStorage);
- } else if (content instanceof MimeMessage) {
- final MimeMessage mimeMessage = (MimeMessage) content;
- attachedMessage(new ReceivedEmail(mimeMessage, false, attachmentStorage));
- } else {
- addAttachment(part, part.getInputStream(), attachmentStorage);
- }
- }
- /**
- * Process the {@link Multipart}.
- *
- * @param mp {@link Multipart}
- * @throws MessagingException if there is a failure.
- * @throws IOException if there is an issue with the {@link Multipart}.
- */
- private void processMultipart(final Multipart mp) throws MessagingException, IOException {
- final int count = mp.getCount();
- for (int i = 0; i < count; i++) {
- final Part innerPart = mp.getBodyPart(i);
- processPart(innerPart);
- }
- }
- /**
- * Adds String content as either {@link EmailAttachment} or as {@link EmailMessage}.
- *
- * @param part {@link Part}
- * @param content Content as {@link String}
- * @throws MessagingException if there is a failure.
- * @throws UnsupportedEncodingException if the named charset is not supported.
- * @see #message(String, String, String)
- */
- private void addStringContent(final Part part, final String content) throws MessagingException, UnsupportedEncodingException {
- final String contentType = part.getContentType();
- final String encoding = EmailUtil.extractEncoding(contentType, StandardCharsets.US_ASCII.name());
- final String disposition = part.getDisposition();
- if (disposition != null && disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
- addAttachment(part, content.getBytes(encoding));
- } else {
- final String mimeType = EmailUtil.extractMimeType(contentType);
- message(content, mimeType, encoding);
- }
- }
- /**
- * @return {@link Message}
- */
- public Message originalMessage() {
- return originalMessage;
- }
- /**
- * Sets the original message.
- *
- * @param originalMessage {@link Message} to set.
- */
- public ReceivedEmail originalMessage(final Message originalMessage) {
- this.originalMessage = originalMessage;
- return this;
- }
- /**
- * @return {@link Flags}
- */
- public Flags flags() {
- return flags;
- }
- /**
- * Sets the flags.
- *
- * @param flags {@link Flags} to set.
- */
- public ReceivedEmail flags(final Flags flags) {
- this.flags = flags;
- return this;
- }
- /**
- * Returns {@code true} if message is answered.
- *
- * @return {@code true} if message is answered.
- */
- public boolean isAnswered() {
- return flags.contains(Flag.ANSWERED);
- }
- // ---------------------------------------------------------------- additional properties
- /**
- * Returns {@code true} if message is deleted.
- *
- * @return {@code true} if message is deleted.
- */
- public boolean isDeleted() {
- return flags.contains(Flag.DELETED);
- }
- /**
- * Returns {@code true} if message is draft.
- */
- public boolean isDraft() {
- return flags.contains(Flag.DRAFT);
- }
- /**
- * Returns {@code true} is message is flagged.
- *
- * @return {@code true} is message is flagged.
- */
- public boolean isFlagged() {
- return flags.contains(Flag.FLAGGED);
- }
- /**
- * Returns {@code true} if message is recent.
- *
- * @return {@code true} if message is recent.
- */
- public boolean isRecent() {
- return flags.contains(Flag.RECENT);
- }
- /**
- * Returns {@code true} if message is seen.
- *
- * @return {@code true} if message is seen.
- */
- public boolean isSeen() {
- return flags.contains(Flag.SEEN);
- }
- /**
- * Returns message number.
- *
- * @return message number
- */
- public int messageNumber() {
- return messageNumber;
- }
- /**
- * Returns message ID if set by server.
- */
- public String messageId() {
- return messageId;
- }
- /**
- * Sets message number.
- *
- * @param messageNumber The message number to set.
- * @return this
- */
- public ReceivedEmail messageNumber(final int messageNumber) {
- this.messageNumber = messageNumber;
- return this;
- }
- /**
- * Sets message ID.
- */
- public ReceivedEmail messageId(final String messageId) {
- this.messageId = messageId;
- return this;
- }
- // ---------------------------------------------------------------- attachments
- /**
- * Sets email's received {@link Date}.
- *
- * @param date The received {@link Date} to set.
- * @return this
- */
- public ReceivedEmail receivedDate(final Date date) {
- receivedDate = date;
- return this;
- }
- /**
- * Returns email's received {@link Date}.
- *
- * @return The email's received {@link Date}.
- */
- public Date receivedDate() {
- return receivedDate;
- }
- /**
- * Adds received attachment.
- *
- * @param part {@link Part}.
- * @param content Content as {@link InputStream}.
- * @return this
- * @see #attachment(EmailAttachment)
- */
- private ReceivedEmail addAttachment(final Part part, final InputStream content, final File attachmentStorage) throws MessagingException, IOException {
- final EmailAttachmentBuilder builder = addAttachmentInfo(part);
- builder.content(content, part.getContentType());
- if (attachmentStorage != null) {
- String name = messageId + "-" + (this.attachments().size() + 1);
- return storeAttachment(builder.buildFileDataSource(name, attachmentStorage));
- }
- return storeAttachment(builder.buildByteArrayDataSource());
- }
- // ---------------------------------------------------------------- inner messages
- /**
- * Adds received attachment.
- *
- * @param part {@link Part}.
- * @param content Content as byte array.
- * @return this
- * @see #attachment(EmailAttachment)
- */
- private ReceivedEmail addAttachment(final Part part, final byte[] content) throws MessagingException {
- final EmailAttachmentBuilder builder = addAttachmentInfo(part);
- builder.content(content, part.getContentType());
- final EmailAttachment<ByteArrayDataSource> attachment = builder.buildByteArrayDataSource();
- attachment.setSize(content.length);
- return storeAttachment(attachment);
- }
- /**
- * Adds attached {@link ReceivedEmail}s.
- *
- * @param emails {@link List} of {@link ReceivedEmail}s to attach.
- */
- public ReceivedEmail attachedMessages(final List<ReceivedEmail> emails) {
- attachedMessages.addAll(emails);
- return this;
- }
- /**
- * Adds attached {@link ReceivedEmail}.
- *
- * @param email {@link ReceivedEmail} to attach.
- * @return this
- */
- public ReceivedEmail attachedMessage(final ReceivedEmail email) {
- attachedMessages.add(email);
- return this;
- }
- /**
- * Returns the {@link List} of attached messages.
- * If no attached message is available, returns an empty {@link List}.
- *
- * @return {@link List} of {@link ReceivedEmail}s.
- */
- public List<ReceivedEmail> attachedMessages() {
- return attachedMessages;
- }
- }
|