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