Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java +1 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java +54 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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>(); Loading Loading @@ -110,6 +115,7 @@ class ImapConnection { open = true; boolean authSuccess = false; commandFactory = ImapCommandFactory.create(this, getLogId()); nextCommandTag = 1; adjustDNSCacheTTL(); Loading Loading @@ -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"); } Loading Loading @@ -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()); Loading Loading @@ -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); Loading Loading @@ -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'); Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +88 −139 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading @@ -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; Loading Loading @@ -133,6 +139,7 @@ class ImapFolder extends Folder<ImapMessage> { synchronized (this) { connection = store.getConnection(); commandFactory = ImapCommandFactory.create(connection, getLogId()); } try { Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -642,9 +651,6 @@ class ImapFolder extends Folder<ImapMessage> { listener.messageFinished(message, i, count); } } } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } return messages; } Loading Loading @@ -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) { /* Loading Loading @@ -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) { Loading Loading @@ -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; } Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapList.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapMessage.java +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 Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java +1 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java +54 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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>(); Loading Loading @@ -110,6 +115,7 @@ class ImapConnection { open = true; boolean authSuccess = false; commandFactory = ImapCommandFactory.create(this, getLogId()); nextCommandTag = 1; adjustDNSCacheTTL(); Loading Loading @@ -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"); } Loading Loading @@ -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()); Loading Loading @@ -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); Loading Loading @@ -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'); Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +88 −139 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading @@ -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; Loading Loading @@ -133,6 +139,7 @@ class ImapFolder extends Folder<ImapMessage> { synchronized (this) { connection = store.getConnection(); commandFactory = ImapCommandFactory.create(connection, getLogId()); } try { Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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 Loading @@ -642,9 +651,6 @@ class ImapFolder extends Folder<ImapMessage> { listener.messageFinished(message, i, count); } } } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } return messages; } Loading Loading @@ -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) { /* Loading Loading @@ -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) { Loading Loading @@ -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; } Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapList.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapMessage.java +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