From dc733425737cd8bee7fb735c9a1622502ca65cb6 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 18 Jan 2018 16:32:15 -0500 Subject: [PATCH] Convert databases if necessary. --- Podfile | 5 +- Podfile.lock | 23 +++++-- Signal.xcodeproj/project.pbxproj | 10 ++- Signal/src/AppDelegate.m | 3 + SignalMessaging/utils/OWSDatabaseConverter.h | 19 ++++++ SignalMessaging/utils/OWSDatabaseConverter.m | 67 +++++++++++++++++++ .../src/Storage/TSStorageManager.h | 2 + 7 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 SignalMessaging/utils/OWSDatabaseConverter.h create mode 100644 SignalMessaging/utils/OWSDatabaseConverter.m diff --git a/Podfile b/Podfile index 06e46e074..1b02e5d0e 100644 --- a/Podfile +++ b/Podfile @@ -5,6 +5,9 @@ use_frameworks! def shared_pods # OWS Pods + pod 'SQLCipher', :git => 'https://github.com/sqlcipher/sqlcipher.git', :commit => 'd5c2bec' + pod 'YapDatabase/SQLCipher', path: '../YapDatabase' + # pod 'YapDatabase/SQLCipher', :git => 'https://github.com/WhisperSystems/YapDatabase.git', branch: 'charlesmchen/signal' pod 'SignalServiceKit', path: '.' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git', branch: 'mkirk/framework-friendly' #pod 'AxolotlKit', path: '../SignalProtocolKit' @@ -21,7 +24,7 @@ def shared_pods #pod 'JSQMessagesViewController', git: 'https://github.com/WhisperSystems/JSQMessagesViewController.git', branch: 'signal-master', :inhibit_warnings => true #pod 'JSQMessagesViewController', path: '../JSQMessagesViewController' pod 'Mantle', :inhibit_warnings => true - pod 'YapDatabase/SQLCipher', :inhibit_warnings => true + # pod 'YapDatabase/SQLCipher', :inhibit_warnings => true pod 'PureLayout', :inhibit_warnings => true pod 'Reachability', :inhibit_warnings => true pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index 9c34fd950..b32abaf96 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -54,10 +54,10 @@ PODS: - TwistedOakCollapsingFutures - YapDatabase/SQLCipher - SocketRocket (0.5.1) - - SQLCipher (3.4.1): - - SQLCipher/standard (= 3.4.1) - - SQLCipher/common (3.4.1) - - SQLCipher/standard (3.4.1): + - SQLCipher (3.4.2): + - SQLCipher/standard (= 3.4.2) + - SQLCipher/common (3.4.2) + - SQLCipher/standard (3.4.2): - SQLCipher/common - SSZipArchive (2.1.1) - TwistedOakCollapsingFutures (1.0.0): @@ -139,8 +139,9 @@ DEPENDENCIES: - Reachability - SignalServiceKit (from `.`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) + - SQLCipher (from `https://github.com/sqlcipher/sqlcipher.git`, commit `d5c2bec`) - SSZipArchive - - YapDatabase/SQLCipher + - YapDatabase/SQLCipher (from `../YapDatabase`) - YYImage EXTERNAL SOURCES: @@ -162,6 +163,11 @@ EXTERNAL SOURCES: :path: . SocketRocket: :git: https://github.com/facebook/SocketRocket.git + SQLCipher: + :commit: d5c2bec + :git: https://github.com/sqlcipher/sqlcipher.git + YapDatabase: + :path: ../YapDatabase CHECKOUT OPTIONS: AxolotlKit: @@ -182,6 +188,9 @@ CHECKOUT OPTIONS: SocketRocket: :commit: 28035e1a98a427853e4038ff1b70479fa8374cfa :git: https://github.com/facebook/SocketRocket.git + SQLCipher: + :commit: d5c2bec + :git: https://github.com/sqlcipher/sqlcipher.git SPEC CHECKSUMS: AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 @@ -201,13 +210,13 @@ SPEC CHECKSUMS: SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SignalServiceKit: 8f9038e584080bee8c367268067e6e0ec0feefcf SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e - SQLCipher: 43d12c0eb9c57fb438749618fc3ce0065509a559 + SQLCipher: f9fcf29b2e59ced7defc2a2bdd0ebe79b40d4990 SSZipArchive: 14401ade5f8e82aba1ff03e9f88e9de60937ae60 TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 -PODFILE CHECKSUM: fe273b089523b52e20652cebcf4854a80934f46c +PODFILE CHECKSUM: 5d70451d47917767e5120e09e4b5967959971dd6 COCOAPODS: 1.3.1 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 5e37de0b7..6e29a065d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -104,6 +104,8 @@ 34612A071FD7238600532771 /* OWSContactsSyncing.m in Sources */ = {isa = PBXBuildFile; fileRef = 34612A051FD7238500532771 /* OWSContactsSyncing.m */; }; 346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; }; 3472229F1EB22FFE00E53955 /* AddToGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */; }; + 34782E452011428D00A6F16B /* OWSDatabaseConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 34782E432011428D00A6F16B /* OWSDatabaseConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34782E462011428D00A6F16B /* OWSDatabaseConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 34782E442011428D00A6F16B /* OWSDatabaseConverter.m */; }; 347850311FD7494A007B8332 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; }; 347850321FD7494A007B8332 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */; }; 347850331FD7494A007B8332 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; }; @@ -591,6 +593,8 @@ 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropScaleImageViewController.swift; sourceTree = ""; }; 3472229D1EB22FFE00E53955 /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = ""; }; 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToGroupViewController.m; sourceTree = ""; }; + 34782E432011428D00A6F16B /* OWSDatabaseConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDatabaseConverter.h; sourceTree = ""; }; + 34782E442011428D00A6F16B /* OWSDatabaseConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDatabaseConverter.m; sourceTree = ""; }; 347850561FD86544007B8332 /* SAEFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEFailedViewController.swift; sourceTree = ""; }; 347850581FD9972E007B8332 /* SwiftSingletons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSingletons.swift; sourceTree = ""; }; 3478505A1FD999D5007B8332 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = translations/et.lproj/Localizable.strings; sourceTree = ""; }; @@ -1108,6 +1112,8 @@ 45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */, 45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */, 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */, + 34782E432011428D00A6F16B /* OWSDatabaseConverter.h */, + 34782E442011428D00A6F16B /* OWSDatabaseConverter.m */, 346129A81FD1F0DF00532771 /* OWSFormat.h */, 346129AA1FD1F0EE00532771 /* OWSFormat.m */, 45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */, @@ -1122,11 +1128,11 @@ 34480B511FD0A7A400BC14EF /* OWSScrubbingLogFormatter.m */, 346129331FD1A88700532771 /* OWSSwiftUtils.swift */, 45360B8C1F9521F800FA666C /* Searcher.swift */, + 347850581FD9972E007B8332 /* SwiftSingletons.swift */, 346129BD1FD2068600532771 /* ThreadUtil.h */, 346129BE1FD2068600532771 /* ThreadUtil.m */, B97940251832BD2400BD66CB /* UIUtil.h */, B97940261832BD2400BD66CB /* UIUtil.m */, - 347850581FD9972E007B8332 /* SwiftSingletons.swift */, 346129751FD1E0B500532771 /* WeakTimer.swift */, ); path = utils; @@ -2034,6 +2040,7 @@ buildActionMask = 2147483647; files = ( 451F8A3A1FD711D9005CB9DA /* ContactsViewHelper.h in Headers */, + 34782E452011428D00A6F16B /* OWSDatabaseConverter.h in Headers */, 34480B491FD0A60200BC14EF /* OWSMath.h in Headers */, 346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */, 344D6CEA20069E070042AF96 /* SelectRecipientViewController.h in Headers */, @@ -2792,6 +2799,7 @@ 346129D01FD207F300532771 /* OWSAlerts.swift in Sources */, 454A965F1FD60EA3008D2A0E /* OWSFlatButton.swift in Sources */, 346129B61FD1F7E800532771 /* ProfileFetcherJob.swift in Sources */, + 34782E462011428D00A6F16B /* OWSDatabaseConverter.m in Sources */, 346129F51FD5F31400532771 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */, 45194F8F1FD71FF500333B2C /* ThreadUtil.m in Sources */, 451F8A3B1FD71297005CB9DA /* UIUtil.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 72418628c..4a91a3440 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import #import @@ -219,6 +220,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; return; } + [OWSDatabaseConverter convertDatabaseIfNecessary]; + [NSUserDefaults migrateToSharedUserDefaults]; [TSStorageManager migrateToSharedData]; diff --git a/SignalMessaging/utils/OWSDatabaseConverter.h b/SignalMessaging/utils/OWSDatabaseConverter.h new file mode 100644 index 000000000..012e955ac --- /dev/null +++ b/SignalMessaging/utils/OWSDatabaseConverter.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +// Used to convert YapDatabase/SQLCipher databases whose header is encrypted +// to databases whose first 32 bytes are unencrypted so that iOS can determine +// that this is a SQLite database using WAL and therefore not terminate the app +// when it is suspended. +@interface OWSDatabaseConverter : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (void)convertDatabaseIfNecessary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/utils/OWSDatabaseConverter.m b/SignalMessaging/utils/OWSDatabaseConverter.m new file mode 100644 index 000000000..491bf27da --- /dev/null +++ b/SignalMessaging/utils/OWSDatabaseConverter.m @@ -0,0 +1,67 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "OWSDatabaseConverter.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation OWSDatabaseConverter + ++ (BOOL)doesDatabaseNeedToBeConverted +{ + NSString *databaseFilePath = [TSStorageManager legacyDatabaseFilePath]; + if (![[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]) { + DDLogVerbose(@"%@ Skipping database conversion; no legacy database found.", self.logTag); + return NO; + } + NSError *error; + // We use NSDataReadingMappedAlways instead of NSDataReadingMappedIfSafe because + // we know the database will always exist for the duration of this instance of NSData. + NSData *_Nullable data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:databaseFilePath] + options:NSDataReadingMappedAlways + error:&error]; + if (!data || error) { + DDLogError(@"%@ Couldn't read legacy database file header.", self.logTag); + // TODO: Make a convenience method (on a category of NSException?) that + // flushes DDLog before raising a terminal exception. + [NSException raise:@"Couldn't read legacy database file header" format:@""]; + } + // Pull this constant out so that we can use it in our YapDatabase fork. + const int kSqliteHeaderLength = 32; + NSData *_Nullable headerData = [data subdataWithRange:NSMakeRange(0, kSqliteHeaderLength)]; + if (!headerData || headerData.length != kSqliteHeaderLength) { + [NSException raise:@"Database database file header has unexpected length" + format:@"Database database file header has unexpected length: %zd", headerData.length]; + } + NSString *kUnencryptedHeader = @"SQLite format 3\0"; + NSData *unencryptedHeaderData = [kUnencryptedHeader dataUsingEncoding:NSUTF8StringEncoding]; + BOOL isUnencrypted = [unencryptedHeaderData + isEqualToData:[headerData subdataWithRange:NSMakeRange(0, unencryptedHeaderData.length)]]; + if (isUnencrypted) { + DDLogVerbose(@"%@ Skipping database conversion; legacy database header already decrypted.", self.logTag); + return NO; + } + + return YES; +} + ++ (void)convertDatabaseIfNecessary +{ + if (![self doesDatabaseNeedToBeConverted]) { + return; + } + + [self convertDatabase]; +} + ++ (void)convertDatabase +{ + // TODO: +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Storage/TSStorageManager.h b/SignalServiceKit/src/Storage/TSStorageManager.h index 12651ae9c..44f9b4407 100644 --- a/SignalServiceKit/src/Storage/TSStorageManager.h +++ b/SignalServiceKit/src/Storage/TSStorageManager.h @@ -25,6 +25,8 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage); + (NSString *)databaseFilePath; ++ (NSString *)legacyDatabaseFilePath; + @end NS_ASSUME_NONNULL_END