001/*
002 * JGrapes Event driven Framework
003 * Copyright (C) 2022 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.mail.events;
020
021import jakarta.mail.Flags.Flag;
022import jakarta.mail.Folder;
023import jakarta.mail.Message;
024import jakarta.mail.MessageRemovedException;
025import jakarta.mail.MessagingException;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.function.Function;
029import java.util.logging.Level;
030import java.util.logging.Logger;
031import org.jgrapes.core.Event;
032import org.jgrapes.mail.MailChannel;
033import org.jgrapes.mail.MailMonitor;
034
035/**
036 * Signals the retrieval of mails (update) by a {@link MailMonitor}.
037 * Must be fired on a {@link MailChannel}.
038 */
039@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
040public class MailFoldersUpdated extends Event<Void> {
041
042    @SuppressWarnings("PMD.FieldNamingConventions")
043    private static final Logger logger
044        = Logger.getLogger(MailFoldersUpdated.class.getName());
045
046    private final List<Folder> folders;
047    private final List<Message> newMessages;
048
049    /**
050     * Instantiates a new event.
051     *
052     * @param folders the folders
053     * @param newMessages the new messages
054     */
055    public MailFoldersUpdated(List<Folder> folders, List<Message> newMessages) {
056        this.folders = folders;
057        this.newMessages = newMessages;
058    }
059
060    /**
061     * Returns the folders.
062     *
063     * @return the list
064     */
065    public List<Folder> folders() {
066        return folders;
067    }
068
069    /**
070     * Return the new messages. New messages have not been reported
071     * before by an event.
072     *
073     * @return the list
074     */
075    public List<Message> newMessages() {
076        return newMessages;
077    }
078
079    /**
080     * Execute the action with the given folder. The method ensures that
081     * the folder is open.
082     *
083     * @param folder the folder
084     * @param action the action
085     * @throws MessagingException the messaging exception
086     */
087    @SuppressWarnings("PMD.GuardLogStatement")
088    public static <R> R withFolder(Folder folder, Function<Folder, R> action)
089            throws MessagingException {
090        synchronized (folder) {
091            if (!folder.isOpen()) {
092                logger.fine("Found folder \"" + folder.getFullName()
093                    + "\" to be unexpectedly closed.");
094                folder.open(Folder.READ_WRITE);
095            }
096            return action.apply(folder);
097        }
098    }
099
100    /**
101     * Return all messages (which are not deleted) from the folder.
102     *
103     * @param folder the folder
104     * @return the message[]
105     * @throws MessagingException the messaging exception
106     */
107    public static List<Message> messages(Folder folder)
108            throws MessagingException {
109        return messages(folder, Integer.MAX_VALUE);
110    }
111
112    /**
113     * Return all (or max) messages (which are not deleted) from the folder,
114     * starting with the newest message.
115     *
116     * @param folder the folder
117     * @param max the limit
118     * @return the message[]
119     * @throws MessagingException the messaging exception
120     */
121    @SuppressWarnings("PMD.CognitiveComplexity")
122    public static List<Message> messages(Folder folder, int max)
123            throws MessagingException {
124        MessagingException[] exception = { null };
125        @SuppressWarnings({ "PMD.PrematureDeclaration",
126            "PMD.GuardLogStatement" })
127        var result = withFolder(folder, f -> {
128            List<Message> msgs = new LinkedList<>();
129            try {
130                int available = folder.getMessageCount();
131                int retrieve = Math.min(available, max);
132                int start = available - retrieve + 1;
133                if (start > available) {
134                    return msgs;
135                }
136                // Loops from older to newer
137                for (var msg : f.getMessages(start, available)) {
138                    if (canBeAdded(msg)) {
139                        // prepend newer
140                        msgs.add(0, msg);
141                    }
142                }
143                // Adds older messages to fill until max
144                while (start > 1 && msgs.size() < max) {
145                    Message msg = f.getMessage(--start);
146                    if (canBeAdded(msg)) {
147                        msgs.add(msg);
148                    }
149                }
150            } catch (MessagingException e) {
151                logger.log(Level.FINE, "Problem getting messages: "
152                    + e.getMessage(), e);
153                exception[0] = e;
154            }
155            return msgs;
156        });
157        if (exception[0] != null) {
158            throw exception[0];
159        }
160        return result;
161    }
162
163    private static boolean canBeAdded(Message msg)
164            throws MessagingException {
165        try {
166            if (msg.getFlags().contains(Flag.DELETED)) {
167                return false;
168            }
169        } catch (MessageRemovedException e) {
170            return false;
171        }
172        return true;
173    }
174}