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

Commit 184d02ff authored by Hari's avatar Hari
Browse files

Finished SEARCH and CAPABILITY commands

parent f785a5f6
Loading
Loading
Loading
Loading
+32 −15
Original line number Diff line number Diff line
@@ -5,13 +5,13 @@ package com.fsck.k9.mail;
 * Flags that can be applied to Messages.
 */
public enum Flag {
    DELETED,
    SEEN,
    ANSWERED,
    FLAGGED,
    DRAFT,
    RECENT,
    FORWARDED,
    DELETED("DELETED", "UNDELETED"),
    SEEN("SEEN", "UNSEEN"),
    ANSWERED("ANSWERED", "UNANSWERED"),
    FLAGGED("FLAGGED", "UNFLAGGED"),
    DRAFT("DRAFT", "UNDRAFT"),
    RECENT("RECENT", "UNRECENT"),
    FORWARDED(null, null),

    /*
     * The following flags are for internal library use only.
@@ -19,35 +19,35 @@ public enum Flag {
    /**
     * Delete and remove from the LocalStore immediately.
     */
    X_DESTROYED,
    X_DESTROYED(null, null),

    /**
     * Sending of an unsent message failed. It will be retried. Used to show status.
     */
    X_SEND_FAILED,
    X_SEND_FAILED(null, null),

    /**
     * Sending of an unsent message is in progress.
     */
    X_SEND_IN_PROGRESS,
    X_SEND_IN_PROGRESS(null, null),

    /**
     * Indicates that a message is fully downloaded from the server and can be viewed normally.
     * This does not include attachments, which are never downloaded fully.
     */
    X_DOWNLOADED_FULL,
    X_DOWNLOADED_FULL(null, null),

    /**
     * Indicates that a message is partially downloaded from the server and can be viewed but
     * more content is available on the server.
     * This does not include attachments, which are never downloaded fully.
     */
    X_DOWNLOADED_PARTIAL,
    X_DOWNLOADED_PARTIAL(null, null),

    /**
     * Indicates that the copy of a message to the Sent folder has started.
     */
    X_REMOTE_COPY_STARTED,
    X_REMOTE_COPY_STARTED(null, null),

    /**
     * Messages with this flag have been migrated from database version 50 or earlier.
@@ -56,10 +56,27 @@ public enum Flag {
     * incomplete or broken.
     * TODO Messages with this flag should be redownloaded, if possible.
     */
    X_MIGRATED_FROM_V50,
    X_MIGRATED_FROM_V50(null, null),

    /**
     * This flag is used for drafts where the message should be sent as PGP/INLINE.
     */
    X_DRAFT_OPENPGP_INLINE,
    X_DRAFT_OPENPGP_INLINE(null, null);

    private String requiredImapString;
    private String forbiddenImapString;

    Flag(String requiredImapString, String forbiddenImapString) {
        this.requiredImapString = requiredImapString;
        this.forbiddenImapString = forbiddenImapString;
    }

    public String getRequiredImapString() {
        return requiredImapString;
    }

