Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f785a5f6 authored by Hari's avatar Hari
Browse files

Added base, search and capability classes

parent 9f4c6fe3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package com.fsck.k9.mail.store.imap;

class Capabilities {
    public static final String IDLE = "IDLE";
    public static final String CONDSTORE = "CONDSTORE";
    public static final String SASL_IR = "SASL-IR";
    public static final String AUTH_XOAUTH2 = "AUTH=XOAUTH2";
    public static final String AUTH_CRAM_MD5 = "AUTH=CRAM-MD5";
+54 −2
Original line number Diff line number Diff line
@@ -41,12 +41,16 @@ import com.fsck.k9.mail.filter.PeekableInputStream;
import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
import com.fsck.k9.mail.oauth.XOAuth2ChallengeParser;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import com.fsck.k9.mail.store.imap.command.CapabilityCommand;
import com.fsck.k9.mail.store.imap.command.ImapCommandFactory;
import com.fsck.k9.mail.store.imap.response.CapabilityResponse;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
import javax.net.ssl.SSLException;
import org.apache.commons.io.IOUtils;
import timber.log.Timber;

import static android.R.attr.tag;
import static com.fsck.k9.mail.ConnectionSecurity.STARTTLS_REQUIRED;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_IMAP;
import static com.fsck.k9.mail.store.RemoteStore.SOCKET_CONNECT_TIMEOUT;
@@ -57,7 +61,7 @@ import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase;
/**
 * A cacheable class that stores the details for a single IMAP connection.
 */
class ImapConnection {
public class ImapConnection {
    private static final int BUFFER_SIZE = 1024;


@@ -70,6 +74,7 @@ class ImapConnection {
    private Socket socket;
    private PeekableInputStream inputStream;
    private OutputStream outputStream;
    private ImapCommandFactory commandFactory;
    private ImapResponseParser responseParser;
    private int nextCommandTag;
    private Set<String> capabilities = new HashSet<String>();
@@ -110,6 +115,7 @@ class ImapConnection {

        open = true;
        boolean authSuccess = false;
        commandFactory = ImapCommandFactory.create(this, getLogId());
        nextCommandTag = 1;

        adjustDNSCacheTTL();
@@ -288,7 +294,8 @@ class ImapConnection {
    }

    private void requestCapabilities() throws IOException, MessagingException {
        List<ImapResponse> responses = extractCapabilities(executeSimpleCommand(Commands.CAPABILITY));
        CapabilityCommand command = commandFactory.createCapabilityCommand();
        List<ImapResponse> responses = extractCapabilities(command.execute());
        if (responses.size() != 2) {
            throw new MessagingException("Invalid CAPABILITY response received");
        }
@@ -683,6 +690,10 @@ class ImapConnection {
        return capabilities.contains(capability.toUpperCase(Locale.US));
    }

    public boolean isCondstoreCapable() throws IOException, MessagingException  {
        return hasCapability(Capabilities.CONDSTORE);
    }

    protected boolean isIdleCapable() {
        if (K9MailLib.isDebug()) {
            Timber.v("Connection %s has %d capabilities", getLogId(), capabilities.size());
@@ -734,6 +745,24 @@ class ImapConnection {
        }
    }

    public List<ImapResponse> executeSimpleCommandNew(String command, boolean sensitive) throws IOException,
            MessagingException {
        String commandToLog = command;

        if (sensitive && !K9MailLib.isDebugSensitive()) {
            commandToLog = "*sensitive*";
        }

        String tag = sendCommandNew(command, sensitive);

        try {
            return responseParser.readStatusResponse(tag, commandToLog, getLogId(), null);
        } catch (IOException e) {
            close();
            throw e;
        }
    }

    public List<ImapResponse> readStatusResponse(String tag, String commandToLog, UntaggedHandler untaggedHandler)
            throws IOException, NegativeImapResponseException {
        return responseParser.readStatusResponse(tag, commandToLog, getLogId(), untaggedHandler);
@@ -788,6 +817,29 @@ class ImapConnection {
        }
    }

    public String sendCommandNew(String command, boolean sensitive) throws MessagingException, IOException {
        try {
            open();

            String commandToSend = command + "\r\n";
            outputStream.write(commandToSend.getBytes());
            outputStream.flush();

            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) {
                if (sensitive && !K9MailLib.isDebugSensitive()) {
                    Timber.v("%s>>> [Command Hidden, Enable Sensitive Debug Logging To Show]", getLogId());
                } else {
                    Timber.v("%s>>> %s %s", getLogId(), tag, command);
                }
            }

            return command.split(" ")[0];
        } catch (IOException | MessagingException e) {
            close();
            throw e;
        }
    }

    public void sendContinuation(String continuation) throws IOException {
        outputStream.write(continuation.getBytes());
        outputStream.write('\r');
+88 −139
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -34,12 +35,16 @@ import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeMessageHelper;
import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.imap.command.ImapCommandFactory;
import com.fsck.k9.mail.store.imap.command.UidSearchCommand;
import com.fsck.k9.mail.store.imap.response.BaseResponse;
import com.fsck.k9.mail.store.imap.response.SearchResponse;
import timber.log.Timber;

import static com.fsck.k9.mail.store.imap.ImapUtility.getLastResponse;


class ImapFolder extends Folder<ImapMessage> {
public class ImapFolder extends Folder<ImapMessage> {
    private static final ThreadLocal<SimpleDateFormat> RFC3501_DATE = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
@@ -54,6 +59,7 @@ class ImapFolder extends Folder<ImapMessage> {
    protected volatile long uidNext = -1L;
    protected volatile ImapConnection connection;
    protected ImapStore store = null;
    private ImapCommandFactory commandFactory;
    protected Map<Long, String> msgSeqUidMap = new ConcurrentHashMap<Long, String>();
    private final FolderNameCodec folderNameCodec;
    private final String name;
@@ -133,6 +139,7 @@ class ImapFolder extends Folder<ImapMessage> {

        synchronized (this) {
            connection = store.getConnection();
            commandFactory = ImapCommandFactory.create(connection, getLogId());
        }

        try {
@@ -469,7 +476,7 @@ class ImapFolder extends Folder<ImapMessage> {
            String command = "UID SEARCH *:*";
            List<ImapResponse> responses = executeSimpleCommand(command);

            SearchResponse searchResponse = SearchResponse.parse(responses);
            SearchResponse searchResponse = SearchResponse.parse(commandFactory, responses);

            return extractHighestUid(searchResponse);
        } catch (NegativeImapResponseException e) {
@@ -518,19 +525,19 @@ class ImapFolder extends Folder<ImapMessage> {
            throw new MessagingException(String.format(Locale.US, "Invalid message set %d %d", start, end));
        }

        final String dateSearchString = getDateSearchString(earliestDate);

        ImapSearcher searcher = new ImapSearcher() {
            @Override
            public List<ImapResponse> search() throws IOException, MessagingException {
                String command = String.format(Locale.US, "UID SEARCH %d:%d%s%s", start, end, dateSearchString,
                        includeDeleted ? "" : " NOT DELETED");

                return executeSimpleCommand(command);
        Set<Flag> forbiddenFlags = null;
        if (!includeDeleted) {
            forbiddenFlags = new HashSet<>();
            forbiddenFlags.add(Flag.DELETED);
        }
        };

        return search(searcher, listener);
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, listener)
                .addSequenceRange(String.valueOf(start), String.valueOf(end))
                .since(earliestDate)
                .forbiddenFlags(forbiddenFlags)
                .build();

        return getMessages(searchCommand.execute(), listener);
    }

    private String getDateSearchString(Date earliestDate) {
@@ -586,41 +593,43 @@ class ImapFolder extends Folder<ImapMessage> {

    protected List<ImapMessage> getMessages(final List<Long> mesgSeqs, final boolean includeDeleted,
            final MessageRetrievalListener<ImapMessage> listener) throws MessagingException {
        ImapSearcher searcher = new ImapSearcher() {
            @Override
            public List<ImapResponse> search() throws IOException, MessagingException {
                String command = String.format("UID SEARCH %s%s", combine(mesgSeqs.toArray(), ','),
                        includeDeleted ? "" : " NOT DELETED");

                return executeSimpleCommand(command);
        Set<Flag> forbiddenFlags = null;
        if (!includeDeleted) {
            forbiddenFlags = new HashSet<>();
            forbiddenFlags.add(Flag.DELETED);
        }
        };

        return search(searcher, listener);
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, listener)
                .sequenceSet(mesgSeqs)
                .forbiddenFlags(forbiddenFlags)
                .build();

        return getMessages(searchCommand.execute(), listener);

    }

    protected List<ImapMessage> getMessagesFromUids(final List<String> mesgUids) throws MessagingException {
        ImapSearcher searcher = new ImapSearcher() {
            @Override
            public List<ImapResponse> search() throws IOException, MessagingException {
                String command = String.format("UID SEARCH UID %s", combine(mesgUids.toArray(), ','));

                return executeSimpleCommand(command);
        Set<Long> uidSet = new HashSet<>();
        for (String uid : mesgUids) {
            uidSet.add(Long.parseLong(uid));
        }
        };

        return search(searcher, null);
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                .uidSet(uidSet)
                .build();

        return getMessages(searchCommand.execute(), null);

    }

    private List<ImapMessage> search(ImapSearcher searcher, MessageRetrievalListener<ImapMessage> listener)
    private List<ImapMessage> getMessages(SearchResponse searchResponse, MessageRetrievalListener<ImapMessage> listener)
            throws MessagingException {
        handleUntaggedResponses(searchResponse);
        checkOpen();

        List<ImapMessage> messages = new ArrayList<>();
        try {
            List<ImapResponse> responses = searcher.search();

            SearchResponse searchResponse = SearchResponse.parse(responses);
        List<Long> uids = searchResponse.getNumbers();

        // Sort the uids in numerically decreasing order
@@ -642,9 +651,6 @@ class ImapFolder extends Folder<ImapMessage> {
                listener.messageFinished(message, i, count);
            }
        }
        } catch (IOException ioe) {
            throw ioExceptionHandler(connection, ioe);
        }

        return messages;
    }
@@ -980,6 +986,25 @@ class ImapFolder extends Folder<ImapMessage> {
        }
    }

    public void handleUntaggedResponses(BaseResponse response) {
        int newMessageCount = response.getMessageCount();
        if (newMessageCount != -1) {
            messageCount = newMessageCount;
        }

        int expungedCount = response.getExpungedCount();
        if (messageCount > expungedCount) {
            messageCount -= expungedCount;
        } else {
            messageCount = 0;
        }

        long newUidNext = response.getUidNext();
        if (newUidNext != -1L) {
            uidNext = newUidNext;
        }
    }

    private void parseBodyStructure(ImapList bs, Part part, String id) throws MessagingException {
        if (bs.get(0) instanceof ImapList) {
            /*
@@ -1353,7 +1378,7 @@ class ImapFolder extends Folder<ImapMessage> {
        }
    }

    private MessagingException ioExceptionHandler(ImapConnection connection, IOException ioe) {
    public MessagingException ioExceptionHandler(ImapConnection connection, IOException ioe) {
        Timber.e(ioe, "IOException for %s", getLogId());

        if (connection != null) {
@@ -1409,97 +1434,21 @@ class ImapFolder extends Folder<ImapMessage> {
            throw new MessagingException("Your settings do not allow remote searching of this account");
        }

        // Setup the searcher
        final ImapSearcher searcher = new ImapSearcher() {
            @Override
            public List<ImapResponse> search() throws IOException, MessagingException {
                String imapQuery = "UID SEARCH ";
                if (requiredFlags != null) {
                    for (Flag flag : requiredFlags) {
                        switch (flag) {
                            case DELETED: {
                                imapQuery += "DELETED ";
                                break;
                            }
                            case SEEN: {
                                imapQuery += "SEEN ";
                                break;
                            }
                            case ANSWERED: {
                                imapQuery += "ANSWERED ";
                                break;
                            }
                            case FLAGGED: {
                                imapQuery += "FLAGGED ";
                                break;
                            }
                            case DRAFT: {
                                imapQuery += "DRAFT ";
                                break;
                            }
                            case RECENT: {
                                imapQuery += "RECENT ";
                                break;
                            }
                            default: {
                                break;
                            }
                        }
                    }
                }

                if (forbiddenFlags != null) {
                    for (Flag flag : forbiddenFlags) {
                        switch (flag) {
                            case DELETED: {
                                imapQuery += "UNDELETED ";
                                break;
                            }
                            case SEEN: {
                                imapQuery += "UNSEEN ";
                                break;
                            }
                            case ANSWERED: {
                                imapQuery += "UNANSWERED ";
                                break;
                            }
                            case FLAGGED: {
                                imapQuery += "UNFLAGGED ";
                                break;
                            }
                            case DRAFT: {
                                imapQuery += "UNDRAFT ";
                                break;
                            }
                            case RECENT: {
                                imapQuery += "UNRECENT ";
                                break;
                            }
                            default: {
                                break;
                            }
                        }
                    }
                }

                String encodedQuery = ImapUtility.encodeString(queryString);
                if (store.getStoreConfig().isRemoteSearchFullText()) {
                    imapQuery += "TEXT " + encodedQuery;
                } else {
                    imapQuery += "OR SUBJECT " + encodedQuery + " FROM " + encodedQuery;
                }

                return executeSimpleCommand(imapQuery);
            }
        };

        try {
            open(OPEN_MODE_RO);
            checkOpen();

            inSearch = true;

            return search(searcher, null);
            UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                    .queryString(queryString)
                    .performFullTextSearch(store.getStoreConfig().isRemoteSearchFullText())
                    .requiredFlags(requiredFlags)
                    .forbiddenFlags(forbiddenFlags)
                    .build();

            return getMessages(searchCommand.execute(), null);

        } finally {
            inSearch = false;
        }
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ import java.util.Locale;
 * Represents an IMAP list response and is also the base class for the
 * ImapResponse.
 */
class ImapList extends ArrayList<Object> {
public class ImapList extends ArrayList<Object> {
    private static final long serialVersionUID = -4067248341419617583L;
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
    private static final DateFormat BAD_DATE_TIME_FORMAT = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US);
+4 −4
Original line number Diff line number Diff line
package com.fsck.k9.mail.store.imap;


import java.util.Collections;

import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage;

import java.util.Collections;


class ImapMessage extends MimeMessage {
    ImapMessage(String uid, Folder folder) {
public class ImapMessage extends MimeMessage {
    public ImapMessage(String uid, Folder folder) {
        this.mUid = uid;
        this.mFolder = folder;
    }
Loading