|
|
|
@ -4,24 +4,24 @@
|
|
|
|
|
|
|
|
|
|
#import "OWSStorage.h" |
|
|
|
|
#import "AppContext.h" |
|
|
|
|
#import "NSNotificationCenter+OWS.h" |
|
|
|
|
#import "NSUserDefaults+OWS.h" |
|
|
|
|
#import "OWSBackgroundTask.h" |
|
|
|
|
#import "OWSFileSystem.h" |
|
|
|
|
#import "OWSPrimaryStorage.h" |
|
|
|
|
#import "OWSStorage+Subclass.h" |
|
|
|
|
#import "TSYapDatabaseObject.h" |
|
|
|
|
#import "TSAttachmentStream.h" |
|
|
|
|
#import <SignalCoreKit/NSData+OWS.h> |
|
|
|
|
#import <SignalCoreKit/SignalCoreKit.h> |
|
|
|
|
#import <SignalCoreKit/Randomness.h> |
|
|
|
|
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h> |
|
|
|
|
#import <YapDatabase/YapDatabase.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseAutoView.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseCrossProcessNotification.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseCryptoUtils.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseCrossProcessNotification.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseFullTextSearch.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseFullTextSearchPrivate.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseSecondaryIndex.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseSecondaryIndexPrivate.h> |
|
|
|
|
#import <YapDatabase/YapDatabaseSecondaryIndexSetup.h> |
|
|
|
|
#import <SessionUtilitiesKit/SessionUtilitiesKit.h> |
|
|
|
|
#import <SessionUtilitiesKit/AppContext.h> |
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN |
|
|
|
|
|
|
|
|
@ -59,8 +59,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
return self; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
OWSAssertDebug(delegate); |
|
|
|
|
|
|
|
|
|
self.delegate = delegate; |
|
|
|
|
|
|
|
|
|
return self; |
|
|
|
@ -76,8 +74,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
- (void)readWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block |
|
|
|
|
{ |
|
|
|
|
id<OWSDatabaseConnectionDelegate> delegate = self.delegate; |
|
|
|
|
OWSAssertDebug(delegate); |
|
|
|
|
OWSAssertDebug(delegate.areAllRegistrationsComplete); |
|
|
|
|
|
|
|
|
|
OWSBackgroundTask *_Nullable backgroundTask = nil; |
|
|
|
|
if (CurrentAppContext().isMainApp && !CurrentAppContext().isRunningTests) { |
|
|
|
@ -103,8 +99,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
completionBlock:(nullable dispatch_block_t)completionBlock |
|
|
|
|
{ |
|
|
|
|
id<OWSDatabaseConnectionDelegate> delegate = self.delegate; |
|
|
|
|
OWSAssertDebug(delegate); |
|
|
|
|
OWSAssertDebug(delegate.areAllRegistrationsComplete); |
|
|
|
|
|
|
|
|
|
__block OWSBackgroundTask *_Nullable backgroundTask = nil; |
|
|
|
|
if (CurrentAppContext().isMainApp) { |
|
|
|
@ -155,8 +149,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
return self; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
OWSAssertDebug(delegate); |
|
|
|
|
|
|
|
|
|
self.delegate = delegate; |
|
|
|
|
|
|
|
|
|
return self; |
|
|
|
@ -169,7 +161,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
- (YapDatabaseConnection *)newConnection |
|
|
|
|
{ |
|
|
|
|
id<OWSDatabaseConnectionDelegate> delegate = self.delegate; |
|
|
|
|
OWSAssertDebug(delegate); |
|
|
|
|
|
|
|
|
|
OWSDatabaseConnection *connection = [[OWSDatabaseConnection alloc] initWithDatabase:self delegate:delegate]; |
|
|
|
|
[self addConnection:connection]; |
|
|
|
@ -201,8 +192,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)aCoder |
|
|
|
|
{ |
|
|
|
|
OWSFailDebug(@"Tried to save object from unknown collection"); |
|
|
|
|
|
|
|
|
|
return [super encodeWithCoder:aCoder]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -218,22 +207,16 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction |
|
|
|
|
{ |
|
|
|
|
OWSFailDebug(@"Tried to save unknown object"); |
|
|
|
|
|
|
|
|
|
// No-op. |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)touchWithTransaction:(YapDatabaseReadWriteTransaction *)transaction |
|
|
|
|
{ |
|
|
|
|
OWSFailDebug(@"Tried to touch unknown object"); |
|
|
|
|
|
|
|
|
|
// No-op. |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction |
|
|
|
|
{ |
|
|
|
|
OWSFailDebug(@"Tried to remove unknown object"); |
|
|
|
|
|
|
|
|
|
// No-op. |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -253,11 +236,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
cannotDecodeObjectOfClassName:(NSString *)name |
|
|
|
|
originalClasses:(NSArray<NSString *> *)classNames |
|
|
|
|
{ |
|
|
|
|
if ([name isEqualToString:@"TSRecipient"]) { |
|
|
|
|
OWSLogError(@"Could not decode object: %@", name); |
|
|
|
|
} else { |
|
|
|
|
NSLog(@"Could not decode object: %@", name); |
|
|
|
|
} |
|
|
|
|
return [OWSUnknownDBObject class]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -295,9 +273,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (void)dealloc |
|
|
|
|
{ |
|
|
|
|
// Surface memory leaks by logging the deallocation of this class. |
|
|
|
|
OWSLogVerbose(@"Dealloc: %@", self.class); |
|
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -308,7 +283,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// |
|
|
|
|
// The best we can try to do is to discard the current database |
|
|
|
|
// and behave like a clean install. |
|
|
|
|
OWSFailDebug(@"Could not load database"); |
|
|
|
|
|
|
|
|
|
// Try to reset app by deleting all databases. |
|
|
|
|
// |
|
|
|
@ -316,34 +290,27 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// [OWSStorage deleteDatabaseFiles]; |
|
|
|
|
|
|
|
|
|
if (![self tryToLoadDatabase]) { |
|
|
|
|
OWSFailDebug(@"Could not load database (second try)"); |
|
|
|
|
|
|
|
|
|
// Sleep to give analytics events time to be delivered. |
|
|
|
|
[NSThread sleepForTimeInterval:15.0f]; |
|
|
|
|
|
|
|
|
|
OWSFail(@"Failed to initialize database."); |
|
|
|
|
NSAssert(NO, @"Couldn't load database"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (nullable id)dbNotificationObject |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(self.database); |
|
|
|
|
|
|
|
|
|
return self.database; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (BOOL)areAsyncRegistrationsComplete |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
return NO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (BOOL)areSyncRegistrationsComplete |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
return NO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -354,26 +321,22 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (void)runSyncRegistrations |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)runAsyncRegistrationsWithCompletion:(void (^_Nonnull)(void))completion |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)registerExtensionsWithMigrationBlock:(OWSStorageMigrationBlock)migrationBlock |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(migrationBlock); |
|
|
|
|
|
|
|
|
|
__block OWSBackgroundTask *_Nullable backgroundTask = |
|
|
|
|
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; |
|
|
|
|
|
|
|
|
|
[OWSPrimaryStorage.sharedManager runSyncRegistrations]; |
|
|
|
|
|
|
|
|
|
[OWSPrimaryStorage.sharedManager runAsyncRegistrationsWithCompletion:^{ |
|
|
|
|
OWSAssertDebug(self.isStorageReady); |
|
|
|
|
|
|
|
|
|
[self postRegistrationCompleteNotification]; |
|
|
|
|
|
|
|
|
|
migrationBlock(); |
|
|
|
@ -390,10 +353,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// Returns YES IFF all registrations are complete. |
|
|
|
|
+ (void)postRegistrationCompleteNotification |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(self.isStorageReady); |
|
|
|
|
|
|
|
|
|
OWSLogInfo(@""); |
|
|
|
|
|
|
|
|
|
static dispatch_once_t onceToken; |
|
|
|
|
dispatch_once(&onceToken, ^{ |
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:StorageIsReadyNotification |
|
|
|
@ -424,15 +383,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_migrate |
|
|
|
|
options.legacyCipherCompatibilityVersion = 3; |
|
|
|
|
|
|
|
|
|
// If any of these asserts fails, we need to verify and update |
|
|
|
|
// OWSDatabaseConverter which assumes the values of these options. |
|
|
|
|
OWSAssertDebug(options.cipherDefaultkdfIterNumber == 0); |
|
|
|
|
OWSAssertDebug(options.kdfIterNumber == 0); |
|
|
|
|
OWSAssertDebug(options.cipherPageSize == 0); |
|
|
|
|
OWSAssertDebug(options.pragmaPageSize == 0); |
|
|
|
|
OWSAssertDebug(options.pragmaJournalSizeLimit == 0); |
|
|
|
|
OWSAssertDebug(options.pragmaMMapSize == 0); |
|
|
|
|
|
|
|
|
|
return options; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -445,13 +395,11 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// (e.g. by using OWSAssertDebug()) or this database will contain a |
|
|
|
|
// circular reference and will leak. |
|
|
|
|
OWSStorage *strongSelf = weakSelf; |
|
|
|
|
OWSCAssertDebug(strongSelf); |
|
|
|
|
|
|
|
|
|
// Rather than compute this once and capture the value of the key |
|
|
|
|
// in the closure, we prefer to fetch the key from the keychain multiple times |
|
|
|
|
// in order to keep the key out of application memory. |
|
|
|
|
NSData *databaseKeySpec = [strongSelf databaseKeySpec]; |
|
|
|
|
OWSCAssertDebug(databaseKeySpec.length == kSQLCipherKeySpecLength); |
|
|
|
|
return databaseKeySpec; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -484,7 +432,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
return ^id(NSString __unused *collection, NSString __unused *key, NSData *data) { |
|
|
|
|
if (!data || data.length <= 0) { |
|
|
|
|
OWSFailDebug(@"can't deserialize null object: %@", collection); |
|
|
|
|
return [OWSUnknownDBObject new]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -502,9 +449,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
- (YapDatabaseConnection *)newDatabaseConnection |
|
|
|
|
{ |
|
|
|
|
YapDatabaseConnection *dbConnection = self.database.newConnection; |
|
|
|
|
if (!dbConnection) { |
|
|
|
|
OWSFail(@"Storage could not open new database connection."); |
|
|
|
|
} |
|
|
|
|
return dbConnection; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -512,8 +456,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
+ (void)incrementVersionOfDatabaseExtension:(NSString *)extensionName |
|
|
|
|
{ |
|
|
|
|
OWSLogError(@"%@", extensionName); |
|
|
|
|
|
|
|
|
|
// Don't increment version of a given extension more than once |
|
|
|
|
// per launch. |
|
|
|
|
static NSMutableSet<NSString *> *incrementedViewSet = nil; |
|
|
|
@ -523,14 +465,12 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
}); |
|
|
|
|
@synchronized(incrementedViewSet) { |
|
|
|
|
if ([incrementedViewSet containsObject:extensionName]) { |
|
|
|
|
OWSLogInfo(@"Ignoring redundant increment: %@", extensionName); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
[incrementedViewSet addObject:extensionName]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
NSUserDefaults *appUserDefaults = [NSUserDefaults appUserDefaults]; |
|
|
|
|
OWSAssertDebug(appUserDefaults); |
|
|
|
|
NSMutableDictionary<NSString *, NSNumber *> *_Nullable versionMap = |
|
|
|
|
[[appUserDefaults valueForKey:kNSUserDefaults_DatabaseExtensionVersionMap] mutableCopy]; |
|
|
|
|
if (!versionMap) { |
|
|
|
@ -545,10 +485,7 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
- (nullable NSString *)appendSuffixToDatabaseExtensionVersionIfNecessary:(nullable NSString *)versionTag |
|
|
|
|
extensionName:(NSString *)extensionName |
|
|
|
|
{ |
|
|
|
|
OWSAssertIsOnMainThread(); |
|
|
|
|
|
|
|
|
|
NSUserDefaults *appUserDefaults = [NSUserDefaults appUserDefaults]; |
|
|
|
|
OWSAssertDebug(appUserDefaults); |
|
|
|
|
NSDictionary<NSString *, NSNumber *> *_Nullable versionMap = |
|
|
|
|
[appUserDefaults valueForKey:kNSUserDefaults_DatabaseExtensionVersionMap]; |
|
|
|
|
NSNumber *_Nullable versionSuffix = versionMap[extensionName]; |
|
|
|
@ -556,7 +493,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
if (versionSuffix) { |
|
|
|
|
NSString *result = |
|
|
|
|
[NSString stringWithFormat:@"%@.%@", (versionTag.length < 1 ? @"0" : versionTag), versionSuffix]; |
|
|
|
|
OWSLogWarn(@"database extension version: %@ + %@ -> %@", versionTag, versionSuffix, result); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
return versionTag; |
|
|
|
@ -564,9 +500,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (YapDatabaseExtension *)updateExtensionVersion:(YapDatabaseExtension *)extension withName:(NSString *)extensionName |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(extension); |
|
|
|
|
OWSAssertDebug(extensionName.length > 0); |
|
|
|
|
|
|
|
|
|
if ([extension isKindOfClass:[YapDatabaseAutoView class]]) { |
|
|
|
|
YapDatabaseAutoView *databaseView = (YapDatabaseAutoView *)extension; |
|
|
|
|
YapDatabaseAutoView *databaseViewCopy = [[YapDatabaseAutoView alloc] |
|
|
|
@ -578,8 +511,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
return databaseViewCopy; |
|
|
|
|
} else if ([extension isKindOfClass:[YapDatabaseSecondaryIndex class]]) { |
|
|
|
|
YapDatabaseSecondaryIndex *secondaryIndex = (YapDatabaseSecondaryIndex *)extension; |
|
|
|
|
OWSAssertDebug(secondaryIndex->setup); |
|
|
|
|
OWSAssertDebug(secondaryIndex->handler); |
|
|
|
|
YapDatabaseSecondaryIndex *secondaryIndexCopy = [[YapDatabaseSecondaryIndex alloc] |
|
|
|
|
initWithSetup:secondaryIndex->setup |
|
|
|
|
handler:secondaryIndex->handler |
|
|
|
@ -606,7 +537,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// This method needs to be able to update the versionTag of all extensions. |
|
|
|
|
// If we start using other extension types, we need to modify this method to |
|
|
|
|
// handle them as well. |
|
|
|
|
OWSFailDebug(@"Unknown extension type: %@", [extension class]); |
|
|
|
|
|
|
|
|
|
return extension; |
|
|
|
|
} |
|
|
|
@ -616,7 +546,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
{ |
|
|
|
|
extension = [self updateExtensionVersion:extension withName:extensionName]; |
|
|
|
|
|
|
|
|
|
OWSAssertDebug(![self.extensionNames containsObject:extensionName]); |
|
|
|
|
[self.extensionNames addObject:extensionName]; |
|
|
|
|
|
|
|
|
|
return [self.database registerExtension:extension withName:extensionName]; |
|
|
|
@ -634,18 +563,11 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
{ |
|
|
|
|
extension = [self updateExtensionVersion:extension withName:extensionName]; |
|
|
|
|
|
|
|
|
|
OWSAssertDebug(![self.extensionNames containsObject:extensionName]); |
|
|
|
|
[self.extensionNames addObject:extensionName]; |
|
|
|
|
|
|
|
|
|
[self.database asyncRegisterExtension:extension |
|
|
|
|
withName:extensionName |
|
|
|
|
completionBlock:^(BOOL ready) { |
|
|
|
|
if (!ready) { |
|
|
|
|
OWSFailDebug(@"asyncRegisterExtension failed: %@", extensionName); |
|
|
|
|
} else { |
|
|
|
|
OWSLogVerbose(@"asyncRegisterExtension succeeded: %@", extensionName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
|
|
|
if (completion) { |
|
|
|
|
completion(); |
|
|
|
@ -710,22 +632,16 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (NSString *)databaseFilePath |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
return @""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (NSString *)databaseFilePath_SHM |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
return @""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (NSString *)databaseFilePath_WAL |
|
|
|
|
{ |
|
|
|
|
OWSAbstractMethod(); |
|
|
|
|
|
|
|
|
|
return @""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -740,10 +656,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
return YES; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
OWSLogWarn(@"Database key couldn't be accessed: %@", error.localizedDescription); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return NO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -755,29 +667,21 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
+ (nullable NSData *)tryToLoadDatabaseCipherKeySpec:(NSError **)errorHandle |
|
|
|
|
{ |
|
|
|
|
NSData *_Nullable data = [self tryToLoadKeyChainValue:keychainDBCipherKeySpec errorHandle:errorHandle]; |
|
|
|
|
OWSAssertDebug(!data || data.length == kSQLCipherKeySpecLength); |
|
|
|
|
|
|
|
|
|
return data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)storeDatabaseCipherKeySpec:(NSData *)cipherKeySpecData |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(cipherKeySpecData.length == kSQLCipherKeySpecLength); |
|
|
|
|
|
|
|
|
|
[self storeKeyChainValue:cipherKeySpecData keychainKey:keychainDBCipherKeySpec]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)removeLegacyPassphrase |
|
|
|
|
{ |
|
|
|
|
OWSLogInfo(@"removing legacy passphrase"); |
|
|
|
|
|
|
|
|
|
NSError *_Nullable error; |
|
|
|
|
BOOL result = [CurrentAppContext().keychainStorage removeWithService:keychainService |
|
|
|
|
key:keychainDBLegacyPassphrase |
|
|
|
|
error:&error]; |
|
|
|
|
if (error || !result) { |
|
|
|
|
OWSFailDebug(@"could not remove legacy passphrase."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)ensureDatabaseKeySpecExists |
|
|
|
@ -800,8 +704,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
errorDescription = [errorDescription |
|
|
|
|
stringByAppendingFormat:@", ApplicationState: %@", NSStringForUIApplicationState(applicationState)]; |
|
|
|
|
} |
|
|
|
|
OWSLogError(@"%@", errorDescription); |
|
|
|
|
[DDLog flushLog]; |
|
|
|
|
|
|
|
|
|
if (CurrentAppContext().isMainApp) { |
|
|
|
|
if (CurrentAppContext().isInBackground) { |
|
|
|
@ -818,9 +720,6 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
// or the keychain has become corrupt. Either way, we want to get back to a |
|
|
|
|
// "known good state" and behave like a new install. |
|
|
|
|
BOOL doesDBExist = [NSFileManager.defaultManager fileExistsAtPath:[self databaseFilePath]]; |
|
|
|
|
if (doesDBExist) { |
|
|
|
|
OWSFailDebug(@"Could not load database metadata"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!CurrentAppContext().isRunningTests) { |
|
|
|
|
// Try to reset app by deleting database. |
|
|
|
@ -838,12 +737,10 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
NSData *_Nullable keySpec = [[self class] tryToLoadDatabaseCipherKeySpec:&error]; |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
OWSLogError(@"failed to fetch databaseKeySpec with error: %@", error); |
|
|
|
|
[self raiseKeySpecInaccessibleExceptionWithErrorDescription:@"CipherKeySpec inaccessible"]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (keySpec.length != kSQLCipherKeySpecLength) { |
|
|
|
|
OWSLogError(@"keyspec had length: %lu", (unsigned long)keySpec.length); |
|
|
|
|
[self raiseKeySpecInaccessibleExceptionWithErrorDescription:@"CipherKeySpec invalid"]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -852,15 +749,12 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
- (void)raiseKeySpecInaccessibleExceptionWithErrorDescription:(NSString *)errorDescription |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(CurrentAppContext().isMainApp && CurrentAppContext().isInBackground); |
|
|
|
|
|
|
|
|
|
// Sleep to give analytics events time to be delivered. |
|
|
|
|
[NSThread sleepForTimeInterval:5.0f]; |
|
|
|
|
|
|
|
|
|
// Presumably this happened in response to a push notification. It's possible that the keychain is corrupted |
|
|
|
|
// but it could also just be that the user hasn't yet unlocked their device since our password is |
|
|
|
|
// kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly |
|
|
|
|
OWSFail(@"%@", errorDescription); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)deleteDBKeys |
|
|
|
@ -869,15 +763,9 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
BOOL result = [CurrentAppContext().keychainStorage removeWithService:keychainService |
|
|
|
|
key:keychainDBLegacyPassphrase |
|
|
|
|
error:&error]; |
|
|
|
|
if (error || !result) { |
|
|
|
|
OWSFailDebug(@"could not remove legacy passphrase."); |
|
|
|
|
} |
|
|
|
|
result = [CurrentAppContext().keychainStorage removeWithService:keychainService |
|
|
|
|
key:keychainDBCipherKeySpec |
|
|
|
|
error:&error]; |
|
|
|
|
if (error || !result) { |
|
|
|
|
OWSFailDebug(@"could not remove cipher key spec."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (unsigned long long)databaseFileSize |
|
|
|
@ -897,42 +785,27 @@ NSString *const kNSUserDefaults_DatabaseExtensionVersionMap = @"kNSUserDefaults_
|
|
|
|
|
|
|
|
|
|
+ (nullable NSData *)tryToLoadKeyChainValue:(NSString *)keychainKey errorHandle:(NSError **)errorHandle |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(keychainKey.length > 0); |
|
|
|
|
OWSAssertDebug(errorHandle); |
|
|
|
|
|
|
|
|
|
NSData *_Nullable data = |
|
|
|
|
[CurrentAppContext().keychainStorage dataForService:keychainService key:keychainKey error:errorHandle]; |
|
|
|
|
if (*errorHandle || !data) { |
|
|
|
|
OWSLogWarn(@"could not load keychain value."); |
|
|
|
|
} |
|
|
|
|
return data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)storeKeyChainValue:(NSData *)data keychainKey:(NSString *)keychainKey |
|
|
|
|
{ |
|
|
|
|
OWSAssertDebug(keychainKey.length > 0); |
|
|
|
|
OWSAssertDebug(data.length > 0); |
|
|
|
|
|
|
|
|
|
NSError *error; |
|
|
|
|
BOOL success = |
|
|
|
|
[CurrentAppContext().keychainStorage setWithData:data service:keychainService key:keychainKey error:&error]; |
|
|
|
|
if (!success || error) { |
|
|
|
|
OWSFailDebug(@"Could not store database metadata"); |
|
|
|
|
|
|
|
|
|
// Sleep to give analytics events time to be delivered. |
|
|
|
|
[NSThread sleepForTimeInterval:15.0f]; |
|
|
|
|
|
|
|
|
|
OWSFail(@"Setting keychain value failed with error: %@", error); |
|
|
|
|
} else { |
|
|
|
|
OWSLogWarn(@"Successfully set new keychain value."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)logFileSizes |
|
|
|
|
{ |
|
|
|
|
OWSLogInfo(@"Database file size: %@", [OWSFileSystem fileSizeOfPath:self.databaseFilePath]); |
|
|
|
|
OWSLogInfo(@"\t SHM file size: %@", [OWSFileSystem fileSizeOfPath:self.databaseFilePath_SHM]); |
|
|
|
|
OWSLogInfo(@"\t WAL file size: %@", [OWSFileSystem fileSizeOfPath:self.databaseFilePath_WAL]); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@end |