1c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hupackage com.android.exchange.adapter;
2c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
3c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentProviderOperation;
4c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentResolver;
5c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentUris;
6c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentValues;
7c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.Context;
8c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.OperationApplicationException;
9c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.database.Cursor;
1036e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.os.Parcel;
11c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.os.RemoteException;
12c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.os.TransactionTooLargeException;
13c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.provider.CalendarContract;
14c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.Html;
15c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.SpannedString;
16c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.TextUtils;
17c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.util.Base64;
1836e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.util.Log;
19c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.webkit.MimeTypeMap;
20c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
21c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.internet.MimeMessage;
22c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.internet.MimeUtility;
23c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.Address;
24c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.MeetingInfo;
25c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.MessagingException;
26c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.PackedString;
27c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.Part;
28c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Account;
29c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.EmailContent;
30e544c545c9cca446538fcf402863d271f85052c2Tony Mantlerimport com.android.emailcommon.provider.EmailContent.MessageColumns;
31e544c545c9cca446538fcf402863d271f85052c2Tony Mantlerimport com.android.emailcommon.provider.EmailContent.SyncColumns;
32c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Mailbox;
33c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Policy;
34c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.ProviderUnavailableException;
35c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.AttachmentUtilities;
36c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.ConversionUtilities;
37c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.TextUtilities;
38c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.Utility;
39c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.CommandStatusException;
40c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.Eas;
41c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.utility.CalendarUtilities;
42c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.mail.utils.LogUtils;
43c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.google.common.annotations.VisibleForTesting;
44c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
45c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.ByteArrayInputStream;
46c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.IOException;
47c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.InputStream;
482f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shraunerimport java.text.ParseException;
49c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.ArrayList;
50c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.HashMap;
51c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.Map;
52c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
53c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu/**
54c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Parser for Sync on an email collection.
55c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */
56c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hupublic class EmailSyncParser extends AbstractSyncParser {
57110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert    private static final String TAG = Eas.LOG_TAG;
58c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
59e544c545c9cca446538fcf402863d271f85052c2Tony Mantler    private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = SyncColumns.SERVER_ID
60e544c545c9cca446538fcf402863d271f85052c2Tony Mantler            + "=? and " + MessageColumns.MAILBOX_KEY + "=?";
61c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
62c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final String mMailboxIdAsString;
63c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
64c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final ArrayList<EmailContent.Message>
65c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            newEmails = new ArrayList<EmailContent.Message>();
66c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final ArrayList<EmailContent.Message> fetchedEmails =
67c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            new ArrayList<EmailContent.Message>();
68c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final ArrayList<Long> deletedEmails = new ArrayList<Long>();
69c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>();
70c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
71c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0;
72c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1;
73c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private static final String[] MESSAGE_ID_SUBJECT_PROJECTION =
74e544c545c9cca446538fcf402863d271f85052c2Tony Mantler            new String[] { MessageColumns._ID, MessageColumns.SUBJECT };
75c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
76c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
77c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    static final int LAST_VERB_REPLY = 1;
78c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
79c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    static final int LAST_VERB_REPLY_ALL = 2;
80c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
81c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    static final int LAST_VERB_FORWARD = 3;
82c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
83c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final Policy mPolicy;
8436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert
8536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    // Max times to retry when we get a TransactionTooLargeException exception
8636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    private static final int MAX_RETRIES = 10;
8736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert
8836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    // Max number of ops per batch. It could end up more than this but once we detect we are at or
8936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    // above this number, we flush.
9036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    private static final int MAX_OPS_PER_BATCH = 50;
9136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert
92c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private boolean mFetchNeeded = false;
93c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
94c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private final Map<String, Integer> mMessageUpdateStatus = new HashMap();
95c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
96c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public EmailSyncParser(final Context context, final ContentResolver resolver,
97c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            final InputStream in, final Mailbox mailbox, final Account account)
98c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            throws IOException {
99c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        super(context, resolver, in, mailbox, account);
100c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        mMailboxIdAsString = Long.toString(mMailbox.mId);
101c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (mAccount.mPolicyKey != 0) {
102c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
103c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        } else {
104c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mPolicy = null;
105c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
106c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
107c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
1080756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon    public EmailSyncParser(final Parser parser, final Context context,
1090756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon            final ContentResolver resolver, final Mailbox mailbox, final Account account)
1100756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon                    throws IOException {
1110756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon        super(parser, context, resolver, mailbox, account);
1120756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon        mMailboxIdAsString = Long.toString(mMailbox.mId);
1130756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon        if (mAccount.mPolicyKey != 0) {
1140756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon            mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
1150756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon        } else {
1160756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon            mPolicy = null;
1170756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon        }
1180756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon    }
1190756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon
120eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu    public EmailSyncParser(final Context context, final InputStream in, final Mailbox mailbox,
121eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu            final Account account) throws IOException {
122eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu        this(context, context.getContentResolver(), in, mailbox, account);
123eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu    }
124eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu
125c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public boolean fetchNeeded() {
126c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return mFetchNeeded;
127c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
128c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
129c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public Map<String, Integer> getMessageStatuses() {
130c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return mMessageUpdateStatus;
131c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
132c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
1330756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon    public void addData(EmailContent.Message msg, int endingTag) throws IOException {
134c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        ArrayList<EmailContent.Attachment> atts = new ArrayList<EmailContent.Attachment>();
135c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        boolean truncated = false;
136c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
137c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(endingTag) != END) {
138c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
139c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ATTACHMENTS:
140c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up
1414d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner                    attachmentsParser(atts, msg, tag);
142c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
143c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_TO:
144a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux                    msg.mTo = Address.toString(Address.parse(getValue()));
145c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
146c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_FROM:
147c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    Address[] froms = Address.parse(getValue());
148c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (froms != null && froms.length > 0) {
149c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mDisplayName = froms[0].toFriendly();
150c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
151a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux                    msg.mFrom = Address.toString(froms);
152c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
153c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_CC:
154a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux                    msg.mCc = Address.toString(Address.parse(getValue()));
155c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
156c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_REPLY_TO:
157a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux                    msg.mReplyTo = Address.toString(Address.parse(getValue()));
158c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
159c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_DATE_RECEIVED:
1602f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                    try {
1612f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                        msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue());
1622f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                    } catch (ParseException e) {
1632f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                        LogUtils.w(TAG, "Parse error for EMAIL_DATE_RECEIVED tag.", e);
1642f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                    }
165c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
166c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_SUBJECT:
167c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mSubject = getValue();
168c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
169c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_READ:
170c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mFlagRead = getValueInt() == 1;
171c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
172c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_BODY:
173c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    bodyParser(msg);
174c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
175c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_FLAG:
176c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mFlagFavorite = flagParser();
177c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
178c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_MIME_TRUNCATED:
179c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    truncated = getValueInt() == 1;
180c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
181c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_MIME_DATA:
182c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    // We get MIME data for EAS 2.5.  First we parse it, then we take the
183c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    // html and/or plain text data and store it in the message
184c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (truncated) {
185c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // If the MIME data is truncated, don't bother parsing it, because
186c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // it will take time and throw an exception anyway when EOF is reached
187c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // In this case, we will load the body separately by tagging the message
188c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // "partially loaded".
189c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // Get the data (and ignore it)
190c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        getValue();
191c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        userLog("Partially loaded: ", msg.mServerId);
192c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL;
193c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        mFetchNeeded = true;
194c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } else {
195c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        mimeBodyParser(msg, getValue());
196c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
197c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
198c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_BODY:
199c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    String text = getValue();
200c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mText = text;
201c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
202c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_MESSAGE_CLASS:
203c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    String messageClass = getValue();
204c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (messageClass.equals("IPM.Schedule.Meeting.Request")) {
205c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_INVITE;
206c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) {
207c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_CANCEL;
208c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
209c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
210c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_MEETING_REQUEST:
211c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    meetingRequestParser(msg);
212c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
213c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_THREAD_TOPIC:
214c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mThreadTopic = getValue();
215c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
216c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.RIGHTS_LICENSE:
217c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipParser(tag);
218c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
219c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL2_CONVERSATION_ID:
220c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mServerConversationId =
221c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            Base64.encodeToString(getValueBytes(), Base64.URL_SAFE);
222c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
223c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL2_CONVERSATION_INDEX:
224c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    // Ignore this byte array since we're not constructing a tree.
225c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    getValueBytes();
226c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
227c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL2_LAST_VERB_EXECUTED:
228c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    int val = getValueInt();
229c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) {
230c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // We aren't required to distinguish between reply and reply all here
231c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mFlags |= EmailContent.Message.FLAG_REPLIED_TO;
232c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } else if (val == LAST_VERB_FORWARD) {
233c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        msg.mFlags |= EmailContent.Message.FLAG_FORWARDED;
234c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
235c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
236c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
237c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
238c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
239c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
240c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
241c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (atts.size() > 0) {
242c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mAttachments = atts;
243c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
244c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
245c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if ((msg.mFlags & EmailContent.Message.FLAG_INCOMING_MEETING_MASK) != 0) {
246c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            String text = TextUtilities.makeSnippetFromHtmlText(
247c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mText != null ? msg.mText : msg.mHtml);
248c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (TextUtils.isEmpty(text)) {
249c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                // Create text for this invitation
250c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                String meetingInfo = msg.mMeetingInfo;
251c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (!TextUtils.isEmpty(meetingInfo)) {
252c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    PackedString ps = new PackedString(meetingInfo);
253c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    ContentValues values = new ContentValues();
254c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    putFromMeeting(ps, MeetingInfo.MEETING_LOCATION, values,
255c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            CalendarContract.Events.EVENT_LOCATION);
256c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    String dtstart = ps.get(MeetingInfo.MEETING_DTSTART);
257c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (!TextUtils.isEmpty(dtstart)) {
2582f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                        try {
2592f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                            final long startTime =
2602f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                                Utility.parseEmailDateTimeToMillis(dtstart);
2612f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                            values.put(CalendarContract.Events.DTSTART, startTime);
2622f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                        } catch (ParseException e) {
2632f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                            LogUtils.w(TAG, "Parse error for MEETING_DTSTART tag.", e);
2642f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner                        }
265c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
266c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values,
267c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            CalendarContract.Events.ALL_DAY);
268c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mText = CalendarUtilities.buildMessageTextFromEntityValues(
269c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            mContext, values, null);
270c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mHtml = Html.toHtml(new SpannedString(msg.mText));
271c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
272c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
273c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
274c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
275c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
276c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private static void putFromMeeting(PackedString ps, String field, ContentValues values,
277c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            String column) {
278c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String val = ps.get(field);
279c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (!TextUtils.isEmpty(val)) {
280c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            values.put(column, val);
281c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
282c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
283c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
284c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /**
285c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * Set up the meetingInfo field in the message with various pieces of information gleaned
286c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * from MeetingRequest tags.  This information will be used later to generate an appropriate
287c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * reply email if the user chooses to respond
288c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @param msg the Message being built
289c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @throws IOException
290c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
291c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private void meetingRequestParser(EmailContent.Message msg) throws IOException {
292c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        PackedString.Builder packedString = new PackedString.Builder();
293c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) {
294c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
295c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_DTSTAMP:
296c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue());
297c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
298c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_START_TIME:
299c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_DTSTART, getValue());
300c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
301c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_END_TIME:
302c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_DTEND, getValue());
303c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
304c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ORGANIZER:
305c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue());
306c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
307c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_LOCATION:
308c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_LOCATION, getValue());
309c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
310c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_GLOBAL_OBJID:
311c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_UID,
312c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            CalendarUtilities.getUidFromGlobalObjId(getValue()));
313c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
314c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_CATEGORIES:
315c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipParser(tag);
316c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
317c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_RECURRENCES:
318c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    recurrencesParser();
319c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
320c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_RESPONSE_REQUESTED:
321c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue());
322c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
323c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ALL_DAY_EVENT:
324c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (getValueInt() == 1) {
325c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        packedString.put(MeetingInfo.MEETING_ALL_DAY, "1");
326c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
327c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
328c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
329c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
330c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
331c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
332c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (msg.mSubject != null) {
333c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject);
334c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
335c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        msg.mMeetingInfo = packedString.toString();
336c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
337c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
338c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private void recurrencesParser() throws IOException {
339c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.EMAIL_RECURRENCES) != END) {
340c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
341c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_RECURRENCE:
342c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipParser(tag);
343c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
344c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
345c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
346c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
347c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
348c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
349c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
350c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /**
351c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * Parse a message from the server stream.
352c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @return the parsed Message
353c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @throws IOException
354c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
3554d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner    private EmailContent.Message addParser(final int endingTag) throws IOException, CommandStatusException {
356c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        EmailContent.Message msg = new EmailContent.Message();
357c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        msg.mAccountKey = mAccount.mId;
358c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        msg.mMailboxKey = mMailbox.mId;
359c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_COMPLETE;
360c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // Default to 1 (success) in case we don't get this tag
361c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        int status = 1;
362c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
3634d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner        while (nextTag(endingTag) != END) {
364c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
365c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_SERVER_ID:
366c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    msg.mServerId = getValue();
367c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
368c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_STATUS:
369c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    status = getValueInt();
370c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
371c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_APPLICATION_DATA:
372c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    addData(msg, tag);
373c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
374c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
375c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
376c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
377c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
378c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // For sync, status 1 = success
379c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (status != 1) {
380c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            throw new CommandStatusException(status, msg.mServerId);
381c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
382c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return msg;
383c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
384c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
385c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    // For now, we only care about the "active" state
386c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private Boolean flagParser() throws IOException {
387c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Boolean state = false;
388c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.EMAIL_FLAG) != END) {
389c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
390c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_FLAG_STATUS:
391c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    state = getValueInt() == 2;
392c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
393c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
394c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
395c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
396c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
397c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return state;
398c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
399c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
400c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private void bodyParser(EmailContent.Message msg) throws IOException {
401c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String bodyType = Eas.BODY_PREFERENCE_TEXT;
402c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String body = "";
4034d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner        while (nextTag(Tags.BASE_BODY) != END) {
404c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
405c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_TYPE:
406c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    bodyType = getValue();
407c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
408c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_DATA:
409c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    body = getValue();
410c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
411c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
412c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
413c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
414c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
415c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // We always ask for TEXT or HTML; there's no third option
416c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) {
417c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mHtml = body;
418c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        } else {
419c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mText = body;
420c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
421c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
422c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
423c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /**
424c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * Parses untruncated MIME data, saving away the text parts
425c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @param msg the message we're building
426c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @param mimeData the MIME data we've received from the server
427c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @throws IOException
428c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
429c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private static void mimeBodyParser(EmailContent.Message msg, String mimeData)
430c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            throws IOException {
431c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        try {
432c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes());
433c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // The constructor parses the message
434c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            MimeMessage mimeMessage = new MimeMessage(in);
435c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // Now process body parts & attachments
436c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            ArrayList<Part> viewables = new ArrayList<Part>();
437c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // We'll ignore the attachments, as we'll get them directly from EAS
438c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            ArrayList<Part> attachments = new ArrayList<Part>();
439c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            MimeUtility.collectParts(mimeMessage, viewables, attachments);
440c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // parseBodyFields fills in the content fields of the Body
441c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            ConversionUtilities.BodyFieldData data =
442c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    ConversionUtilities.parseBodyFields(viewables);
443c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // But we need them in the message itself for handling during commit()
444c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.setFlags(data.isQuotedReply, data.isQuotedForward);
445c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mSnippet = data.snippet;
446c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mHtml = data.htmlContent;
447c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mText = data.textContent;
448c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        } catch (MessagingException e) {
449c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // This would most likely indicate a broken stream
450c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            throw new IOException(e);
451c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
452c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
453c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
4544d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner    private void attachmentsParser(final ArrayList<EmailContent.Attachment> atts,
4554d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner            final EmailContent.Message msg, final int endingTag) throws IOException {
4564d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner        while (nextTag(endingTag) != END) {
457c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
458c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ATTACHMENT:
459c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_ATTACHMENT:  // BASE_ATTACHMENT is used in EAS 12.0 and up
4604d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner                    attachmentParser(atts, msg, tag);
461c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
462c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
463c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
464c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
465c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
466c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
467c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
4684d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner    private void attachmentParser(final ArrayList<EmailContent.Attachment> atts,
4694d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner            final EmailContent.Message msg, final int endingTag) throws IOException {
470c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String fileName = null;
471c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String length = null;
472c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String location = null;
473c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        boolean isInline = false;
474c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String contentId = null;
475c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
4764d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner        while (nextTag(endingTag) != END) {
477c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
478c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                // We handle both EAS 2.5 and 12.0+ attachments here
479c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_DISPLAY_NAME:
480c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_DISPLAY_NAME:
481c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    fileName = getValue();
482c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
483c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ATT_NAME:
484c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_FILE_REFERENCE:
485c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    location = getValue();
486c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
487c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_ATT_SIZE:
488c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_ESTIMATED_DATA_SIZE:
489c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    length = getValue();
490c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
491c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_IS_INLINE:
492c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    isInline = getValueInt() == 1;
493c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
494c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.BASE_CONTENT_ID:
495c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    contentId = getValue();
496c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
497c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
498c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
499c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
500c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
501c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
502c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if ((fileName != null) && (length != null) && (location != null)) {
503c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            EmailContent.Attachment att = new EmailContent.Attachment();
504c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mEncoding = "base64";
505c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mSize = Long.parseLong(length);
506c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mFileName = fileName;
507c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mLocation = location;
508c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mMimeType = getMimeTypeFromFileName(fileName);
509c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            att.mAccountKey = mAccount.mId;
510c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // Save away the contentId, if we've got one (for inline images); note that the
511c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // EAS docs appear to be wrong about the tags used; inline images come with
512c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10
513c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (isInline && !TextUtils.isEmpty(contentId)) {
514c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                att.mContentId = contentId;
515c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
516c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // Check if this attachment can't be downloaded due to an account policy
517c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (mPolicy != null) {
518c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (mPolicy.mDontAllowAttachments ||
519c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        (mPolicy.mMaxAttachmentSize > 0 &&
520c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                (att.mSize > mPolicy.mMaxAttachmentSize))) {
521c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    att.mFlags = EmailContent.Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD;
522c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
523c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
524c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            atts.add(att);
525c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.mFlagAttachment = true;
526c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
527c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
528c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
529c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /**
530c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * Returns an appropriate mimetype for the given file name's extension. If a mimetype
531c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * cannot be determined, {@code application/<<x>>} [where @{code <<x>> is the extension,
532c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * if it exists or {@code application/octet-stream}].
533c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * At the moment, this is somewhat lame, since many file types aren't recognized
534c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @param fileName the file name to ponder
535c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
536c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    // Note: The MimeTypeMap method currently uses a very limited set of mime types
537c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    // A bug has been filed against this issue.
538c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public String getMimeTypeFromFileName(String fileName) {
539c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String mimeType;
540c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        int lastDot = fileName.lastIndexOf('.');
541c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String extension = null;
542c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if ((lastDot > 0) && (lastDot < fileName.length() - 1)) {
543c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            extension = fileName.substring(lastDot + 1).toLowerCase();
544c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
545c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (extension == null) {
546c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // A reasonable default for now.
547c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mimeType = "application/octet-stream";
548c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        } else {
549c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
550c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (mimeType == null) {
551c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                mimeType = "application/" + extension;
552c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
553c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
554c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return mimeType;
555c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
556c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
557c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private Cursor getServerIdCursor(String serverId, String[] projection) {
558c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Cursor c = mContentResolver.query(EmailContent.Message.CONTENT_URI, projection,
559c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                WHERE_SERVER_ID_AND_MAILBOX_KEY, new String[] {serverId, mMailboxIdAsString},
560c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                null);
561c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (c == null) throw new ProviderUnavailableException();
562c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (c.getCount() > 1) {
563c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            userLog("Multiple messages with the same serverId/mailbox: " + serverId);
564c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
565c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return c;
566c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
567c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
568c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
569c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException {
570c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(entryTag) != END) {
571c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
572c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_SERVER_ID:
573c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    String serverId = getValue();
574c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    // Find the message in this mailbox with the given serverId
575c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION);
576c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    try {
577c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        if (c.moveToFirst()) {
578c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN));
579c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            if (Eas.USER_LOG) {
580c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                userLog("Deleting ", serverId + ", "
581c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                        + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN));
582c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            }
583c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        }
584c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } finally {
585c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        c.close();
586c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
587c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
588c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
589c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
590c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
591c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
592c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
593c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
594c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
595c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    class ServerChange {
596c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        final long id;
597c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        final Boolean read;
598c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        final Boolean flag;
599c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        final Integer flags;
600c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
601c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        ServerChange(long _id, Boolean _read, Boolean _flag, Integer _flags) {
602c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            id = _id;
603c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            read = _read;
604c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            flag = _flag;
605c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            flags = _flags;
606c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
607c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
608c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
609c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @VisibleForTesting
610c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    void changeParser(ArrayList<ServerChange> changes) throws IOException {
611c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String serverId = null;
612c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Boolean oldRead = false;
613c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Boolean oldFlag = false;
614c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        int flags = 0;
615c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        long id = 0;
616c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.SYNC_CHANGE) != END) {
617c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
618c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_SERVER_ID:
619c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    serverId = getValue();
620c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    Cursor c = getServerIdCursor(serverId, EmailContent.Message.LIST_PROJECTION);
621c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    try {
622c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        if (c.moveToFirst()) {
623c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            userLog("Changing ", serverId);
624c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            oldRead = c.getInt(EmailContent.Message.LIST_READ_COLUMN)
625c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                    == EmailContent.Message.READ;
626c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            oldFlag = c.getInt(EmailContent.Message.LIST_FAVORITE_COLUMN) == 1;
627c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            flags = c.getInt(EmailContent.Message.LIST_FLAGS_COLUMN);
628c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            id = c.getLong(EmailContent.Message.LIST_ID_COLUMN);
629c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        }
630c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } finally {
631c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        c.close();
632c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
633c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
634c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.SYNC_APPLICATION_DATA:
635c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    changeApplicationDataParser(changes, oldRead, oldFlag, flags, id);
636c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
637c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
638c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
639c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
640c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
641c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
642c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
643c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead,
644c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            Boolean oldFlag, int oldFlags, long id) throws IOException {
645c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Boolean read = null;
646c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Boolean flag = null;
647c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        Integer flags = null;
648c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
649c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            switch (tag) {
650c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_READ:
651c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    read = getValueInt() == 1;
652c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
653c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL_FLAG:
654c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    flag = flagParser();
655c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
656c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                case Tags.EMAIL2_LAST_VERB_EXECUTED:
657c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    int val = getValueInt();
658c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    // Clear out the old replied/forward flags and add in the new flag
659c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    flags = oldFlags & ~(EmailContent.Message.FLAG_REPLIED_TO
660c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                            | EmailContent.Message.FLAG_FORWARDED);
661c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) {
662c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // We aren't required to distinguish between reply and reply all here
663c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        flags |= EmailContent.Message.FLAG_REPLIED_TO;
664c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    } else if (val == LAST_VERB_FORWARD) {
665c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        flags |= EmailContent.Message.FLAG_FORWARDED;
666c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
667c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    break;
668c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                default:
669c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    skipTag();
670c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
671c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
672c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // See if there are flag changes re: read, flag (favorite) or replied/forwarded
673c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (((read != null) && !oldRead.equals(read)) ||
674c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ((flag != null) && !oldFlag.equals(flag)) || (flags != null)) {
675c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            changes.add(new ServerChange(id, read, flag, flags));
676c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
677c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
678c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
679c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /* (non-Javadoc)
680c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @see com.android.exchange.adapter.EasContentParser#commandsParser()
681c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
682c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @Override
683c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public void commandsParser() throws IOException, CommandStatusException {
684c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.SYNC_COMMANDS) != END) {
685c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (tag == Tags.SYNC_ADD) {
6864d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner                newEmails.add(addParser(tag));
687c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) {
688c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                deleteParser(deletedEmails, tag);
689c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else if (tag == Tags.SYNC_CHANGE) {
690c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                changeParser(changedEmails);
691c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else
692c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                skipTag();
693c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
694c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
695c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
696c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    // EAS values for status element of sync responses.
697c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    // TODO: Not all are used yet, but I wanted to transcribe all possible values.
698c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_SUCCESS = 1;
699c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_BAD_SYNC_KEY = 3;
700c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_PROTOCOL_ERROR = 4;
701c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_SERVER_ERROR = 5;
702c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_BAD_CLIENT_DATA = 6;
703c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_CONFLICT = 7;
704c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_OBJECT_NOT_FOUND = 8;
705c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_CANNOT_COMPLETE = 9;
706c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_FOLDER_SYNC_NEEDED = 12;
707c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_INCOMPLETE_REQUEST = 13;
708c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_BAD_HEARTBEAT_VALUE = 14;
709c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_TOO_MANY_COLLECTIONS = 15;
710c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static final int EAS_SYNC_STATUS_RETRY = 16;
711c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
712c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public static boolean shouldRetry(final int status) {
713c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        return status == EAS_SYNC_STATUS_SERVER_ERROR || status == EAS_SYNC_STATUS_RETRY;
714c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
715c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
716c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    /**
717c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * Parse the status for a single message update.
718c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @param endTag the tag we end with
719c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     * @throws IOException
720c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu     */
721c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public void messageUpdateParser(int endTag) throws IOException {
722c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // We get serverId and status in the responses
723c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        String serverId = null;
724c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        int status = -1;
725c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(endTag) != END) {
726c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (tag == Tags.SYNC_STATUS) {
727c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                status = getValueInt();
728c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else if (tag == Tags.SYNC_SERVER_ID) {
729c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                serverId = getValue();
730c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else {
731c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                skipTag();
732c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
733c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
734c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (serverId != null && status != -1) {
735c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mMessageUpdateStatus.put(serverId, status);
736c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
737c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
738c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
739c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @Override
740c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    public void responsesParser() throws IOException {
741c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        while (nextTag(Tags.SYNC_RESPONSES) != END) {
742c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) {
743c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                messageUpdateParser(tag);
744c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } else if (tag == Tags.SYNC_FETCH) {
745c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                try {
7464d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner                    fetchedEmails.add(addParser(tag));
747c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                } catch (CommandStatusException sse) {
748c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    if (sse.mStatus == 8) {
749c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // 8 = object not found; delete the message from EmailProvider
750c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // No other status should be seen in a fetch response, except, perhaps,
751c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // for some temporary server failure
752c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        mContentResolver.delete(EmailContent.Message.CONTENT_URI,
753c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                WHERE_SERVER_ID_AND_MAILBOX_KEY,
754c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                new String[] {sse.mItemId, mMailboxIdAsString});
755c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
756c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
757c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
758c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
759c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
760c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
761722006c7b116240d9383c84d5e76ee69796dd571Alon Albert    @Override
762722006c7b116240d9383c84d5e76ee69796dd571Alon Albert    protected void wipe() {
76302a024ed34fc81370961d439159ef4e84929cac8Jay Shrauner        LogUtils.i(TAG, "Wiping mailbox %s", mMailbox);
7645cc7ea3e24f1c05f71a3223ac6fa8b69d211735cYu Ping Hu        Mailbox.resyncMailbox(mContentResolver, new android.accounts.Account(mAccount.mEmailAddress,
7655cc7ea3e24f1c05f71a3223ac6fa8b69d211735cYu Ping Hu                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mMailbox.mId);
766722006c7b116240d9383c84d5e76ee69796dd571Alon Albert    }
767722006c7b116240d9383c84d5e76ee69796dd571Alon Albert
76871cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu    @Override
76971cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu    public boolean parse() throws IOException, CommandStatusException {
77071cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu        final boolean result = super.parse();
77171cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu        return result || fetchNeeded();
77271cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu    }
77371cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu
77436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    /**
77536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * Commit all changes. This results in a Binder IPC call which has constraint on the size of
77636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * the data, the docs say it currently 1MB. We set a limit to the size of the message we fetch
77736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * with {@link Eas#EAS12_TRUNCATION_SIZE} & {@link Eas#EAS12_TRUNCATION_SIZE} which are at 200k
77836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * or bellow. As long as these limits are bellow 500k, we should be able to apply a single
77936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * message (the transaction size is about double the message size because Java strings are 16
78036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * bit.
78136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * <b/>
78236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * We first try to apply the changes in normal chunk size {@link #MAX_OPS_PER_BATCH}. If we get
78336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * a {@link TransactionTooLargeException} we try again with but this time, we apply each change
78436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     * immediately.
78536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert     */
786c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    @Override
78736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    public void commit() throws RemoteException, OperationApplicationException {
78836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        try {
78936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            commitImpl(MAX_OPS_PER_BATCH);
79036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        } catch (TransactionTooLargeException e) {
79136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            // Try again but apply batch after every message. The max message size defined in
79236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            // Eas.EAS12_TRUNCATION_SIZE or Eas.EAS2_5_TRUNCATION_SIZE is small enough to fit
79336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            // in a single Binder call.
79436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            LogUtils.w(TAG, "Transaction too large, retrying in single mode", e);
79536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            commitImpl(1);
79636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        }
797c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
798c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
79936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    public void commitImpl(int maxOpsPerBatch)
80036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            throws RemoteException, OperationApplicationException {
801c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // Use a batch operation to handle the changes
802c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
803c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
804c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // Maximum size of message text per fetch
805c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        int numFetched = fetchedEmails.size();
80636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        LogUtils.d(TAG, "commitImpl: maxOpsPerBatch=%d numFetched=%d numNew=%d "
80736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                + "numDeleted=%d numChanged=%d",
80836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                maxOpsPerBatch,
80936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                numFetched,
81036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                newEmails.size(),
81136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                deletedEmails.size(),
81236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                changedEmails.size());
813c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        for (EmailContent.Message msg: fetchedEmails) {
814c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // Find the original message's id (by serverId and mailbox)
815c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION);
816c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            String id = null;
817c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            try {
818c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (c.moveToFirst()) {
819c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    id = c.getString(EmailContent.ID_PROJECTION_COLUMN);
820c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    while (c.moveToNext()) {
821c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        // This shouldn't happen, but clean up if it does
822c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        Long dupId =
823c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                Long.parseLong(c.getString(EmailContent.ID_PROJECTION_COLUMN));
824c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        userLog("Delete duplicate with id: " + dupId);
825c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        deletedEmails.add(dupId);
826c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    }
827c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
828c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            } finally {
829c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                c.close();
830c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
831c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
832c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // If we find one, we do two things atomically: 1) set the body text for the
833c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // message, and 2) mark the message loaded (i.e. completely loaded)
834c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            if (id != null) {
83571cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu                LogUtils.i(TAG, "Fetched body successfully for %s", id);
836c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                final String[] bindArgument = new String[] {id};
837c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ops.add(ContentProviderOperation.newUpdate(EmailContent.Body.CONTENT_URI)
83871cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu                        .withSelection(EmailContent.Body.SELECTION_BY_MESSAGE_KEY, bindArgument)
839e544c545c9cca446538fcf402863d271f85052c2Tony Mantler                        .withValue(EmailContent.BodyColumns.TEXT_CONTENT, msg.mText)
840c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        .build());
841c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ops.add(ContentProviderOperation.newUpdate(EmailContent.Message.CONTENT_URI)
842e544c545c9cca446538fcf402863d271f85052c2Tony Mantler                        .withSelection(MessageColumns._ID + "=?", bindArgument)
843e544c545c9cca446538fcf402863d271f85052c2Tony Mantler                        .withValue(MessageColumns.FLAG_LOADED,
844c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                                EmailContent.Message.FLAG_LOADED_COMPLETE)
845c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        .build());
846c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
84736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            applyBatchIfNeeded(ops, maxOpsPerBatch, false);
848c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
849c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
850c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        for (EmailContent.Message msg: newEmails) {
851c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            msg.addSaveOps(ops);
85236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            applyBatchIfNeeded(ops, maxOpsPerBatch, false);
853c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
854c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
855c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        for (Long id : deletedEmails) {
856c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            ops.add(ContentProviderOperation.newDelete(
857c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, id)).build());
858c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
85936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            applyBatchIfNeeded(ops, maxOpsPerBatch, false);
860c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
861c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
862c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        if (!changedEmails.isEmpty()) {
863c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            // Server wins in a conflict...
864c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            for (ServerChange change : changedEmails) {
865c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ContentValues cv = new ContentValues();
866c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (change.read != null) {
867c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    cv.put(EmailContent.MessageColumns.FLAG_READ, change.read);
868c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
869c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (change.flag != null) {
870c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    cv.put(EmailContent.MessageColumns.FLAG_FAVORITE, change.flag);
871c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
872c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                if (change.flags != null) {
873c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                    cv.put(EmailContent.MessageColumns.FLAGS, change.flags);
874c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                }
875c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ops.add(ContentProviderOperation.newUpdate(
876c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, change.id))
877c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        .withValues(cv)
878c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                        .build());
879c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            }
88036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            applyBatchIfNeeded(ops, maxOpsPerBatch, false);
881c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
882c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
883c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        // We only want to update the sync key here
884c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        ContentValues mailboxValues = new ContentValues();
885c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey);
886c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        ops.add(ContentProviderOperation.newUpdate(
887c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId))
888c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu                .withValues(mailboxValues).build());
889c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu
89036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        applyBatchIfNeeded(ops, maxOpsPerBatch, true);
89136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey);
89236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    }
89336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert
89436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    // Check if there at least MAX_OPS_PER_BATCH ops in queue and flush if there are.
89536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    // If force is true, flush regardless of size.
89636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert    private void applyBatchIfNeeded(ArrayList<ContentProviderOperation> ops, int maxOpsPerBatch,
89736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            boolean force)
89836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            throws RemoteException, OperationApplicationException {
89936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert        if (force ||  ops.size() >= maxOpsPerBatch) {
90036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            // STOPSHIP Remove calculating size of data before ship
90136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
90236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                final Parcel parcel = Parcel.obtain();
90336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                for (ContentProviderOperation op : ops) {
90436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                    op.writeToParcel(parcel, 0);
90536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                }
90636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                Log.d(TAG, String.format("Committing %d ops total size=%d",
90736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                        ops.size(), parcel.dataSize()));
90836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert                parcel.recycle();
90936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            }
910c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu            mContentResolver.applyBatch(EmailContent.AUTHORITY, ops);
91136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert            ops.clear();
912c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu        }
913c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu    }
9140756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon}
915