EmailSyncAdapter.java revision 134346f5b886e6b53074238546653cdc76bbe868
1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 20068e073ffcb68e06785929208d6a6761b29030f3Marc Blankimport com.android.email.LegacyConversions; 218e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Predaimport com.android.email.Utility; 2267698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.mail.Address; 235c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.MeetingInfo; 244f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.MessagingException; 255c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.PackedString; 264f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.Part; 274f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.internet.MimeMessage; 28068e073ffcb68e06785929208d6a6761b29030f3Marc Blankimport com.android.email.mail.internet.MimeUtility; 291f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blankimport com.android.email.provider.AttachmentProvider; 30c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent; 31134346f5b886e6b53074238546653cdc76bbe868Marc Blankimport com.android.email.provider.EmailProvider; 32894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadlerimport com.android.email.provider.EmailContent.Account; 33c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent.AccountColumns; 3467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment; 35c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blankimport com.android.email.provider.EmailContent.Body; 3667698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox; 3767698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message; 3867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MessageColumns; 3967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.SyncColumns; 40a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blankimport com.android.email.service.MailService; 4100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas; 4200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService; 434471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.MessageMoveRequest; 44c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport com.android.exchange.utility.CalendarUtilities; 45ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 46ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentProviderOperation; 47ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver; 48ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentUris; 49ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues; 50ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.OperationApplicationException; 51ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor; 5277424af660458104b732bdcb718874b17d0cab3aMarc Blankimport android.net.Uri; 53ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.os.RemoteException; 5400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport android.webkit.MimeTypeMap; 55ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 564f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport java.io.ByteArrayInputStream; 5700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 5800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 5900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 6036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar; 6100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar; 6200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone; 63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 64ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 65ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email 66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter { 6900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 7091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_READ_COLUMN = 0; 7191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_MAILBOX_KEY_COLUMN = 1; 7291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_SERVER_ID_COLUMN = 2; 7336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private static final int UPDATES_FLAG_COLUMN = 3; 7491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final String[] UPDATES_PROJECTION = 7536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID, 7636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank MessageColumns.FLAG_FAVORITE}; 7749c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank 7849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 7949c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 8018e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 8118e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank new String[] { Message.RECORD_ID, MessageColumns.SUBJECT }; 8218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank 83c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?"; 84a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank private static final String WHERE_MAILBOX_KEY_AND_MOVED = 85a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank MessageColumns.MAILBOX_KEY + "=? AND (" + MessageColumns.FLAGS + "&" + 86a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE + ")!=0"; 874f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String[] FETCH_REQUEST_PROJECTION = 884f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {EmailContent.RECORD_ID, SyncColumns.SERVER_ID}; 894f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_RECORD_ID = 0; 904f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_SERVER_ID = 1; 914f15001bdfd11c79524b4e44d60041967779e763Marc Blank 924f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 9391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 94c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank String[] mBindArguments = new String[2]; 95c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank String[] mBindArgument = new String[1]; 96ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 974f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); 984f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); 994f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<FetchRequest> mFetchRequestList = new ArrayList<FetchRequest>(); 1004f15001bdfd11c79524b4e44d60041967779e763Marc Blank private boolean mFetchNeeded = false; 101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1028efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Holds the parser's value for isLooping() 1038efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean mIsLooping = false; 1048efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 1056f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public EmailSyncAdapter(EasSyncService service) { 1066f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank super(service); 1076f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank } 1086f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank 1096f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank @Override 1106f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public void wipe() { 1116f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1126f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1136f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.DELETED_CONTENT_URI, 1146f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1156f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.UPDATED_CONTENT_URI, 1166f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 117134346f5b886e6b53074238546653cdc76bbe868Marc Blank // Delete attachments... 118134346f5b886e6b53074238546653cdc76bbe868Marc Blank AttachmentProvider.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId); 119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 120ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1214f15001bdfd11c79524b4e44d60041967779e763Marc Blank private String getEmailFilter() { 1224f15001bdfd11c79524b4e44d60041967779e763Marc Blank switch (mAccount.mSyncLookback) { 1234f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_DAY: 1244f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_DAY; 1254f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_3_DAYS: 1264f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_3_DAYS; 1274f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_WEEK: 1284f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1294f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_2_WEEKS: 1304f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_2_WEEKS; 1314f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_MONTH: 1324f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_MONTH; 1334f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_ALL: 1344f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_ALL; 1354f15001bdfd11c79524b4e44d60041967779e763Marc Blank default: 1364f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1374f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1384f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1394f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1404f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 1414f15001bdfd11c79524b4e44d60041967779e763Marc Blank * Holder for fetch request information (record id and server id) 1424f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 1434f15001bdfd11c79524b4e44d60041967779e763Marc Blank static class FetchRequest { 1444f15001bdfd11c79524b4e44d60041967779e763Marc Blank final long messageId; 1454f15001bdfd11c79524b4e44d60041967779e763Marc Blank final String serverId; 1464f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1474f15001bdfd11c79524b4e44d60041967779e763Marc Blank FetchRequest(long _messageId, String _serverId) { 1484f15001bdfd11c79524b4e44d60041967779e763Marc Blank messageId = _messageId; 1494f15001bdfd11c79524b4e44d60041967779e763Marc Blank serverId = _serverId; 1504f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1514f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1524f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1534f15001bdfd11c79524b4e44d60041967779e763Marc Blank @Override 1544f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void sendSyncOptions(Double protocolVersion, Serializer s) 1554f15001bdfd11c79524b4e44d60041967779e763Marc Blank throws IOException { 1564f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.clear(); 1574f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find partially loaded messages; this should typically be a rare occurrence 1584f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, 1594f15001bdfd11c79524b4e44d60041967779e763Marc Blank FETCH_REQUEST_PROJECTION, 1604f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " + 1614f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.MAILBOX_KEY + "=?", 1624f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {Long.toString(mMailbox.mId)}, null); 1634f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 1644f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Put all of these messages into a list; we'll need both id and server id 1654f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (c.moveToNext()) { 1664f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.add(new FetchRequest(c.getLong(FETCH_REQUEST_RECORD_ID), 1674f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.getString(FETCH_REQUEST_SERVER_ID))); 1684f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1694f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 1704f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 1714f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1724f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1734f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The "empty" case is typical; we send a request for changes, and also specify a sync 1744f15001bdfd11c79524b4e44d60041967779e763Marc Blank // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and 1754f15001bdfd11c79524b4e44d60041967779e763Marc Blank // truncation 1764f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there are fetch requests, we only want the fetches (i.e. no changes from the server) 1774f15001bdfd11c79524b4e44d60041967779e763Marc Blank // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch 1784f15001bdfd11c79524b4e44d60041967779e763Marc Blank // requests 1794f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchRequestList.isEmpty()) { 1804f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_DELETES_AS_MOVES); 1814f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 1824f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_WINDOW_SIZE, EMAIL_WINDOW_SIZE); 1834f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 1844f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the lookback appropriately (EAS calls this a "filter") 1854f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter()); 1864f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the truncation amount for all classes 1874f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 1884f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.BASE_BODY_PREFERENCE); 1894f15001bdfd11c79524b4e44d60041967779e763Marc Blank // HTML for email 1904f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); 1914f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); 1924f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 1934f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 1944f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Use MIME data for EAS 2.5 1954f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); 1964f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 1974f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1984f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 1994f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2004f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2014f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Ask for plain text, rather than MIME data. This guarantees that we'll get a usable 2024f15001bdfd11c79524b4e44d60041967779e763Marc Blank // text body 2034f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); 2044f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2054f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2064f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2074f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2084f15001bdfd11c79524b4e44d60041967779e763Marc Blank 209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 21048af7392c82262d17700e3fbdccf3a582809d449Marc Blank public boolean parse(InputStream is) throws IOException { 21148af7392c82262d17700e3fbdccf3a582809d449Marc Blank EasEmailSyncParser p = new EasEmailSyncParser(is, this); 2124f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = false; 2138efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean res = p.parse(); 2148efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Hold on to the parser's value for isLooping() to pass back to the service 2158efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mIsLooping = p.isLooping(); 2164f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we've need a body fetch, or we've just finished one, return true in order to continue 2174f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchNeeded || !mFetchRequestList.isEmpty()) { 2184f15001bdfd11c79524b4e44d60041967779e763Marc Blank return true; 2194f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2208efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return res; 2218efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 2228efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 2238efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank /** 2248efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank * Return the value of isLooping() as returned from the parser 2258efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank */ 2268efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank @Override 2278efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 2288efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mIsLooping; 229ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 230147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 231aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank @Override 232aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank public boolean isSyncable() { 233aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return true; 234aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 235aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 236368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank public class EasEmailSyncParser extends AbstractSyncParser { 237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 238147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = 239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 241ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String mMailboxIdAsString; 242ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 24348af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<Message> newEmails = new ArrayList<Message>(); 2444f15001bdfd11c79524b4e44d60041967779e763Marc Blank ArrayList<Message> fetchedEmails = new ArrayList<Message>(); 24548af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<Long> deletedEmails = new ArrayList<Long>(); 24648af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 24748af7392c82262d17700e3fbdccf3a582809d449Marc Blank 24848af7392c82262d17700e3fbdccf3a582809d449Marc Blank public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException { 24948af7392c82262d17700e3fbdccf3a582809d449Marc Blank super(in, adapter); 250ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 251ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 252ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 253ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void addData (Message msg) throws IOException { 254ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<Attachment> atts = new ArrayList<Attachment>(); 2554f15001bdfd11c79524b4e44d60041967779e763Marc Blank boolean truncated = false; 256ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2577c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 258ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 2597c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATTACHMENTS: 2605ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 261d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank attachmentsParser(atts, msg); 262ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2637c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_TO: 26467698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mTo = Address.pack(Address.parse(getValue())); 265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2667c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FROM: 267705a309bd78efd77469ac90a57849619de3317e3Mihai Preda Address[] froms = Address.parse(getValue()); 268705a309bd78efd77469ac90a57849619de3317e3Mihai Preda if (froms != null && froms.length > 0) { 2694f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mDisplayName = froms[0].toFriendly(); 270ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 271705a309bd78efd77469ac90a57849619de3317e3Mihai Preda msg.mFrom = Address.pack(froms); 272ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2737c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_CC: 27467698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mCc = Address.pack(Address.parse(getValue())); 275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_REPLY_TO: 27767698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mReplyTo = Address.pack(Address.parse(getValue())); 278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DATE_RECEIVED: 2808e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_SUBJECT: 283ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mSubject = getValue(); 284ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 286ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagRead = getValueInt() == 1; 287ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_BODY: 28900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyParser(msg); 29000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 2917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 292ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank msg.mFlagFavorite = flagParser(); 293ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 2944f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_TRUNCATED: 2954f15001bdfd11c79524b4e44d60041967779e763Marc Blank truncated = getValueInt() == 1; 2964f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 2974f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_DATA: 2984f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We get MIME data for EAS 2.5. First we parse it, then we take the 2994f15001bdfd11c79524b4e44d60041967779e763Marc Blank // html and/or plain text data and store it in the message 300068e073ffcb68e06785929208d6a6761b29030f3Marc Blank if (truncated) { 301068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // If the MIME data is truncated, don't bother parsing it, because 302068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // it will take time and throw an exception anyway when EOF is reached 303068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // In this case, we will load the body separately by tagging the message 304068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // "partially loaded". 3054f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Partially loaded: ", msg.mServerId); 3064f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL; 3074f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = true; 308068e073ffcb68e06785929208d6a6761b29030f3Marc Blank } else { 309068e073ffcb68e06785929208d6a6761b29030f3Marc Blank mimeBodyParser(msg, getValue()); 3104f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 3114f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 3127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_BODY: 31367698e240187c902bed123bf18d342ff25ec75c7Marc Blank String text = getValue(); 31467698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mText = text; 315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 3167531be7774769c84b499b1de5dc46da3a9468316Marc Blank case Tags.EMAIL_MESSAGE_CLASS: 3177531be7774769c84b499b1de5dc46da3a9468316Marc Blank String messageClass = getValue(); 3187531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 319888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_INVITE; 3207531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 321888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_CANCEL; 3227531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 3237531be7774769c84b499b1de5dc46da3a9468316Marc Blank break; 324c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_MEETING_REQUEST: 325c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank meetingRequestParser(msg); 326c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (atts.size() > 0) { 333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAttachments = atts; 334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 33667698e240187c902bed123bf18d342ff25ec75c7Marc Blank 3375c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 3385c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Set up the meetingInfo field in the message with various pieces of information gleaned 3395c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * from MeetingRequest tags. This information will be used later to generate an appropriate 3405c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * reply email if the user chooses to respond 3415c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the Message being built 3425c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @throws IOException 3435c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 344c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void meetingRequestParser(Message msg) throws IOException { 3455c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString.Builder packedString = new PackedString.Builder(); 346c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 347c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 3485c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_DTSTAMP: 3495c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 3505c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 351c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_START_TIME: 3525c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 3535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_END_TIME: 3555c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 3565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_ORGANIZER: 3585c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 3595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3605c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_LOCATION: 3615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 3625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_GLOBAL_OBJID: 364b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank packedString.put(MeetingInfo.MEETING_UID, 365b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank CalendarUtilities.getUidFromGlobalObjId(getValue())); 366c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 367c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_CATEGORIES: 368c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank nullParser(); 369c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 370c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCES: 371c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank recurrencesParser(); 372c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 37339fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank case Tags.EMAIL_RESPONSE_REQUESTED: 37439fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue()); 37539fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank break; 376c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 377c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 378c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 379c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 3805c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (msg.mSubject != null) { 3815c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 3825c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 3835c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank msg.mMeetingInfo = packedString.toString(); 384c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 385c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 386c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void nullParser() throws IOException { 387c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_CATEGORIES) != END) { 388c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 389c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 390c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 391c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 392c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void recurrencesParser() throws IOException { 393c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 394c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 395c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCE: 396c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank nullParser(); 397f69266d6671aa2c55fd04117a36f74dd17f73067Marc Blank break; 398c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 399c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 400c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 401c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 402c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 403c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 40400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void addParser(ArrayList<Message> emails) throws IOException { 405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message msg = new Message(); 406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAccountKey = mAccount.mId; 407ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mMailboxKey = mMailbox.mId; 408f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE; 409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 4107c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_ADD) != END) { 411ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 4127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mServerId = getValue(); 414ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4157c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_APPLICATION_DATA: 416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank addData(msg); 417ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 418ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 419ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 420ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 421ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 422ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank emails.add(msg); 423ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 424ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 425ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank // For now, we only care about the "active" state 426ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank private Boolean flagParser() throws IOException { 427ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean state = false; 4287c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_FLAG) != END) { 429ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank switch (tag) { 4307c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG_STATUS: 431ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank state = getValueInt() == 2; 432ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 433ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank default: 434ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank skipTag(); 435ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 436ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 437ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank return state; 438ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 439ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank 44000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void bodyParser(Message msg) throws IOException { 44100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String bodyType = Eas.BODY_PREFERENCE_TEXT; 44200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String body = ""; 4437c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_BODY) != END) { 44400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank switch (tag) { 4457c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_TYPE: 44600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyType = getValue(); 44700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 4487c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_DATA: 44900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank body = getValue(); 45000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 45100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank default: 45200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank skipTag(); 45300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 45400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 45500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // We always ask for TEXT or HTML; there's no third option 45600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 45700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mHtml = body; 45800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 45900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mText = body; 46000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 46100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 46200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 4634f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 464068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * Parses untruncated MIME data, saving away the text parts 4654f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param msg the message we're building 4664f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param mimeData the MIME data we've received from the server 467068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * @throws IOException 4684f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 469068e073ffcb68e06785929208d6a6761b29030f3Marc Blank private void mimeBodyParser(Message msg, String mimeData) throws IOException { 4704f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 4714f15001bdfd11c79524b4e44d60041967779e763Marc Blank ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 4724f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The constructor parses the message 473068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeMessage mimeMessage = new MimeMessage(in); 474068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // Now process body parts & attachments 475068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> viewables = new ArrayList<Part>(); 476068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // We'll ignore the attachments, as we'll get them directly from EAS 477068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> attachments = new ArrayList<Part>(); 478068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeUtility.collectParts(mimeMessage, viewables, attachments); 479068e073ffcb68e06785929208d6a6761b29030f3Marc Blank Body tempBody = new Body(); 480068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // updateBodyFields fills in the content fields of the Body 481068e073ffcb68e06785929208d6a6761b29030f3Marc Blank LegacyConversions.updateBodyFields(tempBody, msg, viewables); 482068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // But we need them in the message itself for handling during commit() 483068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mHtml = tempBody.mHtmlContent; 484068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mText = tempBody.mTextContent; 4854f15001bdfd11c79524b4e44d60041967779e763Marc Blank } catch (MessagingException e) { 486068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // This would most likely indicate a broken stream 487068e073ffcb68e06785929208d6a6761b29030f3Marc Blank throw new IOException(e); 4884f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 4894f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 4904f15001bdfd11c79524b4e44d60041967779e763Marc Blank 4915ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { 4925ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 4935ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank switch (tag) { 4945ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.EMAIL_ATTACHMENT: 4955ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 4965ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank attachmentParser(atts, msg); 4975ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank break; 4985ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank default: 4995ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank skipTag(); 5005ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 5015ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 5025ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 5035ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank 50400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { 505ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String fileName = null; 506ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String length = null; 507d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank String location = null; 508ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 5097c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 510ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5115ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank // We handle both EAS 2.5 and 12.0+ attachments here 5127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DISPLAY_NAME: 5135ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_DISPLAY_NAME: 514ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank fileName = getValue(); 515ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_NAME: 5175ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_FILE_REFERENCE: 518d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank location = getValue(); 519ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5207c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_SIZE: 5215ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ESTIMATED_DATA_SIZE: 522ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank length = getValue(); 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 526ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 5295ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((fileName != null) && (length != null) && (location != null)) { 530ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Attachment att = new Attachment(); 531ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mEncoding = "base64"; 532ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mSize = Long.parseLong(length); 533ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mFileName = fileName; 534d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.mLocation = location; 53500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank att.mMimeType = getMimeTypeFromFileName(fileName); 536ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank atts.add(att); 537ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagAttachment = true; 538ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 539ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 540ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 54100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /** 54200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * Try to determine a mime type from a file name, defaulting to application/x, where x 54300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * is either the extension or (if none) octet-stream 54400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * At the moment, this is somewhat lame, since many file types aren't recognized 54500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @param fileName the file name to ponder 54600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @return 54700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 54800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // Note: The MimeTypeMap method currently uses a very limited set of mime types 54900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A bug has been filed against this issue. 55000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public String getMimeTypeFromFileName(String fileName) { 55100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String mimeType; 55200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank int lastDot = fileName.lastIndexOf('.'); 55300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String extension = null; 5545ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 55519fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank extension = fileName.substring(lastDot + 1).toLowerCase(); 55600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 55700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (extension == null) { 55800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A reasonable default for now. 55900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/octet-stream"; 56000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 56100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 56200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (mimeType == null) { 56300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/" + extension; 56400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 56500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 56600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank return mimeType; 56700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 56800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 569ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private Cursor getServerIdCursor(String serverId, String[] projection) { 570c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[0] = serverId; 571c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[1] = mMailboxIdAsString; 572ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return mContentResolver.query(Message.CONTENT_URI, projection, 573c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments, null); 574ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 57602e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank /*package*/ void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 577048d45641b88c87172074aa5f29b3de307bc3712Marc Blank while (nextTag(entryTag) != END) { 578ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 580ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = getValue(); 581ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find the message in this mailbox with the given serverId 58218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 583ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 584ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 58549c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 58618e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank if (Eas.USER_LOG) { 58749c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank userLog("Deleting ", serverId + ", " 58849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 58918e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank } 590ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 591ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 592ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 593ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 594ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 595ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 596ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 598ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 599ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 600ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 601ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank class ServerChange { 602ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id; 603ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean read; 604ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean flag; 605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 606ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ServerChange(long _id, Boolean _read, Boolean _flag) { 607ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = _id; 608ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = _read; 609ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = _flag; 610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 61302e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank /*package*/ void changeParser(ArrayList<ServerChange> changes) throws IOException { 614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = null; 615ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldRead = false; 616ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldFlag = false; 617ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = 0; 6187c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_CHANGE) != END) { 619ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 6207c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank serverId = getValue(); 622ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION); 623ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 624ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 6250a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Changing ", serverId); 626ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ; 627ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1; 628ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = c.getLong(Message.LIST_ID_COLUMN); 629ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 630ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 631ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 632ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 633ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 634d82abe7213806e478338af4202b3622f34b5d6feMarc Blank case Tags.SYNC_APPLICATION_DATA: 635d82abe7213806e478338af4202b3622f34b5d6feMarc Blank changeApplicationDataParser(changes, oldRead, oldFlag, id); 636d82abe7213806e478338af4202b3622f34b5d6feMarc Blank break; 637d82abe7213806e478338af4202b3622f34b5d6feMarc Blank default: 638d82abe7213806e478338af4202b3622f34b5d6feMarc Blank skipTag(); 639d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 640d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 641d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 642d82abe7213806e478338af4202b3622f34b5d6feMarc Blank 643d82abe7213806e478338af4202b3622f34b5d6feMarc Blank private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 644d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean oldFlag, long id) throws IOException { 645d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean read = null; 646d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean flag = null; 647d82abe7213806e478338af4202b3622f34b5d6feMarc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 648d82abe7213806e478338af4202b3622f34b5d6feMarc Blank switch (tag) { 6497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 650ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = getValueInt() == 1; 651ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 6527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 653ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = flagParser(); 654ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 655ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 656ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 657ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 658ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 6595ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if (((read != null) && !oldRead.equals(read)) || 6605ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank ((flag != null) && !oldFlag.equals(flag))) { 661ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank changes.add(new ServerChange(id, read, flag)); 662ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 663ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 664ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 66500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /* (non-Javadoc) 66600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @see com.android.exchange.adapter.EasContentParser#commandsParser() 66700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 668147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 669ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void commandsParser() throws IOException { 6707c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_COMMANDS) != END) { 6717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_ADD) { 672ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank addParser(newEmails); 6730a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 674048d45641b88c87172074aa5f29b3de307bc3712Marc Blank } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 675048d45641b88c87172074aa5f29b3de307bc3712Marc Blank deleteParser(deletedEmails, tag); 6760a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 6777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_CHANGE) { 678ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank changeParser(changedEmails); 6790a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 680ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else 681ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 682ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 68348af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 68448af7392c82262d17700e3fbdccf3a582809d449Marc Blank 68548af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 6864f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void responsesParser() throws IOException { 6874f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (nextTag(Tags.SYNC_RESPONSES) != END) { 6884f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 6894f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We can ignore all of these 6904f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else if (tag == Tags.SYNC_FETCH) { 6914f15001bdfd11c79524b4e44d60041967779e763Marc Blank addParser(fetchedEmails); 6924f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 6934f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 69448af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 69548af7392c82262d17700e3fbdccf3a582809d449Marc Blank 69648af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 697a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler public void commit() { 69848af7392c82262d17700e3fbdccf3a582809d449Marc Blank int notifyCount = 0; 699ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 700ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Use a batch operation to handle the changes 701ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // TODO New mail notifications? Who looks for these? 702ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 7034f15001bdfd11c79524b4e44d60041967779e763Marc Blank 7044f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (Message msg: fetchedEmails) { 7054f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there's really no text, we're done here 7064f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (msg.mText == null) continue; 7074f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find the original message's id (by serverId and mailbox) 7084f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 7094f15001bdfd11c79524b4e44d60041967779e763Marc Blank String id = null; 7104f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 7114f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (c.moveToFirst()) { 7124f15001bdfd11c79524b4e44d60041967779e763Marc Blank id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 7134f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7144f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 7154f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 7164f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7174f15001bdfd11c79524b4e44d60041967779e763Marc Blank 7184f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we find one, we do two things atomically: 1) set the body text for the 7194f15001bdfd11c79524b4e44d60041967779e763Marc Blank // message, and 2) mark the message loaded (i.e. completely loaded) 7204f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (id != null) { 7214f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Fetched body successfully for ", id); 7224f15001bdfd11c79524b4e44d60041967779e763Marc Blank mBindArgument[0] = id; 7234f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI) 7244f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(Body.MESSAGE_KEY + "=?", mBindArgument) 7254f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withValue(Body.TEXT_CONTENT, msg.mText) 7264f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 7274f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Message.CONTENT_URI) 7284f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(EmailContent.RECORD_ID + "=?", mBindArgument) 729068e073ffcb68e06785929208d6a6761b29030f3Marc Blank .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) 7304f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 7314f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7324f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7334f15001bdfd11c79524b4e44d60041967779e763Marc Blank 73477424af660458104b732bdcb718874b17d0cab3aMarc Blank for (Message msg: newEmails) { 73577424af660458104b732bdcb718874b17d0cab3aMarc Blank if (!msg.mFlagRead) { 73677424af660458104b732bdcb718874b17d0cab3aMarc Blank notifyCount++; 73777424af660458104b732bdcb718874b17d0cab3aMarc Blank } 73877424af660458104b732bdcb718874b17d0cab3aMarc Blank msg.addSaveOps(ops); 739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7404f15001bdfd11c79524b4e44d60041967779e763Marc Blank 741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (Long id : deletedEmails) { 742ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newDelete( 743ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, id)).build()); 7441f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blank AttachmentProvider.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 745ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7464f15001bdfd11c79524b4e44d60041967779e763Marc Blank 747ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!changedEmails.isEmpty()) { 748ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Server wins in a conflict... 749ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (ServerChange change : changedEmails) { 750ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ContentValues cv = new ContentValues(); 751ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.read != null) { 752ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_READ, change.read); 753ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 754ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.flag != null) { 755ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_FAVORITE, change.flag); 756ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 757ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 758ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, change.id)) 759ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .withValues(cv) 760ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .build()); 761ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 762ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7638d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 7648d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We only want to update the sync key here 7658d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentValues mailboxValues = new ContentValues(); 7668d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 767ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 7688d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 7698d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank .withValues(mailboxValues).build()); 770ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7718480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 772147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 7731b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // No commits if we're stopped 7741b275b9408d5b856e2482fa3951827489e9585ccMarc Blank synchronized (mService.getSynchronizer()) { 7751b275b9408d5b856e2482fa3951827489e9585ccMarc Blank if (mService.isStopped()) return; 7761b275b9408d5b856e2482fa3951827489e9585ccMarc Blank try { 7770a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank mContentResolver.applyBatch(EmailProvider.EMAIL_AUTHORITY, ops); 7780a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 7791b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (RemoteException e) { 7801b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 7811b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (OperationApplicationException e) { 7821b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 7831b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } 784ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7858480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 78677424af660458104b732bdcb718874b17d0cab3aMarc Blank if (notifyCount > 0) { 787c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // Use the new atomic add URI in EmailProvider 788c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // We could add this to the operations being done, but it's not strictly 789c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // speaking necessary, as the previous batch preserves the integrity of the 790c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // database, whereas this is purely for notification purposes, and is itself atomic 791c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank ContentValues cv = new ContentValues(); 792c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT); 793c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount); 794c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId); 795c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank mContentResolver.update(uri, cv, null, null); 796a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blank MailService.actionNotifyNewMessages(mContext, mAccount.mId); 797894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadler } 79877424af660458104b732bdcb718874b17d0cab3aMarc Blank } 799ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 800ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 801ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 802ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getCollectionName() { 803ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 804ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 805ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8068480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank private void addCleanupOps(ArrayList<ContentProviderOperation> ops) { 8078480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // If we've sent local deletions, clear out the deleted table 8088480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mDeletedIdList) { 8098480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 8108480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build()); 8118480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8128480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // And same with the updates 8138480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mUpdatedIdList) { 8148480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 8158480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build()); 8168480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 817a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // Delete any moved messages (since we've just synced the mailbox, and no longer need the 818a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // placeholder message); this prevents duplicates from appearing in the mailbox. 819a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank mBindArgument[0] = Long.toString(mMailbox.mId); 820a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank ops.add(ContentProviderOperation.newDelete(Message.CONTENT_URI) 821a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank .withSelection(WHERE_MAILBOX_KEY_AND_MOVED, mBindArgument).build()); 8228480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8238480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 8248480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank @Override 82548af7392c82262d17700e3fbdccf3a582809d449Marc Blank public void cleanup() { 8268480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) { 8278480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 8288480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 8298480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank try { 83048af7392c82262d17700e3fbdccf3a582809d449Marc Blank mContext.getContentResolver() 8318480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops); 8328480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (RemoteException e) { 8338480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 8348480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (OperationApplicationException e) { 8358480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 8368480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8378480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8388480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8398480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 84036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private String formatTwo(int num) { 84136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (num < 10) { 84236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return "0" + (char)('0' + num); 84336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else 84436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return Integer.toString(num); 84536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 84636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 84736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank /** 84836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 84936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * a different format that excludes the punctuation (this is why I'm not putting this in a 85036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * parent class) 85136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank */ 85236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank public String formatDateTime(Calendar calendar) { 85336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank StringBuilder sb = new StringBuilder(); 85436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank //YYYY-MM-DDTHH:MM:SS.MSSZ 85536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(calendar.get(Calendar.YEAR)); 85636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 85736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1)); 85836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 85936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH))); 86036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('T'); 86136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY))); 86236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 86336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MINUTE))); 86436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 86536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.SECOND))); 86636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(".000Z"); 86736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return sb.toString(); 86836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 86936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 870c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /** 871c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Note that messages in the deleted database preserve the message's unique id; therefore, we 872c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * can utilize this id to find references to the message. The only reference situation at this 873c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * point is in the Body table; it is when sending messages via SmartForward and SmartReply 874c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 875c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private boolean messageReferenced(ContentResolver cr, long id) { 876c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument[0] = Long.toString(id); 877c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // See if this id is referenced in a body 878c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY, 879c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument, null); 880c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank try { 881c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return c.moveToFirst(); 882c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } finally { 883c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank c.close(); 8845acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 885c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 886c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 887c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /*private*/ /** 888c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Serialize commands to delete items from the server; as we find items to delete, add their 889c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * id's to the deletedId's array 890c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * 891c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param s the Serializer we're using to create post data 892c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param deletedIds ids whose deletions are being sent to the server 893c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param first whether or not this is the first command being sent 894c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @return true if SYNC_COMMANDS hasn't been sent (false otherwise) 895c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @throws IOException 896c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 897c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first) 898c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank throws IOException { 899c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 9005acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank 901ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find any of our deleted items 902ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION, 903ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 904ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We keep track of the list of deleted item id's so that we can remove them from the 905ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // deleted table after the server receives our command 906c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.clear(); 907ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 908ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 9095acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN); 9105acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 9115acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 9125acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 913c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Also check if this message is referenced elsewhere 914c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) { 915c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank userLog("Postponing deletion of referenced message: ", serverId); 916c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank continue; 9175acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } else if (first) { 9187c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 919ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank first = false; 920ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 921ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send the command to delete this message 9225acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 923c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.add(c.getLong(Message.LIST_ID_COLUMN)); 924ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 925ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 926ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 927ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 928ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 929c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return first; 930c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 931c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 932c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank @Override 933c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank public boolean sendLocalChanges(Serializer s) throws IOException { 934c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 935c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 936c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Never upsync from these folders 937c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) { 938c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return false; 939c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 940c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 941c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // This code is split out for unit testing purposes 942c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true); 943c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 9444f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (!mFetchRequestList.isEmpty()) { 9454f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Add FETCH commands for messages that need a body (i.e. we didn't find it during 9464f15001bdfd11c79524b4e44d60041967779e763Marc Blank // our earlier sync; this happens only in EAS 2.5 where the body couldn't be found 9474f15001bdfd11c79524b4e44d60041967779e763Marc Blank // after parsing the message's MIME data) 9484f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (firstCommand) { 9494f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_COMMANDS); 9504f15001bdfd11c79524b4e44d60041967779e763Marc Blank firstCommand = false; 9514f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9524f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (FetchRequest req: mFetchRequestList) { 9534f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, req.serverId).end(); 9544f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9554f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9564f15001bdfd11c79524b4e44d60041967779e763Marc Blank 95791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Find our trash mailbox, since deletions will have been moved there... 95891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank long trashMailboxId = 9590a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH); 96091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 961ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do the same now for updated items 962c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION, 963ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 96491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 96591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // We keep track of the list of updated item id's as we did above with deleted items 966ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.clear(); 967ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 968ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 969ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = c.getLong(Message.LIST_ID_COLUMN); 970ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Say we've handled this update 971ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.add(id); 972ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We have the id of the changed item. But first, we have to find out its current 973ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // state, since the updated table saves the opriginal state 974ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id), 975ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank UPDATES_PROJECTION, null, null, null); 976ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 977ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If this item no longer exists (shouldn't be possible), just move along 978ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!currentCursor.moveToFirst()) { 979ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 980ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 9815acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 9825acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN); 9835acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 9845acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 9855acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 98691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // If the message is now in the trash folder, it has been deleted by the user 98791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank if (currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN) == trashMailboxId) { 988c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 9897c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 990c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 99191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 99291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Send the command to delete this message 9935acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 99491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank continue; 99591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 99691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 99736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean flagChange = false; 99836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean readChange = false; 99936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 10004471a6960d352242cc65bddf7888cc5335840c74Marc Blank long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN); 10014471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) { 10024471a6960d352242cc65bddf7888cc5335840c74Marc Blank // The message has moved to another mailbox; add a request for this 10034471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Note: The Sync command doesn't handle moving messages, so we need 10044471a6960d352242cc65bddf7888cc5335840c74Marc Blank // to handle this as a "request" (similar to meeting response and 10054471a6960d352242cc65bddf7888cc5335840c74Marc Blank // attachment load) 10064471a6960d352242cc65bddf7888cc5335840c74Marc Blank mService.addRequest(new MessageMoveRequest(id, mailbox)); 10074471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Regardless of other changes that might be made, we don't want to indicate 10084471a6960d352242cc65bddf7888cc5335840c74Marc Blank // that this message has been updated until the move request has been 10094471a6960d352242cc65bddf7888cc5335840c74Marc Blank // handled (without this, a crash between the flag upsync and the move 10104471a6960d352242cc65bddf7888cc5335840c74Marc Blank // would cause the move to be lost) 10114471a6960d352242cc65bddf7888cc5335840c74Marc Blank mUpdatedIdList.remove(id); 10124471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 101336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 101436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // We can only send flag changes to the server in 12.0 or later 10154471a6960d352242cc65bddf7888cc5335840c74Marc Blank int flag = 0; 1016d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 101736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flag = currentCursor.getInt(UPDATES_FLAG_COLUMN); 101836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) { 101936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flagChange = true; 102036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 102136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 102236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 102391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank int read = currentCursor.getInt(UPDATES_READ_COLUMN); 102476eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank if (read != c.getInt(Message.LIST_READ_COLUMN)) { 102536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank readChange = true; 102636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 102736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 102836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (!flagChange && !readChange) { 102936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // In this case, we've got nothing to send to the server 1030ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1031ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 103236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1033c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 10347c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1035c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 1036ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 103736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Send the change to "read" and "favorite" (flagged) 10387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_CHANGE) 10397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN)) 104036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank .start(Tags.SYNC_APPLICATION_DATA); 104136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (readChange) { 104236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_READ, Integer.toString(read)); 104336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 104436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 104536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // the boolean "favorite" that we think of in Gmail, but it also represents a 104636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // follow up action, which can include a subject, start and due dates, and even 104736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 104836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // require that a flag contain a status, a type, and four date fields, two each 104936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // for start date and end (due) date. 105036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flagChange) { 105136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != 0) { 105236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Status 2 = set flag 105336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 105436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "FollowUp" is the standard type 105536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 105636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank long now = System.currentTimeMillis(); 105736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank Calendar calendar = 105836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 105936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now); 106036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Flags are required to have a start date and end date (duplicated) 106136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // First, we'll set the current date/time in GMT as the start time 106236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank String utc = formatDateTime(calendar); 106336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 106436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // And then we'll use one week from today for completion date 106536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now + 1*WEEKS); 106636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank utc = formatDateTime(calendar); 106736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 106836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end(); 106936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else { 107036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.tag(Tags.EMAIL_FLAG); 107136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 107236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 107336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 1074ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1075ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank currentCursor.close(); 1076ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1077ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1078ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1079ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1080ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1081ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1082c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (!firstCommand) { 10837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end(); // SYNC_COMMANDS 1084ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1085ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 1086ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1087ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1088