mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-27 19:52:17 -05:00
Merge imap-parser branch.
Fixes issue 1547.
This commit is contained in:
parent
0e3f9a9db4
commit
22ce159fe6
@ -1727,11 +1727,7 @@ public class MessagingController implements Runnable
|
|||||||
*/
|
*/
|
||||||
for (Part part : viewables)
|
for (Part part : viewables)
|
||||||
{
|
{
|
||||||
fp.clear();
|
remoteFolder.fetchPart(message, part, null);
|
||||||
fp.add(part);
|
|
||||||
// TODO what happens if the network connection dies? We've got partial
|
|
||||||
// messages with incorrect status stored.
|
|
||||||
remoteFolder.fetch(new Message[] { message }, fp, null);
|
|
||||||
}
|
}
|
||||||
// Store the updated message locally
|
// Store the updated message locally
|
||||||
localFolder.appendMessages(new Message[] { message });
|
localFolder.appendMessages(new Message[] { message });
|
||||||
@ -3175,9 +3171,11 @@ public class MessagingController implements Runnable
|
|||||||
remoteFolder = remoteStore.getFolder(message.getFolder().getName());
|
remoteFolder = remoteStore.getFolder(message.getFolder().getName());
|
||||||
remoteFolder.open(OpenMode.READ_WRITE);
|
remoteFolder.open(OpenMode.READ_WRITE);
|
||||||
|
|
||||||
FetchProfile fp = new FetchProfile();
|
//FIXME: This is an ugly hack that won't be needed once the Message objects have been united.
|
||||||
fp.add(part);
|
Message remoteMessage = remoteFolder.getMessage(message.getUid());
|
||||||
remoteFolder.fetch(new Message[] { message }, fp, null);
|
remoteMessage.setBody(message.getBody());
|
||||||
|
remoteFolder.fetchPart(remoteMessage, part, null);
|
||||||
|
|
||||||
localFolder.updateMessage((LocalMessage)message);
|
localFolder.updateMessage((LocalMessage)message);
|
||||||
for (MessagingListener l : getListeners())
|
for (MessagingListener l : getListeners())
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ import java.util.ArrayList;
|
|||||||
* any information it needs to download the content.
|
* any information it needs to download the content.
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class FetchProfile extends ArrayList<Object>
|
public class FetchProfile extends ArrayList<FetchProfile.Item>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Default items available for pre-fetching. It should be expected that any
|
* Default items available for pre-fetching. It should be expected that any
|
||||||
|
@ -132,6 +132,12 @@ public abstract class Folder
|
|||||||
public abstract void fetch(Message[] messages, FetchProfile fp,
|
public abstract void fetch(Message[] messages, FetchProfile fp,
|
||||||
MessageRetrievalListener listener) throws MessagingException;
|
MessageRetrievalListener listener) throws MessagingException;
|
||||||
|
|
||||||
|
public void fetchPart(Message message, Part part,
|
||||||
|
MessageRetrievalListener listener) throws MessagingException
|
||||||
|
{
|
||||||
|
throw new RuntimeException("fetchPart() not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void delete(boolean recurse) throws MessagingException;
|
public abstract void delete(boolean recurse) throws MessagingException;
|
||||||
|
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.fsck.k9.mail.store;
|
package com.fsck.k9.mail.store;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -9,7 +5,6 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.FixedLengthInputStream;
|
import com.fsck.k9.FixedLengthInputStream;
|
||||||
import com.fsck.k9.PeekableInputStream;
|
import com.fsck.k9.PeekableInputStream;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
@ -20,19 +15,24 @@ import java.util.Locale;
|
|||||||
|
|
||||||
public class ImapResponseParser
|
public class ImapResponseParser
|
||||||
{
|
{
|
||||||
SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
|
private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
|
||||||
|
private static final SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US);
|
||||||
|
private static final SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
||||||
|
|
||||||
SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US);
|
private PeekableInputStream mIn;
|
||||||
SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
private ImapResponse mResponse;
|
||||||
|
private Exception mException;
|
||||||
PeekableInputStream mIn;
|
|
||||||
InputStream mActiveLiteral;
|
|
||||||
|
|
||||||
public ImapResponseParser(PeekableInputStream in)
|
public ImapResponseParser(PeekableInputStream in)
|
||||||
{
|
{
|
||||||
this.mIn = in;
|
this.mIn = in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImapResponse readResponse() throws IOException
|
||||||
|
{
|
||||||
|
return readResponse(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the next response available on the stream and returns an
|
* Reads the next response available on the stream and returns an
|
||||||
* ImapResponse object that represents it.
|
* ImapResponse object that represents it.
|
||||||
@ -40,49 +40,60 @@ public class ImapResponseParser
|
|||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public ImapResponse readResponse() throws IOException
|
public ImapResponse readResponse(IImapResponseCallback callback) throws IOException
|
||||||
{
|
{
|
||||||
ImapResponse response = new ImapResponse();
|
try
|
||||||
if (mActiveLiteral != null)
|
|
||||||
{
|
{
|
||||||
while (mActiveLiteral.read() != -1)
|
ImapResponse response = new ImapResponse();
|
||||||
;
|
mResponse = response;
|
||||||
mActiveLiteral = null;
|
mResponse.mCallback = callback;
|
||||||
|
|
||||||
|
int ch = mIn.peek();
|
||||||
|
if (ch == '*')
|
||||||
|
{
|
||||||
|
parseUntaggedResponse();
|
||||||
|
readTokens(response);
|
||||||
|
}
|
||||||
|
else if (ch == '+')
|
||||||
|
{
|
||||||
|
response.mCommandContinuationRequested =
|
||||||
|
parseCommandContinuationRequest();
|
||||||
|
readTokens(response);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.mTag = parseTaggedResponse();
|
||||||
|
readTokens(response);
|
||||||
|
}
|
||||||
|
if (K9.DEBUG)
|
||||||
|
{
|
||||||
|
Log.v(K9.LOG_TAG, "<<< " + response.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mException != null)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("readResponse(): Exception in callback method", mException);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
int ch = mIn.peek();
|
finally
|
||||||
if (ch == '*')
|
|
||||||
{
|
{
|
||||||
parseUntaggedResponse();
|
mResponse.mCallback = null;
|
||||||
readTokens(response);
|
mResponse = null;
|
||||||
|
mException = null;
|
||||||
}
|
}
|
||||||
else if (ch == '+')
|
|
||||||
{
|
|
||||||
response.mCommandContinuationRequested =
|
|
||||||
parseCommandContinuationRequest();
|
|
||||||
readTokens(response);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response.mTag = parseTaggedResponse();
|
|
||||||
readTokens(response);
|
|
||||||
}
|
|
||||||
if (K9.DEBUG)
|
|
||||||
{
|
|
||||||
Log.v(K9.LOG_TAG, "<<< " + response.toString());
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readTokens(ImapResponse response) throws IOException
|
private void readTokens(ImapResponse response) throws IOException
|
||||||
{
|
{
|
||||||
response.clear();
|
response.clear();
|
||||||
Object token;
|
Object token;
|
||||||
while ((token = readToken()) != null)
|
while ((token = readToken(response)) != null)
|
||||||
{
|
{
|
||||||
response.add(token);
|
if (!(token instanceof ImapList))
|
||||||
if (mActiveLiteral != null)
|
|
||||||
{
|
{
|
||||||
break;
|
response.add(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.mCompleted = token == null;
|
response.mCompleted = token == null;
|
||||||
@ -99,36 +110,30 @@ public class ImapResponseParser
|
|||||||
* tokens.
|
* tokens.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Object readToken() throws IOException
|
private Object readToken(ImapResponse response) throws IOException
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Object token = parseToken();
|
Object token = parseToken(response);
|
||||||
if (token == null || !token.equals(")") || !token.equals("]"))
|
if (token == null || !(token.equals(")") || token.equals("]")))
|
||||||
{
|
{
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object parseToken() throws IOException
|
private Object parseToken(ImapList parent) throws IOException
|
||||||
{
|
{
|
||||||
if (mActiveLiteral != null)
|
|
||||||
{
|
|
||||||
while (mActiveLiteral.read() != -1)
|
|
||||||
;
|
|
||||||
mActiveLiteral = null;
|
|
||||||
}
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int ch = mIn.peek();
|
int ch = mIn.peek();
|
||||||
if (ch == '(')
|
if (ch == '(')
|
||||||
{
|
{
|
||||||
return parseList();
|
return parseList(parent);
|
||||||
}
|
}
|
||||||
else if (ch == '[')
|
else if (ch == '[')
|
||||||
{
|
{
|
||||||
return parseSequence();
|
return parseSequence(parent);
|
||||||
}
|
}
|
||||||
else if (ch == ')')
|
else if (ch == ')')
|
||||||
{
|
{
|
||||||
@ -146,8 +151,7 @@ public class ImapResponseParser
|
|||||||
}
|
}
|
||||||
else if (ch == '{')
|
else if (ch == '{')
|
||||||
{
|
{
|
||||||
mActiveLiteral = parseLiteral();
|
return parseLiteral();
|
||||||
return mActiveLiteral;
|
|
||||||
}
|
}
|
||||||
else if (ch == ' ')
|
else if (ch == ' ')
|
||||||
{
|
{
|
||||||
@ -196,27 +200,27 @@ public class ImapResponseParser
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImapList parseList() throws IOException
|
private ImapList parseList(ImapList parent) throws IOException
|
||||||
{
|
{
|
||||||
expect('(');
|
expect('(');
|
||||||
ImapList list = new ImapList();
|
ImapList list = new ImapList();
|
||||||
|
parent.add(list);
|
||||||
Object token;
|
Object token;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
token = parseToken();
|
token = parseToken(list);
|
||||||
if (token == null)
|
if (token == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (token instanceof InputStream)
|
|
||||||
{
|
|
||||||
list.add(token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (token.equals(")"))
|
else if (token.equals(")"))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (token instanceof ImapList)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
list.add(token);
|
list.add(token);
|
||||||
@ -225,27 +229,27 @@ public class ImapResponseParser
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImapList parseSequence() throws IOException
|
private ImapList parseSequence(ImapList parent) throws IOException
|
||||||
{
|
{
|
||||||
expect('[');
|
expect('[');
|
||||||
ImapList list = new ImapList();
|
ImapList list = new ImapList();
|
||||||
|
parent.add(list);
|
||||||
Object token;
|
Object token;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
token = parseToken();
|
token = parseToken(list);
|
||||||
if (token == null)
|
if (token == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (token instanceof InputStream)
|
|
||||||
{
|
|
||||||
list.add(token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (token.equals("]"))
|
else if (token.equals("]"))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (token instanceof ImapList)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
list.add(token);
|
list.add(token);
|
||||||
@ -297,14 +301,66 @@ public class ImapResponseParser
|
|||||||
* @param mListener
|
* @param mListener
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private InputStream parseLiteral() throws IOException
|
private Object parseLiteral() throws IOException
|
||||||
{
|
{
|
||||||
expect('{');
|
expect('{');
|
||||||
int size = Integer.parseInt(readStringUntil('}'));
|
int size = Integer.parseInt(readStringUntil('}'));
|
||||||
expect('\r');
|
expect('\r');
|
||||||
expect('\n');
|
expect('\n');
|
||||||
FixedLengthInputStream fixed = new FixedLengthInputStream(mIn, size);
|
|
||||||
return fixed;
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mResponse.mCallback != null)
|
||||||
|
{
|
||||||
|
FixedLengthInputStream fixed = new FixedLengthInputStream(mIn, size);
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = mResponse.mCallback.foundLiteral(mResponse, fixed);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// Pass IOExceptions through
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Catch everything else and save it for later.
|
||||||
|
mException = e;
|
||||||
|
//Log.e(K9.LOG_TAG, "parseLiteral(): Exception in callback method", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if only some of the literal data was read
|
||||||
|
int available = fixed.available();
|
||||||
|
if ((available > 0) && (available != size))
|
||||||
|
{
|
||||||
|
// If so, skip the rest
|
||||||
|
fixed.skip(fixed.available());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
int read = 0;
|
||||||
|
while (read != size)
|
||||||
|
{
|
||||||
|
int count = mIn.read(data, read, size - read);
|
||||||
|
if (count == -1)
|
||||||
|
{
|
||||||
|
throw new IOException("parseLiteral(): end of stream reached");
|
||||||
|
}
|
||||||
|
read += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(data, "US-ASCII");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -536,6 +592,7 @@ public class ImapResponseParser
|
|||||||
public class ImapResponse extends ImapList
|
public class ImapResponse extends ImapList
|
||||||
{
|
{
|
||||||
private boolean mCompleted;
|
private boolean mCompleted;
|
||||||
|
private IImapResponseCallback mCallback;
|
||||||
|
|
||||||
boolean mCommandContinuationRequested;
|
boolean mCommandContinuationRequested;
|
||||||
String mTag;
|
String mTag;
|
||||||
@ -595,4 +652,27 @@ public class ImapResponseParser
|
|||||||
return o1 == o2;
|
return o1 == o2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IImapResponseCallback
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Callback method that is called by the parser when a literal string
|
||||||
|
* is found in an IMAP response.
|
||||||
|
*
|
||||||
|
* @param response ImapResponse object with the fields that have been
|
||||||
|
* parsed up until now (excluding the literal string).
|
||||||
|
* @param literal FixedLengthInputStream that can be used to access
|
||||||
|
* the literal string.
|
||||||
|
*
|
||||||
|
* @return an Object that will be put in the ImapResponse object at the
|
||||||
|
* place of the literal string.
|
||||||
|
*
|
||||||
|
* @throws IOException passed-through if thrown by FixedLengthInputStream
|
||||||
|
* @throws Exception if something goes wrong. Parsing will be resumed
|
||||||
|
* and the exception will be thrown after the
|
||||||
|
* complete IMAP response has been parsed.
|
||||||
|
*/
|
||||||
|
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal)
|
||||||
|
throws IOException, Exception;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.net.NetworkInfo;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
|
import com.fsck.k9.FixedLengthInputStream;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.PeekableInputStream;
|
import com.fsck.k9.PeekableInputStream;
|
||||||
import com.fsck.k9.Utility;
|
import com.fsck.k9.Utility;
|
||||||
@ -50,8 +51,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
* <pre>
|
* <pre>
|
||||||
* TODO Need to start keeping track of UIDVALIDITY
|
* TODO Need to start keeping track of UIDVALIDITY
|
||||||
* TODO Need a default response handler for things like folder updates
|
* TODO Need a default response handler for things like folder updates
|
||||||
* TODO In fetch(), if we need a ImapMessage and were given
|
|
||||||
* something else we can try to do a pre-fetch first.
|
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class ImapStore extends Store
|
public class ImapStore extends Store
|
||||||
@ -1133,26 +1132,6 @@ public class ImapStore extends Store
|
|||||||
{
|
{
|
||||||
fetchFields.add("BODY.PEEK[]");
|
fetchFields.add("BODY.PEEK[]");
|
||||||
}
|
}
|
||||||
for (Object o : fp)
|
|
||||||
{
|
|
||||||
if (o != null && o instanceof Part)
|
|
||||||
{
|
|
||||||
Part part = (Part) o;
|
|
||||||
String[] parts = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
|
|
||||||
if (parts != null)
|
|
||||||
{
|
|
||||||
String partId = parts[0];
|
|
||||||
if ("TEXT".equalsIgnoreCase(partId))
|
|
||||||
{
|
|
||||||
fetchFields.add(String.format("BODY.PEEK[TEXT]<0.%d>", FETCH_BODY_SANE_SUGGESTED_SIZE));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fetchFields.add("BODY.PEEK[" + partId + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1162,11 +1141,16 @@ public class ImapStore extends Store
|
|||||||
), false);
|
), false);
|
||||||
ImapResponse response;
|
ImapResponse response;
|
||||||
int messageNumber = 0;
|
int messageNumber = 0;
|
||||||
|
|
||||||
|
ImapResponseParser.IImapResponseCallback callback = null;
|
||||||
|
if (fp.contains(FetchProfile.Item.BODY) || fp.contains(FetchProfile.Item.BODY_SANE) || fp.contains(FetchProfile.Item.ENVELOPE))
|
||||||
|
{
|
||||||
|
callback = new FetchBodyCallback(messageMap);
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
response = mConnection.readResponse();
|
response = mConnection.readResponse(callback);
|
||||||
if (K9.DEBUG)
|
|
||||||
Log.v(K9.LOG_TAG, "response for fetch: " + response + " for " + getLogId());
|
|
||||||
|
|
||||||
if (response.mTag == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))
|
if (response.mTag == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))
|
||||||
{
|
{
|
||||||
@ -1205,117 +1189,25 @@ public class ImapStore extends Store
|
|||||||
|
|
||||||
ImapMessage imapMessage = (ImapMessage) message;
|
ImapMessage imapMessage = (ImapMessage) message;
|
||||||
|
|
||||||
if (fetchList.containsKey("FLAGS"))
|
Object literal = handleFetchResponse(imapMessage, fetchList);
|
||||||
{
|
|
||||||
ImapList flags = fetchList.getKeyedList("FLAGS");
|
|
||||||
if (flags != null)
|
|
||||||
{
|
|
||||||
for (int i = 0, count = flags.size(); i < count; i++)
|
|
||||||
{
|
|
||||||
String flag = flags.getString(i);
|
|
||||||
if (flag.equalsIgnoreCase("\\Deleted"))
|
|
||||||
{
|
|
||||||
imapMessage.setFlagInternal(Flag.DELETED, true);
|
|
||||||
}
|
|
||||||
else if (flag.equalsIgnoreCase("\\Answered"))
|
|
||||||
{
|
|
||||||
imapMessage.setFlagInternal(Flag.ANSWERED, true);
|
|
||||||
}
|
|
||||||
else if (flag.equalsIgnoreCase("\\Seen"))
|
|
||||||
{
|
|
||||||
imapMessage.setFlagInternal(Flag.SEEN, true);
|
|
||||||
}
|
|
||||||
else if (flag.equalsIgnoreCase("\\Flagged"))
|
|
||||||
{
|
|
||||||
imapMessage.setFlagInternal(Flag.FLAGGED, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fetchList.containsKey("INTERNALDATE"))
|
if (literal != null)
|
||||||
{
|
{
|
||||||
Date internalDate = fetchList.getKeyedDate("INTERNALDATE");
|
|
||||||
message.setInternalDate(internalDate);
|
|
||||||
}
|
|
||||||
if (fetchList.containsKey("RFC822.SIZE"))
|
|
||||||
{
|
|
||||||
int size = fetchList.getKeyedNumber("RFC822.SIZE");
|
|
||||||
imapMessage.setSize(size);
|
|
||||||
}
|
|
||||||
if (fetchList.containsKey("BODYSTRUCTURE"))
|
|
||||||
{
|
|
||||||
ImapList bs = fetchList.getKeyedList("BODYSTRUCTURE");
|
|
||||||
if (bs != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
parseBodyStructure(bs, message, "TEXT");
|
|
||||||
}
|
|
||||||
catch (MessagingException e)
|
|
||||||
{
|
|
||||||
if (K9.DEBUG)
|
|
||||||
Log.d(K9.LOG_TAG, "Error handling message for " + getLogId(), e);
|
|
||||||
message.setBody(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fetchList.containsKey("BODY"))
|
|
||||||
{
|
|
||||||
Part part = null;
|
|
||||||
for (Object o : fp)
|
|
||||||
{
|
|
||||||
if (o instanceof Part)
|
|
||||||
{
|
|
||||||
part = (Part) o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = fetchList.getKeyIndex("BODY") + 2;
|
|
||||||
Object literal = fetchList.getObject(index);
|
|
||||||
|
|
||||||
// Check if there's an origin octet
|
|
||||||
if (literal instanceof String)
|
if (literal instanceof String)
|
||||||
{
|
|
||||||
String originOctet = (String)literal;
|
|
||||||
if (originOctet.startsWith("<"))
|
|
||||||
{
|
|
||||||
literal = fetchList.getObject(index + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream bodyStream;
|
|
||||||
if (literal instanceof InputStream)
|
|
||||||
{
|
|
||||||
bodyStream = (InputStream)literal;
|
|
||||||
}
|
|
||||||
else if (literal instanceof String)
|
|
||||||
{
|
{
|
||||||
String bodyString = (String)literal;
|
String bodyString = (String)literal;
|
||||||
|
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
||||||
if (K9.DEBUG)
|
imapMessage.parse(bodyStream);
|
||||||
Log.v(K9.LOG_TAG, "Part is a String: '" + bodyString + "' for " + getLogId());
|
}
|
||||||
|
else if (literal instanceof Integer)
|
||||||
bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
{
|
||||||
|
// All the work was done in FetchBodyCallback.foundLiteral()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
throw new MessagingException("Got FETCH response with bogus parameters");
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part != null)
|
|
||||||
{
|
|
||||||
String contentTransferEncoding = part.getHeader(
|
|
||||||
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
|
||||||
part.setBody(MimeUtility.decodeBody(bodyStream, contentTransferEncoding));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
imapMessage.parse(bodyStream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listener != null)
|
if (listener != null)
|
||||||
@ -1339,6 +1231,193 @@ public class ImapStore extends Store
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetchPart(Message message, Part part, MessageRetrievalListener listener)
|
||||||
|
throws MessagingException
|
||||||
|
{
|
||||||
|
checkOpen();
|
||||||
|
|
||||||
|
String[] parts = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
|
||||||
|
if (parts == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fetch;
|
||||||
|
String partId = parts[0];
|
||||||
|
if ("TEXT".equalsIgnoreCase(partId))
|
||||||
|
{
|
||||||
|
fetch = String.format("BODY.PEEK[TEXT]<0.%d>", FETCH_BODY_SANE_SUGGESTED_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fetch = String.format("BODY.PEEK[%s]", partId);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mConnection.sendCommand(
|
||||||
|
String.format("UID FETCH %s (UID %s)", message.getUid(), fetch),
|
||||||
|
false);
|
||||||
|
|
||||||
|
ImapResponse response;
|
||||||
|
int messageNumber = 0;
|
||||||
|
|
||||||
|
ImapResponseParser.IImapResponseCallback callback = new FetchPartCallback(part);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
response = mConnection.readResponse(callback);
|
||||||
|
|
||||||
|
if ((response.mTag == null) &&
|
||||||
|
(ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")))
|
||||||
|
{
|
||||||
|
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
|
||||||
|
String uid = fetchList.getKeyedString("UID");
|
||||||
|
|
||||||
|
if (!message.getUid().equals(uid))
|
||||||
|
{
|
||||||
|
if (K9.DEBUG)
|
||||||
|
Log.d(K9.LOG_TAG, "Did not ask for UID " + uid + " for " + getLogId());
|
||||||
|
|
||||||
|
handleUntaggedResponse(response);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (listener != null)
|
||||||
|
{
|
||||||
|
listener.messageStarted(uid, messageNumber++, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImapMessage imapMessage = (ImapMessage) message;
|
||||||
|
|
||||||
|
Object literal = handleFetchResponse(imapMessage, fetchList);
|
||||||
|
|
||||||
|
if (literal != null)
|
||||||
|
{
|
||||||
|
if (literal instanceof Body)
|
||||||
|
{
|
||||||
|
// Most of the work was done in FetchAttchmentCallback.foundLiteral()
|
||||||
|
part.setBody((Body)literal);
|
||||||
|
}
|
||||||
|
else if (literal instanceof String)
|
||||||
|
{
|
||||||
|
String bodyString = (String)literal;
|
||||||
|
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
||||||
|
|
||||||
|
String contentTransferEncoding = part.getHeader(
|
||||||
|
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
|
part.setBody(MimeUtility.decodeBody(bodyStream, contentTransferEncoding));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This shouldn't happen
|
||||||
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener != null)
|
||||||
|
{
|
||||||
|
listener.messageFinished(message, messageNumber, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handleUntaggedResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (response.more());
|
||||||
|
|
||||||
|
}
|
||||||
|
while (response.mTag == null);
|
||||||
|
}
|
||||||
|
catch (IOException ioe)
|
||||||
|
{
|
||||||
|
throw ioExceptionHandler(mConnection, ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns value of body field
|
||||||
|
private Object handleFetchResponse(ImapMessage message, ImapList fetchList) throws MessagingException
|
||||||
|
{
|
||||||
|
Object result = null;
|
||||||
|
if (fetchList.containsKey("FLAGS"))
|
||||||
|
{
|
||||||
|
ImapList flags = fetchList.getKeyedList("FLAGS");
|
||||||
|
if (flags != null)
|
||||||
|
{
|
||||||
|
for (int i = 0, count = flags.size(); i < count; i++)
|
||||||
|
{
|
||||||
|
String flag = flags.getString(i);
|
||||||
|
if (flag.equalsIgnoreCase("\\Deleted"))
|
||||||
|
{
|
||||||
|
message.setFlagInternal(Flag.DELETED, true);
|
||||||
|
}
|
||||||
|
else if (flag.equalsIgnoreCase("\\Answered"))
|
||||||
|
{
|
||||||
|
message.setFlagInternal(Flag.ANSWERED, true);
|
||||||
|
}
|
||||||
|
else if (flag.equalsIgnoreCase("\\Seen"))
|
||||||
|
{
|
||||||
|
message.setFlagInternal(Flag.SEEN, true);
|
||||||
|
}
|
||||||
|
else if (flag.equalsIgnoreCase("\\Flagged"))
|
||||||
|
{
|
||||||
|
message.setFlagInternal(Flag.FLAGGED, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchList.containsKey("INTERNALDATE"))
|
||||||
|
{
|
||||||
|
Date internalDate = fetchList.getKeyedDate("INTERNALDATE");
|
||||||
|
message.setInternalDate(internalDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchList.containsKey("RFC822.SIZE"))
|
||||||
|
{
|
||||||
|
int size = fetchList.getKeyedNumber("RFC822.SIZE");
|
||||||
|
message.setSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchList.containsKey("BODYSTRUCTURE"))
|
||||||
|
{
|
||||||
|
ImapList bs = fetchList.getKeyedList("BODYSTRUCTURE");
|
||||||
|
if (bs != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parseBodyStructure(bs, message, "TEXT");
|
||||||
|
}
|
||||||
|
catch (MessagingException e)
|
||||||
|
{
|
||||||
|
if (K9.DEBUG)
|
||||||
|
Log.d(K9.LOG_TAG, "Error handling message for " + getLogId(), e);
|
||||||
|
message.setBody(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchList.containsKey("BODY"))
|
||||||
|
{
|
||||||
|
int index = fetchList.getKeyIndex("BODY") + 2;
|
||||||
|
result = fetchList.getObject(index);
|
||||||
|
|
||||||
|
// Check if there's an origin octet
|
||||||
|
if (result instanceof String)
|
||||||
|
{
|
||||||
|
String originOctet = (String)result;
|
||||||
|
if (originOctet.startsWith("<"))
|
||||||
|
{
|
||||||
|
result = fetchList.getObject(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flag[] getPermanentFlags() throws MessagingException
|
public Flag[] getPermanentFlags() throws MessagingException
|
||||||
{
|
{
|
||||||
@ -2384,10 +2463,15 @@ public class ImapStore extends Store
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ImapResponse readResponse() throws IOException, MessagingException
|
private ImapResponse readResponse() throws IOException, MessagingException
|
||||||
|
{
|
||||||
|
return readResponse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImapResponse readResponse(ImapResponseParser.IImapResponseCallback callback) throws IOException, MessagingException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ImapResponse response = mParser.readResponse();
|
ImapResponse response = mParser.readResponse(callback);
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
||||||
|
|
||||||
@ -3369,4 +3453,61 @@ public class ImapStore extends Store
|
|||||||
{
|
{
|
||||||
List<ImapResponse> search() throws IOException, MessagingException;
|
List<ImapResponse> search() throws IOException, MessagingException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FetchBodyCallback implements ImapResponseParser.IImapResponseCallback
|
||||||
|
{
|
||||||
|
private HashMap<String, Message> mMessageMap;
|
||||||
|
|
||||||
|
FetchBodyCallback(HashMap<String, Message> mesageMap)
|
||||||
|
{
|
||||||
|
mMessageMap = mesageMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object foundLiteral(ImapResponse response,
|
||||||
|
FixedLengthInputStream literal) throws IOException, Exception
|
||||||
|
{
|
||||||
|
if (response.mTag == null &&
|
||||||
|
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))
|
||||||
|
{
|
||||||
|
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
|
||||||
|
String uid = fetchList.getKeyedString("UID");
|
||||||
|
|
||||||
|
ImapMessage message = (ImapMessage) mMessageMap.get(uid);
|
||||||
|
message.parse(literal);
|
||||||
|
|
||||||
|
// Return placeholder object
|
||||||
|
return new Integer(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FetchPartCallback implements ImapResponseParser.IImapResponseCallback
|
||||||
|
{
|
||||||
|
private Part mPart;
|
||||||
|
|
||||||
|
FetchPartCallback(Part part)
|
||||||
|
{
|
||||||
|
mPart = part;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object foundLiteral(ImapResponse response,
|
||||||
|
FixedLengthInputStream literal) throws IOException, Exception
|
||||||
|
{
|
||||||
|
if (response.mTag == null &&
|
||||||
|
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))
|
||||||
|
{
|
||||||
|
//TODO: check for correct UID
|
||||||
|
|
||||||
|
String contentTransferEncoding = mPart.getHeader(
|
||||||
|
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
||||||
|
|
||||||
|
return MimeUtility.decodeBody(literal, contentTransferEncoding);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user