Automatically re-synchronize the IMAP protocol. Previously, losses of

synchronization could happen if an Exception was thrown while parsing
an untagged response without using executeSimpleCommand.  For
instance, while doing a fetch.  Once synchronization was lost, later
commands would fail in surprising ways.  One manifestation of such
failures was spontaneously emptying of folders when search results
were not returned properly.  The new code makes sure to only accept OK
responses with the tag of the command, and discards the untagged
responses from previous command.
This commit is contained in:
Daniel Applebaum 2009-05-05 00:42:05 +00:00
parent 7daf4ef1e6
commit 651642faeb
2 changed files with 44 additions and 9 deletions

View file

@ -753,15 +753,12 @@ public class MessagingController implements Runnable {
for (Message thisMess : remoteMessageArray)
{
remoteMessages.add(thisMess);
remoteUidMap.put(thisMess.getUid(), thisMess);
}
if (Config.LOGV) {
Log.v(Email.LOG_TAG, "SYNC: Got messages for folder " + folder);
Log.v(Email.LOG_TAG, "SYNC: Got " + remoteUidMap.size() + " messages for folder " + folder);
}
for (Message message : remoteMessages) {
remoteUidMap.put(message.getUid(), message);
}
/*
* Get a list of the messages that are in the remote list but not on the
* local store, or messages that are in the local store but failed to download
@ -923,6 +920,7 @@ s * critical data as fast as possible, and then we'll fill in the de
!localMessage.isSet(Flag.DELETED))
{
localMessage.setFlag(Flag.X_DESTROYED, true);
// Log.d(Email.LOG_TAG, "Destroying message " + localMessage.getUid() + " which isn't in the most recent group on server");
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxRemovedMessage(account, folder, localMessage);
}

View file

@ -384,10 +384,13 @@ public class ImapStore extends Store {
mPathDelimeter = nameResponses.get(0).getString(2);
}
}
// executeSimpleCommand("CLOSE");
String command = String.format("SELECT \"%s\"",
encodeFolderName(getPrefixedName()));
mMessageCount = -1;
//mMessageCount = -1;
List<ImapResponse> responses = executeSimpleCommand(command);
@ -429,6 +432,11 @@ public class ImapStore extends Store {
}
public void close(boolean expunge) throws MessagingException {
if (mMessageCount != -1)
{
// close();
mMessageCount = -1;
}
if (!isOpen()) {
return;
}
@ -436,7 +444,6 @@ public class ImapStore extends Store {
{
expunge();
}
mMessageCount = -1;
synchronized (this) {
releaseConnection(mConnection);
mConnection = null;
@ -584,6 +591,7 @@ public class ImapStore extends Store {
List<ImapResponse> responses =
executeSimpleCommand(String.format("UID SEARCH UID %S", uid));
for (ImapResponse response : responses) {
// Log.d(Email.LOG_TAG, "Got search response: " + response.get(0) + ", size " + response.size());
if (response.mTag == null && response.get(0).equals("SEARCH")) {
for (int i = 1, count = response.size(); i < count; i++) {
if (uid.equals(response.get(i))) {
@ -614,15 +622,24 @@ public class ImapStore extends Store {
checkOpen();
ArrayList<Message> messages = new ArrayList<Message>();
try {
boolean gotSearchValues = false;
ArrayList<Integer> uids = new ArrayList<Integer>();
List<ImapResponse> responses = executeSimpleCommand(String.format("UID SEARCH %d:%d NOT DELETED", start, end));
for (ImapResponse response : responses) {
// Log.d(Email.LOG_TAG, "Got search response: " + response.get(0) + ", size " + response.size());
if (response.get(0).equals("SEARCH")) {
gotSearchValues = true;
for (int i = 1, count = response.size(); i < count; i++) {
// Log.d(Email.LOG_TAG, "Got search response UID: " + response.getString(i));
uids.add(Integer.parseInt(response.getString(i)));
}
}
}
if (gotSearchValues == false)
{
throw new MessagingException("Did not get proper search response");
}
// Sort the uids in numerically ascending order
Collections.sort(uids);
for (int i = 0, count = uids.size(); i < count; i++) {
@ -868,7 +885,7 @@ public class ImapStore extends Store {
private void handleUntaggedResponse(ImapResponse response) {
if (response.mTag == null && response.size() > 1 && response.get(1).equals("EXISTS")) {
mMessageCount = response.getNumber(0);
Log.i(Email.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount);
//Log.i(Email.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount);
}
}
@ -1137,6 +1154,15 @@ public class ImapStore extends Store {
return null;
}
private void close() throws MessagingException {
checkOpen();
try {
executeSimpleCommand("CLOSE");
} catch (IOException ioe) {
}
}
private String combineFlags(Flag[] flags)
{
ArrayList<String> flagNames = new ArrayList<String>();
@ -1467,9 +1493,13 @@ public class ImapStore extends Store {
throws IOException, ImapException, MessagingException {
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "Sending IMAP command " + command);
Log.v(Email.LOG_TAG, "Sending IMAP command " + command + " on connection " + this.hashCode());
}
String tag = sendCommand(command, sensitive);
if (Config.LOGV)
{
Log.v(Email.LOG_TAG, "Sent IMAP command " + command + " with tag " + tag);
}
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
ImapResponse response;
do {
@ -1478,6 +1508,13 @@ public class ImapStore extends Store {
{
Log.v(Email.LOG_TAG, "Got IMAP response " + response);
}
if (response.mTag != null && response.mTag.equals(tag) == false)
{
Log.w(Email.LOG_TAG, "Got tag response from previous command " + response);
responses.clear();
response.mTag = null;
continue;
}
responses.add(response);
} while (response.mTag == null);
if (response.size() < 1 || !response.get(0).equals("OK")) {