All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.icegreen.greenmail.imap.ImapSessionFolder Maven / Gradle / Ivy

/*
 * Copyright (c) 2014 Wael Chatila / Icegreen Technologies. All Rights Reserved.
 * This software is released under the Apache license 2.0
 * This file has been modified by the copyright holder.
 * Original file can be found at http://james.apache.org
 */
package com.icegreen.greenmail.imap;

import com.icegreen.greenmail.foedus.util.MsgRangeFilter;
import com.icegreen.greenmail.imap.commands.IdRange;
import com.icegreen.greenmail.mail.MovingMessage;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.FolderListener;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.store.StoredMessage;
import jakarta.mail.Flags;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.UIDFolder;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.search.SearchTerm;

import java.util.*;

public class ImapSessionFolder implements MailFolder, FolderListener, UIDFolder {
    private MailFolder folder;
    private ImapSession session;
    private boolean readonly;
    private volatile boolean sizeChanged;
    private final List expungedMsns = Collections.synchronizedList(new LinkedList<>());
    private final Map modifiedFlags = Collections.synchronizedMap(new TreeMap<>());

    public ImapSessionFolder(MailFolder folder, ImapSession session, boolean readonly) {
        this.folder = folder;
        this.session = session;
        this.readonly = readonly;
        // TODO make this a weak reference (or make sure deselect() is *always* called).
        this.folder.addListener(this);
    }

    public void deselect() {
        folder.removeListener(this);
        folder = null;
    }

    @Override
    public int getMsn(long uid) throws FolderException {
        long[] uids = folder.getMessageUids();
        for (int i = 0; i < uids.length; i++) {
            long messageUid = uids[i];
            if (uid == messageUid) {
                return i + 1;
            }
        }
        throw new FolderException("No such message with uid " + uid + " in folder " + folder.getName());
    }

    @Override
    public void signalDeletion() {
        folder.signalDeletion();
    }

    @Override
    public List getMessages(MsgRangeFilter msgRangeFilter) {
        return folder.getMessages(msgRangeFilter);
    }

    @Override
    public List getMessages() {
        return folder.getMessages();
    }

    @Override
    public List getNonDeletedMessages() {
        return folder.getNonDeletedMessages();
    }

    public boolean isReadonly() {
        return readonly;
    }

    public int[] getExpunged() {
        synchronized (expungedMsns) {
            int[] expungedMsnsArray = new int[this.expungedMsns.size()];
            for (int i = 0; i < expungedMsnsArray.length; i++) {
                int msn = this.expungedMsns.get(i);
                expungedMsnsArray[i] = msn;
            }
            this.expungedMsns.clear();

            // TODO - renumber any cached ids (for now we assume the modifiedFlags has been cleared)\
            if (!(modifiedFlags.isEmpty() && !sizeChanged)) {
                throw new IllegalStateException("Need to do this properly...");
            }
            return expungedMsnsArray;
        }
    }

    public List getFlagUpdates() {
        if (modifiedFlags.isEmpty()) {
            return Collections.emptyList();
        }

        List retVal = new ArrayList<>(modifiedFlags.values());
        modifiedFlags.clear();
        return retVal;
    }

    @Override
    public void expunged(int msn) {
        synchronized (expungedMsns) {
            expungedMsns.add(msn);
        }
    }

    @Override
    public void added(int msn) {
        sizeChanged = true;
    }

    @Override
    public void flagsUpdated(int msn, Flags flags, Long uid) {
        // This will overwrite any earlier changes
        modifiedFlags.put(msn, new FlagUpdate(msn, uid, flags));
    }

    @Override
    public void mailboxDeleted() {
        session.closeConnection("Mailbox " + folder.getName() + " has been deleted");
    }

    @Override
    public String getName() {
        return folder.getName();
    }

    @Override
    public String getFullName() {
        return folder.getFullName();
    }

    @Override
    public Flags getAvailableFlags() {
        return folder.getAvailableFlags();
    }

    @Override
    public Flags getPermanentFlags() {
        return folder.getPermanentFlags();
    }

    @Override
    public int getMessageCount() {
        return folder.getMessageCount();
    }

    @Override
    public int getRecentCount(boolean reset) {
        return folder.getRecentCount(reset);
    }