    public String getForbiddenImapString() {
        return forbiddenImapString;
    }

}
+3 −3
Original line number Diff line number Diff line
@@ -257,7 +257,7 @@ public class ImapConnection {
    }

    private List<ImapResponse> extractCapabilities(List<ImapResponse> responses) {
        CapabilityResponse capabilityResponse = CapabilityResponse.parse(responses);
        CapabilityResponse capabilityResponse = CapabilityResponse.parse(commandFactory, responses);
        if (capabilityResponse != null) {
            Set<String> receivedCapabilities = capabilityResponse.getCapabilities();
            if (K9MailLib.isDebug()) {
@@ -270,7 +270,7 @@ public class ImapConnection {

    private List<ImapResponse> extractOrRequestCapabilities(List<ImapResponse> responses)
            throws IOException, MessagingException {
        CapabilityResponse capabilityResponse = CapabilityResponse.parse(responses);
        CapabilityResponse capabilityResponse = CapabilityResponse.parse(commandFactory, responses);
        if (capabilityResponse != null) {
            Set<String> receivedCapabilities = capabilityResponse.getCapabilities();
            Timber.d("Saving %s capabilities for %s", receivedCapabilities, getLogId());
@@ -295,7 +295,7 @@ public class ImapConnection {

    private void requestCapabilities() throws IOException, MessagingException {
        CapabilityCommand command = commandFactory.createCapabilityCommand();
        List<ImapResponse> responses = extractCapabilities(command.execute());
        List<ImapResponse> responses = extractCapabilities(command.executeInternal());
        if (responses.size() != 2) {
            throw new MessagingException("Invalid CAPABILITY response received");
        }
+59 −89
Original line number Diff line number Diff line
@@ -439,50 +439,46 @@ public class ImapFolder extends Folder<ImapMessage> {
        return messageCount;
    }

    private int getRemoteMessageCount(String criteria) throws MessagingException {
    private int getRemoteMessageCount(Set<Flag> requiredFlags, Set<Flag> forbiddenFlags) throws MessagingException {
        checkOpen();

        try {
            int count = 0;
            int start = 1;
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                .allIds(true)
                .requiredFlags(requiredFlags)
                .forbiddenFlags(forbiddenFlags)
                .build();

            String command = String.format(Locale.US, "SEARCH %d:* %s", start, criteria);
            List<ImapResponse> responses = executeSimpleCommand(command);
        SearchResponse searchResponse = searchCommand.execute();
        return searchResponse.getNumbers().size();

            for (ImapResponse response : responses) {
                if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
                    count += response.size() - 1;
                }
            }

            return count;
        } catch (IOException ioe) {
            throw ioExceptionHandler(connection, ioe);
        }
    }

    @Override
    public int getUnreadMessageCount() throws MessagingException {
        return getRemoteMessageCount("UNSEEN NOT DELETED");
        Set<Flag> forbiddenFlags = new HashSet<>(2);
        Collections.addAll(forbiddenFlags, Flag.SEEN, Flag.DELETED);
        return getRemoteMessageCount(null, forbiddenFlags);
    }

    @Override
    public int getFlaggedMessageCount() throws MessagingException {
        return getRemoteMessageCount("FLAGGED NOT DELETED");
        Set<Flag> requiredFlags = Collections.singleton(Flag.FLAGGED);
        Set<Flag> forbiddenFlags = Collections.singleton(Flag.DELETED);
        return getRemoteMessageCount(requiredFlags, forbiddenFlags);
    }

    protected long getHighestUid() throws MessagingException {
        try {
            String command = "UID SEARCH *:*";
            List<ImapResponse> responses = executeSimpleCommand(command);

            SearchResponse searchResponse = SearchResponse.parse(commandFactory, responses);
            UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                    .onlyHighestId(true)
                    .build();

            SearchResponse searchResponse = searchCommand.execute();

            return extractHighestUid(searchResponse);
        } catch (NegativeImapResponseException e) {
            return -1L;
        } catch (IOException ioe) {
            throw ioExceptionHandler(connection, ioe);
        }
    }

@@ -525,29 +521,16 @@ public class ImapFolder extends Folder<ImapMessage> {
            throw new MessagingException(String.format(Locale.US, "Invalid message set %d %d", start, end));
        }

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

        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, listener)
                .addSequenceRange(String.valueOf(start), String.valueOf(end))
                .useUids(false)
                .addIdRange((long) start, (long) end)
                .since(earliestDate)
                .forbiddenFlags(forbiddenFlags)
                .forbiddenFlags(includeDeleted ? null : Collections.singleton(Flag.DELETED))
                .build();

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

    private String getDateSearchString(Date earliestDate) {
        if (earliestDate == null) {
            return "";
        }

        return " SINCE " + RFC3501_DATE.get().format(earliestDate);
    }

    @Override
    public boolean areMoreMessagesAvailable(int indexOfOldestMessage, Date earliestDate) throws IOException,
            MessagingException {
@@ -558,13 +541,12 @@ public class ImapFolder extends Folder<ImapMessage> {
            return false;
        }

        String dateSearchString = getDateSearchString(earliestDate);
        int endIndex = indexOfOldestMessage - 1;

        while (endIndex > 0) {
            int startIndex = Math.max(0, endIndex - MORE_MESSAGES_WINDOW_SIZE) + 1;

            if (existsNonDeletedMessageInRange(startIndex, endIndex, dateSearchString)) {
            if (existsNonDeletedMessageInRange(startIndex, endIndex, earliestDate)) {
                return true;
            }

@@ -574,35 +556,27 @@ public class ImapFolder extends Folder<ImapMessage> {
        return false;
    }

    private boolean existsNonDeletedMessageInRange(int startIndex, int endIndex, String dateSearchString)
    private boolean existsNonDeletedMessageInRange(int startIndex, int endIndex, Date earliestDate)
            throws MessagingException, IOException {

        String command = String.format(Locale.US, "SEARCH %d:%d%s NOT DELETED", startIndex, endIndex, dateSearchString);
        List<ImapResponse> responses = executeSimpleCommand(command);

        for (ImapResponse response : responses) {
            if (response.getTag() == null && ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
                if (response.size() > 1) {
                    return true;
                }
            }
        }
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                .useUids(false)
                .addIdRange((long) startIndex, (long) endIndex)
                .since(earliestDate)
                .forbiddenFlags(Collections.singleton(Flag.DELETED))
                .build();

        return false;
        SearchResponse response = searchCommand.execute();
        return response.getNumbers().size() > 0;
    }

    protected List<ImapMessage> getMessages(final List<Long> mesgSeqs, final boolean includeDeleted,
            final MessageRetrievalListener<ImapMessage> listener) throws MessagingException {

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

        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, listener)
                .sequenceSet(mesgSeqs)
                .forbiddenFlags(forbiddenFlags)
                .useUids(false)
                .idSet(mesgSeqs)
                .forbiddenFlags(includeDeleted ? null : Collections.singleton(Flag.DELETED))
                .build();

        return getMessages(searchCommand.execute(), listener);
@@ -617,7 +591,8 @@ public class ImapFolder extends Folder<ImapMessage> {
        }

        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                .uidSet(uidSet)
                .useUids(true)
                .idSet(uidSet)
                .build();

        return getMessages(searchCommand.execute(), null);
@@ -626,7 +601,7 @@ public class ImapFolder extends Folder<ImapMessage> {

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

        checkOpen();

        List<ImapMessage> messages = new ArrayList<>();
@@ -1250,7 +1225,6 @@ public class ImapFolder extends Folder<ImapMessage> {

    @Override
    public String getUidFromMessageId(Message message) throws MessagingException {
        try {
        /*
        * Try to find the UID of the message we just appended using the
        * Message-ID header.
@@ -1269,20 +1243,16 @@ public class ImapFolder extends Folder<ImapMessage> {
            Timber.d("Looking for UID for message with message-id %s for %s", messageId, getLogId());
        }

            String command = String.format("UID SEARCH HEADER MESSAGE-ID %s", ImapUtility.encodeString(messageId));
            List<ImapResponse> responses = executeSimpleCommand(command);
        UidSearchCommand searchCommand = commandFactory.createUidSearchCommandBuilder(this, null)
                .messageId(messageId)
                .build();

            for (ImapResponse response : responses) {
                if (response.getTag() == null && ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")
                        && response.size() > 1) {
                    return response.getString(1);
                }
        List<Long> uids = searchCommand.execute().getNumbers();
        if (uids.size() > 0) {
            return Long.toString(uids.get(0));
        }

        return null;
        } catch (IOException ioe) {
            throw new MessagingException("Could not find UID for message based on Message-ID", ioe);
        }
    }

    @Override
+2 −2
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;


public class ImapMessage extends MimeMessage {
    public ImapMessage(String uid, Folder folder) {
+1 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@ public class ImapUtility {
     *
     * @return The string encoded as quoted (IMAP) string.
     */
    //TODO use a literal string
    public static String encodeString(String str) {
        return "\"" + str.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }
Loading