diff --git a/Podfile.lock b/Podfile.lock
index bebd66947e..d389890282 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -188,6 +188,6 @@ SPEC CHECKSUMS:
YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
-PODFILE CHECKSUM: 8e42a06de2d254e448580cfd6212545739de62d6
+PODFILE CHECKSUM: 558b99b22f87c7fb6e4a9c3084b14a384203d431
COCOAPODS: 1.3.1
diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj
index 29fef9e46a..5ba2b140a6 100644
--- a/Signal.xcodeproj/project.pbxproj
+++ b/Signal.xcodeproj/project.pbxproj
@@ -2052,6 +2052,9 @@
LastSwiftMigration = 0800;
ProvisioningStyle = Automatic;
SystemCapabilities = {
+ com.apple.ApplicationGroups.iOS = {
+ enabled = 1;
+ };
com.apple.DataProtection = {
enabled = 1;
};
@@ -2061,6 +2064,9 @@
com.apple.InterAppAudio = {
enabled = 0;
};
+ com.apple.Keychain = {
+ enabled = 1;
+ };
com.apple.Push = {
enabled = 1;
};
diff --git a/Signal/Signal.entitlements b/Signal/Signal.entitlements
index dfec421eaa..79da5ce675 100644
--- a/Signal/Signal.entitlements
+++ b/Signal/Signal.entitlements
@@ -4,6 +4,8 @@
aps-environment
development
+ com.apple.developer.default-data-protection
+ NSFileProtectionComplete
com.apple.developer.icloud-container-identifiers
iCloud.$(CFBundleIdentifier)
@@ -16,5 +18,13 @@
iCloud.$(CFBundleIdentifier)
+ com.apple.security.application-groups
+
+ group.org.whispersystems.signal.group
+
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)org.whispersystems.signal
+
diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m
index 5f27070441..fff346c7c9 100644
--- a/Signal/src/AppDelegate.m
+++ b/Signal/src/AppDelegate.m
@@ -24,6 +24,8 @@
#import "VersionMigrations.h"
#import "ViewControllerUtils.h"
#import
+#import
+#import
#import
#import
#import
@@ -38,7 +40,6 @@
#import
#import
#import
-#import
@import WebRTC;
@import Intents;
@@ -103,6 +104,19 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
DDLogWarn(@"%@ application: didFinishLaunchingWithOptions.", self.logTag);
+ // We need to do this _after_ we set up logging but _before_ we do
+ // anything else.
+ [self ensureIsReadyForAppExtensions];
+
+#if RELEASE
+ // ensureIsReadyForAppExtensions may have changed the state of the logging
+ // preference (due to [NSUserDefaults migrateToSharedUserDefaults]), so honor
+ // that change if necessary.
+ if (loggingIsEnabled && !OWSPreferences.loggingIsEnabled) {
+ [DebugLogger.sharedLogger disableFileLogging];
+ }
+#endif
+
[AppVersion instance];
[self startupLogging];
@@ -176,6 +190,21 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
return YES;
}
+- (void)ensureIsReadyForAppExtensions
+{
+ if ([OWSPreferences isReadyForAppExtensions]) {
+ return;
+ }
+
+ [NSUserDefaults migrateToSharedUserDefaults];
+
+ [TSStorageManager migrateToSharedData];
+ [OWSProfileManager migrateToSharedData];
+ [TSAttachmentStream migrateToSharedData];
+
+ [OWSPreferences setIsReadyForAppExtensions:YES];
+}
+
- (void)startupLogging
{
DDLogInfo(@"iOS Version: %@", [UIDevice currentDevice].systemVersion);
diff --git a/Signal/src/Profiles/OWSProfileManager.h b/Signal/src/Profiles/OWSProfileManager.h
index 3d67415f02..331dddb230 100644
--- a/Signal/src/Profiles/OWSProfileManager.h
+++ b/Signal/src/Profiles/OWSProfileManager.h
@@ -29,6 +29,8 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
- (void)resetProfileStorage;
++ (void)migrateToSharedData;
+
#pragma mark - Local Profile
// These two methods should only be called from the main thread.
diff --git a/Signal/src/Profiles/OWSProfileManager.m b/Signal/src/Profiles/OWSProfileManager.m
index cc07377599..8c05020f7d 100644
--- a/Signal/src/Profiles/OWSProfileManager.m
+++ b/Signal/src/Profiles/OWSProfileManager.m
@@ -1384,14 +1384,29 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
return image;
}
++ (NSString *)legacyProfileAvatarsDirPath
+{
+ return [[OWSFileSystem appDocumentDirectoryPath] stringByAppendingPathComponent:@"ProfileAvatars"];
+}
+
++ (NSString *)sharedDataProfileAvatarsDirPath
+{
+ return [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"ProfileAvatars"];
+}
+
++ (void)migrateToSharedData
+{
+ [OWSFileSystem moveAppFilePath:self.legacyProfileAvatarsDirPath
+ sharedDataFilePath:self.sharedDataProfileAvatarsDirPath
+ exceptionName:@"ProfileManagerCouldNotMigrateProfileDirectory"];
+}
+
- (NSString *)profileAvatarsDirPath
{
static NSString *profileAvatarsDirPath = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- NSString *documentsPath =
- [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
- profileAvatarsDirPath = [documentsPath stringByAppendingPathComponent:@"ProfileAvatars"];
+ profileAvatarsDirPath = OWSProfileManager.sharedDataProfileAvatarsDirPath;
BOOL isDirectory;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:profileAvatarsDirPath isDirectory:&isDirectory];
diff --git a/Signal/src/environment/OWSPreferences.h b/Signal/src/environment/OWSPreferences.h
index 27a0d674cc..6a0cb1882b 100644
--- a/Signal/src/environment/OWSPreferences.h
+++ b/Signal/src/environment/OWSPreferences.h
@@ -27,6 +27,9 @@ extern NSString *const OWSPreferencesKeyEnableDebugLog;
#pragma mark - Specific Preferences
++ (BOOL)isReadyForAppExtensions;
++ (void)setIsReadyForAppExtensions:(BOOL)value;
+
- (BOOL)getHasSentAMessage;
- (void)setHasSentAMessage:(BOOL)enabled;
diff --git a/Signal/src/environment/OWSPreferences.m b/Signal/src/environment/OWSPreferences.m
index 6e7e66467f..8548715f8a 100644
--- a/Signal/src/environment/OWSPreferences.m
+++ b/Signal/src/environment/OWSPreferences.m
@@ -4,6 +4,7 @@
#import "OWSPreferences.h"
#import "TSStorageHeaders.h"
+#import
NS_ASSUME_NONNULL_BEGIN
@@ -22,6 +23,7 @@ NSString *const OWSPreferencesKeyCallKitPrivacyEnabled = @"CallKitPrivacyEnabled
NSString *const OWSPreferencesKeyCallsHideIPAddress = @"CallsHideIPAddress";
NSString *const OWSPreferencesKeyHasDeclinedNoContactsView = @"hasDeclinedNoContactsView";
NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion";
+NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExtensions";
@implementation OWSPreferences
@@ -40,10 +42,7 @@ NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion";
#pragma mark - Helpers
- (void)clear {
- @synchronized(self) {
- NSString *appDomain = NSBundle.mainBundle.bundleIdentifier;
- [NSUserDefaults.standardUserDefaults removePersistentDomainForName:appDomain];
- }
+ [NSUserDefaults removeAll];
}
- (nullable id)tryGetValueForKey:(NSString *)key
@@ -61,6 +60,23 @@ NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion";
#pragma mark - Specific Preferences
++ (BOOL)isReadyForAppExtensions
+{
+ NSNumber *preference = [NSUserDefaults.appUserDefaults objectForKey:OWSPreferencesKey_IsReadyForAppExtensions];
+
+ if (preference) {
+ return [preference boolValue];
+ } else {
+ return NO;
+ }
+}
+
++ (void)setIsReadyForAppExtensions:(BOOL)value
+{
+ [NSUserDefaults.appUserDefaults setObject:@(value) forKey:OWSPreferencesKey_IsReadyForAppExtensions];
+ [NSUserDefaults.appUserDefaults synchronize];
+}
+
- (BOOL)screenSecurityIsEnabled
{
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyScreenSecurity];
@@ -94,7 +110,7 @@ NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion";
+ (BOOL)loggingIsEnabled
{
- NSNumber *preference = [NSUserDefaults.standardUserDefaults objectForKey:OWSPreferencesKeyEnableDebugLog];
+ NSNumber *preference = [NSUserDefaults.appUserDefaults objectForKey:OWSPreferencesKeyEnableDebugLog];
if (preference) {
return [preference boolValue];
@@ -108,8 +124,8 @@ NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion";
// Logging preferences are stored in UserDefaults instead of the database, so that we can (optionally) start
// logging before the database is initialized. This is important because sometimes there are problems *with* the
// database initialization, and without logging it would be hard to track down.
- [NSUserDefaults.standardUserDefaults setObject:@(flag) forKey:OWSPreferencesKeyEnableDebugLog];
- [NSUserDefaults.standardUserDefaults synchronize];
+ [NSUserDefaults.appUserDefaults setObject:@(flag) forKey:OWSPreferencesKeyEnableDebugLog];
+ [NSUserDefaults.appUserDefaults synchronize];
}
- (void)setHasSentAMessage:(BOOL)enabled
diff --git a/Signal/src/environment/VersionMigrations.m b/Signal/src/environment/VersionMigrations.m
index 5afe4810d9..ebf2caf298 100644
--- a/Signal/src/environment/VersionMigrations.m
+++ b/Signal/src/environment/VersionMigrations.m
@@ -8,9 +8,10 @@
#import "OWSDatabaseMigrationRunner.h"
#import "PushManager.h"
#import "SignalKeyingStorage.h"
-#import "TSAccountManager.h"
-#import "TSNetworkManager.h"
#import
+#import
+#import
+#import
#define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push"
#define NEEDS_TO_REGISTER_ATTRIBUTES @"Register Attributes"
@@ -125,39 +126,39 @@
+ (void)blockingAttributesUpdate {
LIControllerBlockingOperation blockingOperation = ^BOOL(void) {
- [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:NEEDS_TO_REGISTER_ATTRIBUTES];
+ [[NSUserDefaults appUserDefaults] setObject:@YES forKey:NEEDS_TO_REGISTER_ATTRIBUTES];
- __block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+ __block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- __block BOOL success;
+ __block BOOL success;
- TSUpdateAttributesRequest *request = [[TSUpdateAttributesRequest alloc] initWithManualMessageFetching:NO];
- [[TSNetworkManager sharedManager] makeRequest:request
- success:^(NSURLSessionDataTask *task, id responseObject) {
- success = YES;
- dispatch_semaphore_signal(sema);
- }
- failure:^(NSURLSessionDataTask *task, NSError *error) {
- if (!IsNSErrorNetworkFailure(error)) {
- OWSProdError([OWSAnalyticsEvents errorUpdateAttributesRequestFailed]);
- }
- success = NO;
- DDLogError(@"Updating attributess failed with error: %@", error.description);
- dispatch_semaphore_signal(sema);
- }];
+ TSUpdateAttributesRequest *request = [[TSUpdateAttributesRequest alloc] initWithManualMessageFetching:NO];
+ [[TSNetworkManager sharedManager] makeRequest:request
+ success:^(NSURLSessionDataTask *task, id responseObject) {
+ success = YES;
+ dispatch_semaphore_signal(sema);
+ }
+ failure:^(NSURLSessionDataTask *task, NSError *error) {
+ if (!IsNSErrorNetworkFailure(error)) {
+ OWSProdError([OWSAnalyticsEvents errorUpdateAttributesRequestFailed]);
+ }
+ success = NO;
+ DDLogError(@"Updating attributess failed with error: %@", error.description);
+ dispatch_semaphore_signal(sema);
+ }];
- dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
- return success;
+ return success;
};
LIControllerRetryBlock retryBlock = [LockInteractionController defaultNetworkRetry];
[LockInteractionController performBlock:blockingOperation
completionBlock:^{
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:NEEDS_TO_REGISTER_ATTRIBUTES];
- DDLogWarn(@"Successfully updated attributes.");
+ [[NSUserDefaults appUserDefaults] removeObjectForKey:NEEDS_TO_REGISTER_ATTRIBUTES];
+ DDLogWarn(@"Successfully updated attributes.");
}
retryBlock:retryBlock
usesNetwork:YES];
diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h
index ac8f9c4906..21cd8eb986 100644
--- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h
+++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h
@@ -57,6 +57,8 @@ NS_ASSUME_NONNULL_BEGIN
- (CGFloat)audioDurationSeconds;
++ (void)migrateToSharedData;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m
index 5c6a740037..390f6b9d0d 100644
--- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m
+++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m
@@ -180,14 +180,29 @@ NS_ASSUME_NONNULL_BEGIN
return [dataSource writeToPath:filePath];
}
++ (NSString *)legacyAttachmentsDirPath
+{
+ return [[OWSFileSystem appDocumentDirectoryPath] stringByAppendingPathComponent:@"Attachments"];
+}
+
++ (NSString *)sharedDataAttachmentsDirPath
+{
+ return [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"Attachments"];
+}
+
++ (void)migrateToSharedData
+{
+ [OWSFileSystem moveAppFilePath:self.legacyAttachmentsDirPath
+ sharedDataFilePath:self.sharedDataAttachmentsDirPath
+ exceptionName:@"CouldNotMigrateAttachmentsDirectory"];
+}
+
+ (NSString *)attachmentsFolder
{
static NSString *attachmentsFolder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- NSString *documentsPath =
- [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
- attachmentsFolder = [documentsPath stringByAppendingPathComponent:@"Attachments"];
+ attachmentsFolder = TSAttachmentStream.sharedDataAttachmentsDirPath;
BOOL isDirectory;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentsFolder isDirectory:&isDirectory];
diff --git a/SignalServiceKit/src/Storage/TSStorageManager.h b/SignalServiceKit/src/Storage/TSStorageManager.h
index b8e5f00075..38ce979888 100644
--- a/SignalServiceKit/src/Storage/TSStorageManager.h
+++ b/SignalServiceKit/src/Storage/TSStorageManager.h
@@ -63,9 +63,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadConnection;
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection;
-#pragma mark - Utilities
-
-- (void)protectFolderAtPath:(NSString *)path;
++ (void)migrateToSharedData;
@end
diff --git a/SignalServiceKit/src/Storage/TSStorageManager.m b/SignalServiceKit/src/Storage/TSStorageManager.m
index fb9fe3c65e..5a2dd722c1 100644
--- a/SignalServiceKit/src/Storage/TSStorageManager.m
+++ b/SignalServiceKit/src/Storage/TSStorageManager.m
@@ -24,13 +24,18 @@
NS_ASSUME_NONNULL_BEGIN
-NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessible = @"TSStorageManagerExceptionNameDatabasePasswordInaccessible";
-NSString *const TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded =
- @"TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded";
-NSString *const TSStorageManagerExceptionNameDatabasePasswordUnwritable = @"TSStorageManagerExceptionNameDatabasePasswordUnwritable";
-NSString *const TSStorageManagerExceptionNameNoDatabase = @"TSStorageManagerExceptionNameNoDatabase";
+NSString *const TSStorageManagerExceptionName_DatabasePasswordInaccessible
+ = @"TSStorageManagerExceptionName_DatabasePasswordInaccessible";
+NSString *const TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded
+ = @"TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded";
+NSString *const TSStorageManagerExceptionName_DatabasePasswordUnwritable
+ = @"TSStorageManagerExceptionName_DatabasePasswordUnwritable";
+NSString *const TSStorageManagerExceptionName_NoDatabase = @"TSStorageManagerExceptionName_NoDatabase";
+NSString *const TSStorageManagerExceptionName_CouldNotMoveDatabaseFile
+ = @"TSStorageManagerExceptionName_CouldNotMoveDatabaseFile";
+NSString *const TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory
+ = @"TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory";
-static const NSString *const databaseName = @"Signal.sqlite";
static NSString *keychainService = @"TSKeyChainService";
static NSString *keychainDBPassAccount = @"TSDatabasePass";
@@ -202,10 +207,11 @@ void setDatabaseInitialized()
static TSStorageManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- sharedManager = [[self alloc] initDefault];
#if TARGET_OS_IPHONE
- [sharedManager protectSignalFiles];
+ [TSStorageManager protectSignalFiles];
#endif
+
+ sharedManager = [[self alloc] initDefault];
});
return sharedManager;
}
@@ -231,7 +237,7 @@ void setDatabaseInitialized()
// Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f];
- [NSException raise:TSStorageManagerExceptionNameNoDatabase format:@"Failed to initialize database."];
+ [NSException raise:TSStorageManagerExceptionName_NoDatabase format:@"Failed to initialize database."];
}
OWSSingletonAssert();
@@ -253,6 +259,7 @@ void setDatabaseInitialized()
options.cipherKeyBlock = ^{
return databasePassword;
};
+ options.enableMultiProcessSupport = YES;
#ifdef DEBUG
_database = [[OWSDatabase alloc] initWithPath:[self dbPath]
@@ -349,10 +356,16 @@ void setDatabaseInitialized()
[TSDatabaseView asyncRegistrationCompletion];
}
-- (void)protectSignalFiles {
- [OWSFileSystem protectFolderAtPath:[self dbPath]];
- [OWSFileSystem protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-shm"]];
- [OWSFileSystem protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-wal"]];
++ (void)protectSignalFiles
+{
+ // The old database location was in the Document directory,
+ // so protect the database files individually.
+ [OWSFileSystem protectFolderAtPath:self.legacyDatabaseFilePath];
+ [OWSFileSystem protectFolderAtPath:self.legacyDatabaseFilePath_SHM];
+ [OWSFileSystem protectFolderAtPath:self.legacyDatabaseFilePath_WAL];
+
+ // Protect the entire new database directory.
+ [OWSFileSystem protectFolderAtPath:self.sharedDataDatabaseDirPath];
}
- (nullable YapDatabaseConnection *)newDatabaseConnection
@@ -364,33 +377,99 @@ void setDatabaseInitialized()
return FALSE;
}
-- (BOOL)dbExists {
- return [[NSFileManager defaultManager] fileExistsAtPath:[self dbPath]];
++ (NSString *)legacyDatabaseDirPath
+{
+ return [OWSFileSystem appDocumentDirectoryPath];
}
-- (NSString *)dbPath {
- NSString *databasePath;
++ (NSString *)sharedDataDatabaseDirPath
+{
+ NSString *databaseDirPath = [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"database"];
NSFileManager *fileManager = [NSFileManager defaultManager];
-#if TARGET_OS_IPHONE
- NSURL *fileURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
- NSString *path = [fileURL path];
- databasePath = [path stringByAppendingPathComponent:databaseName];
-#elif TARGET_OS_MAC
-
- NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
- NSArray *urlPaths = [fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
-
- NSURL *appDirectory = [[urlPaths objectAtIndex:0] URLByAppendingPathComponent:bundleID isDirectory:YES];
-
- if (![fileManager fileExistsAtPath:[appDirectory path]]) {
- [fileManager createDirectoryAtURL:appDirectory withIntermediateDirectories:NO attributes:nil error:nil];
+ if (![fileManager fileExistsAtPath:databaseDirPath]) {
+ NSError *_Nullable error;
+ BOOL success = [fileManager createDirectoryAtPath:databaseDirPath
+ withIntermediateDirectories:NO
+ attributes:nil
+ error:&error];
+ if (!success || error) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"%@ Could not create new database directory: %@, error: %@",
+ self.logTag,
+ databaseDirPath,
+ error];
+ OWSFail(@"%@", errorDescription);
+ [NSException raise:TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory
+ format:@"%@", errorDescription];
+ }
}
+ return databaseDirPath;
+}
- databasePath = [appDirectory.filePathURL.absoluteString stringByAppendingPathComponent:databaseName];
-#endif
++ (NSString *)databaseFilename
+{
+ return @"Signal.sqlite";
+}
- return databasePath;
++ (NSString *)databaseFilename_SHM
+{
+ return [self.databaseFilename stringByAppendingString:@"-shm"];
+}
+
++ (NSString *)databaseFilename_WAL
+{
+ return [self.databaseFilename stringByAppendingString:@"-wal"];
+}
+
++ (NSString *)legacyDatabaseFilePath
+{
+ return [self.legacyDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename];
+}
+
++ (NSString *)legacyDatabaseFilePath_SHM
+{
+ return [self.legacyDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_SHM];
+}
+
++ (NSString *)legacyDatabaseFilePath_WAL
+{
+ return [self.legacyDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_WAL];
+}
+
++ (NSString *)sharedDataDatabaseFilePath
+{
+ return [self.sharedDataDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename];
+}
+
++ (NSString *)sharedDataDatabaseFilePath_SHM
+{
+ return [self.sharedDataDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_SHM];
+}
+
++ (NSString *)sharedDataDatabaseFilePath_WAL
+{
+ return [self.sharedDataDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_WAL];
+}
+
++ (void)migrateToSharedData
+{
+ [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath
+ sharedDataFilePath:self.sharedDataDatabaseFilePath
+ exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
+ [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_SHM
+ sharedDataFilePath:self.sharedDataDatabaseFilePath_SHM
+ exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
+ [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_WAL
+ sharedDataFilePath:self.sharedDataDatabaseFilePath_WAL
+ exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile];
+}
+
+- (NSString *)dbPath
+{
+ DDLogVerbose(@"databasePath: %@", TSStorageManager.sharedDataDatabaseFilePath);
+
+ return TSStorageManager.sharedDataDatabaseFilePath;
}
+ (BOOL)isDatabasePasswordAccessible
@@ -421,7 +500,7 @@ void setDatabaseInitialized()
// 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
- [NSException raise:TSStorageManagerExceptionNameDatabasePasswordInaccessibleWhileBackgrounded
+ [NSException raise:TSStorageManagerExceptionName_DatabasePasswordInaccessibleWhileBackgrounded
format:@"%@", errorDescription];
}
@@ -483,7 +562,7 @@ void setDatabaseInitialized()
// Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f];
- [NSException raise:TSStorageManagerExceptionNameDatabasePasswordUnwritable
+ [NSException raise:TSStorageManagerExceptionName_DatabasePasswordUnwritable
format:@"Setting DB password failed with error: %@", keySetError];
} else {
DDLogWarn(@"Succesfully set new DB password.");
diff --git a/SignalServiceKit/src/TSConstants.h b/SignalServiceKit/src/TSConstants.h
index cefd281a0d..717e2e0fc0 100644
--- a/SignalServiceKit/src/TSConstants.h
+++ b/SignalServiceKit/src/TSConstants.h
@@ -57,6 +57,8 @@ typedef enum { kSMSVerification, kPhoneNumberVerification } VerificationTranspor
#define textSecureSetProfileNameAPIFormat @"v1/profile/name/%@"
#define textSecureProfileAvatarFormAPI @"v1/profile/form/avatar"
+#define SignalApplicationGroup @"group.org.whispersystems.signal.group"
+
#pragma mark Push RegistrationSpecific Constants
typedef NS_ENUM(NSInteger, TSPushRegistrationError) {
TSPushRegistrationErrorNetwork,
diff --git a/SignalServiceKit/src/Util/AppVersion.m b/SignalServiceKit/src/Util/AppVersion.m
index 44fc998075..2c5dd88158 100755
--- a/SignalServiceKit/src/Util/AppVersion.m
+++ b/SignalServiceKit/src/Util/AppVersion.m
@@ -3,6 +3,7 @@
//
#import "AppVersion.h"
+#import
NSString *const kNSUserDefaults_FirstAppVersion = @"kNSUserDefaults_FirstAppVersion";
NSString *const kNSUserDefaults_LastAppVersion = @"kNSUserDefaults_LastVersion";
@@ -36,24 +37,22 @@ NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion = @"kNSUserDefault
// The version of the app when it was first launched.
// nil if the app has never been launched before.
- self.firstAppVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kNSUserDefaults_FirstAppVersion];
+ self.firstAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_FirstAppVersion];
// The version of the app the last time it was launched.
// nil if the app has never been launched before.
- self.lastAppVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kNSUserDefaults_LastAppVersion];
+ self.lastAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastAppVersion];
self.lastCompletedLaunchAppVersion =
- [[NSUserDefaults standardUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
+ [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
// Ensure the value for the "first launched version".
if (!self.firstAppVersion) {
self.firstAppVersion = self.currentAppVersion;
- [[NSUserDefaults standardUserDefaults] setObject:self.currentAppVersion
- forKey:kNSUserDefaults_FirstAppVersion];
+ [[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_FirstAppVersion];
}
// Update the value for the "most recently launched version".
- [[NSUserDefaults standardUserDefaults] setObject:self.currentAppVersion
- forKey:kNSUserDefaults_LastAppVersion];
- [[NSUserDefaults standardUserDefaults] synchronize];
+ [[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_LastAppVersion];
+ [[NSUserDefaults appUserDefaults] synchronize];
DDLogInfo(@"%@ firstAppVersion: %@", self.logTag, self.firstAppVersion);
DDLogInfo(@"%@ lastAppVersion: %@", self.logTag, self.lastAppVersion);
@@ -68,9 +67,9 @@ NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion = @"kNSUserDefault
self.lastCompletedLaunchAppVersion = self.currentAppVersion;
// Update the value for the "most recently launch-completed version".
- [[NSUserDefaults standardUserDefaults] setObject:self.currentAppVersion
- forKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
- [[NSUserDefaults standardUserDefaults] synchronize];
+ [[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
+ forKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
+ [[NSUserDefaults appUserDefaults] synchronize];
}
- (BOOL)isFirstLaunch
diff --git a/SignalServiceKit/src/Util/NSUserDefaults+OWS.h b/SignalServiceKit/src/Util/NSUserDefaults+OWS.h
new file mode 100644
index 0000000000..24bbbcbc23
--- /dev/null
+++ b/SignalServiceKit/src/Util/NSUserDefaults+OWS.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSUserDefaults (OWS)
+
++ (NSUserDefaults *)appUserDefaults;
+
++ (void)migrateToSharedUserDefaults;
+
++ (void)removeAll;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SignalServiceKit/src/Util/NSUserDefaults+OWS.m b/SignalServiceKit/src/Util/NSUserDefaults+OWS.m
new file mode 100644
index 0000000000..746630d0ea
--- /dev/null
+++ b/SignalServiceKit/src/Util/NSUserDefaults+OWS.m
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
+
+#import "NSUserDefaults+OWS.h"
+#import "TSConstants.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation NSUserDefaults (OWS)
+
++ (NSUserDefaults *)appUserDefaults
+{
+ return [[NSUserDefaults alloc] initWithSuiteName:SignalApplicationGroup];
+}
+
++ (void)migrateToSharedUserDefaults
+{
+ NSUserDefaults *appUserDefaults = self.appUserDefaults;
+
+ NSDictionary *dictionary = [NSUserDefaults standardUserDefaults].dictionaryRepresentation;
+ for (NSString *key in dictionary) {
+ id value = dictionary[key];
+ OWSAssert(value);
+ [appUserDefaults setObject:value forKey:key];
+ }
+}
+
++ (void)removeAll
+{
+ [NSUserDefaults.standardUserDefaults removeAll];
+ [self.appUserDefaults removeAll];
+}
+
+- (void)removeAll
+{
+ NSDictionary *dictionary = self.dictionaryRepresentation;
+ for (NSString *key in dictionary) {
+ [self removeObjectForKey:key];
+ }
+ [self synchronize];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h
index 3eae177bb7..b9abd6a5d8 100644
--- a/SignalServiceKit/src/Util/OWSFileSystem.h
+++ b/SignalServiceKit/src/Util/OWSFileSystem.h
@@ -10,6 +10,14 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)protectFolderAtPath:(NSString *)path;
++ (NSString *)appDocumentDirectoryPath;
+
++ (NSString *)appSharedDataDirectoryPath;
+
++ (void)moveAppFilePath:(NSString *)oldFilePath
+ sharedDataFilePath:(NSString *)newFilePath
+ exceptionName:(NSString *)exceptionName;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m
index 6e69eb0e20..8746b14433 100644
--- a/SignalServiceKit/src/Util/OWSFileSystem.m
+++ b/SignalServiceKit/src/Util/OWSFileSystem.m
@@ -3,6 +3,7 @@
//
#import "OWSFileSystem.h"
+#import "TSConstants.h"
NS_ASSUME_NONNULL_BEGIN
@@ -28,6 +29,59 @@ NS_ASSUME_NONNULL_BEGIN
}
}
++ (NSString *)appDocumentDirectoryPath
+{
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSURL *documentDirectoryURL =
+ [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
+ return [documentDirectoryURL path];
+}
+
++ (NSString *)appSharedDataDirectoryPath
+{
+ NSURL *groupContainerDirectoryURL =
+ [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:SignalApplicationGroup];
+ return [groupContainerDirectoryURL path];
+}
+
++ (void)moveAppFilePath:(NSString *)oldFilePath
+ sharedDataFilePath:(NSString *)newFilePath
+ exceptionName:(NSString *)exceptionName
+{
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ if (![fileManager fileExistsAtPath:oldFilePath]) {
+ return;
+ }
+ if ([fileManager fileExistsAtPath:newFilePath]) {
+ OWSFail(@"%@ Can't move file or directory from %@ to %@; destination already exists.",
+ self.logTag,
+ oldFilePath,
+ newFilePath);
+ return;
+ }
+
+ NSDate *startDate = [NSDate new];
+
+ NSError *_Nullable error;
+ BOOL success = [fileManager moveItemAtPath:oldFilePath toPath:newFilePath error:&error];
+ if (!success || error) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"%@ Could not move file or directory from %@ to %@, error: %@",
+ self.logTag,
+ oldFilePath,
+ newFilePath,
+ error];
+ OWSFail(@"%@", errorDescription);
+ [NSException raise:exceptionName format:@"%@", errorDescription];
+ }
+
+ DDLogInfo(@"%@ Moving file or directory from %@ to %@ in: %f",
+ self.logTag,
+ oldFilePath,
+ newFilePath,
+ fabs([startDate timeIntervalSinceNow]));
+}
+
@end
NS_ASSUME_NONNULL_END