    @Override
    public long getUidValidity() {
        return folder.getUidValidity();
    }

    @Override
    public int getFirstUnseen() {
        return correctForExpungedMessages(folder.getFirstUnseen());
    }

    /**
     * Adjust an actual mailbox msn for the expunged messages in this mailbox that have not
     * yet been notified.
     * TODO - need a test for this
     */
    private int correctForExpungedMessages(int absoluteMsn) {
        int correctedMsn = absoluteMsn;
        // Loop through the expunged list backwards, adjusting the msn as we go.
        for (int i = expungedMsns.size() - 1; i >= 0; i--) {
            int expunged = expungedMsns.get(i);
            if (expunged <= absoluteMsn) {
                correctedMsn++;
            }
        }
        return correctedMsn;
    }

    @Override
    public boolean isSelectable() {
        return folder.isSelectable();
    }

    @Override
    public int getUnseenCount() {
        return folder.getUnseenCount();
    }

    @Override
    public long appendMessage(MimeMessage message, Flags flags, Date receivedDate) {
        return folder.appendMessage(message, flags, receivedDate);
    }

    @Override
    public void store(MovingMessage mail) throws Exception {
        folder.store(mail);
    }

    @Override
    public void store(MimeMessage mail) throws Exception {
        folder.store(mail);
    }

    @Override
    public StoredMessage getMessage(long uid) {
        return folder.getMessage(uid);
    }

    @Override
    public long[] getMessageUids() {
        return folder.getMessageUids();
    }

    @Override
    public void expunge() throws FolderException {
        folder.expunge();
    }

    @Override
    public void expunge(IdRange[] idRanges) {
        folder.expunge(idRanges);
    }

    @Override
    public long[] search(SearchTerm searchTerm) {
        return folder.search(searchTerm);
    }

    @Override
    public long copyMessage(long uid, MailFolder toFolder) throws FolderException {
        return folder.copyMessage(uid, toFolder);
    }

    @Override
    public long moveMessage(long uid, MailFolder toFolder) throws FolderException {
        return folder.moveMessage(uid, toFolder);
    }

    @Override
    public void addListener(FolderListener listener) {
        folder.addListener(listener);
    }

    @Override
    public void removeListener(FolderListener listener) {
        folder.removeListener(listener);
    }

    @Override
    public void setFlags(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws FolderException {
        folder.setFlags(flags, value, uid, silentListener, addUid);
    }

    @Override
    public void replaceFlags(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws FolderException {
        folder.replaceFlags(flags, uid, silentListener, addUid);
    }

    @Override
    public void deleteAllMessages() {
        folder.deleteAllMessages();
    }

    public boolean isSizeChanged() {
        return sizeChanged;
    }

    public void setSizeChanged(boolean sizeChanged) {
        this.sizeChanged = sizeChanged;
    }

    private UIDFolder unwrapUIDFolder() {
        if (folder instanceof UIDFolder) {
            return (UIDFolder) folder;
        }
        throw new IllegalStateException("No UIDFolder supported by "+ folder.getClass());
    }

    @Override
    public long getUIDValidity() throws MessagingException {
        return unwrapUIDFolder().getUIDValidity();
    }

    @Override
    public Message getMessageByUID(long uid) throws MessagingException {
        return unwrapUIDFolder().getMessageByUID(uid);
    }

    @Override
    public Message[] getMessagesByUID(long start, long end) throws MessagingException {
        return unwrapUIDFolder().getMessagesByUID(start, end);
    }

    @Override
    public Message[] getMessagesByUID(long[] uids) throws MessagingException {
        return unwrapUIDFolder().getMessagesByUID(uids);
    }

    @Override
    public long getUID(Message message) throws MessagingException {
        return unwrapUIDFolder().getUID(message);
    }

    @Override
    public long getUIDNext() {
        return folder.getUIDNext();
    }

    static final class FlagUpdate {
        private int msn;
        private Long uid;
        private Flags flags;

        public FlagUpdate(int msn, Long uid, Flags flags) {
            this.msn = msn;
            this.uid = uid;
            this.flags = flags;
        }

        public int getMsn() {
            return msn;
        }

        public Long getUid() {
            return uid;
        }

        public Flags getFlags() {
            return flags;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy