From c4376503ec66972de871eb5be82c506d037978b8 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 11 Oct 2019 10:26:43 -0300 Subject: [PATCH 1/3] Prevent GRDB users from reverting to YDB. --- Signal/src/AppDelegate.m | 64 ++++++++++++++----- .../translations/en.lproj/Localizable.strings | 9 +++ .../src/Storage/StorageCoordinator.h | 2 + .../src/Storage/StorageCoordinator.m | 11 ++++ SignalServiceKit/src/Util/FeatureFlags.swift | 8 +++ 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 2248d28931..34b8c3ba3e 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -53,6 +53,12 @@ static NSString *const kURLHostAddStickersPrefix = @"addstickers"; static NSTimeInterval launchStartedAt; +typedef NS_ENUM(NSUInteger, LaunchFailure) { + LaunchFailure_None, + LaunchFailure_CouldNotLoadDatabase, + LaunchFailure_UnknownDatabaseVersion, +}; + @interface AppDelegate () @property (nonatomic) BOOL hasInitialRootViewController; @@ -230,17 +236,21 @@ static NSTimeInterval launchStartedAt; // We need to do this _after_ we set up logging, when the keychain is unlocked, // but before we access YapDatabase, files on disk, or NSUserDefaults - NSError *error; - if (![YDBLegacyMigration ensureIsYDBReadyForAppExtensions:&error]) { - if (error != nil) { - [self showLaunchFailureUI:error]; - } + NSError *_Nullable launchError = nil; + LaunchFailure launchFailure = LaunchFailure_None; - // If this method has failed; do nothing. - // - // ensureIsReadyForAppExtensions will show a failure mode UI that - // lets users report this error. + BOOL isYdbNotReady = ![YDBLegacyMigration ensureIsYDBReadyForAppExtensions:&launchError]; + if (isYdbNotReady || launchError != nil) { + launchFailure = LaunchFailure_CouldNotLoadDatabase; + } else if (StorageCoordinator.hasInvalidDatabaseVersion) { + // Prevent: + // * Users who have used GRDB revert to using YDB. + // * Users with an unknown GRDB schema revert to using an earlier GRDB schema. + launchFailure = LaunchFailure_UnknownDatabaseVersion; + } + if (launchFailure != LaunchFailure_None) { OWSLogInfo(@"application: didFinishLaunchingWithOptions failed."); + [self showUIForLaunchFailure:launchFailure]; return YES; } @@ -374,7 +384,7 @@ static NSTimeInterval launchStartedAt; exit(0); } -- (void)showLaunchFailureUI:(NSError *)error +- (void)showUIForLaunchFailure:(LaunchFailure)launchFailure { // Disable normal functioning of app. self.didAppLaunchFail = YES; @@ -391,12 +401,30 @@ static NSTimeInterval launchStartedAt; [self.window makeKeyAndVisible]; - UIAlertController *alert = - [UIAlertController alertControllerWithTitle:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_TITLE", - @"Title for the 'app launch failed' alert.") - message:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_MESSAGE", - @"Message for the 'app launch failed' alert.") - preferredStyle:UIAlertControllerStyleAlert]; + NSString *alertTitle; + NSString *alertMessage + = NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_MESSAGE", @"Message for the 'app launch failed' alert."); + switch (launchFailure) { + case LaunchFailure_CouldNotLoadDatabase: + alertTitle = NSLocalizedString(@"APP_LAUNCH_FAILURE_COULD_NOT_LOAD_DATABASE", + @"Error indicating that the app could not launch because the database could not be loaded."); + break; + case LaunchFailure_UnknownDatabaseVersion: + alertTitle = NSLocalizedString(@"APP_LAUNCH_FAILURE_INVALID_DATABASE_VERSION_TITLE", + @"Error indicating that the app could not launch without reverting unknown database migrations."); + alertMessage = NSLocalizedString(@"APP_LAUNCH_FAILURE_INVALID_DATABASE_VERSION_MESSAGE", + @"Error indicating that the app could not launch without reverting unknown database migrations."); + break; + default: + OWSFailDebug(@"Unknown launch failure."); + alertTitle + = NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_TITLE", @"Title for the 'app launch failed' alert."); + break; + } + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:alertTitle + message:alertMessage + preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", nil) style:UIAlertActionStyleDefault @@ -1024,6 +1052,10 @@ static NSTimeInterval launchStartedAt; - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window { + if (self.didAppLaunchFail) { + return UIInterfaceOrientationMaskPortrait; + } + if (self.hasCall) { OWSLogInfo(@"has call"); // The call-banner window is only suitable for portrait display diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index a93e1e25ed..5967fcb256 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -74,6 +74,15 @@ /* Title for the 'app launch failed' alert. */ "APP_LAUNCH_FAILURE_ALERT_TITLE" = "Error"; +/* Error indicating that the app could not launch because the database could not be loaded. */ +"APP_LAUNCH_FAILURE_COULD_NOT_LOAD_DATABASE" = "Could Not Load Database"; + +/* Error indicating that the app could not launch without reverting unknown database migrations. */ +"APP_LAUNCH_FAILURE_INVALID_DATABASE_VERSION_TITLE" = "Unknown Database Version."; + +/* Error indicating that the app could not launch without reverting unknown database migrations. */ +"APP_LAUNCH_FAILURE_INVALID_DATABASE_VERSION_MESSAGE" = "Please upgrade to the latest version of Signal."; + /* Text prompting user to edit their profile name. */ "APP_SETTINGS_EDIT_PROFILE_NAME_PROMPT" = "Enter your name"; diff --git a/SignalServiceKit/src/Storage/StorageCoordinator.h b/SignalServiceKit/src/Storage/StorageCoordinator.h index bbb3345105..c4e63825b6 100644 --- a/SignalServiceKit/src/Storage/StorageCoordinator.h +++ b/SignalServiceKit/src/Storage/StorageCoordinator.h @@ -38,6 +38,8 @@ NSString *NSStringFromStorageCoordinatorState(StorageCoordinatorState value); @property (class, nonatomic, readonly) BOOL hasYdbFile; @property (class, nonatomic, readonly) BOOL hasGrdbFile; +@property (class, nonatomic, readonly) BOOL hasInvalidDatabaseVersion; + - (BOOL)isDatabasePasswordAccessible; #ifdef TESTABLE_BUILD diff --git a/SignalServiceKit/src/Storage/StorageCoordinator.m b/SignalServiceKit/src/Storage/StorageCoordinator.m index 4a22686bc1..cc4491545d 100644 --- a/SignalServiceKit/src/Storage/StorageCoordinator.m +++ b/SignalServiceKit/src/Storage/StorageCoordinator.m @@ -71,6 +71,17 @@ NSString *NSStringFromStorageCoordinatorState(StorageCoordinatorState value) return [OWSFileSystem fileOrFolderExistsAtPath:grdbFilePath]; } ++ (BOOL)hasInvalidDatabaseVersion +{ + // A check to avoid trying to revert to YDB when we've already migrated to GRDB. + return (SSKFeatureFlags.storageMode == StorageModeYdb && self.hasGrdbFile && [SSKPreferences isYdbMigrated] && + // Allow developers to do this, but not QA, internal, + // public beta or production. + !SSKFeatureFlags.canRevertToYDB); + + // TODO: also return true if unknown GRDB version. +} + - (StorageCoordinatorState)storageCoordinatorState { return self.state; diff --git a/SignalServiceKit/src/Util/FeatureFlags.swift b/SignalServiceKit/src/Util/FeatureFlags.swift index 87a63d9692..40ee6a1c33 100644 --- a/SignalServiceKit/src/Util/FeatureFlags.swift +++ b/SignalServiceKit/src/Util/FeatureFlags.swift @@ -106,6 +106,14 @@ public class FeatureFlags: NSObject { @objc public static let preserveYdb = true + @objc + public static var canRevertToYDB: Bool { + // Only let developers should be allowed to use YDB after migrating to GRDB. + // We don't want to let QA, public beta or production users risk + // data loss. + return build == .dev + } + @objc public static var audibleErrorLogging = build.includes(.internalPreview) From e8e839bc9e0d139320f7667948e2f03872936203 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 11 Oct 2019 10:32:52 -0300 Subject: [PATCH 2/3] Prevent GRDB users from reverting to YDB. --- Signal/src/AppDelegate.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 34b8c3ba3e..890711cc23 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -396,8 +396,9 @@ typedef NS_ENUM(NSUInteger, LaunchFailure) { self.window = [[OWSWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Show the launch screen - self.window.rootViewController = - [[UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil] instantiateInitialViewController]; + UIViewController *viewController = [[UIStoryboard storyboardWithName:@"Launch Screen" + bundle:nil] instantiateInitialViewController]; + self.window.rootViewController = viewController; [self.window makeKeyAndVisible]; @@ -433,8 +434,7 @@ typedef NS_ENUM(NSUInteger, LaunchFailure) { OWSFail(@"exiting after sharing debug logs."); }]; }]]; - UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController]; - [fromViewController presentAlert:alert]; + [viewController presentAlert:alert]; } - (void)startupLogging From 109d7e3e6f3545df261c6d74c7fc77653ebecce7 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 11 Oct 2019 14:40:50 -0300 Subject: [PATCH 3/3] Respond to CR. --- SignalServiceKit/src/Util/FeatureFlags.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Util/FeatureFlags.swift b/SignalServiceKit/src/Util/FeatureFlags.swift index 40ee6a1c33..e155f7ae39 100644 --- a/SignalServiceKit/src/Util/FeatureFlags.swift +++ b/SignalServiceKit/src/Util/FeatureFlags.swift @@ -108,7 +108,7 @@ public class FeatureFlags: NSObject { @objc public static var canRevertToYDB: Bool { - // Only let developers should be allowed to use YDB after migrating to GRDB. + // Only developers should be allowed to use YDB after migrating to GRDB. // We don't want to let QA, public beta or production users risk // data loss. return build == .dev