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