1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.provider;
18
19import android.accounts.AccountManager;
20import android.content.ContentResolver;
21import android.content.ContentValues;
22import android.content.Context;
23import android.database.Cursor;
24import android.database.SQLException;
25import android.database.sqlite.SQLiteDatabase;
26import android.database.sqlite.SQLiteDoneException;
27import android.database.sqlite.SQLiteOpenHelper;
28import android.database.sqlite.SQLiteStatement;
29import android.provider.BaseColumns;
30import android.provider.CalendarContract;
31import android.provider.ContactsContract;
32import android.text.TextUtils;
33
34import com.android.email.DebugUtils;
35import com.android.email.R;
36import com.android.emailcommon.mail.Address;
37import com.android.emailcommon.provider.Account;
38import com.android.emailcommon.provider.Credential;
39import com.android.emailcommon.provider.EmailContent;
40import com.android.emailcommon.provider.EmailContent.AccountColumns;
41import com.android.emailcommon.provider.EmailContent.Attachment;
42import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
43import com.android.emailcommon.provider.EmailContent.Body;
44import com.android.emailcommon.provider.EmailContent.BodyColumns;
45import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
46import com.android.emailcommon.provider.EmailContent.MailboxColumns;
47import com.android.emailcommon.provider.EmailContent.Message;
48import com.android.emailcommon.provider.EmailContent.MessageColumns;
49import com.android.emailcommon.provider.EmailContent.PolicyColumns;
50import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
51import com.android.emailcommon.provider.EmailContent.SyncColumns;
52import com.android.emailcommon.provider.HostAuth;
53import com.android.emailcommon.provider.Mailbox;
54import com.android.emailcommon.provider.MessageChangeLogTable;
55import com.android.emailcommon.provider.MessageMove;
56import com.android.emailcommon.provider.MessageStateChange;
57import com.android.emailcommon.provider.Policy;
58import com.android.emailcommon.provider.QuickResponse;
59import com.android.emailcommon.service.LegacyPolicySet;
60import com.android.emailcommon.service.SyncWindow;
61import com.android.mail.providers.UIProvider;
62import com.android.mail.utils.LogUtils;
63import com.google.common.annotations.VisibleForTesting;
64import com.google.common.collect.ImmutableMap;
65
66import java.io.File;
67import java.io.FileWriter;
68import java.io.IOException;
69import java.util.Map;
70
71public final class DBHelper {
72    private static final String TAG = "EmailProvider";
73
74    private static final String LEGACY_SCHEME_IMAP = "imap";
75    private static final String LEGACY_SCHEME_POP3 = "pop3";
76    private static final String LEGACY_SCHEME_EAS = "eas";
77
78
79    private static final String WHERE_ID = BaseColumns._ID + "=?";
80
81    private static final String TRIGGER_MAILBOX_DELETE =
82        "create trigger mailbox_delete before delete on " + Mailbox.TABLE_NAME +
83        " begin" +
84        " delete from " + Message.TABLE_NAME +
85        "  where " + MessageColumns.MAILBOX_KEY + "=old." + BaseColumns._ID +
86        "; delete from " + Message.UPDATED_TABLE_NAME +
87        "  where " + MessageColumns.MAILBOX_KEY + "=old." + BaseColumns._ID +
88        "; delete from " + Message.DELETED_TABLE_NAME +
89        "  where " + MessageColumns.MAILBOX_KEY + "=old." + BaseColumns._ID +
90        "; end";
91
92    private static final String TRIGGER_ACCOUNT_DELETE =
93        "create trigger account_delete before delete on " + Account.TABLE_NAME +
94        " begin delete from " + Mailbox.TABLE_NAME +
95        " where " + MailboxColumns.ACCOUNT_KEY + "=old." + BaseColumns._ID +
96        "; delete from " + HostAuth.TABLE_NAME +
97        " where " + BaseColumns._ID + "=old." + AccountColumns.HOST_AUTH_KEY_RECV +
98        "; delete from " + HostAuth.TABLE_NAME +
99        " where " + BaseColumns._ID + "=old." + AccountColumns.HOST_AUTH_KEY_SEND +
100        "; delete from " + Policy.TABLE_NAME +
101        " where " + BaseColumns._ID + "=old." + AccountColumns.POLICY_KEY +
102        "; end";
103
104    private static final String TRIGGER_HOST_AUTH_DELETE =
105            "create trigger host_auth_delete after delete on " + HostAuth.TABLE_NAME +
106            " begin delete from " + Credential.TABLE_NAME +
107            " where " + Credential._ID + "=old." + HostAuthColumns.CREDENTIAL_KEY +
108            " and (select count(*) from " + HostAuth.TABLE_NAME + " where " +
109            HostAuthColumns.CREDENTIAL_KEY + "=old." + HostAuthColumns.CREDENTIAL_KEY + ")=0" +
110            "; end";
111
112
113    // Any changes to the database format *must* include update-in-place code.
114    // Original version: 3
115    // Version 4: Database wipe required; changing AccountManager interface w/Exchange
116    // Version 5: Database wipe required; changing AccountManager interface w/Exchange
117    // Version 6: Adding Message.mServerTimeStamp column
118    // Version 7: Replace the mailbox_delete trigger with a version that removes orphaned messages
119    //            from the Message_Deletes and Message_Updates tables
120    // Version 8: Add security flags column to accounts table
121    // Version 9: Add security sync key and signature to accounts table
122    // Version 10: Add meeting info to message table
123    // Version 11: Add content and flags to attachment table
124    // Version 12: Add content_bytes to attachment table. content is deprecated.
125    // Version 13: Add messageCount to Mailbox table.
126    // Version 14: Add snippet to Message table
127    // Version 15: Fix upgrade problem in version 14.
128    // Version 16: Add accountKey to Attachment table
129    // Version 17: Add parentKey to Mailbox table
130    // Version 18: Copy Mailbox.displayName to Mailbox.serverId for all IMAP & POP3 mailboxes.
131    //             Column Mailbox.serverId is used for the server-side pathname of a mailbox.
132    // Version 19: Add Policy table; add policyKey to Account table and trigger to delete an
133    //             Account's policy when the Account is deleted
134    // Version 20: Add new policies to Policy table
135    // Version 21: Add lastSeenMessageKey column to Mailbox table
136    // Version 22: Upgrade path for IMAP/POP accounts to integrate with AccountManager
137    // Version 23: Add column to mailbox table for time of last access
138    // Version 24: Add column to hostauth table for client cert alias
139    // Version 25: Added QuickResponse table
140    // Version 26: Update IMAP accounts to add FLAG_SUPPORTS_SEARCH flag
141    // Version 27: Add protocolSearchInfo to Message table
142    // Version 28: Add notifiedMessageId and notifiedMessageCount to Account
143    // Version 29: Add protocolPoliciesEnforced and protocolPoliciesUnsupported to Policy
144    // Version 30: Use CSV of RFC822 addresses instead of "packed" values
145    // Version 31: Add columns to mailbox for ui status/last result
146    // Version 32: Add columns to mailbox for last notified message key/count; insure not null
147    //             for "notified" columns
148    // Version 33: Add columns to attachment for ui provider columns
149    // Version 34: Add total count to mailbox
150    // Version 35: Set up defaults for lastTouchedCount for drafts and sent
151    // Version 36: mblank intentionally left this space
152    // Version 37: Add flag for settings support in folders
153    // Version 38&39: Add threadTopic to message (for future support)
154    // Version 39 is last Email1 version
155    // Version 100 is first Email2 version
156    // Version 101 SHOULD NOT BE USED
157    // Version 102&103: Add hierarchicalName to Mailbox
158    // Version 104&105: add syncData to Message
159    // Version 106: Add certificate to HostAuth
160    // Version 107: Add a SEEN column to the message table
161    // Version 108: Add a cachedFile column to the attachments table
162    // Version 109: Migrate the account so they have the correct account manager types
163    // Version 110: Stop updating message_count, don't use auto lookback, and don't use
164    //              ping/push_hold sync states. Note that message_count updating is restored in 113.
165    // Version 111: Delete Exchange account mailboxes.
166    // Version 112: Convert Mailbox syncInterval to a boolean (whether or not this mailbox
167    //              syncs along with the account).
168    // Version 113: Restore message_count to being useful.
169    // Version 114: Add lastFullSyncTime column
170    // Version 115: Add pingDuration column
171    // Version 116: Add MessageMove & MessageStateChange tables.
172    // Version 117: Add trigger to delete duplicate messages on sync.
173    // Version 118: Set syncInterval to 0 for all IMAP mailboxes
174    // Version 119: Disable syncing of DRAFTS type folders.
175    // Version 120: Changed duplicateMessage deletion trigger to ignore search mailboxes.
176    // Version 121: Add mainMailboxKey, which will be set for messages that are in the fake
177    //              "search_results" folder to reflect the mailbox that the server considers
178    //              the message to be in. Also, wipe out any stale search_result folders.
179    // Version 122: Need to update Message_Updates and Message_Deletes to match previous.
180    // Version 123: Changed the duplicateMesage deletion trigger to ignore accounts that aren't
181    //              exchange accounts.
182    // Version 124: Added MAX_ATTACHMENT_SIZE to the account table
183    // Version 125: Add credentials table for OAuth.
184    // Version 126: Decode address lists for To, From, Cc, Bcc and Reply-To columns in Message.
185    // Version 127: Force mFlags to contain the correct flags for EAS accounts given a protocol
186    //              version above 12.0
187    public static final int DATABASE_VERSION = 127;
188
189    // Any changes to the database format *must* include update-in-place code.
190    // Original version: 2
191    // Version 3: Add "sourceKey" column
192    // Version 4: Database wipe required; changing AccountManager interface w/Exchange
193    // Version 5: Database wipe required; changing AccountManager interface w/Exchange
194    // Version 6: Adding Body.mIntroText column
195    // Version 7/8: Adding quoted text start pos
196    // Version 8 is last Email1 version
197    // Version 100 is the first Email2 version
198    // Version 101: Move body contents to external files
199    public static final int BODY_DATABASE_VERSION = 101;
200
201    /*
202     * Internal helper method for index creation.
203     * Example:
204     * "create index message_" + MessageColumns.FLAG_READ
205     * + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_READ + ");"
206     */
207    /* package */
208    static String createIndex(String tableName, String columnName) {
209        return "create index " + tableName.toLowerCase() + '_' + columnName
210            + " on " + tableName + " (" + columnName + ");";
211    }
212
213    static void createMessageCountTriggers(final SQLiteDatabase db) {
214        // Insert a message.
215        db.execSQL("create trigger message_count_message_insert after insert on " +
216                Message.TABLE_NAME +
217                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
218                '=' + MailboxColumns.MESSAGE_COUNT + "+1" +
219                "  where " + BaseColumns._ID + "=NEW." + MessageColumns.MAILBOX_KEY +
220                "; end");
221
222        // Delete a message.
223        db.execSQL("create trigger message_count_message_delete after delete on " +
224                Message.TABLE_NAME +
225                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
226                '=' + MailboxColumns.MESSAGE_COUNT + "-1" +
227                "  where " + BaseColumns._ID + "=OLD." + MessageColumns.MAILBOX_KEY +
228                "; end");
229
230        // Change a message's mailbox.
231        db.execSQL("create trigger message_count_message_move after update of " +
232                MessageColumns.MAILBOX_KEY + " on " + Message.TABLE_NAME +
233                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
234                '=' + MailboxColumns.MESSAGE_COUNT + "-1" +
235                "  where " + BaseColumns._ID + "=OLD." + MessageColumns.MAILBOX_KEY +
236                "; update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
237                '=' + MailboxColumns.MESSAGE_COUNT + "+1" +
238                " where " + BaseColumns._ID + "=NEW." + MessageColumns.MAILBOX_KEY +
239                "; end");
240    }
241
242    static void createCredentialsTable(SQLiteDatabase db) {
243        String s = " (" + Credential._ID + " integer primary key autoincrement, "
244                + Credential.PROVIDER_COLUMN + " text,"
245                + Credential.ACCESS_TOKEN_COLUMN + " text,"
246                + Credential.REFRESH_TOKEN_COLUMN + " text,"
247                + Credential.EXPIRATION_COLUMN + " integer"
248                + ");";
249        db.execSQL("create table " + Credential.TABLE_NAME + s);
250        db.execSQL(TRIGGER_HOST_AUTH_DELETE);
251    }
252
253    static void dropDeleteDuplicateMessagesTrigger(final SQLiteDatabase db) {
254        db.execSQL("drop trigger message_delete_duplicates_on_insert");
255    }
256
257    /**
258     * Add a trigger to delete duplicate server side messages before insertion.
259     * This should delete any messages older messages that have the same serverId and account as
260     * the new message, if:
261     *    Neither message is in a SEARCH type mailbox, and
262     *    The new message's mailbox's account is an exchange account.
263     *
264     * Here is the plain text of this sql:
265     *   create trigger message_delete_duplicates_on_insert before insert on
266     *   Message for each row when new.syncServerId is not null and
267     *    (select type from Mailbox where _id=new.mailboxKey) != 8 and
268     *    (select HostAuth.protocol from HostAuth, Account where
269     *       new.accountKey=account._id and account.hostAuthKeyRecv=hostAuth._id) = 'gEas'
270     *   begin delete from Message where new.syncServerId=syncSeverId and
271     *   new.accountKey=accountKey and
272     *    (select Mailbox.type from Mailbox where _id=mailboxKey) != 8; end
273     */
274    static void createDeleteDuplicateMessagesTrigger(final Context context,
275            final SQLiteDatabase db) {
276        db.execSQL("create trigger message_delete_duplicates_on_insert before insert on "
277                + Message.TABLE_NAME + " for each row when new." + SyncColumns.SERVER_ID
278                + " is not null and "
279                + "(select " + MailboxColumns.TYPE + " from " + Mailbox.TABLE_NAME
280                + " where " + MailboxColumns._ID + "=new."
281                + MessageColumns.MAILBOX_KEY + ")!=" + Mailbox.TYPE_SEARCH
282                + " and (select "
283                + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + " from "
284                + HostAuth.TABLE_NAME + "," + Account.TABLE_NAME
285                + " where new." + MessageColumns.ACCOUNT_KEY
286                + "=" + Account.TABLE_NAME + "." + AccountColumns._ID
287                + " and " + Account.TABLE_NAME + "." + AccountColumns.HOST_AUTH_KEY_RECV
288                + "=" + HostAuth.TABLE_NAME + "." + HostAuthColumns._ID
289                + ")='" + context.getString(R.string.protocol_eas) + "'"
290                + " begin delete from " + Message.TABLE_NAME + " where new."
291                + SyncColumns.SERVER_ID + "=" + SyncColumns.SERVER_ID + " and new."
292                + MessageColumns.ACCOUNT_KEY + "=" + MessageColumns.ACCOUNT_KEY
293                + " and (select " + Mailbox.TABLE_NAME + "." + MailboxColumns.TYPE + " from "
294                + Mailbox.TABLE_NAME + " where " + MailboxColumns._ID + "="
295                + MessageColumns.MAILBOX_KEY + ")!=" + Mailbox.TYPE_SEARCH +"; end");
296    }
297
298    static void createMessageTable(Context context, SQLiteDatabase db) {
299        String messageColumns = MessageColumns.DISPLAY_NAME + " text, "
300            + MessageColumns.TIMESTAMP + " integer, "
301            + MessageColumns.SUBJECT + " text, "
302            + MessageColumns.FLAG_READ + " integer, "
303            + MessageColumns.FLAG_LOADED + " integer, "
304            + MessageColumns.FLAG_FAVORITE + " integer, "
305            + MessageColumns.FLAG_ATTACHMENT + " integer, "
306            + MessageColumns.FLAGS + " integer, "
307            + MessageColumns.DRAFT_INFO + " integer, "
308            + MessageColumns.MESSAGE_ID + " text, "
309            + MessageColumns.MAILBOX_KEY + " integer, "
310            + MessageColumns.ACCOUNT_KEY + " integer, "
311            + MessageColumns.FROM_LIST + " text, "
312            + MessageColumns.TO_LIST + " text, "
313            + MessageColumns.CC_LIST + " text, "
314            + MessageColumns.BCC_LIST + " text, "
315            + MessageColumns.REPLY_TO_LIST + " text, "
316            + MessageColumns.MEETING_INFO + " text, "
317            + MessageColumns.SNIPPET + " text, "
318            + MessageColumns.PROTOCOL_SEARCH_INFO + " text, "
319            + MessageColumns.THREAD_TOPIC + " text, "
320            + MessageColumns.SYNC_DATA + " text, "
321            + MessageColumns.FLAG_SEEN + " integer, "
322            + MessageColumns.MAIN_MAILBOX_KEY + " integer"
323            + ");";
324
325        // This String and the following String MUST have the same columns, except for the type
326        // of those columns!
327        String createString = " (" + BaseColumns._ID + " integer primary key autoincrement, "
328            + SyncColumns.SERVER_ID + " text, "
329            + SyncColumns.SERVER_TIMESTAMP + " integer, "
330            + messageColumns;
331
332        // For the updated and deleted tables, the id is assigned, but we do want to keep track
333        // of the ORDER of updates using an autoincrement primary key.  We use the DATA column
334        // at this point; it has no other function
335        String altCreateString = " (" + BaseColumns._ID + " integer unique, "
336            + SyncColumns.SERVER_ID + " text, "
337            + SyncColumns.SERVER_TIMESTAMP + " integer, "
338            + messageColumns;
339
340        // The three tables have the same schema
341        db.execSQL("create table " + Message.TABLE_NAME + createString);
342        db.execSQL("create table " + Message.UPDATED_TABLE_NAME + altCreateString);
343        db.execSQL("create table " + Message.DELETED_TABLE_NAME + altCreateString);
344
345        String indexColumns[] = {
346            MessageColumns.TIMESTAMP,
347            MessageColumns.FLAG_READ,
348            MessageColumns.FLAG_LOADED,
349            MessageColumns.MAILBOX_KEY,
350            SyncColumns.SERVER_ID
351        };
352
353        for (String columnName : indexColumns) {
354            db.execSQL(createIndex(Message.TABLE_NAME, columnName));
355        }
356
357        // Deleting a Message deletes all associated Attachments
358        // Deleting the associated Body cannot be done in a trigger, because the Body is stored
359        // in a separate database, and trigger cannot operate on attached databases.
360        db.execSQL("create trigger message_delete before delete on " + Message.TABLE_NAME +
361                " begin delete from " + Attachment.TABLE_NAME +
362                "  where " + AttachmentColumns.MESSAGE_KEY + "=old." + BaseColumns._ID +
363                "; end");
364
365        // Add triggers to keep unread count accurate per mailbox
366
367        // NOTE: SQLite's before triggers are not safe when recursive triggers are involved.
368        // Use caution when changing them.
369
370        // Insert a message; if flagRead is zero, add to the unread count of the message's mailbox
371        db.execSQL("create trigger unread_message_insert before insert on " + Message.TABLE_NAME +
372                " when NEW." + MessageColumns.FLAG_READ + "=0" +
373                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
374                '=' + MailboxColumns.UNREAD_COUNT + "+1" +
375                "  where " + BaseColumns._ID + "=NEW." + MessageColumns.MAILBOX_KEY +
376                "; end");
377
378        // Delete a message; if flagRead is zero, decrement the unread count of the msg's mailbox
379        db.execSQL("create trigger unread_message_delete before delete on " + Message.TABLE_NAME +
380                " when OLD." + MessageColumns.FLAG_READ + "=0" +
381                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
382                '=' + MailboxColumns.UNREAD_COUNT + "-1" +
383                "  where " + BaseColumns._ID + "=OLD." + MessageColumns.MAILBOX_KEY +
384                "; end");
385
386        // Change a message's mailbox
387        db.execSQL("create trigger unread_message_move before update of " +
388                MessageColumns.MAILBOX_KEY + " on " + Message.TABLE_NAME +
389                " when OLD." + MessageColumns.FLAG_READ + "=0" +
390                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
391                '=' + MailboxColumns.UNREAD_COUNT + "-1" +
392                "  where " + BaseColumns._ID + "=OLD." + MessageColumns.MAILBOX_KEY +
393                "; update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
394                '=' + MailboxColumns.UNREAD_COUNT + "+1" +
395                " where " + BaseColumns._ID + "=NEW." + MessageColumns.MAILBOX_KEY +
396                "; end");
397
398        // Change a message's read state
399        db.execSQL("create trigger unread_message_read before update of " +
400                MessageColumns.FLAG_READ + " on " + Message.TABLE_NAME +
401                " when OLD." + MessageColumns.FLAG_READ + "!=NEW." + MessageColumns.FLAG_READ +
402                " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
403                '=' + MailboxColumns.UNREAD_COUNT + "+ case OLD." + MessageColumns.FLAG_READ +
404                " when 0 then -1 else 1 end" +
405                "  where " + BaseColumns._ID + "=OLD." + MessageColumns.MAILBOX_KEY +
406                "; end");
407
408        // Add triggers to maintain message_count.
409        createMessageCountTriggers(db);
410        createDeleteDuplicateMessagesTrigger(context, db);
411    }
412
413    static void resetMessageTable(Context context, SQLiteDatabase db,
414            int oldVersion, int newVersion) {
415        try {
416            db.execSQL("drop table " + Message.TABLE_NAME);
417            db.execSQL("drop table " + Message.UPDATED_TABLE_NAME);
418            db.execSQL("drop table " + Message.DELETED_TABLE_NAME);
419        } catch (SQLException e) {
420        }
421        createMessageTable(context, db);
422    }
423
424    /**
425     * Common columns for all {@link MessageChangeLogTable} tables.
426     */
427    private static String MESSAGE_CHANGE_LOG_COLUMNS =
428            MessageChangeLogTable.ID + " integer primary key autoincrement, "
429            + MessageChangeLogTable.MESSAGE_KEY + " integer, "
430            + MessageChangeLogTable.SERVER_ID + " text, "
431            + MessageChangeLogTable.ACCOUNT_KEY + " integer, "
432            + MessageChangeLogTable.STATUS + " integer, ";
433
434    /**
435     * Create indices common to all {@link MessageChangeLogTable} tables.
436     * @param db The {@link SQLiteDatabase}.
437     * @param tableName The name of this particular table.
438     */
439    private static void createMessageChangeLogTableIndices(final SQLiteDatabase db,
440            final String tableName) {
441        db.execSQL(createIndex(tableName, MessageChangeLogTable.MESSAGE_KEY));
442        db.execSQL(createIndex(tableName, MessageChangeLogTable.ACCOUNT_KEY));
443    }
444
445    /**
446     * Create triggers common to all {@link MessageChangeLogTable} tables.
447     * @param db The {@link SQLiteDatabase}.
448     * @param tableName The name of this particular table.
449     */
450    private static void createMessageChangeLogTableTriggers(final SQLiteDatabase db,
451            final String tableName) {
452        // Trigger to delete from the change log when a message is deleted.
453        db.execSQL("create trigger " + tableName + "_delete_message before delete on "
454                + Message.TABLE_NAME + " for each row begin delete from " + tableName
455                + " where " + MessageChangeLogTable.MESSAGE_KEY + "=old." + MessageColumns._ID
456                + "; end");
457
458        // Trigger to delete from the change log when an account is deleted.
459        db.execSQL("create trigger " + tableName + "_delete_account before delete on "
460                + Account.TABLE_NAME + " for each row begin delete from " + tableName
461                + " where " + MessageChangeLogTable.ACCOUNT_KEY + "=old." + AccountColumns._ID
462                + "; end");
463    }
464
465    /**
466     * Create the MessageMove table.
467     * @param db The {@link SQLiteDatabase}.
468     */
469    private static void createMessageMoveTable(final SQLiteDatabase db) {
470        db.execSQL("create table " + MessageMove.TABLE_NAME + " ("
471                + MESSAGE_CHANGE_LOG_COLUMNS
472                + MessageMove.SRC_FOLDER_KEY + " integer, "
473                + MessageMove.DST_FOLDER_KEY + " integer, "
474                + MessageMove.SRC_FOLDER_SERVER_ID + " text, "
475                + MessageMove.DST_FOLDER_SERVER_ID + " text);");
476
477        createMessageChangeLogTableIndices(db, MessageMove.TABLE_NAME);
478        createMessageChangeLogTableTriggers(db, MessageMove.TABLE_NAME);
479    }
480
481    /**
482     * Create the MessageStateChange table.
483     * @param db The {@link SQLiteDatabase}.
484     */
485    private static void createMessageStateChangeTable(final SQLiteDatabase db) {
486        db.execSQL("create table " + MessageStateChange.TABLE_NAME + " ("
487                + MESSAGE_CHANGE_LOG_COLUMNS
488                + MessageStateChange.OLD_FLAG_READ + " integer, "
489                + MessageStateChange.NEW_FLAG_READ + " integer, "
490                + MessageStateChange.OLD_FLAG_FAVORITE + " integer, "
491                + MessageStateChange.NEW_FLAG_FAVORITE + " integer);");
492
493        createMessageChangeLogTableIndices(db, MessageStateChange.TABLE_NAME);
494        createMessageChangeLogTableTriggers(db, MessageStateChange.TABLE_NAME);
495    }
496
497    @SuppressWarnings("deprecation")
498    static void createAccountTable(SQLiteDatabase db) {
499        String s = " (" + AccountColumns._ID + " integer primary key autoincrement, "
500            + AccountColumns.DISPLAY_NAME + " text, "
501            + AccountColumns.EMAIL_ADDRESS + " text, "
502            + AccountColumns.SYNC_KEY + " text, "
503            + AccountColumns.SYNC_LOOKBACK + " integer, "
504            + AccountColumns.SYNC_INTERVAL + " text, "
505            + AccountColumns.HOST_AUTH_KEY_RECV + " integer, "
506            + AccountColumns.HOST_AUTH_KEY_SEND + " integer, "
507            + AccountColumns.FLAGS + " integer, "
508            + AccountColumns.IS_DEFAULT + " integer, "
509            + AccountColumns.COMPATIBILITY_UUID + " text, "
510            + AccountColumns.SENDER_NAME + " text, "
511            + AccountColumns.RINGTONE_URI + " text, "
512            + AccountColumns.PROTOCOL_VERSION + " text, "
513            + AccountColumns.NEW_MESSAGE_COUNT + " integer, "
514            + AccountColumns.SECURITY_FLAGS + " integer, "
515            + AccountColumns.SECURITY_SYNC_KEY + " text, "
516            + AccountColumns.SIGNATURE + " text, "
517            + AccountColumns.POLICY_KEY + " integer, "
518            + AccountColumns.MAX_ATTACHMENT_SIZE + " integer, "
519            + AccountColumns.PING_DURATION + " integer"
520            + ");";
521        db.execSQL("create table " + Account.TABLE_NAME + s);
522        // Deleting an account deletes associated Mailboxes and HostAuth's
523        db.execSQL(TRIGGER_ACCOUNT_DELETE);
524    }
525
526    static void resetAccountTable(SQLiteDatabase db, int oldVersion, int newVersion) {
527        try {
528            db.execSQL("drop table " +  Account.TABLE_NAME);
529        } catch (SQLException e) {
530        }
531        createAccountTable(db);
532    }
533
534    static void createPolicyTable(SQLiteDatabase db) {
535        String s = " (" + PolicyColumns._ID + " integer primary key autoincrement, "
536            + PolicyColumns.PASSWORD_MODE + " integer, "
537            + PolicyColumns.PASSWORD_MIN_LENGTH + " integer, "
538            + PolicyColumns.PASSWORD_EXPIRATION_DAYS + " integer, "
539            + PolicyColumns.PASSWORD_HISTORY + " integer, "
540            + PolicyColumns.PASSWORD_COMPLEX_CHARS + " integer, "
541            + PolicyColumns.PASSWORD_MAX_FAILS + " integer, "
542            + PolicyColumns.MAX_SCREEN_LOCK_TIME + " integer, "
543            + PolicyColumns.REQUIRE_REMOTE_WIPE + " integer, "
544            + PolicyColumns.REQUIRE_ENCRYPTION + " integer, "
545            + PolicyColumns.REQUIRE_ENCRYPTION_EXTERNAL + " integer, "
546            + PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING + " integer, "
547            + PolicyColumns.DONT_ALLOW_CAMERA + " integer, "
548            + PolicyColumns.DONT_ALLOW_ATTACHMENTS + " integer, "
549            + PolicyColumns.DONT_ALLOW_HTML + " integer, "
550            + PolicyColumns.MAX_ATTACHMENT_SIZE + " integer, "
551            + PolicyColumns.MAX_TEXT_TRUNCATION_SIZE + " integer, "
552            + PolicyColumns.MAX_HTML_TRUNCATION_SIZE + " integer, "
553            + PolicyColumns.MAX_EMAIL_LOOKBACK + " integer, "
554            + PolicyColumns.MAX_CALENDAR_LOOKBACK + " integer, "
555            + PolicyColumns.PASSWORD_RECOVERY_ENABLED + " integer, "
556            + PolicyColumns.PROTOCOL_POLICIES_ENFORCED + " text, "
557            + PolicyColumns.PROTOCOL_POLICIES_UNSUPPORTED + " text"
558            + ");";
559        db.execSQL("create table " + Policy.TABLE_NAME + s);
560    }
561
562    static void createHostAuthTable(SQLiteDatabase db) {
563        String s = " (" + HostAuthColumns._ID + " integer primary key autoincrement, "
564            + HostAuthColumns.PROTOCOL + " text, "
565            + HostAuthColumns.ADDRESS + " text, "
566            + HostAuthColumns.PORT + " integer, "
567            + HostAuthColumns.FLAGS + " integer, "
568            + HostAuthColumns.LOGIN + " text, "
569            + HostAuthColumns.PASSWORD + " text, "
570            + HostAuthColumns.DOMAIN + " text, "
571            + HostAuthColumns.ACCOUNT_KEY + " integer,"
572            + HostAuthColumns.CLIENT_CERT_ALIAS + " text,"
573            + HostAuthColumns.SERVER_CERT + " blob,"
574            + HostAuthColumns.CREDENTIAL_KEY + " integer"
575            + ");";
576        db.execSQL("create table " + HostAuth.TABLE_NAME + s);
577    }
578
579    static void resetHostAuthTable(SQLiteDatabase db, int oldVersion, int newVersion) {
580        try {
581            db.execSQL("drop table " + HostAuth.TABLE_NAME);
582        } catch (SQLException e) {
583        }
584        createHostAuthTable(db);
585    }
586
587    @SuppressWarnings("deprecation")
588    static void createMailboxTable(SQLiteDatabase db) {
589        String s = " (" + MailboxColumns._ID + " integer primary key autoincrement, "
590            + MailboxColumns.DISPLAY_NAME + " text, "
591            + MailboxColumns.SERVER_ID + " text, "
592            + MailboxColumns.PARENT_SERVER_ID + " text, "
593            + MailboxColumns.PARENT_KEY + " integer, "
594            + MailboxColumns.ACCOUNT_KEY + " integer, "
595            + MailboxColumns.TYPE + " integer, "
596            + MailboxColumns.DELIMITER + " integer, "
597            + MailboxColumns.SYNC_KEY + " text, "
598            + MailboxColumns.SYNC_LOOKBACK + " integer, "
599            + MailboxColumns.SYNC_INTERVAL + " integer, "
600            + MailboxColumns.SYNC_TIME + " integer, "
601            + MailboxColumns.UNREAD_COUNT + " integer, "
602            + MailboxColumns.FLAG_VISIBLE + " integer, "
603            + MailboxColumns.FLAGS + " integer, "
604            + MailboxColumns.VISIBLE_LIMIT + " integer, "
605            + MailboxColumns.SYNC_STATUS + " text, "
606            + MailboxColumns.MESSAGE_COUNT + " integer not null default 0, "
607            + MailboxColumns.LAST_TOUCHED_TIME + " integer default 0, "
608            + MailboxColumns.UI_SYNC_STATUS + " integer default 0, "
609            + MailboxColumns.UI_LAST_SYNC_RESULT + " integer default 0, "
610            + MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY + " integer not null default 0, "
611            + MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT + " integer not null default 0, "
612            + MailboxColumns.TOTAL_COUNT + " integer, "
613            + MailboxColumns.HIERARCHICAL_NAME + " text, "
614            + MailboxColumns.LAST_FULL_SYNC_TIME + " integer"
615            + ");";
616        db.execSQL("create table " + Mailbox.TABLE_NAME + s);
617        db.execSQL("create index mailbox_" + MailboxColumns.SERVER_ID
618                + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.SERVER_ID + ")");
619        db.execSQL("create index mailbox_" + MailboxColumns.ACCOUNT_KEY
620                + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.ACCOUNT_KEY + ")");
621        // Deleting a Mailbox deletes associated Messages in all three tables
622        db.execSQL(TRIGGER_MAILBOX_DELETE);
623    }
624
625    static void resetMailboxTable(SQLiteDatabase db, int oldVersion, int newVersion) {
626        try {
627            db.execSQL("drop table " + Mailbox.TABLE_NAME);
628        } catch (SQLException e) {
629        }
630        createMailboxTable(db);
631    }
632
633    static void createAttachmentTable(SQLiteDatabase db) {
634        String s = " (" + AttachmentColumns._ID + " integer primary key autoincrement, "
635            + AttachmentColumns.FILENAME + " text, "
636            + AttachmentColumns.MIME_TYPE + " text, "
637            + AttachmentColumns.SIZE + " integer, "
638            + AttachmentColumns.CONTENT_ID + " text, "
639            + AttachmentColumns.CONTENT_URI + " text, "
640            + AttachmentColumns.MESSAGE_KEY + " integer, "
641            + AttachmentColumns.LOCATION + " text, "
642            + AttachmentColumns.ENCODING + " text, "
643            + AttachmentColumns.CONTENT + " text, "
644            + AttachmentColumns.FLAGS + " integer, "
645            + AttachmentColumns.CONTENT_BYTES + " blob, "
646            + AttachmentColumns.ACCOUNT_KEY + " integer, "
647            + AttachmentColumns.UI_STATE + " integer, "
648            + AttachmentColumns.UI_DESTINATION + " integer, "
649            + AttachmentColumns.UI_DOWNLOADED_SIZE + " integer, "
650            + AttachmentColumns.CACHED_FILE + " text"
651            + ");";
652        db.execSQL("create table " + Attachment.TABLE_NAME + s);
653        db.execSQL(createIndex(Attachment.TABLE_NAME, AttachmentColumns.MESSAGE_KEY));
654    }
655
656    static void resetAttachmentTable(SQLiteDatabase db, int oldVersion, int newVersion) {
657        try {
658            db.execSQL("drop table " + Attachment.TABLE_NAME);
659        } catch (SQLException e) {
660        }
661        createAttachmentTable(db);
662    }
663
664    static void createQuickResponseTable(SQLiteDatabase db) {
665        String s = " (" + QuickResponseColumns._ID + " integer primary key autoincrement, "
666                + QuickResponseColumns.TEXT + " text, "
667                + QuickResponseColumns.ACCOUNT_KEY + " integer"
668                + ");";
669        db.execSQL("create table " + QuickResponse.TABLE_NAME + s);
670    }
671
672    @SuppressWarnings("deprecation")
673    static void createBodyTable(SQLiteDatabase db) {
674        String s = " (" + BodyColumns._ID + " integer primary key autoincrement, "
675            + BodyColumns.MESSAGE_KEY + " integer, "
676            + BodyColumns.HTML_CONTENT + " text, "
677            + BodyColumns.TEXT_CONTENT + " text, "
678            + BodyColumns.HTML_REPLY + " text, "
679            + BodyColumns.TEXT_REPLY + " text, "
680            + BodyColumns.SOURCE_MESSAGE_KEY + " text, "
681            + BodyColumns.INTRO_TEXT + " text, "
682            + BodyColumns.QUOTED_TEXT_START_POS + " integer"
683            + ");";
684        db.execSQL("create table " + Body.TABLE_NAME + s);
685        db.execSQL(createIndex(Body.TABLE_NAME, BodyColumns.MESSAGE_KEY));
686    }
687
688    private static void upgradeBodyToVersion5(final SQLiteDatabase db) {
689        try {
690            db.execSQL("drop table " + Body.TABLE_NAME);
691            createBodyTable(db);
692        } catch (final SQLException e) {
693            // Shouldn't be needed unless we're debugging and interrupt the process
694            LogUtils.w(TAG, e, "Exception upgrading EmailProviderBody.db from <v5");
695        }
696    }
697
698    @SuppressWarnings("deprecation")
699    private static void upgradeBodyFromVersion5ToVersion6(final SQLiteDatabase db) {
700        try {
701            db.execSQL("alter table " + Body.TABLE_NAME
702                    + " add " + BodyColumns.INTRO_TEXT + " text");
703        } catch (final SQLException e) {
704            // Shouldn't be needed unless we're debugging and interrupt the process
705            LogUtils.w(TAG, e, "Exception upgrading EmailProviderBody.db from v5 to v6");
706        }
707    }
708
709    private static void upgradeBodyFromVersion6ToVersion8(final SQLiteDatabase db) {
710        try {
711            db.execSQL("alter table " + Body.TABLE_NAME
712                    + " add " + BodyColumns.QUOTED_TEXT_START_POS + " integer");
713        } catch (final SQLException e) {
714            // Shouldn't be needed unless we're debugging and interrupt the process
715            LogUtils.w(TAG, e, "Exception upgrading EmailProviderBody.db from v6 to v8");
716        }
717    }
718
719    /**
720     * This upgrade migrates email bodies out of the database and into individual files.
721     */
722    private static void upgradeBodyFromVersion100ToVersion101(final Context context,
723            final SQLiteDatabase db) {
724        try {
725            // We can't read the body parts through the cursor because they might be over 2MB
726            final String projection[] = { BodyColumns.MESSAGE_KEY };
727            final Cursor cursor = db.query(Body.TABLE_NAME, projection,
728                    null, null, null, null, null);
729            if (cursor == null) {
730                throw new IllegalStateException("Could not read body table for upgrade");
731            }
732
733            final SQLiteStatement htmlSql = db.compileStatement(
734                    "SELECT " + BodyColumns.HTML_CONTENT +
735                            " FROM " + Body.TABLE_NAME +
736                            " WHERE " + BodyColumns.MESSAGE_KEY + "=?"
737            );
738
739            final SQLiteStatement textSql = db.compileStatement(
740                    "SELECT " + BodyColumns.TEXT_CONTENT +
741                            " FROM " + Body.TABLE_NAME +
742                            " WHERE " + BodyColumns.MESSAGE_KEY + "=?"
743            );
744
745            while (cursor.moveToNext()) {
746                final long messageId = cursor.getLong(0);
747                htmlSql.bindLong(1, messageId);
748                try {
749                    final String htmlString = htmlSql.simpleQueryForString();
750                    if (!TextUtils.isEmpty(htmlString)) {
751                        final File htmlFile = EmailProvider.getBodyFile(context, messageId, "html");
752                        final FileWriter w = new FileWriter(htmlFile);
753                        try {
754                            w.write(htmlString);
755                        } finally {
756                            w.close();
757                        }
758                    }
759                } catch (final SQLiteDoneException e) {
760                    LogUtils.v(LogUtils.TAG, e, "Done with the HTML column");
761                }
762                textSql.bindLong(1, messageId);
763                try {
764                    final String textString = textSql.simpleQueryForString();
765                    if (!TextUtils.isEmpty(textString)) {
766                        final File textFile = EmailProvider.getBodyFile(context, messageId, "txt");
767                        final FileWriter w = new FileWriter(textFile);
768                        try {
769                            w.write(textString);
770                        } finally {
771                            w.close();
772                        }
773                    }
774                } catch (final SQLiteDoneException e) {
775                    LogUtils.v(LogUtils.TAG, e, "Done with the text column");
776                }
777            }
778
779            db.execSQL("update " + Body.TABLE_NAME +
780                    " set " + BodyColumns.HTML_CONTENT + "=NULL,"
781                    + BodyColumns.TEXT_CONTENT + "=NULL");
782        } catch (final SQLException e) {
783            // Shouldn't be needed unless we're debugging and interrupt the process
784            LogUtils.w(TAG, e, "Exception upgrading EmailProviderBody.db from v100 to v101");
785        } catch (final IOException e) {
786            throw new RuntimeException(e);
787        }
788    }
789
790
791    protected static class BodyDatabaseHelper extends SQLiteOpenHelper {
792        final Context mContext;
793
794        BodyDatabaseHelper(Context context, String name) {
795            super(context, name, null, BODY_DATABASE_VERSION);
796            mContext = context;
797        }
798
799        @Override
800        public void onCreate(SQLiteDatabase db) {
801            LogUtils.d(TAG, "Creating EmailProviderBody database");
802            createBodyTable(db);
803        }
804
805        @Override
806        public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
807            if (oldVersion < 5) {
808                upgradeBodyToVersion5(db);
809            }
810            if (oldVersion < 6) {
811                upgradeBodyFromVersion5ToVersion6(db);
812            }
813            if (oldVersion < 8) {
814                upgradeBodyFromVersion6ToVersion8(db);
815            }
816            if (oldVersion < 101) {
817                upgradeBodyFromVersion100ToVersion101(mContext, db);
818            }
819        }
820
821        @Override
822        public void onOpen(SQLiteDatabase db) {
823        }
824    }
825
826    /** Counts the number of messages in each mailbox, and updates the message count column. */
827    @VisibleForTesting
828    static void recalculateMessageCount(SQLiteDatabase db) {
829        db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
830                "= (select count(*) from " + Message.TABLE_NAME +
831                " where " + MessageColumns.MAILBOX_KEY + " = " +
832                    Mailbox.TABLE_NAME + "." + MailboxColumns._ID + ")");
833    }
834
835    protected static class DatabaseHelper extends SQLiteOpenHelper {
836        final Context mContext;
837
838        DatabaseHelper(Context context, String name) {
839            super(context, name, null, DATABASE_VERSION);
840            mContext = context;
841        }
842
843        @Override
844        public void onCreate(SQLiteDatabase db) {
845            LogUtils.d(TAG, "Creating EmailProvider database");
846            // Create all tables here; each class has its own method
847            createMessageTable(mContext, db);
848            createAttachmentTable(db);
849            createMailboxTable(db);
850            createHostAuthTable(db);
851            createAccountTable(db);
852            createMessageMoveTable(db);
853            createMessageStateChangeTable(db);
854            createPolicyTable(db);
855            createQuickResponseTable(db);
856            createCredentialsTable(db);
857        }
858
859        @Override
860        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
861            if (oldVersion == 101 && newVersion == 100) {
862                LogUtils.d(TAG, "Downgrade from v101 to v100");
863            } else {
864                super.onDowngrade(db, oldVersion, newVersion);
865            }
866        }
867
868        @Override
869        @SuppressWarnings("deprecation")
870        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
871            // For versions prior to 5, delete all data
872            // Versions >= 5 require that data be preserved!
873            if (oldVersion < 5) {
874                android.accounts.Account[] accounts = AccountManager.get(mContext)
875                        .getAccountsByType(LEGACY_SCHEME_EAS);
876                for (android.accounts.Account account: accounts) {
877                    AccountManager.get(mContext).removeAccount(account, null, null);
878                }
879                resetMessageTable(mContext, db, oldVersion, newVersion);
880                resetAttachmentTable(db, oldVersion, newVersion);
881                resetMailboxTable(db, oldVersion, newVersion);
882                resetHostAuthTable(db, oldVersion, newVersion);
883                resetAccountTable(db, oldVersion, newVersion);
884                return;
885            }
886            if (oldVersion == 5) {
887                // Message Tables: Add SyncColumns.SERVER_TIMESTAMP
888                try {
889                    db.execSQL("alter table " + Message.TABLE_NAME
890                            + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
891                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
892                            + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
893                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
894                            + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
895                } catch (SQLException e) {
896                    // Shouldn't be needed unless we're debugging and interrupt the process
897                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v5 to v6", e);
898                }
899            }
900            // TODO: Change all these to strict inequalities
901            if (oldVersion <= 6) {
902                // Use the newer mailbox_delete trigger
903                db.execSQL("drop trigger mailbox_delete;");
904                db.execSQL(TRIGGER_MAILBOX_DELETE);
905            }
906            if (oldVersion <= 7) {
907                // add the security (provisioning) column
908                try {
909                    db.execSQL("alter table " + Account.TABLE_NAME
910                            + " add column " + AccountColumns.SECURITY_FLAGS + " integer" + ";");
911                } catch (SQLException e) {
912                    // Shouldn't be needed unless we're debugging and interrupt the process
913                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 7 to 8 " + e);
914                }
915            }
916            if (oldVersion <= 8) {
917                // accounts: add security sync key & user signature columns
918                try {
919                    db.execSQL("alter table " + Account.TABLE_NAME
920                            + " add column " + AccountColumns.SECURITY_SYNC_KEY + " text" + ";");
921                    db.execSQL("alter table " + Account.TABLE_NAME
922                            + " add column " + AccountColumns.SIGNATURE + " text" + ";");
923                } catch (SQLException e) {
924                    // Shouldn't be needed unless we're debugging and interrupt the process
925                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 8 to 9 " + e);
926                }
927            }
928            if (oldVersion <= 9) {
929                // Message: add meeting info column into Message tables
930                try {
931                    db.execSQL("alter table " + Message.TABLE_NAME
932                            + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
933                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
934                            + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
935                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
936                            + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
937                } catch (SQLException e) {
938                    // Shouldn't be needed unless we're debugging and interrupt the process
939                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 9 to 10 " + e);
940                }
941            }
942            if (oldVersion <= 10) {
943                // Attachment: add content and flags columns
944                try {
945                    db.execSQL("alter table " + Attachment.TABLE_NAME
946                            + " add column " + AttachmentColumns.CONTENT + " text" + ";");
947                    db.execSQL("alter table " + Attachment.TABLE_NAME
948                            + " add column " + AttachmentColumns.FLAGS + " integer" + ";");
949                } catch (SQLException e) {
950                    // Shouldn't be needed unless we're debugging and interrupt the process
951                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 10 to 11 " + e);
952                }
953            }
954            if (oldVersion <= 11) {
955                // Attachment: add content_bytes
956                try {
957                    db.execSQL("alter table " + Attachment.TABLE_NAME
958                            + " add column " + AttachmentColumns.CONTENT_BYTES + " blob" + ";");
959                } catch (SQLException e) {
960                    // Shouldn't be needed unless we're debugging and interrupt the process
961                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 11 to 12 " + e);
962                }
963            }
964            if (oldVersion <= 12) {
965                try {
966                    db.execSQL("alter table " + Mailbox.TABLE_NAME
967                            + " add column " + Mailbox.MESSAGE_COUNT
968                                    +" integer not null default 0" + ";");
969                    recalculateMessageCount(db);
970                } catch (SQLException e) {
971                    // Shouldn't be needed unless we're debugging and interrupt the process
972                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 12 to 13 " + e);
973                }
974            }
975            if (oldVersion <= 13) {
976                try {
977                    db.execSQL("alter table " + Message.TABLE_NAME
978                            + " add column " + MessageColumns.SNIPPET
979                                    +" text" + ";");
980                } catch (SQLException e) {
981                    // Shouldn't be needed unless we're debugging and interrupt the process
982                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 13 to 14 " + e);
983                }
984            }
985            if (oldVersion <= 14) {
986                try {
987                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
988                            + " add column " + MessageColumns.SNIPPET +" text" + ";");
989                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
990                            + " add column " + MessageColumns.SNIPPET +" text" + ";");
991                } catch (SQLException e) {
992                    // Shouldn't be needed unless we're debugging and interrupt the process
993                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 14 to 15 " + e);
994                }
995            }
996            if (oldVersion <= 15) {
997                try {
998                    db.execSQL("alter table " + Attachment.TABLE_NAME
999                            + " add column " + AttachmentColumns.ACCOUNT_KEY +" integer" + ";");
1000                    // Update all existing attachments to add the accountKey data
1001                    db.execSQL("update " + Attachment.TABLE_NAME + " set " +
1002                            AttachmentColumns.ACCOUNT_KEY + "= (SELECT " + Message.TABLE_NAME +
1003                            "." + MessageColumns.ACCOUNT_KEY + " from " + Message.TABLE_NAME +
1004                            " where " + Message.TABLE_NAME + "." + MessageColumns._ID + " = " +
1005                            Attachment.TABLE_NAME + "." + AttachmentColumns.MESSAGE_KEY + ")");
1006                } catch (SQLException e) {
1007                    // Shouldn't be needed unless we're debugging and interrupt the process
1008                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 15 to 16 " + e);
1009                }
1010            }
1011            if (oldVersion <= 16) {
1012                try {
1013                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1014                            + " add column " + Mailbox.PARENT_KEY + " integer;");
1015                } catch (SQLException e) {
1016                    // Shouldn't be needed unless we're debugging and interrupt the process
1017                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 16 to 17 " + e);
1018                }
1019            }
1020            if (oldVersion <= 17) {
1021                upgradeFromVersion17ToVersion18(db);
1022            }
1023            if (oldVersion <= 18) {
1024                try {
1025                    db.execSQL("alter table " + Account.TABLE_NAME
1026                            + " add column " + AccountColumns.POLICY_KEY + " integer;");
1027                    db.execSQL("drop trigger account_delete;");
1028                    db.execSQL(TRIGGER_ACCOUNT_DELETE);
1029                    createPolicyTable(db);
1030                    convertPolicyFlagsToPolicyTable(db);
1031                } catch (SQLException e) {
1032                    // Shouldn't be needed unless we're debugging and interrupt the process
1033                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 18 to 19 " + e);
1034                }
1035            }
1036            if (oldVersion <= 19) {
1037                try {
1038                    db.execSQL("alter table " + Policy.TABLE_NAME
1039                            + " add column " + PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING +
1040                            " integer;");
1041                    db.execSQL("alter table " + Policy.TABLE_NAME
1042                            + " add column " + PolicyColumns.DONT_ALLOW_CAMERA + " integer;");
1043                    db.execSQL("alter table " + Policy.TABLE_NAME
1044                            + " add column " + PolicyColumns.DONT_ALLOW_ATTACHMENTS + " integer;");
1045                    db.execSQL("alter table " + Policy.TABLE_NAME
1046                            + " add column " + PolicyColumns.DONT_ALLOW_HTML + " integer;");
1047                    db.execSQL("alter table " + Policy.TABLE_NAME
1048                            + " add column " + PolicyColumns.MAX_ATTACHMENT_SIZE + " integer;");
1049                    db.execSQL("alter table " + Policy.TABLE_NAME
1050                            + " add column " + PolicyColumns.MAX_TEXT_TRUNCATION_SIZE +
1051                            " integer;");
1052                    db.execSQL("alter table " + Policy.TABLE_NAME
1053                            + " add column " + PolicyColumns.MAX_HTML_TRUNCATION_SIZE +
1054                            " integer;");
1055                    db.execSQL("alter table " + Policy.TABLE_NAME
1056                            + " add column " + PolicyColumns.MAX_EMAIL_LOOKBACK + " integer;");
1057                    db.execSQL("alter table " + Policy.TABLE_NAME
1058                            + " add column " + PolicyColumns.MAX_CALENDAR_LOOKBACK + " integer;");
1059                    db.execSQL("alter table " + Policy.TABLE_NAME
1060                            + " add column " + PolicyColumns.PASSWORD_RECOVERY_ENABLED +
1061                            " integer;");
1062                } catch (SQLException e) {
1063                    // Shouldn't be needed unless we're debugging and interrupt the process
1064                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 19 to 20 " + e);
1065                }
1066            }
1067            if (oldVersion <= 21) {
1068                upgradeFromVersion21ToVersion22(db, mContext);
1069                oldVersion = 22;
1070            }
1071            if (oldVersion <= 22) {
1072                upgradeFromVersion22ToVersion23(db);
1073            }
1074            if (oldVersion <= 23) {
1075                upgradeFromVersion23ToVersion24(db);
1076            }
1077            if (oldVersion <= 24) {
1078                upgradeFromVersion24ToVersion25(db);
1079            }
1080            if (oldVersion <= 25) {
1081                upgradeFromVersion25ToVersion26(db);
1082            }
1083            if (oldVersion <= 26) {
1084                try {
1085                    db.execSQL("alter table " + Message.TABLE_NAME
1086                            + " add column " + MessageColumns.PROTOCOL_SEARCH_INFO + " text;");
1087                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
1088                            + " add column " + MessageColumns.PROTOCOL_SEARCH_INFO +" text" + ";");
1089                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
1090                            + " add column " + MessageColumns.PROTOCOL_SEARCH_INFO +" text" + ";");
1091                } catch (SQLException e) {
1092                    // Shouldn't be needed unless we're debugging and interrupt the process
1093                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 26 to 27 " + e);
1094                }
1095            }
1096            if (oldVersion <= 28) {
1097                try {
1098                    db.execSQL("alter table " + Policy.TABLE_NAME
1099                            + " add column " + Policy.PROTOCOL_POLICIES_ENFORCED + " text;");
1100                    db.execSQL("alter table " + Policy.TABLE_NAME
1101                            + " add column " + Policy.PROTOCOL_POLICIES_UNSUPPORTED + " text;");
1102                } catch (SQLException e) {
1103                    // Shouldn't be needed unless we're debugging and interrupt the process
1104                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 28 to 29 " + e);
1105                }
1106            }
1107            if (oldVersion <= 29) {
1108                upgradeFromVersion29ToVersion30(db);
1109            }
1110            if (oldVersion <= 30) {
1111                try {
1112                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1113                            + " add column " + Mailbox.UI_SYNC_STATUS + " integer;");
1114                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1115                            + " add column " + Mailbox.UI_LAST_SYNC_RESULT + " integer;");
1116                } catch (SQLException e) {
1117                    // Shouldn't be needed unless we're debugging and interrupt the process
1118                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 30 to 31 " + e);
1119                }
1120            }
1121            if (oldVersion <= 31) {
1122                try {
1123                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1124                            + " add column " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY + " integer;");
1125                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1126                            + " add column " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT + " integer;");
1127                    db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY +
1128                            "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY + " IS NULL");
1129                    db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT +
1130                            "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT + " IS NULL");
1131                } catch (SQLException e) {
1132                    // Shouldn't be needed unless we're debugging and interrupt the process
1133                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 31 to 32 " + e);
1134                }
1135            }
1136            if (oldVersion <= 32) {
1137                try {
1138                    db.execSQL("alter table " + Attachment.TABLE_NAME
1139                            + " add column " + AttachmentColumns.UI_STATE + " integer;");
1140                    db.execSQL("alter table " + Attachment.TABLE_NAME
1141                            + " add column " + AttachmentColumns.UI_DESTINATION + " integer;");
1142                    db.execSQL("alter table " + Attachment.TABLE_NAME
1143                            + " add column " + AttachmentColumns.UI_DOWNLOADED_SIZE + " integer;");
1144                    // If we have a contentUri then the attachment is saved
1145                    // uiDestination of 0 = "cache", so we don't have to set this
1146                    db.execSQL("update " + Attachment.TABLE_NAME + " set " +
1147                            AttachmentColumns.UI_STATE + "=" + UIProvider.AttachmentState.SAVED +
1148                            " where " + AttachmentColumns.CONTENT_URI + " is not null;");
1149                } catch (SQLException e) {
1150                    // Shouldn't be needed unless we're debugging and interrupt the process
1151                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 32 to 33 " + e);
1152                }
1153            }
1154            if (oldVersion <= 33) {
1155                try {
1156                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1157                            + " add column " + MailboxColumns.TOTAL_COUNT + " integer;");
1158                } catch (SQLException e) {
1159                    // Shouldn't be needed unless we're debugging and interrupt the process
1160                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 33 to 34 " + e);
1161                }
1162            }
1163            if (oldVersion <= 34) {
1164                try {
1165                    db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1166                            MailboxColumns.LAST_TOUCHED_TIME + " = " +
1167                            Mailbox.DRAFTS_DEFAULT_TOUCH_TIME + " WHERE " + MailboxColumns.TYPE +
1168                            " = " + Mailbox.TYPE_DRAFTS);
1169                    db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1170                            MailboxColumns.LAST_TOUCHED_TIME + " = " +
1171                            Mailbox.SENT_DEFAULT_TOUCH_TIME + " WHERE " + MailboxColumns.TYPE +
1172                            " = " + Mailbox.TYPE_SENT);
1173                } catch (SQLException e) {
1174                    // Shouldn't be needed unless we're debugging and interrupt the process
1175                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 34 to 35 " + e);
1176                }
1177            }
1178            if (oldVersion <= 36) {
1179                try {
1180                    // Set "supports settings" for EAS mailboxes
1181                    db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1182                            MailboxColumns.FLAGS + "=" + MailboxColumns.FLAGS + "|" +
1183                            Mailbox.FLAG_SUPPORTS_SETTINGS + " where (" +
1184                            MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HOLDS_MAIL + ")!=0 and " +
1185                            MailboxColumns.ACCOUNT_KEY + " IN (SELECT " + Account.TABLE_NAME +
1186                            "." + AccountColumns._ID + " from " + Account.TABLE_NAME + "," +
1187                            HostAuth.TABLE_NAME + " where " + Account.TABLE_NAME + "." +
1188                            AccountColumns.HOST_AUTH_KEY_RECV + "=" + HostAuth.TABLE_NAME + "." +
1189                            HostAuthColumns._ID + " and " + HostAuthColumns.PROTOCOL + "='" +
1190                            LEGACY_SCHEME_EAS + "')");
1191                } catch (SQLException e) {
1192                    // Shouldn't be needed unless we're debugging and interrupt the process
1193                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 35 to 36 " + e);
1194                }
1195            }
1196            if (oldVersion <= 37) {
1197                try {
1198                    db.execSQL("alter table " + Message.TABLE_NAME
1199                            + " add column " + MessageColumns.THREAD_TOPIC + " text;");
1200                } catch (SQLException e) {
1201                    // Shouldn't be needed unless we're debugging and interrupt the process
1202                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 37 to 38 " + e);
1203                }
1204            }
1205            if (oldVersion <= 38) {
1206                try {
1207                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
1208                            + " add column " + MessageColumns.THREAD_TOPIC + " text;");
1209                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
1210                            + " add column " + MessageColumns.THREAD_TOPIC + " text;");
1211                } catch (SQLException e) {
1212                    // Shouldn't be needed unless we're debugging and interrupt the process
1213                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 38 to 39 " + e);
1214                }
1215            }
1216            if (oldVersion <= 39) {
1217                upgradeToEmail2(db);
1218            }
1219            if (oldVersion <= 102) {
1220                try {
1221                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1222                            + " add " + MailboxColumns.HIERARCHICAL_NAME + " text");
1223                } catch (SQLException e) {
1224                    // Shouldn't be needed unless we're debugging and interrupt the process
1225                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v10x to v103", e);
1226                }
1227            }
1228            if (oldVersion <= 103) {
1229                try {
1230                    db.execSQL("alter table " + Message.TABLE_NAME
1231                            + " add " + MessageColumns.SYNC_DATA + " text");
1232                } catch (SQLException e) {
1233                    // Shouldn't be needed unless we're debugging and interrupt the process
1234                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v103 to v104", e);
1235                }
1236            }
1237            if (oldVersion <= 104) {
1238                try {
1239                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
1240                            + " add " + MessageColumns.SYNC_DATA + " text");
1241                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
1242                            + " add " + MessageColumns.SYNC_DATA + " text");
1243                } catch (SQLException e) {
1244                    // Shouldn't be needed unless we're debugging and interrupt the process
1245                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v104 to v105", e);
1246                }
1247            }
1248            if (oldVersion <= 105) {
1249                try {
1250                    db.execSQL("alter table " + HostAuth.TABLE_NAME
1251                            + " add " + HostAuthColumns.SERVER_CERT + " blob");
1252                } catch (SQLException e) {
1253                    // Shouldn't be needed unless we're debugging and interrupt the process
1254                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v105 to v106", e);
1255                }
1256            }
1257            if (oldVersion <= 106) {
1258                try {
1259                    db.execSQL("alter table " + Message.TABLE_NAME
1260                            + " add " + MessageColumns.FLAG_SEEN + " integer");
1261                    db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
1262                            + " add " + MessageColumns.FLAG_SEEN + " integer");
1263                    db.execSQL("alter table " + Message.DELETED_TABLE_NAME
1264                            + " add " + MessageColumns.FLAG_SEEN + " integer");
1265                } catch (SQLException e) {
1266                    // Shouldn't be needed unless we're debugging and interrupt the process
1267                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v106 to v107", e);
1268                }
1269            }
1270            if (oldVersion <= 107) {
1271                try {
1272                    db.execSQL("alter table " + Attachment.TABLE_NAME
1273                            + " add column " + AttachmentColumns.CACHED_FILE +" text" + ";");
1274                } catch (SQLException e) {
1275                    // Shouldn't be needed unless we're debugging and interrupt the process
1276                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v107 to v108", e);
1277                }
1278            }
1279            if (oldVersion <= 108) {
1280                // Migrate the accounts with the correct account type
1281                migrateLegacyAccounts(db, mContext);
1282            }
1283            if (oldVersion <= 109) {
1284                // Fix any mailboxes that have ping or push_hold states.
1285                db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.SYNC_INTERVAL
1286                        + "=" + Mailbox.CHECK_INTERVAL_PUSH + " where "
1287                        + MailboxColumns.SYNC_INTERVAL + "<" + Mailbox.CHECK_INTERVAL_PUSH);
1288
1289                // Fix invalid syncLookback values.
1290                db.execSQL("update " + Account.TABLE_NAME + " set " + AccountColumns.SYNC_LOOKBACK
1291                        + "=" + SyncWindow.SYNC_WINDOW_1_WEEK + " where "
1292                        + AccountColumns.SYNC_LOOKBACK + " is null or "
1293                        + AccountColumns.SYNC_LOOKBACK + "<" + SyncWindow.SYNC_WINDOW_1_DAY + " or "
1294                        + AccountColumns.SYNC_LOOKBACK + ">" + SyncWindow.SYNC_WINDOW_ALL);
1295
1296                db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.SYNC_LOOKBACK
1297                        + "=" + SyncWindow.SYNC_WINDOW_ACCOUNT + " where "
1298                        + MailboxColumns.SYNC_LOOKBACK + " is null or "
1299                        + MailboxColumns.SYNC_LOOKBACK + "<" + SyncWindow.SYNC_WINDOW_1_DAY + " or "
1300                        + MailboxColumns.SYNC_LOOKBACK + ">" + SyncWindow.SYNC_WINDOW_ALL);
1301            }
1302            if (oldVersion <= 110) {
1303                // Delete account mailboxes.
1304                db.execSQL("delete from " + Mailbox.TABLE_NAME + " where " + MailboxColumns.TYPE
1305                        + "=" +Mailbox.TYPE_EAS_ACCOUNT_MAILBOX);
1306            }
1307            if (oldVersion <= 111) {
1308                // Mailbox sync interval now indicates whether this mailbox syncs with the rest
1309                // of the account. Anyone who was syncing at all, plus outboxes, are set to 1,
1310                // everyone else is 0.
1311                db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.SYNC_INTERVAL
1312                        + "=case when " + MailboxColumns.SYNC_INTERVAL + "="
1313                        + Mailbox.CHECK_INTERVAL_NEVER + " then 0 else 1 end");
1314            }
1315            if (oldVersion >= 110 && oldVersion <= 112) {
1316                // v110 had dropped these triggers, but starting with v113 we restored them
1317                // (and altered the 109 -> 110 upgrade code to stop dropping them).
1318                // We therefore only add them back for the versions in between. We also need to
1319                // compute the correct value at this point as well.
1320                recalculateMessageCount(db);
1321                createMessageCountTriggers(db);
1322            }
1323
1324            if (oldVersion <= 113) {
1325                try {
1326                    db.execSQL("alter table " + Mailbox.TABLE_NAME
1327                            + " add column " + MailboxColumns.LAST_FULL_SYNC_TIME +" integer" + ";");
1328                    final ContentValues cv = new ContentValues(1);
1329                    cv.put(MailboxColumns.LAST_FULL_SYNC_TIME, 0);
1330                    db.update(Mailbox.TABLE_NAME, cv, null, null);
1331                } catch (final SQLException e) {
1332                    // Shouldn't be needed unless we're debugging and interrupt the process
1333                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v113 to v114", e);
1334                }
1335            }
1336
1337            if (oldVersion <= 114) {
1338                try {
1339                    db.execSQL("alter table " + Account.TABLE_NAME
1340                            + " add column " + AccountColumns.PING_DURATION +" integer" + ";");
1341                    final ContentValues cv = new ContentValues(1);
1342                    cv.put(AccountColumns.PING_DURATION, 0);
1343                    db.update(Account.TABLE_NAME, cv, null, null);
1344                } catch (final SQLException e) {
1345                    // Shouldn't be needed unless we're debugging and interrupt the process
1346                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v113 to v114", e);
1347                }
1348            }
1349
1350            if (oldVersion <= 115) {
1351                createMessageMoveTable(db);
1352                createMessageStateChangeTable(db);
1353            }
1354
1355            /**
1356             * Originally, at 116, we added a trigger to delete duplicate messages.
1357             * But we needed to change that trigger for version 120, so when we get
1358             * there, we'll drop the trigger if it exists and create a new version.
1359             */
1360
1361            /**
1362             * This statement changes the syncInterval column to 0 for all IMAP mailboxes.
1363             * It does this by matching mailboxes against all account IDs whose receive auth is
1364             * either R.string.protocol_legacy_imap, R.string.protocol_imap or "imap"
1365             */
1366            if (oldVersion <= 117) {
1367                db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.SYNC_INTERVAL
1368                        + "=0 where " + MailboxColumns.ACCOUNT_KEY + " in (select "
1369                        + Account.TABLE_NAME + "." + AccountColumns._ID + " from "
1370                        + Account.TABLE_NAME + " join " + HostAuth.TABLE_NAME + " where "
1371                        + HostAuth.TABLE_NAME + "." + HostAuthColumns._ID + "="
1372                        + Account.TABLE_NAME + "." + AccountColumns.HOST_AUTH_KEY_RECV
1373                        + " and (" + HostAuth.TABLE_NAME + "."
1374                        + HostAuthColumns.PROTOCOL + "='"
1375                        + mContext.getString(R.string.protocol_legacy_imap) + "' or "
1376                        + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='"
1377                        + mContext.getString(R.string.protocol_imap) + "' or "
1378                        + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='imap'));");
1379            }
1380
1381            /**
1382             * This statement changes the sync interval column to 0 for all DRAFTS type mailboxes,
1383             * and deletes any messages that are:
1384             *   * synced from the server, and
1385             *   * in an exchange account draft folder
1386             *
1387             * This is primary for Exchange (b/11158759) but we don't sync draft folders for any
1388             * other account type anyway.
1389             * This will only affect people who used intermediate builds between email1 and email2,
1390             * it should be a no-op for most users.
1391             */
1392            if (oldVersion <= 118) {
1393                db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.SYNC_INTERVAL
1394                        + "=0 where " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_DRAFTS);
1395
1396                db.execSQL("delete from " + Message.TABLE_NAME + " where "
1397                        + "(" + SyncColumns.SERVER_ID + " not null and "
1398                        + SyncColumns.SERVER_ID + "!='') and "
1399                        + MessageColumns.MAILBOX_KEY + " in (select "
1400                        + MailboxColumns._ID + " from " + Mailbox.TABLE_NAME + " where "
1401                        + MailboxColumns.TYPE + "=" + Mailbox.TYPE_DRAFTS + ")");
1402            }
1403
1404            // We originally dropped and recreated the deleteDuplicateMessagesTrigger here at
1405            // version 120. We needed to update it again at version 123, so there's no reason
1406            // to do it twice.
1407
1408            // Add the mainMailboxKey column, and get rid of any messages in the search_results
1409            // folder.
1410            if (oldVersion <= 120) {
1411                db.execSQL("alter table " + Message.TABLE_NAME
1412                        + " add " + MessageColumns.MAIN_MAILBOX_KEY + " integer");
1413
1414                // Delete all TYPE_SEARCH mailboxes. These will be for stale queries anyway, and
1415                // the messages in them will not have the mainMailboxKey column correctly populated.
1416                // We have a trigger (See TRIGGER_MAILBOX_DELETE) that will delete any messages
1417                // in the deleted mailboxes.
1418                db.execSQL("delete from " + Mailbox.TABLE_NAME + " where "
1419                        + Mailbox.TYPE + "=" + Mailbox.TYPE_SEARCH);
1420            }
1421
1422            if (oldVersion <= 121) {
1423                // The previous update omitted making these changes to the Message_Updates and
1424                // Message_Deletes tables. The app will actually crash in between these versions!
1425                db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
1426                        + " add " + MessageColumns.MAIN_MAILBOX_KEY + " integer");
1427                db.execSQL("alter table " + Message.DELETED_TABLE_NAME
1428                        + " add " + MessageColumns.MAIN_MAILBOX_KEY + " integer");
1429            }
1430
1431            if (oldVersion <= 122) {
1432                if (oldVersion >= 117) {
1433                    /**
1434                     * This trigger was originally created at version 117, but we needed to change
1435                     * it for version 122. So if our oldVersion is 117 or more, we know we have that
1436                     * trigger and must drop it before re creating it.
1437                     */
1438                    dropDeleteDuplicateMessagesTrigger(db);
1439                }
1440                createDeleteDuplicateMessagesTrigger(mContext, db);
1441            }
1442
1443            if (oldVersion <= 123) {
1444                try {
1445                    db.execSQL("alter table " + Account.TABLE_NAME
1446                            + " add column " + AccountColumns.MAX_ATTACHMENT_SIZE +" integer" + ";");
1447                    final ContentValues cv = new ContentValues(1);
1448                    cv.put(AccountColumns.MAX_ATTACHMENT_SIZE, 0);
1449                    db.update(Account.TABLE_NAME, cv, null, null);
1450                } catch (final SQLException e) {
1451                    // Shouldn't be needed unless we're debugging and interrupt the process
1452                    LogUtils.w(TAG, "Exception upgrading EmailProvider.db from v123 to v124", e);
1453                }
1454            }
1455
1456            if (oldVersion <= 124) {
1457                createCredentialsTable(db);
1458                // Add the credentialKey column, and set it to -1 for all pre-existing hostAuths.
1459                db.execSQL("alter table " + HostAuth.TABLE_NAME
1460                        + " add " + HostAuthColumns.CREDENTIAL_KEY + " integer");
1461                db.execSQL("update " + HostAuth.TABLE_NAME + " set "
1462                        + HostAuthColumns.CREDENTIAL_KEY + "=-1");
1463            }
1464
1465            if (oldVersion <= 125) {
1466                upgradeFromVersion125ToVersion126(db);
1467            }
1468
1469            if (oldVersion <= 126) {
1470                upgradeFromVersion126ToVersion127(mContext, db);
1471            }
1472        }
1473
1474        @Override
1475        public void onOpen(SQLiteDatabase db) {
1476            try {
1477                // Cleanup some nasty records
1478                db.execSQL("DELETE FROM " + Account.TABLE_NAME
1479                        + " WHERE " + AccountColumns.DISPLAY_NAME + " ISNULL;");
1480                db.execSQL("DELETE FROM " + HostAuth.TABLE_NAME
1481                        + " WHERE " + HostAuthColumns.PROTOCOL + " ISNULL;");
1482            } catch (SQLException e) {
1483                // Shouldn't be needed unless we're debugging and interrupt the process
1484                LogUtils.e(TAG, e, "Exception cleaning EmailProvider.db");
1485            }
1486        }
1487    }
1488
1489    @VisibleForTesting
1490    @SuppressWarnings("deprecation")
1491    static void convertPolicyFlagsToPolicyTable(SQLiteDatabase db) {
1492        Cursor c = db.query(Account.TABLE_NAME,
1493                new String[] {BaseColumns._ID /*0*/, AccountColumns.SECURITY_FLAGS /*1*/},
1494                AccountColumns.SECURITY_FLAGS + ">0", null, null, null, null);
1495        try {
1496            ContentValues cv = new ContentValues();
1497            String[] args = new String[1];
1498            while (c.moveToNext()) {
1499                long securityFlags = c.getLong(1 /*SECURITY_FLAGS*/);
1500                Policy policy = LegacyPolicySet.flagsToPolicy(securityFlags);
1501                long policyId = db.insert(Policy.TABLE_NAME, null, policy.toContentValues());
1502                cv.put(AccountColumns.POLICY_KEY, policyId);
1503                cv.putNull(AccountColumns.SECURITY_FLAGS);
1504                args[0] = Long.toString(c.getLong(0 /*_ID*/));
1505                db.update(Account.TABLE_NAME, cv, BaseColumns._ID + "=?", args);
1506            }
1507        } finally {
1508            c.close();
1509        }
1510    }
1511
1512    /** Upgrades the database from v17 to v18 */
1513    @VisibleForTesting
1514    static void upgradeFromVersion17ToVersion18(SQLiteDatabase db) {
1515        // Copy the displayName column to the serverId column. In v18 of the database,
1516        // we use the serverId for IMAP/POP3 mailboxes instead of overloading the
1517        // display name.
1518        //
1519        // For posterity; this is the command we're executing:
1520        //sqlite> UPDATE mailbox SET serverid=displayname WHERE mailbox._id in (
1521        //        ...> SELECT mailbox._id FROM mailbox,account,hostauth WHERE
1522        //        ...> (mailbox.parentkey isnull OR mailbox.parentkey=0) AND
1523        //        ...> mailbox.accountkey=account._id AND
1524        //        ...> account.hostauthkeyrecv=hostauth._id AND
1525        //        ...> (hostauth.protocol='imap' OR hostauth.protocol='pop3'));
1526        try {
1527            db.execSQL(
1528                    "UPDATE " + Mailbox.TABLE_NAME + " SET "
1529                    + MailboxColumns.SERVER_ID + "=" + MailboxColumns.DISPLAY_NAME
1530                    + " WHERE "
1531                    + Mailbox.TABLE_NAME + "." + MailboxColumns._ID + " IN ( SELECT "
1532                    + Mailbox.TABLE_NAME + "." + MailboxColumns._ID + " FROM "
1533                    + Mailbox.TABLE_NAME + "," + Account.TABLE_NAME + ","
1534                    + HostAuth.TABLE_NAME + " WHERE "
1535                    + "("
1536                    + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_KEY + " isnull OR "
1537                    + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_KEY + "=0 "
1538                    + ") AND "
1539                    + Mailbox.TABLE_NAME + "." + MailboxColumns.ACCOUNT_KEY + "="
1540                    + Account.TABLE_NAME + "." + AccountColumns._ID + " AND "
1541                    + Account.TABLE_NAME + "." + AccountColumns.HOST_AUTH_KEY_RECV + "="
1542                    + HostAuth.TABLE_NAME + "." + HostAuthColumns._ID + " AND ( "
1543                    + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='imap' OR "
1544                    + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='pop3' ) )");
1545        } catch (SQLException e) {
1546            // Shouldn't be needed unless we're debugging and interrupt the process
1547            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 17 to 18 " + e);
1548        }
1549        ContentCache.invalidateAllCaches();
1550    }
1551
1552    /**
1553     * Upgrade the database from v21 to v22
1554     * This entails creating AccountManager accounts for all pop3 and imap accounts
1555     */
1556
1557    private static final String[] V21_ACCOUNT_PROJECTION =
1558        new String[] {AccountColumns.HOST_AUTH_KEY_RECV, AccountColumns.EMAIL_ADDRESS};
1559    private static final int V21_ACCOUNT_RECV = 0;
1560    private static final int V21_ACCOUNT_EMAIL = 1;
1561
1562    private static final String[] V21_HOSTAUTH_PROJECTION =
1563        new String[] {HostAuthColumns.PROTOCOL, HostAuthColumns.PASSWORD};
1564    private static final int V21_HOSTAUTH_PROTOCOL = 0;
1565    private static final int V21_HOSTAUTH_PASSWORD = 1;
1566
1567    private static void createAccountManagerAccount(Context context, String login, String type,
1568            String password) {
1569        final AccountManager accountManager = AccountManager.get(context);
1570
1571        if (isAccountPresent(accountManager, login, type)) {
1572            // The account already exists,just return
1573            return;
1574        }
1575        LogUtils.v("Email", "Creating account %s %s", login, type);
1576        final android.accounts.Account amAccount = new android.accounts.Account(login, type);
1577        accountManager.addAccountExplicitly(amAccount, password, null);
1578        ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
1579        ContentResolver.setSyncAutomatically(amAccount, EmailContent.AUTHORITY, true);
1580        ContentResolver.setIsSyncable(amAccount, ContactsContract.AUTHORITY, 0);
1581        ContentResolver.setIsSyncable(amAccount, CalendarContract.AUTHORITY, 0);
1582    }
1583
1584    private static boolean isAccountPresent(AccountManager accountManager, String name,
1585            String type) {
1586        final android.accounts.Account[] amAccounts = accountManager.getAccountsByType(type);
1587        if (amAccounts != null) {
1588            for (android.accounts.Account account : amAccounts) {
1589                if (TextUtils.equals(account.name, name) && TextUtils.equals(account.type, type)) {
1590                    return true;
1591                }
1592            }
1593        }
1594        return false;
1595    }
1596
1597    @VisibleForTesting
1598    static void upgradeFromVersion21ToVersion22(SQLiteDatabase db, Context accountManagerContext) {
1599        migrateLegacyAccounts(db, accountManagerContext);
1600    }
1601
1602    private static void migrateLegacyAccounts(SQLiteDatabase db, Context accountManagerContext) {
1603        final Map<String, String> legacyToNewTypeMap = new ImmutableMap.Builder<String, String>()
1604                .put(LEGACY_SCHEME_POP3,
1605                        accountManagerContext.getString(R.string.account_manager_type_pop3))
1606                .put(LEGACY_SCHEME_IMAP,
1607                        accountManagerContext.getString(R.string.account_manager_type_legacy_imap))
1608                .put(LEGACY_SCHEME_EAS,
1609                        accountManagerContext.getString(R.string.account_manager_type_exchange))
1610                .build();
1611        try {
1612            // Loop through accounts, looking for pop/imap accounts
1613            final Cursor accountCursor = db.query(Account.TABLE_NAME, V21_ACCOUNT_PROJECTION, null,
1614                    null, null, null, null);
1615            try {
1616                final String[] hostAuthArgs = new String[1];
1617                while (accountCursor.moveToNext()) {
1618                    hostAuthArgs[0] = accountCursor.getString(V21_ACCOUNT_RECV);
1619                    // Get the "receive" HostAuth for this account
1620                    final Cursor hostAuthCursor = db.query(HostAuth.TABLE_NAME,
1621                            V21_HOSTAUTH_PROJECTION, HostAuthColumns._ID + "=?", hostAuthArgs,
1622                            null, null, null);
1623                    try {
1624                        if (hostAuthCursor.moveToFirst()) {
1625                            final String protocol = hostAuthCursor.getString(V21_HOSTAUTH_PROTOCOL);
1626                            // If this is a pop3 or imap account, create the account manager account
1627                            if (LEGACY_SCHEME_IMAP.equals(protocol) ||
1628                                    LEGACY_SCHEME_POP3.equals(protocol)) {
1629                                // If this is a pop3 or imap account, create the account manager
1630                                // account
1631                                if (DebugUtils.DEBUG) {
1632                                    LogUtils.d(TAG, "Create AccountManager account for " + protocol
1633                                            + "account: "
1634                                            + accountCursor.getString(V21_ACCOUNT_EMAIL));
1635                                }
1636                                createAccountManagerAccount(accountManagerContext,
1637                                        accountCursor.getString(V21_ACCOUNT_EMAIL),
1638                                        legacyToNewTypeMap.get(protocol),
1639                                        hostAuthCursor.getString(V21_HOSTAUTH_PASSWORD));
1640                            } else if (LEGACY_SCHEME_EAS.equals(protocol)) {
1641                                // If an EAS account, make Email sync automatically (equivalent of
1642                                // checking the "Sync Email" box in settings
1643
1644                                android.accounts.Account amAccount = new android.accounts.Account(
1645                                        accountCursor.getString(V21_ACCOUNT_EMAIL),
1646                                        legacyToNewTypeMap.get(protocol));
1647                                ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
1648                                ContentResolver.setSyncAutomatically(amAccount,
1649                                        EmailContent.AUTHORITY, true);
1650                            }
1651                        }
1652                    } finally {
1653                        hostAuthCursor.close();
1654                    }
1655                }
1656            } finally {
1657                accountCursor.close();
1658            }
1659        } catch (SQLException e) {
1660            // Shouldn't be needed unless we're debugging and interrupt the process
1661            LogUtils.w(TAG, "Exception while migrating accounts " + e);
1662        }
1663    }
1664
1665    /** Upgrades the database from v22 to v23 */
1666    private static void upgradeFromVersion22ToVersion23(SQLiteDatabase db) {
1667        try {
1668            db.execSQL("alter table " + Mailbox.TABLE_NAME
1669                    + " add column " + Mailbox.LAST_TOUCHED_TIME + " integer default 0;");
1670        } catch (SQLException e) {
1671            // Shouldn't be needed unless we're debugging and interrupt the process
1672            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 22 to 23 " + e);
1673        }
1674    }
1675
1676    /** Adds in a column for information about a client certificate to use. */
1677    private static void upgradeFromVersion23ToVersion24(SQLiteDatabase db) {
1678        try {
1679            db.execSQL("alter table " + HostAuth.TABLE_NAME
1680                    + " add column " + HostAuthColumns.CLIENT_CERT_ALIAS + " text;");
1681        } catch (SQLException e) {
1682            // Shouldn't be needed unless we're debugging and interrupt the process
1683            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 23 to 24 " + e);
1684        }
1685    }
1686
1687    /** Upgrades the database from v24 to v25 by creating table for quick responses */
1688    private static void upgradeFromVersion24ToVersion25(SQLiteDatabase db) {
1689        try {
1690            createQuickResponseTable(db);
1691        } catch (SQLException e) {
1692            // Shouldn't be needed unless we're debugging and interrupt the process
1693            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 24 to 25 " + e);
1694        }
1695    }
1696
1697    private static final String[] V25_ACCOUNT_PROJECTION =
1698        new String[] {AccountColumns._ID, AccountColumns.FLAGS, AccountColumns.HOST_AUTH_KEY_RECV};
1699    private static final int V25_ACCOUNT_ID = 0;
1700    private static final int V25_ACCOUNT_FLAGS = 1;
1701    private static final int V25_ACCOUNT_RECV = 2;
1702
1703    private static final String[] V25_HOSTAUTH_PROJECTION = new String[] {HostAuthColumns.PROTOCOL};
1704    private static final int V25_HOSTAUTH_PROTOCOL = 0;
1705
1706    /** Upgrades the database from v25 to v26 by adding FLAG_SUPPORTS_SEARCH to IMAP accounts */
1707    private static void upgradeFromVersion25ToVersion26(SQLiteDatabase db) {
1708        try {
1709            // Loop through accounts, looking for imap accounts
1710            Cursor accountCursor = db.query(Account.TABLE_NAME, V25_ACCOUNT_PROJECTION, null,
1711                    null, null, null, null);
1712            ContentValues cv = new ContentValues();
1713            try {
1714                String[] hostAuthArgs = new String[1];
1715                while (accountCursor.moveToNext()) {
1716                    hostAuthArgs[0] = accountCursor.getString(V25_ACCOUNT_RECV);
1717                    // Get the "receive" HostAuth for this account
1718                    Cursor hostAuthCursor = db.query(HostAuth.TABLE_NAME,
1719                            V25_HOSTAUTH_PROJECTION, HostAuthColumns._ID + "=?", hostAuthArgs,
1720                            null, null, null);
1721                    try {
1722                        if (hostAuthCursor.moveToFirst()) {
1723                            String protocol = hostAuthCursor.getString(V25_HOSTAUTH_PROTOCOL);
1724                            // If this is an imap account, add the search flag
1725                            if (LEGACY_SCHEME_IMAP.equals(protocol)) {
1726                                String id = accountCursor.getString(V25_ACCOUNT_ID);
1727                                int flags = accountCursor.getInt(V25_ACCOUNT_FLAGS);
1728                                cv.put(AccountColumns.FLAGS, flags | Account.FLAGS_SUPPORTS_SEARCH);
1729                                db.update(Account.TABLE_NAME, cv, AccountColumns._ID + "=?",
1730                                        new String[] {id});
1731                            }
1732                        }
1733                    } finally {
1734                        hostAuthCursor.close();
1735                    }
1736                }
1737            } finally {
1738                accountCursor.close();
1739            }
1740        } catch (SQLException e) {
1741            // Shouldn't be needed unless we're debugging and interrupt the process
1742            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 25 to 26 " + e);
1743        }
1744    }
1745
1746    /** Upgrades the database from v29 to v30 by updating all address fields in Message */
1747    private static final int[] ADDRESS_COLUMN_INDICES = {
1748            Message.CONTENT_BCC_LIST_COLUMN,
1749            Message.CONTENT_CC_LIST_COLUMN,
1750            Message.CONTENT_FROM_LIST_COLUMN,
1751            Message.CONTENT_REPLY_TO_COLUMN,
1752            Message.CONTENT_TO_LIST_COLUMN
1753    };
1754    private static final String[] ADDRESS_COLUMN_NAMES = {
1755            MessageColumns.BCC_LIST,
1756            MessageColumns.CC_LIST,
1757            MessageColumns.FROM_LIST,
1758            MessageColumns.REPLY_TO_LIST,
1759            MessageColumns.TO_LIST
1760    };
1761
1762    private static void upgradeFromVersion29ToVersion30(SQLiteDatabase db) {
1763        try {
1764            // Loop through all messages, updating address columns to new format (CSV, RFC822)
1765            Cursor messageCursor = db.query(Message.TABLE_NAME, Message.CONTENT_PROJECTION, null,
1766                    null, null, null, null);
1767            ContentValues cv = new ContentValues();
1768            String[] whereArgs = new String[1];
1769            try {
1770                while (messageCursor.moveToNext()) {
1771                    for (int i = 0; i < ADDRESS_COLUMN_INDICES.length; i++) {
1772                        Address[] addrs =
1773                                Address.fromHeader(messageCursor.getString(ADDRESS_COLUMN_INDICES[i]));
1774                        cv.put(ADDRESS_COLUMN_NAMES[i], Address.toHeader(addrs));
1775                    }
1776                    whereArgs[0] = messageCursor.getString(Message.CONTENT_ID_COLUMN);
1777                    db.update(Message.TABLE_NAME, cv, WHERE_ID, whereArgs);
1778                }
1779            } finally {
1780                messageCursor.close();
1781            }
1782        } catch (SQLException e) {
1783            // Shouldn't be needed unless we're debugging and interrupt the process
1784            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 29 to 30 " + e);
1785        }
1786    }
1787
1788    private static void upgradeFromVersion125ToVersion126(SQLiteDatabase db) {
1789        try {
1790            // Loop through all messages, updating address columns to their decoded form
1791            Cursor messageCursor = db.query(Message.TABLE_NAME, Message.CONTENT_PROJECTION, null,
1792                    null, null, null, null);
1793            ContentValues cv = new ContentValues();
1794            String[] whereArgs = new String[1];
1795            try {
1796                while (messageCursor.moveToNext()) {
1797                    for (int i = 0; i < ADDRESS_COLUMN_INDICES.length; i++) {
1798                        Address[] addrs =
1799                                Address.fromHeader(messageCursor.getString(ADDRESS_COLUMN_INDICES[i]));
1800                        cv.put(ADDRESS_COLUMN_NAMES[i], Address.toString(addrs));
1801                    }
1802                    whereArgs[0] = messageCursor.getString(Message.CONTENT_ID_COLUMN);
1803                    db.update(Message.TABLE_NAME, cv, WHERE_ID, whereArgs);
1804                }
1805            } finally {
1806                messageCursor.close();
1807            }
1808        } catch (SQLException e) {
1809            // Shouldn't be needed unless we're debugging and interrupt the process
1810            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 125 to 126 " + e);
1811        }
1812    }
1813
1814    /**
1815     * Update all accounts that are EAS v12.0 or greater with SmartForward and search flags
1816     */
1817    private static void upgradeFromVersion126ToVersion127(final Context context,
1818            final SQLiteDatabase db) {
1819        try {
1820            // These are the flags that we want to add to the Account table for the
1821            // appropriate rows.
1822            final long newFlags = Account.FLAGS_SUPPORTS_GLOBAL_SEARCH +
1823                    Account.FLAGS_SUPPORTS_SEARCH + Account.FLAGS_SUPPORTS_SMART_FORWARD;
1824
1825            // For posterity; this is the command we're executing:
1826            // UPDATE Account SET flags=flags|[new flags] WHERE _id IN (SELECT t1._id FROM Account
1827            // t1 INNER JOIN HostAuth t2 ON t1.hostAuthKeyRecv=t2._id WHERE t2.protocol='gEas' AND
1828            // CAST(t1.protocolVersion AS REAL)>=12.0)
1829            db.execSQL(
1830                    "UPDATE " + Account.TABLE_NAME + " SET " + AccountColumns.FLAGS + "=" +
1831                            AccountColumns.FLAGS + "|" + Long.toString(newFlags) + " WHERE " +
1832                            AccountColumns._ID + " IN (SELECT t1." + AccountColumns._ID + " FROM " +
1833                            Account.TABLE_NAME + " t1 INNER JOIN " + HostAuth.TABLE_NAME +
1834                            " t2 ON t1." + AccountColumns.HOST_AUTH_KEY_RECV + "=t2._id WHERE t2." +
1835                            HostAuthColumns.PROTOCOL + "='" +
1836                            context.getString(R.string.protocol_eas) + "' AND CAST(t1." +
1837                            AccountColumns.PROTOCOL_VERSION + " AS REAL)>=12.0)");
1838        } catch (SQLException e) {
1839            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 126 to 127 " + e);
1840        }
1841    }
1842
1843    private static void upgradeToEmail2(SQLiteDatabase db) {
1844        // Perform cleanup operations from Email1 to Email2; Email1 will have added new
1845        // data that won't conform to what's expected in Email2
1846
1847        // From 31->32 upgrade
1848        try {
1849            db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY +
1850                    "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY + " IS NULL");
1851            db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT +
1852                    "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT + " IS NULL");
1853        } catch (SQLException e) {
1854            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 31 to 32/100 " + e);
1855        }
1856
1857        // From 32->33 upgrade
1858        try {
1859            db.execSQL("update " + Attachment.TABLE_NAME + " set " + AttachmentColumns.UI_STATE +
1860                    "=" + UIProvider.AttachmentState.SAVED + " where " +
1861                    AttachmentColumns.CONTENT_URI + " is not null;");
1862        } catch (SQLException e) {
1863            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 32 to 33/100 " + e);
1864        }
1865
1866        // From 34->35 upgrade
1867        try {
1868            db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1869                    MailboxColumns.LAST_TOUCHED_TIME + " = " +
1870                    Mailbox.DRAFTS_DEFAULT_TOUCH_TIME + " WHERE " + MailboxColumns.TYPE +
1871                    " = " + Mailbox.TYPE_DRAFTS);
1872            db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1873                    MailboxColumns.LAST_TOUCHED_TIME + " = " +
1874                    Mailbox.SENT_DEFAULT_TOUCH_TIME + " WHERE " + MailboxColumns.TYPE +
1875                    " = " + Mailbox.TYPE_SENT);
1876        } catch (SQLException e) {
1877            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 34 to 35/100 " + e);
1878        }
1879
1880        // From 35/36->37
1881        try {
1882            db.execSQL("update " + Mailbox.TABLE_NAME + " set " +
1883                    MailboxColumns.FLAGS + "=" + MailboxColumns.FLAGS + "|" +
1884                    Mailbox.FLAG_SUPPORTS_SETTINGS + " where (" +
1885                    MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HOLDS_MAIL + ")!=0 and " +
1886                    MailboxColumns.ACCOUNT_KEY + " IN (SELECT " + Account.TABLE_NAME +
1887                    "." + AccountColumns._ID + " from " + Account.TABLE_NAME + "," +
1888                    HostAuth.TABLE_NAME + " where " + Account.TABLE_NAME + "." +
1889                    AccountColumns.HOST_AUTH_KEY_RECV + "=" + HostAuth.TABLE_NAME + "." +
1890                    HostAuthColumns._ID + " and " + HostAuthColumns.PROTOCOL + "='" +
1891                    LEGACY_SCHEME_EAS + "')");
1892        } catch (SQLException e) {
1893            LogUtils.w(TAG, "Exception upgrading EmailProvider.db from 35/36 to 37/100 " + e);
1894        }
1895    }
1896}
1897