From 9e3c02f79bb4909d9dda09dc88fe3ea189a01df3 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Thu, 24 Mar 2022 14:35:23 +1100 Subject: [PATCH 1/4] Removed the unused legacy OWSBackup code --- Session.xcodeproj/project.pbxproj | 56 - .../Backups/BackupRestoreViewController.swift | 175 --- Session/Backups/OWSBackup.h | 105 -- Session/Backups/OWSBackup.m | 912 ------------- Session/Backups/OWSBackupAPI.swift | 740 ----------- Session/Backups/OWSBackupExportJob.h | 15 - Session/Backups/OWSBackupExportJob.m | 1181 ----------------- Session/Backups/OWSBackupIO.h | 61 - Session/Backups/OWSBackupIO.m | 273 ---- Session/Backups/OWSBackupImportJob.h | 15 - Session/Backups/OWSBackupImportJob.m | 635 --------- Session/Backups/OWSBackupJob.h | 92 -- Session/Backups/OWSBackupJob.m | 316 ----- Session/Backups/OWSBackupLazyRestore.swift | 176 --- .../Backups/OWSBackupSettingsViewController.h | 13 - .../Backups/OWSBackupSettingsViewController.m | 214 --- Session/Meta/AppDelegate.m | 12 +- Session/Meta/AppEnvironment.swift | 8 - Session/Meta/Signal-Bridging-Header.h | 2 - SessionMessagingKit/To Do/TSAccountManager.h | 3 - SessionMessagingKit/To Do/TSAccountManager.m | 16 - 21 files changed, 1 insertion(+), 5019 deletions(-) delete mode 100644 Session/Backups/BackupRestoreViewController.swift delete mode 100644 Session/Backups/OWSBackup.h delete mode 100644 Session/Backups/OWSBackup.m delete mode 100644 Session/Backups/OWSBackupAPI.swift delete mode 100644 Session/Backups/OWSBackupExportJob.h delete mode 100644 Session/Backups/OWSBackupExportJob.m delete mode 100644 Session/Backups/OWSBackupIO.h delete mode 100644 Session/Backups/OWSBackupIO.m delete mode 100644 Session/Backups/OWSBackupImportJob.h delete mode 100644 Session/Backups/OWSBackupImportJob.m delete mode 100644 Session/Backups/OWSBackupJob.h delete mode 100644 Session/Backups/OWSBackupJob.m delete mode 100644 Session/Backups/OWSBackupLazyRestore.swift delete mode 100644 Session/Backups/OWSBackupSettingsViewController.h delete mode 100644 Session/Backups/OWSBackupSettingsViewController.m diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 906428ff1..179733f8b 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87C204DAC8C007AEB0F /* NotificationSettingsViewController.m */; }; 340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */; }; 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */; }; - 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */; }; 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */; }; 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89A204DAC8D007AEB0F /* OWSConversationSettingsViewController.m */; }; 341341EF2187467A00192D59 /* ConversationViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 341341EE2187467900192D59 /* ConversationViewModel.m */; }; @@ -22,7 +21,6 @@ 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; }; 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; }; 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; }; - 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */; }; 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */; }; 346129991FD1E4DA00532771 /* SignalApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129971FD1E4D900532771 /* SignalApp.m */; }; 34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; }; @@ -35,13 +33,6 @@ 3496955D219B605E00DCFE74 /* PhotoCollectionPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496955A219B605E00DCFE74 /* PhotoCollectionPickerController.swift */; }; 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496955B219B605E00DCFE74 /* PhotoLibrary.swift */; }; 3496956021A2FC8100DCFE74 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3496955F21A2FC8100DCFE74 /* CloudKit.framework */; }; - 3496956E21A301A100DCFE74 /* OWSBackupExportJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956221A301A100DCFE74 /* OWSBackupExportJob.m */; }; - 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */; }; - 3496957021A301A100DCFE74 /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956521A301A100DCFE74 /* OWSBackupIO.m */; }; - 3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956621A301A100DCFE74 /* OWSBackupImportJob.m */; }; - 3496957221A301A100DCFE74 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956921A301A100DCFE74 /* OWSBackup.m */; }; - 3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956A21A301A100DCFE74 /* OWSBackupJob.m */; }; - 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */; }; 34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */; }; 34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */; }; 34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34ABC0E321DD20C500ED9469 /* ConversationMessageMapping.swift */; }; @@ -959,12 +950,10 @@ 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = ""; }; 340FC87C204DAC8C007AEB0F /* NotificationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsViewController.m; sourceTree = ""; }; 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrivacySettingsTableViewController.m; sourceTree = ""; }; - 340FC87F204DAC8C007AEB0F /* OWSBackupSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupSettingsViewController.h; sourceTree = ""; }; 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSoundSettingsViewController.m; sourceTree = ""; }; 340FC888204DAC8C007AEB0F /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = ""; }; 340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = ""; }; 340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsOptionsViewController.h; sourceTree = ""; }; - 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupSettingsViewController.m; sourceTree = ""; }; 340FC88F204DAC8C007AEB0F /* PrivacySettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivacySettingsTableViewController.h; sourceTree = ""; }; 340FC894204DAC8C007AEB0F /* OWSSoundSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSoundSettingsViewController.h; sourceTree = ""; }; 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = ""; }; @@ -979,7 +968,6 @@ 34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = ""; }; 34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = ""; }; 34386A53207D271C009F5D9C /* NeverClearView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeverClearView.swift; sourceTree = ""; }; - 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackupRestoreViewController.swift; sourceTree = ""; }; 34480B371FD092A900BC14EF /* SignalShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Bridging-Header.h"; sourceTree = ""; }; 34480B381FD092E300BC14EF /* SessionShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SessionShareExtension-Prefix.pch"; sourceTree = ""; }; 344825C4211390C700DB4BD8 /* OWSOrphanDataCleaner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOrphanDataCleaner.h; sourceTree = ""; }; @@ -994,18 +982,6 @@ 3496955A219B605E00DCFE74 /* PhotoCollectionPickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoCollectionPickerController.swift; sourceTree = ""; }; 3496955B219B605E00DCFE74 /* PhotoLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibrary.swift; sourceTree = ""; }; 3496955F21A2FC8100DCFE74 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; - 3496956221A301A100DCFE74 /* OWSBackupExportJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupExportJob.m; sourceTree = ""; }; - 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupLazyRestore.swift; sourceTree = ""; }; - 3496956421A301A100DCFE74 /* OWSBackup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackup.h; sourceTree = ""; }; - 3496956521A301A100DCFE74 /* OWSBackupIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupIO.m; sourceTree = ""; }; - 3496956621A301A100DCFE74 /* OWSBackupImportJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupImportJob.m; sourceTree = ""; }; - 3496956721A301A100DCFE74 /* OWSBackupJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupJob.h; sourceTree = ""; }; - 3496956821A301A100DCFE74 /* OWSBackupExportJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupExportJob.h; sourceTree = ""; }; - 3496956921A301A100DCFE74 /* OWSBackup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackup.m; sourceTree = ""; }; - 3496956A21A301A100DCFE74 /* OWSBackupJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupJob.m; sourceTree = ""; }; - 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupAPI.swift; sourceTree = ""; }; - 3496956C21A301A100DCFE74 /* OWSBackupImportJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupImportJob.h; sourceTree = ""; }; - 3496956D21A301A100DCFE74 /* OWSBackupIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupIO.h; sourceTree = ""; }; 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSImagePickerController.swift; sourceTree = ""; }; 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaAlbumView.swift; sourceTree = ""; }; 34ABC0E321DD20C500ED9469 /* ConversationMessageMapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationMessageMapping.swift; sourceTree = ""; }; @@ -2969,28 +2945,6 @@ path = Notifications; sourceTree = ""; }; - C36096BC25AD1C3E008B62B2 /* Backups */ = { - isa = PBXGroup; - children = ( - 340FC87F204DAC8C007AEB0F /* OWSBackupSettingsViewController.h */, - 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */, - 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */, - 3496956421A301A100DCFE74 /* OWSBackup.h */, - 3496956921A301A100DCFE74 /* OWSBackup.m */, - 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */, - 3496956821A301A100DCFE74 /* OWSBackupExportJob.h */, - 3496956221A301A100DCFE74 /* OWSBackupExportJob.m */, - 3496956C21A301A100DCFE74 /* OWSBackupImportJob.h */, - 3496956621A301A100DCFE74 /* OWSBackupImportJob.m */, - 3496956D21A301A100DCFE74 /* OWSBackupIO.h */, - 3496956521A301A100DCFE74 /* OWSBackupIO.m */, - 3496956721A301A100DCFE74 /* OWSBackupJob.h */, - 3496956A21A301A100DCFE74 /* OWSBackupJob.m */, - 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */, - ); - path = Backups; - sourceTree = ""; - }; C36096ED25AD20FD008B62B2 /* Media Viewing & Editing */ = { isa = PBXGroup; children = ( @@ -3616,7 +3570,6 @@ isa = PBXGroup; children = ( C3F0A58F255C8E3D007BE2A3 /* Meta */, - C36096BC25AD1C3E008B62B2 /* Backups */, C360969C25AD18BA008B62B2 /* Closed Groups */, B835246C25C38AA20089A44F /* Conversations */, C32B405424A961E1001117B5 /* Dependencies */, @@ -4866,16 +4819,13 @@ B8041AA725C90927003C2166 /* TypingIndicatorCell.swift in Sources */, B8CCF63723961D6D0091D419 /* NewDMVC.swift in Sources */, 452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */, - 3496956E21A301A100DCFE74 /* OWSBackupExportJob.m in Sources */, 4C1885D2218F8E1C00B67051 /* PhotoGridViewCell.swift in Sources */, 34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */, - 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */, C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */, B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */, B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */, B879D449247E1BE300DB3608 /* PathVC.swift in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, - 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */, 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */, 451A13B11E13DED2000A50FD /* AppNotifications.swift in Sources */, 34D99CE4217509C2000AFB39 /* AppEnvironment.swift in Sources */, @@ -4911,7 +4861,6 @@ C3548F0624456447009433A8 /* PNModeVC.swift in Sources */, B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */, D221A09A169C9E5E00537ABF /* main.m in Sources */, - 3496957221A301A100DCFE74 /* OWSBackup.m in Sources */, B835247925C38D880089A44F /* MessageCell.swift in Sources */, B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */, 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */, @@ -4921,7 +4870,6 @@ B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */, B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */, 346129991FD1E4DA00532771 /* SignalApp.m in Sources */, - 3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */, 34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */, C331FFFE2558FF3B00070591 /* ConversationCell.swift in Sources */, FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */, @@ -4969,7 +4917,6 @@ B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */, B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */, C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */, - 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */, 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */, 4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */, B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */, @@ -4991,7 +4938,6 @@ 34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */, C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */, 4C21D5D6223A9DC500EF8A77 /* UIAlerts+iOS9.m in Sources */, - 3496957021A301A100DCFE74 /* OWSBackupIO.m in Sources */, B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */, B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, @@ -5011,7 +4957,6 @@ B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */, 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */, B8D0A25025E3678700C1835E /* LinkDeviceVC.swift in Sources */, - 3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */, B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */, 7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */, B897621C25D201F7004F83B2 /* ScrollToBottomButton.swift in Sources */, @@ -5028,7 +4973,6 @@ 7BA7F4BD27A216B600B3A466 /* Storage+RecentSearchResults.swift in Sources */, B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */, C374EEE225DA26740073A857 /* LinkPreviewModal.swift in Sources */, - 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Session/Backups/BackupRestoreViewController.swift b/Session/Backups/BackupRestoreViewController.swift deleted file mode 100644 index 1887575c0..000000000 --- a/Session/Backups/BackupRestoreViewController.swift +++ /dev/null @@ -1,175 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import UIKit - -@objc -public class BackupRestoreViewController: OWSTableViewController { - - private var hasBegunImport = false - - // MARK: - Dependencies - - private var backup: OWSBackup { - return AppEnvironment.shared.backup - } - - // MARK: - - - override public func loadView() { - super.loadView() - - navigationItem.title = NSLocalizedString("SETTINGS_BACKUP", comment: "Label for the backup view in app settings.") - - navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didPressCancelButton)) - } - - override public func viewDidLoad() { - super.viewDidLoad() - - NotificationCenter.default.addObserver(self, - selector: #selector(backupStateDidChange), - name: NSNotification.Name(NSNotificationNameBackupStateDidChange), - object: nil) - - updateTableContents() - } - - private func updateTableContents() { - if hasBegunImport { - updateProgressContents() - } else { - updateDecisionContents() - } - } - - private func updateDecisionContents() { - let contents = OWSTableContents() - - let section = OWSTableSection() - - section.headerTitle = NSLocalizedString("BACKUP_RESTORE_DECISION_TITLE", comment: "Label for the backup restore decision section.") - - section.add(OWSTableItem.actionItem(withText: NSLocalizedString("CHECK_FOR_BACKUP_DO_NOT_RESTORE", - comment: "The label for the 'do not restore backup' button."), actionBlock: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.cancelAndDismiss() - })) - section.add(OWSTableItem.actionItem(withText: NSLocalizedString("CHECK_FOR_BACKUP_RESTORE", - comment: "The label for the 'restore backup' button."), actionBlock: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.startImport() - })) - - contents.addSection(section) - self.contents = contents - } - - private var progressFormatter: NumberFormatter = { - let numberFormatter = NumberFormatter() - numberFormatter.numberStyle = .percent - numberFormatter.maximumFractionDigits = 0 - numberFormatter.multiplier = 1 - return numberFormatter - }() - - private func updateProgressContents() { - let contents = OWSTableContents() - - let section = OWSTableSection() - - section.add(OWSTableItem.label(withText: NSLocalizedString("BACKUP_RESTORE_STATUS", comment: "Label for the backup restore status."), accessoryText: NSStringForBackupImportState(backup.backupImportState))) - - if backup.backupImportState == .inProgress { - if let backupImportDescription = backup.backupImportDescription { - section.add(OWSTableItem.label(withText: NSLocalizedString("BACKUP_RESTORE_DESCRIPTION", comment: "Label for the backup restore description."), accessoryText: backupImportDescription)) - } - - if let backupImportProgress = backup.backupImportProgress { - let progressInt = backupImportProgress.floatValue * 100 - if let progressString = progressFormatter.string(from: NSNumber(value: progressInt)) { - section.add(OWSTableItem.label(withText: NSLocalizedString("BACKUP_RESTORE_PROGRESS", comment: "Label for the backup restore progress."), accessoryText: progressString)) - } else { - owsFailDebug("Could not format progress: \(progressInt)") - } - } - } - - contents.addSection(section) - self.contents = contents - - // TODO: Add cancel button. - } - - // MARK: Helpers - - @objc - private func didPressCancelButton(sender: UIButton) { - Logger.info("") - - // TODO: Cancel import. - - cancelAndDismiss() - } - - @objc - private func cancelAndDismiss() { - Logger.info("") - - backup.setHasPendingRestoreDecision(false) - - showHomeView() - } - - @objc - private func startImport() { - Logger.info("") - - hasBegunImport = true - - backup.tryToImport() - } - - private func showHomeView() { - // In production, this view will never be presented in a modal. - // During testing (debug UI, etc.), it may be a modal. - let isModal = navigationController?.presentingViewController != nil - if isModal { - dismiss(animated: true, completion: { - SignalApp.shared().showHomeView() - }) - } else { - SignalApp.shared().showHomeView() - } - - NotificationCenter.default.removeObserver(self) - } - - // MARK: - Notifications - - @objc func backupStateDidChange() { - AssertIsOnMainThread() - - Logger.verbose("backup.backupImportState: \(NSStringForBackupImportState(backup.backupImportState))") - Logger.flush() - - if backup.backupImportState == .succeeded { - backup.setHasPendingRestoreDecision(false) - - showHomeView() - } else { - updateTableContents() - } - } - - // MARK: Orientation - - public override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return .portrait - } -} diff --git a/Session/Backups/OWSBackup.h b/Session/Backups/OWSBackup.h deleted file mode 100644 index 4ad58c34e..000000000 --- a/Session/Backups/OWSBackup.h +++ /dev/null @@ -1,105 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const NSNotificationNameBackupStateDidChange; - -typedef void (^OWSBackupBoolBlock)(BOOL value); -typedef void (^OWSBackupStringListBlock)(NSArray *value); -typedef void (^OWSBackupErrorBlock)(NSError *error); - -typedef NS_ENUM(NSUInteger, OWSBackupState) { - // Has never backed up, not trying to backup yet. - OWSBackupState_Idle = 0, - // Backing up. - OWSBackupState_InProgress, - // Last backup failed. - OWSBackupState_Failed, - // Last backup succeeded. - OWSBackupState_Succeeded, -}; - -NSString *NSStringForBackupExportState(OWSBackupState state); -NSString *NSStringForBackupImportState(OWSBackupState state); - -NSArray *MiscCollectionsToBackup(void); - -NSError *OWSBackupErrorWithDescription(NSString *description); - -@class AnyPromise; -@class OWSBackupIO; -@class TSAttachmentPointer; -@class TSThread; -@class YapDatabaseConnection; - -@interface OWSBackup : NSObject - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -+ (instancetype)sharedManager NS_SWIFT_NAME(shared()); - -#pragma mark - Backup Export - -@property (atomic, readonly) OWSBackupState backupExportState; - -// If a "backup export" is in progress (see backupExportState), -// backupExportDescription _might_ contain a string that describes -// the current phase and backupExportProgress _might_ contain a -// 0.0<=x<=1.0 progress value that indicates progress within the -// current phase. -@property (nonatomic, readonly, nullable) NSString *backupExportDescription; -@property (nonatomic, readonly, nullable) NSNumber *backupExportProgress; - -+ (BOOL)isFeatureEnabled; - -- (BOOL)isBackupEnabled; -- (void)setIsBackupEnabled:(BOOL)value; - -- (BOOL)hasPendingRestoreDecision; -- (void)setHasPendingRestoreDecision:(BOOL)value; - -- (void)tryToExportBackup; -- (void)cancelExportBackup; - -#pragma mark - Backup Import - -@property (atomic, readonly) OWSBackupState backupImportState; - -// If a "backup import" is in progress (see backupImportState), -// backupImportDescription _might_ contain a string that describes -// the current phase and backupImportProgress _might_ contain a -// 0.0<=x<=1.0 progress value that indicates progress within the -// current phase. -@property (nonatomic, readonly, nullable) NSString *backupImportDescription; -@property (nonatomic, readonly, nullable) NSNumber *backupImportProgress; - -- (void)allRecipientIdsWithManifestsInCloud:(OWSBackupStringListBlock)success failure:(OWSBackupErrorBlock)failure; - -- (AnyPromise *)ensureCloudKitAccess __attribute__((warn_unused_result)); - -- (void)checkCanImportBackup:(OWSBackupBoolBlock)success failure:(OWSBackupErrorBlock)failure; - -// TODO: After a successful import, we should enable backup and -// preserve our PIN and/or private key so that restored users -// continues to backup. -- (void)tryToImportBackup; -- (void)cancelImportBackup; - -- (void)logBackupRecords; -- (void)clearAllCloudKitRecords; - -- (void)logBackupMetadataCache:(YapDatabaseConnection *)dbConnection; - -#pragma mark - Lazy Restore - -- (NSArray *)attachmentRecordNamesForLazyRestore; - -- (NSArray *)attachmentIdsForLazyRestore; - -- (AnyPromise *)lazyRestoreAttachment:(TSAttachmentPointer *)attachment backupIO:(OWSBackupIO *)backupIO __attribute__((warn_unused_result)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackup.m b/Session/Backups/OWSBackup.m deleted file mode 100644 index 4f0ec36b0..000000000 --- a/Session/Backups/OWSBackup.m +++ /dev/null @@ -1,912 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackup.h" -#import "OWSBackupExportJob.h" -#import "OWSBackupIO.h" -#import "OWSBackupImportJob.h" -#import "Session-Swift.h" -#import -#import -#import -#import - -@import CloudKit; - -NS_ASSUME_NONNULL_BEGIN - -NSString *const NSNotificationNameBackupStateDidChange = @"NSNotificationNameBackupStateDidChange"; - -NSString *const OWSPrimaryStorage_OWSBackupCollection = @"OWSPrimaryStorage_OWSBackupCollection"; -NSString *const OWSBackup_IsBackupEnabledKey = @"OWSBackup_IsBackupEnabledKey"; -NSString *const OWSBackup_LastExportSuccessDateKey = @"OWSBackup_LastExportSuccessDateKey"; -NSString *const OWSBackup_LastExportFailureDateKey = @"OWSBackup_LastExportFailureDateKey"; -NSString *const OWSBackupErrorDomain = @"OWSBackupErrorDomain"; - -NSString *NSStringForBackupExportState(OWSBackupState state) -{ - switch (state) { - case OWSBackupState_Idle: - return NSLocalizedString(@"SETTINGS_BACKUP_STATUS_IDLE", @"Indicates that app is not backing up."); - case OWSBackupState_InProgress: - return NSLocalizedString(@"SETTINGS_BACKUP_STATUS_IN_PROGRESS", @"Indicates that app is backing up."); - case OWSBackupState_Failed: - return NSLocalizedString(@"SETTINGS_BACKUP_STATUS_FAILED", @"Indicates that the last backup failed."); - case OWSBackupState_Succeeded: - return NSLocalizedString(@"SETTINGS_BACKUP_STATUS_SUCCEEDED", @"Indicates that the last backup succeeded."); - } -} - -NSString *NSStringForBackupImportState(OWSBackupState state) -{ - switch (state) { - case OWSBackupState_Idle: - return NSLocalizedString(@"SETTINGS_BACKUP_IMPORT_STATUS_IDLE", @"Indicates that app is not restoring up."); - case OWSBackupState_InProgress: - return NSLocalizedString( - @"SETTINGS_BACKUP_IMPORT_STATUS_IN_PROGRESS", @"Indicates that app is restoring up."); - case OWSBackupState_Failed: - return NSLocalizedString( - @"SETTINGS_BACKUP_IMPORT_STATUS_FAILED", @"Indicates that the last backup restore failed."); - case OWSBackupState_Succeeded: - return NSLocalizedString( - @"SETTINGS_BACKUP_IMPORT_STATUS_SUCCEEDED", @"Indicates that the last backup restore succeeded."); - } -} - -NSArray *MiscCollectionsToBackup(void) -{ - return @[ - kOWSBlockingManager_BlockListCollection, - OWSUserProfile.collection, - SSKIncrementingIdFinder.collectionName, - OWSPreferencesSignalDatabaseCollection, - ]; -} - -typedef NS_ENUM(NSInteger, OWSBackupErrorCode) { - OWSBackupErrorCodeAssertionFailure = 0, -}; - -NSError *OWSBackupErrorWithDescription(NSString *description) -{ - return [NSError errorWithDomain:@"OWSBackupErrorDomain" - code:OWSBackupErrorCodeAssertionFailure - userInfo:@{ NSLocalizedDescriptionKey : description }]; -} - -// TODO: Observe Reachability. -@interface OWSBackup () - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; - -// This property should only be accessed on the main thread. -@property (nonatomic, nullable) OWSBackupExportJob *backupExportJob; - -// This property should only be accessed on the main thread. -@property (nonatomic, nullable) OWSBackupImportJob *backupImportJob; - -@property (nonatomic, nullable) NSString *backupExportDescription; -@property (nonatomic, nullable) NSNumber *backupExportProgress; - -@property (nonatomic, nullable) NSString *backupImportDescription; -@property (nonatomic, nullable) NSNumber *backupImportProgress; - -@property (atomic) OWSBackupState backupExportState; -@property (atomic) OWSBackupState backupImportState; - -@end - -#pragma mark - - -@implementation OWSBackup - -@synthesize dbConnection = _dbConnection; - -+ (instancetype)sharedManager -{ - OWSAssertDebug(AppEnvironment.shared.backup); - - return AppEnvironment.shared.backup; -} - -- (instancetype)init -{ - self = [super init]; - - if (!self) { - return self; - } - - self.backupExportState = OWSBackupState_Idle; - self.backupImportState = OWSBackupState_Idle; - - OWSSingletonAssert(); - - [AppReadiness runNowOrWhenAppDidBecomeReady:^{ - [self setup]; - }]; - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)setup -{ - if (!OWSBackup.isFeatureEnabled) { - return; - } - - [OWSBackupAPI setup]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive:) - name:OWSApplicationDidBecomeActiveNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(registrationStateDidChange) - name:RegistrationStateDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(ckAccountChanged) - name:CKAccountChangedNotification - object:nil]; - - // We want to start a backup if necessary on app launch, but app launch is a - // busy time and it's important to remain responsive, so wait a few seconds before - // starting the backup. - // - // TODO: Make this period longer. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self ensureBackupExportState]; - }); -} - -- (YapDatabaseConnection *)dbConnection -{ - @synchronized(self) { - if (!_dbConnection) { - _dbConnection = self.primaryStorage.newDatabaseConnection; - } - return _dbConnection; - } -} - -#pragma mark - Dependencies - -- (OWSPrimaryStorage *)primaryStorage -{ - OWSAssertDebug(SSKEnvironment.shared.primaryStorage); - - return SSKEnvironment.shared.primaryStorage; -} - -- (TSAccountManager *)tsAccountManager -{ - OWSAssertDebug(SSKEnvironment.shared.tsAccountManager); - - return SSKEnvironment.shared.tsAccountManager; -} - -+ (BOOL)isFeatureEnabled -{ - return NO; -} - -#pragma mark - Backup Export - -- (void)tryToExportBackup -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(!self.backupExportJob); - - if (!self.canBackupExport) { - // TODO: Offer a reason in the UI. - return; - } - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSFailDebug(@"Can't backup; not registered and ready."); - return; - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSFailDebug(@"Can't backup; missing recipientId."); - return; - } - - // In development, make sure there's no export or import in progress. - [self.backupExportJob cancel]; - self.backupExportJob = nil; - [self.backupImportJob cancel]; - self.backupImportJob = nil; - - self.backupExportState = OWSBackupState_InProgress; - - self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self recipientId:recipientId]; - [self.backupExportJob start]; - - [self postDidChangeNotification]; -} - -- (void)cancelExportBackup -{ - [self.backupExportJob cancel]; - self.backupExportJob = nil; - - [self ensureBackupExportState]; -} - -- (void)setLastExportSuccessDate:(NSDate *)value -{ - OWSAssertDebug(value); - - [self.dbConnection setDate:value - forKey:OWSBackup_LastExportSuccessDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; -} - -- (nullable NSDate *)lastExportSuccessDate -{ - return [self.dbConnection dateForKey:OWSBackup_LastExportSuccessDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; -} - -- (void)setLastExportFailureDate:(NSDate *)value -{ - OWSAssertDebug(value); - - [self.dbConnection setDate:value - forKey:OWSBackup_LastExportFailureDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; -} - - -- (nullable NSDate *)lastExportFailureDate -{ - return [self.dbConnection dateForKey:OWSBackup_LastExportFailureDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; -} - -- (BOOL)isBackupEnabled -{ - return [self.dbConnection boolForKey:OWSBackup_IsBackupEnabledKey - inCollection:OWSPrimaryStorage_OWSBackupCollection - defaultValue:NO]; -} - -- (void)setIsBackupEnabled:(BOOL)value -{ - [self.dbConnection setBool:value - forKey:OWSBackup_IsBackupEnabledKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; - - if (!value) { - [self.dbConnection removeObjectForKey:OWSBackup_LastExportSuccessDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; - [self.dbConnection removeObjectForKey:OWSBackup_LastExportFailureDateKey - inCollection:OWSPrimaryStorage_OWSBackupCollection]; - } - - [self postDidChangeNotification]; - - [self ensureBackupExportState]; -} - -- (BOOL)hasPendingRestoreDecision -{ - return [self.tsAccountManager hasPendingBackupRestoreDecision]; -} - -- (void)setHasPendingRestoreDecision:(BOOL)value -{ - [self.tsAccountManager setHasPendingBackupRestoreDecision:value]; -} - -- (BOOL)canBackupExport -{ - if (!self.isBackupEnabled) { - return NO; - } - if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive) { - // Don't start backups when app is in the background. - return NO; - } - if (![self.tsAccountManager isRegisteredAndReady]) { - return NO; - } - return YES; -} - -- (BOOL)shouldHaveBackupExport -{ - if (!self.canBackupExport) { - return NO; - } - if (self.backupExportJob) { - // If there's already a job in progress, let it complete. - return YES; - } - NSDate *_Nullable lastExportSuccessDate = self.lastExportSuccessDate; - NSDate *_Nullable lastExportFailureDate = self.lastExportFailureDate; - // Wait N hours before retrying after a success. - const NSTimeInterval kRetryAfterSuccess = 24 * kHourInterval; - if (lastExportSuccessDate && fabs(lastExportSuccessDate.timeIntervalSinceNow) < kRetryAfterSuccess) { - return NO; - } - // Wait N hours before retrying after a failure. - const NSTimeInterval kRetryAfterFailure = 6 * kHourInterval; - if (lastExportFailureDate && fabs(lastExportFailureDate.timeIntervalSinceNow) < kRetryAfterFailure) { - return NO; - } - // Don't export backup if there's an import in progress. - // - // This conflict shouldn't occur in production since we won't enable backup - // export until an import is complete, but this could happen in development. - if (self.backupImportJob) { - return NO; - } - - // TODO: There's other conditions that affect this decision, - // e.g. Reachability, wifi v. cellular, etc. - return YES; -} - -- (void)ensureBackupExportState -{ - OWSAssertIsOnMainThread(); - - if (!OWSBackup.isFeatureEnabled) { - return; - } - - if (!CurrentAppContext().isMainApp) { - return; - } - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't backup; not registered and ready."); - return; - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSFailDebug(@"Can't backup; missing recipientId."); - return; - } - - // Start or abort a backup export if neccessary. - if (!self.shouldHaveBackupExport && self.backupExportJob) { - [self.backupExportJob cancel]; - self.backupExportJob = nil; - } else if (self.shouldHaveBackupExport && !self.backupExportJob) { - self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self recipientId:recipientId]; - [self.backupExportJob start]; - } - - // Update the state flag. - OWSBackupState backupExportState = OWSBackupState_Idle; - if (self.backupExportJob) { - backupExportState = OWSBackupState_InProgress; - } else { - NSDate *_Nullable lastExportSuccessDate = self.lastExportSuccessDate; - NSDate *_Nullable lastExportFailureDate = self.lastExportFailureDate; - if (!lastExportSuccessDate && !lastExportFailureDate) { - backupExportState = OWSBackupState_Idle; - } else if (lastExportSuccessDate && lastExportFailureDate) { - backupExportState = ([lastExportSuccessDate isAfterDate:lastExportFailureDate] ? OWSBackupState_Succeeded - : OWSBackupState_Failed); - } else if (lastExportSuccessDate) { - backupExportState = OWSBackupState_Succeeded; - } else if (lastExportFailureDate) { - backupExportState = OWSBackupState_Failed; - } else { - OWSFailDebug(@"unexpected condition."); - } - } - - BOOL stateDidChange = self.backupExportState != backupExportState; - self.backupExportState = backupExportState; - if (stateDidChange) { - [self postDidChangeNotification]; - } -} - -#pragma mark - Backup Import - -- (void)allRecipientIdsWithManifestsInCloud:(OWSBackupStringListBlock)success failure:(OWSBackupErrorBlock)failure -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - [OWSBackupAPI - allRecipientIdsWithManifestsInCloudWithSuccess:^(NSArray *recipientIds) { - dispatch_async(dispatch_get_main_queue(), ^{ - success(recipientIds); - }); - } - failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; -} - -- (AnyPromise *)ensureCloudKitAccess -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - AnyPromise * (^failWithUnexpectedError)(void) = ^{ - NSError *error = [NSError errorWithDomain:OWSBackupErrorDomain - code:1 - userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"BACKUP_UNEXPECTED_ERROR", - @"Error shown when backup fails due to an unexpected error.") - }]; - return [AnyPromise promiseWithValue:error]; - }; - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't backup; not registered and ready."); - return failWithUnexpectedError(); - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSFailDebug(@"Can't backup; missing recipientId."); - return failWithUnexpectedError(); - } - - return [OWSBackupAPI ensureCloudKitAccessObjc]; -} - -- (void)checkCanImportBackup:(OWSBackupBoolBlock)success failure:(OWSBackupErrorBlock)failure -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - if (!OWSBackup.isFeatureEnabled) { - dispatch_async(dispatch_get_main_queue(), ^{ - success(NO); - }); - return; - } - - void (^failWithUnexpectedError)(void) = ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - NSError *error = - [NSError errorWithDomain:OWSBackupErrorDomain - code:1 - userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"BACKUP_UNEXPECTED_ERROR", - @"Error shown when backup fails due to an unexpected error.") - }]; - failure(error); - }); - }; - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't backup; not registered and ready."); - return failWithUnexpectedError(); - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSFailDebug(@"Can't backup; missing recipientId."); - return failWithUnexpectedError(); - } - - [[OWSBackupAPI ensureCloudKitAccessObjc] - .thenInBackground(^{ - return [OWSBackupAPI checkForManifestInCloudObjcWithRecipientId:recipientId]; - }) - .then(^(NSNumber *value) { - success(value.boolValue); - }) - .catch(^(NSError *error) { - failure(error); - }) retainUntilComplete]; -} - -- (void)tryToImportBackup -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(!self.backupImportJob); - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't restore backup; not registered and ready."); - return; - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSLogError(@"Can't restore backup; missing recipientId."); - return; - } - - // In development, make sure there's no export or import in progress. - [self.backupExportJob cancel]; - self.backupExportJob = nil; - [self.backupImportJob cancel]; - self.backupImportJob = nil; - - self.backupImportState = OWSBackupState_InProgress; - - self.backupImportJob = [[OWSBackupImportJob alloc] initWithDelegate:self recipientId:recipientId]; - [self.backupImportJob start]; - - [self postDidChangeNotification]; -} - -- (void)cancelImportBackup -{ - [self.backupImportJob cancel]; - self.backupImportJob = nil; - - self.backupImportState = OWSBackupState_Idle; - - [self postDidChangeNotification]; -} - -#pragma mark - - -- (void)applicationDidBecomeActive:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self ensureBackupExportState]; -} - -- (void)registrationStateDidChange -{ - OWSAssertIsOnMainThread(); - - [self ensureBackupExportState]; - - [self postDidChangeNotification]; -} - -- (void)ckAccountChanged -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self ensureBackupExportState]; - - [self postDidChangeNotification]; - }); -} - -#pragma mark - OWSBackupJobDelegate - -// We use a delegate method to avoid storing this key in memory. -- (nullable NSData *)backupEncryptionKey -{ - // TODO: Use actual encryption key. - return [@"temp" dataUsingEncoding:NSUTF8StringEncoding]; -} - -- (void)backupJobDidSucceed:(OWSBackupJob *)backupJob -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@"."); - - if (self.backupImportJob == backupJob) { - self.backupImportJob = nil; - - self.backupImportState = OWSBackupState_Succeeded; - } else if (self.backupExportJob == backupJob) { - self.backupExportJob = nil; - - [self setLastExportSuccessDate:[NSDate new]]; - - [self ensureBackupExportState]; - } else { - OWSLogWarn(@"obsolete job succeeded: %@", [backupJob class]); - return; - } - - [self postDidChangeNotification]; -} - -- (void)backupJobDidFail:(OWSBackupJob *)backupJob error:(NSError *)error -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@": %@", error); - - if (self.backupImportJob == backupJob) { - self.backupImportJob = nil; - - self.backupImportState = OWSBackupState_Failed; - } else if (self.backupExportJob == backupJob) { - self.backupExportJob = nil; - - [self setLastExportFailureDate:[NSDate new]]; - - [self ensureBackupExportState]; - } else { - OWSLogInfo(@"obsolete backup job failed."); - return; - } - - [self postDidChangeNotification]; -} - -- (void)backupJobDidUpdate:(OWSBackupJob *)backupJob - description:(nullable NSString *)description - progress:(nullable NSNumber *)progress -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - // TODO: Should we consolidate this state? - BOOL didChange; - if (self.backupImportJob == backupJob) { - didChange = !([NSObject isNullableObject:self.backupImportDescription equalTo:description] && - [NSObject isNullableObject:self.backupImportProgress equalTo:progress]); - - self.backupImportDescription = description; - self.backupImportProgress = progress; - } else if (self.backupExportJob == backupJob) { - didChange = !([NSObject isNullableObject:self.backupExportDescription equalTo:description] && - [NSObject isNullableObject:self.backupExportProgress equalTo:progress]); - - self.backupExportDescription = description; - self.backupExportProgress = progress; - } else { - return; - } - - if (didChange) { - [self postDidChangeNotification]; - } -} - -- (void)logBackupRecords -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't interact with backup; not registered and ready."); - return; - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSLogError(@"Can't interact with backup; missing recipientId."); - return; - } - - [OWSBackupAPI fetchAllRecordNamesWithRecipientId:recipientId - success:^(NSArray *recordNames) { - for (NSString *recordName in [recordNames sortedArrayUsingSelector:@selector(compare:)]) { - OWSLogInfo(@"\t %@", recordName); - } - OWSLogInfo(@"record count: %zd", recordNames.count); - } - failure:^(NSError *error) { - OWSLogError(@"Failed to retrieve backup records: %@", error); - }]; -} - -- (void)clearAllCloudKitRecords -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - if (!self.tsAccountManager.isRegisteredAndReady) { - OWSLogError(@"Can't interact with backup; not registered and ready."); - return; - } - NSString *_Nullable recipientId = self.tsAccountManager.localNumber; - if (recipientId.length < 1) { - OWSLogError(@"Can't interact with backup; missing recipientId."); - return; - } - - [OWSBackupAPI fetchAllRecordNamesWithRecipientId:recipientId - success:^(NSArray *recordNames) { - if (recordNames.count < 1) { - OWSLogInfo(@"No CloudKit records found to clear."); - return; - } - [OWSBackupAPI deleteRecordsFromCloudWithRecordNames:recordNames - success:^{ - OWSLogInfo(@"Clear all CloudKit records succeeded."); - } - failure:^(NSError *error) { - OWSLogError(@"Clear all CloudKit records failed: %@.", error); - }]; - } - failure:^(NSError *error) { - OWSLogError(@"Failed to retrieve CloudKit records: %@", error); - }]; -} - -#pragma mark - Lazy Restore - -- (NSArray *)attachmentRecordNamesForLazyRestore -{ - NSMutableArray *recordNames = [NSMutableArray new]; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - id ext = [transaction ext:TSLazyRestoreAttachmentsDatabaseViewExtensionName]; - if (!ext) { - OWSFailDebug(@"Could not load database view."); - return; - } - - [ext enumerateKeysAndObjectsInGroup:TSLazyRestoreAttachmentsGroup - usingBlock:^( - NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) { - if (![object isKindOfClass:[TSAttachmentPointer class]]) { - OWSFailDebug( - @"Unexpected object: %@ in collection:%@", [object class], collection); - return; - } - TSAttachmentPointer *attachmentPointer = object; - if (!attachmentPointer.lazyRestoreFragment) { - OWSFailDebug( - @"Invalid object: %@ in collection:%@", [object class], collection); - return; - } - [recordNames addObject:attachmentPointer.lazyRestoreFragment.recordName]; - }]; - }]; - return recordNames; -} - -- (NSArray *)attachmentIdsForLazyRestore -{ - NSMutableArray *attachmentIds = [NSMutableArray new]; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - id ext = [transaction ext:TSLazyRestoreAttachmentsDatabaseViewExtensionName]; - if (!ext) { - OWSFailDebug(@"Could not load database view."); - return; - } - - [ext enumerateKeysInGroup:TSLazyRestoreAttachmentsGroup - usingBlock:^(NSString *collection, NSString *key, NSUInteger index, BOOL *stop) { - [attachmentIds addObject:key]; - }]; - }]; - return attachmentIds; -} - -- (AnyPromise *)lazyRestoreAttachment:(TSAttachmentPointer *)attachment backupIO:(OWSBackupIO *)backupIO -{ - OWSAssertDebug(attachment); - OWSAssertDebug(backupIO); - - OWSBackupFragment *_Nullable lazyRestoreFragment = attachment.lazyRestoreFragment; - if (!lazyRestoreFragment) { - OWSLogError(@"Attachment missing lazy restore metadata."); - return - [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Attachment missing lazy restore metadata.")]; - } - if (lazyRestoreFragment.recordName.length < 1 || lazyRestoreFragment.encryptionKey.length < 1) { - OWSLogError(@"Incomplete lazy restore metadata."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Incomplete lazy restore metadata.")]; - } - - // Use a predictable file path so that multiple "import backup" attempts - // will leverage successful file downloads from previous attempts. - // - // TODO: This will also require imports using a predictable jobTempDirPath. - NSString *tempFilePath = [backupIO generateTempFilePath]; - - return [OWSBackupAPI downloadFileFromCloudObjcWithRecordName:lazyRestoreFragment.recordName - toFileUrl:[NSURL fileURLWithPath:tempFilePath]] - .thenInBackground(^{ - return [self lazyRestoreAttachment:attachment - backupIO:backupIO - encryptedFilePath:tempFilePath - encryptionKey:lazyRestoreFragment.encryptionKey]; - }); -} - -- (AnyPromise *)lazyRestoreAttachment:(TSAttachmentPointer *)attachmentPointer - backupIO:(OWSBackupIO *)backupIO - encryptedFilePath:(NSString *)encryptedFilePath - encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(attachmentPointer); - OWSAssertDebug(backupIO); - OWSAssertDebug(encryptedFilePath.length > 0); - OWSAssertDebug(encryptionKey.length > 0); - - NSData *_Nullable data = [NSData dataWithContentsOfFile:encryptedFilePath]; - if (!data) { - OWSLogError(@"Could not load encrypted file."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not load encrypted file.")]; - } - - NSString *decryptedFilePath = [backupIO generateTempFilePath]; - - @autoreleasepool { - if (![backupIO decryptFileAsFile:encryptedFilePath dstFilePath:decryptedFilePath encryptionKey:encryptionKey]) { - OWSLogError(@"Could not load decrypt file."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not load decrypt file.")]; - } - } - - TSAttachmentStream *stream = [[TSAttachmentStream alloc] initWithPointer:attachmentPointer]; - - NSString *attachmentFilePath = stream.originalFilePath; - if (attachmentFilePath.length < 1) { - OWSLogError(@"Attachment has invalid file path."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Attachment has invalid file path.")]; - } - - NSString *attachmentDirPath = [attachmentFilePath stringByDeletingLastPathComponent]; - if (![OWSFileSystem ensureDirectoryExists:attachmentDirPath]) { - OWSLogError(@"Couldn't create directory for attachment file."); - return [AnyPromise - promiseWithValue:OWSBackupErrorWithDescription(@"Couldn't create directory for attachment file.")]; - } - - if (![OWSFileSystem deleteFileIfExists:attachmentFilePath]) { - OWSFailDebug(@"Couldn't delete existing file at attachment path."); - return [AnyPromise - promiseWithValue:OWSBackupErrorWithDescription(@"Couldn't delete existing file at attachment path.")]; - } - - NSError *error; - BOOL success = - [NSFileManager.defaultManager moveItemAtPath:decryptedFilePath toPath:attachmentFilePath error:&error]; - if (!success || error) { - OWSLogError(@"Attachment file could not be restored: %@.", error); - return [AnyPromise promiseWithValue:error]; - } - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - // This should overwrite the attachment pointer with an attachment stream. - [stream saveWithTransaction:transaction]; - }]; - - return [AnyPromise promiseWithValue:@(1)]; -} - -- (void)logBackupMetadataCache:(YapDatabaseConnection *)dbConnection -{ - OWSLogInfo(@""); - - [dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - [transaction enumerateKeysAndObjectsInCollection:[OWSBackupFragment collection] - usingBlock:^(NSString *key, OWSBackupFragment *fragment, BOOL *stop) { - OWSLogVerbose(@"fragment: %@, %@, %lu, %@, %@, %@, %@", - key, - fragment.recordName, - (unsigned long)fragment.encryptionKey.length, - fragment.relativeFilePath, - fragment.attachmentId, - fragment.downloadFilePath, - fragment.uncompressedDataLength); - }]; - OWSLogVerbose(@"Number of fragments: %lu", - (unsigned long)[transaction numberOfKeysInCollection:[OWSBackupFragment collection]]); - }]; -} - -#pragma mark - Notifications - -- (void)postDidChangeNotification -{ - OWSAssertIsOnMainThread(); - - [[NSNotificationCenter defaultCenter] postNotificationNameAsync:NSNotificationNameBackupStateDidChange - object:nil - userInfo:nil]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupAPI.swift b/Session/Backups/OWSBackupAPI.swift deleted file mode 100644 index 02b7837c0..000000000 --- a/Session/Backups/OWSBackupAPI.swift +++ /dev/null @@ -1,740 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit -import CloudKit -import PromiseKit - -// We don't worry about atomic writes. Each backup export -// will diff against last successful backup. -// -// Note that all of our CloudKit records are immutable. -// "Persistent" records are only uploaded once. -// "Ephemeral" records are always uploaded to a new record name. -@objc public class OWSBackupAPI: NSObject { - - // If we change the record types, we need to ensure indices - // are configured properly in the CloudKit dashboard. - // - // TODO: Change the record types when we ship to production. - static let signalBackupRecordType = "signalBackup" - static let manifestRecordNameSuffix = "manifest" - static let payloadKey = "payload" - static let maxRetries = 5 - - private class func database() -> CKDatabase { - let myContainer = CKContainer.default() - let privateDatabase = myContainer.privateCloudDatabase - return privateDatabase - } - - private class func invalidServiceResponseError() -> Error { - return OWSErrorWithCodeDescription(.backupFailure, - NSLocalizedString("BACKUP_EXPORT_ERROR_INVALID_CLOUDKIT_RESPONSE", - comment: "Error indicating that the app received an invalid response from CloudKit.")) - } - - // MARK: - Upload - - @objc - public class func recordNameForTestFile(recipientId: String) -> String { - return "\(recordNamePrefix(forRecipientId: recipientId))test-\(NSUUID().uuidString)" - } - - // "Ephemeral" files are specific to this backup export and will always need to - // be saved. For example, a complete image of the database is exported each time. - // We wouldn't want to overwrite previous images until the entire backup export is - // complete. - @objc - public class func recordNameForEphemeralFile(recipientId: String, - label: String) -> String { - return "\(recordNamePrefix(forRecipientId: recipientId))ephemeral-\(label)-\(NSUUID().uuidString)" - } - - // "Persistent" files may be shared between backup export; they should only be saved - // once. For example, attachment files should only be uploaded once. Subsequent - // backups can reuse the same record. - @objc - public class func recordNameForPersistentFile(recipientId: String, - fileId: String) -> String { - return "\(recordNamePrefix(forRecipientId: recipientId))persistentFile-\(fileId)" - } - - // "Persistent" files may be shared between backup export; they should only be saved - // once. For example, attachment files should only be uploaded once. Subsequent - // backups can reuse the same record. - @objc - public class func recordNameForManifest(recipientId: String) -> String { - return "\(recordNamePrefix(forRecipientId: recipientId))\(manifestRecordNameSuffix)" - } - - private class func isManifest(recordName: String) -> Bool { - return recordName.hasSuffix(manifestRecordNameSuffix) - } - - private class func recordNamePrefix(forRecipientId recipientId: String) -> String { - return "\(recipientId)-" - } - - private class func recipientId(forRecordName recordName: String) -> String? { - let recipientIds = self.recipientIds(forRecordNames: [recordName]) - guard let recipientId = recipientIds.first else { - return nil - } - return recipientId - } - - private static var recordNamePrefixRegex = { - return try! NSRegularExpression(pattern: "^(\\+[0-9]+)\\-") - }() - - private class func recipientIds(forRecordNames recordNames: [String]) -> [String] { - var recipientIds = [String]() - for recordName in recordNames { - let regex = recordNamePrefixRegex - guard let match: NSTextCheckingResult = regex.firstMatch(in: recordName, options: [], range: NSRange(location: 0, length: recordName.utf16.count)) else { - Logger.warn("no match: \(recordName)") - continue - } - guard match.numberOfRanges > 0 else { - // Match must include first group. - Logger.warn("invalid match: \(recordName)") - continue - } - let firstRange = match.range(at: 1) - guard firstRange.location == 0, - firstRange.length > 0 else { - // Match must be at start of string and non-empty. - Logger.warn("invalid match: \(recordName) \(firstRange)") - continue - } - let recipientId = (recordName as NSString).substring(with: firstRange) as String - recipientIds.append(recipientId) - } - return recipientIds - } - - @objc - public class func record(forFileUrl fileUrl: URL, - recordName: String) -> CKRecord { - let recordType = signalBackupRecordType - let recordID = CKRecord.ID(recordName: recordName) - let record = CKRecord(recordType: recordType, recordID: recordID) - let asset = CKAsset(fileURL: fileUrl) - record[payloadKey] = asset - - return record - } - - @objc - public class func saveRecordsToCloudObjc(records: [CKRecord]) -> AnyPromise { - return AnyPromise(saveRecordsToCloud(records: records)) - } - - public class func saveRecordsToCloud(records: [CKRecord]) -> Promise { - - // CloudKit's internal limit is 400, but I haven't found a constant for this. - let kMaxBatchSize = 100 - return records.chunked(by: kMaxBatchSize).reduce(Promise.value(())) { (promise, batch) -> Promise in - return promise.then(on: .global()) { - saveRecordsToCloud(records: batch, remainingRetries: maxRetries) - }.done { - Logger.verbose("Saved batch: \(batch.count)") - } - } - } - - private class func saveRecordsToCloud(records: [CKRecord], - remainingRetries: Int) -> Promise { - - let recordNames = records.map { (record) in - return record.recordID.recordName - } - Logger.verbose("recordNames[\(recordNames.count)] \(recordNames[0..<10])...") - - return Promise { resolver in - let saveOperation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil) - saveOperation.modifyRecordsCompletionBlock = { (savedRecords: [CKRecord]?, _, error) in - - let retry = { - // Only retry records which didn't already succeed. - var savedRecordNames = [String]() - if let savedRecords = savedRecords { - savedRecordNames = savedRecords.map { (record) in - return record.recordID.recordName - } - } - let retryRecords = records.filter({ (record) in - return !savedRecordNames.contains(record.recordID.recordName) - }) - - saveRecordsToCloud(records: retryRecords, - remainingRetries: remainingRetries - 1) - .done { _ in - resolver.fulfill(()) - }.catch { (error) in - resolver.reject(error) - }.retainUntilComplete() - } - - let outcome = outcomeForCloudKitError(error: error, - remainingRetries: remainingRetries, - label: "Save Records[\(recordNames.count)]") - switch outcome { - case .success: - resolver.fulfill(()) - case .failureDoNotRetry(let outcomeError): - resolver.reject(outcomeError) - case .failureRetryAfterDelay(let retryDelay): - DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: { - retry() - }) - case .failureRetryWithoutDelay: - DispatchQueue.global().async { - retry() - } - case .unknownItem: - owsFailDebug("unexpected CloudKit response.") - resolver.reject(invalidServiceResponseError()) - } - } - saveOperation.isAtomic = false - saveOperation.savePolicy = .allKeys - - // TODO: use perRecordProgressBlock and perRecordCompletionBlock. -// open var perRecordProgressBlock: ((CKRecord, Double) -> Void)? -// open var perRecordCompletionBlock: ((CKRecord, Error?) -> Void)? - - // These APIs are only available in iOS 9.3 and later. - if #available(iOS 9.3, *) { - saveOperation.isLongLived = true - saveOperation.qualityOfService = .background - } - - database().add(saveOperation) - } - } - - // MARK: - Delete - - @objc - public class func deleteRecordsFromCloud(recordNames: [String], - success: @escaping () -> Void, - failure: @escaping (Error) -> Void) { - deleteRecordsFromCloud(recordNames: recordNames, - remainingRetries: maxRetries, - success: success, - failure: failure) - } - - private class func deleteRecordsFromCloud(recordNames: [String], - remainingRetries: Int, - success: @escaping () -> Void, - failure: @escaping (Error) -> Void) { - - let recordIDs = recordNames.map { CKRecord.ID(recordName: $0) } - let deleteOperation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: recordIDs) - deleteOperation.modifyRecordsCompletionBlock = { (records, recordIds, error) in - - let outcome = outcomeForCloudKitError(error: error, - remainingRetries: remainingRetries, - label: "Delete Records") - switch outcome { - case .success: - success() - case .failureDoNotRetry(let outcomeError): - failure(outcomeError) - case .failureRetryAfterDelay(let retryDelay): - DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: { - deleteRecordsFromCloud(recordNames: recordNames, - remainingRetries: remainingRetries - 1, - success: success, - failure: failure) - }) - case .failureRetryWithoutDelay: - DispatchQueue.global().async { - deleteRecordsFromCloud(recordNames: recordNames, - remainingRetries: remainingRetries - 1, - success: success, - failure: failure) - } - case .unknownItem: - owsFailDebug("unexpected CloudKit response.") - failure(invalidServiceResponseError()) - } - } - database().add(deleteOperation) - } - - // MARK: - Exists? - - private class func checkForFileInCloud(recordName: String, - remainingRetries: Int) -> Promise { - - Logger.verbose("checkForFileInCloud \(recordName)") - - let (promise, resolver) = Promise.pending() - - let recordId = CKRecord.ID(recordName: recordName) - let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordId ]) - // Don't download the file; we're just using the fetch to check whether or - // not this record already exists. - fetchOperation.desiredKeys = [] - fetchOperation.perRecordCompletionBlock = { (record, recordId, error) in - - let outcome = outcomeForCloudKitError(error: error, - remainingRetries: remainingRetries, - label: "Check for Record") - switch outcome { - case .success: - guard let record = record else { - owsFailDebug("missing fetching record.") - resolver.reject(invalidServiceResponseError()) - return - } - // Record found. - resolver.fulfill(record) - case .failureDoNotRetry(let outcomeError): - resolver.reject(outcomeError) - case .failureRetryAfterDelay(let retryDelay): - DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: { - checkForFileInCloud(recordName: recordName, - remainingRetries: remainingRetries - 1) - .done { (record) in - resolver.fulfill(record) - }.catch { (error) in - resolver.reject(error) - }.retainUntilComplete() - }) - case .failureRetryWithoutDelay: - DispatchQueue.global().async { - checkForFileInCloud(recordName: recordName, - remainingRetries: remainingRetries - 1) - .done { (record) in - resolver.fulfill(record) - }.catch { (error) in - resolver.reject(error) - }.retainUntilComplete() - } - case .unknownItem: - // Record not found. - resolver.fulfill(nil) - } - } - database().add(fetchOperation) - return promise - } - - @objc - public class func checkForManifestInCloudObjc(recipientId: String) -> AnyPromise { - return AnyPromise(checkForManifestInCloud(recipientId: recipientId)) - } - - public class func checkForManifestInCloud(recipientId: String) -> Promise { - - let recordName = recordNameForManifest(recipientId: recipientId) - return checkForFileInCloud(recordName: recordName, - remainingRetries: maxRetries) - .map { (record) in - return record != nil - } - } - - @objc - public class func allRecipientIdsWithManifestsInCloud(success: @escaping ([String]) -> Void, - failure: @escaping (Error) -> Void) { - - let processResults = { (recordNames: [String]) in - DispatchQueue.global().async { - let manifestRecordNames = recordNames.filter({ (recordName) -> Bool in - self.isManifest(recordName: recordName) - }) - let recipientIds = self.recipientIds(forRecordNames: manifestRecordNames) - success(recipientIds) - } - } - - let query = CKQuery(recordType: signalBackupRecordType, predicate: NSPredicate(value: true)) - // Fetch the first page of results for this query. - fetchAllRecordNamesStep(recipientId: nil, - query: query, - previousRecordNames: [String](), - cursor: nil, - remainingRetries: maxRetries, - success: processResults, - failure: failure) - } - - @objc - public class func fetchAllRecordNames(recipientId: String, - success: @escaping ([String]) -> Void, - failure: @escaping (Error) -> Void) { - - let query = CKQuery(recordType: signalBackupRecordType, predicate: NSPredicate(value: true)) - // Fetch the first page of results for this query. - fetchAllRecordNamesStep(recipientId: recipientId, - query: query, - previousRecordNames: [String](), - cursor: nil, - remainingRetries: maxRetries, - success: success, - failure: failure) - } - - private class func fetchAllRecordNamesStep(recipientId: String?, - query: CKQuery, - previousRecordNames: [String], - cursor: CKQueryOperation.Cursor?, - remainingRetries: Int, - success: @escaping ([String]) -> Void, - failure: @escaping (Error) -> Void) { - - var allRecordNames = previousRecordNames - - let queryOperation = CKQueryOperation(query: query) - // If this isn't the first page of results for this query, resume - // where we left off. - queryOperation.cursor = cursor - // Don't download the file; we're just using the query to get a list of record names. - queryOperation.desiredKeys = [] - queryOperation.recordFetchedBlock = { (record) in - assert(record.recordID.recordName.count > 0) - - let recordName = record.recordID.recordName - - if let recipientId = recipientId { - let prefix = recordNamePrefix(forRecipientId: recipientId) - guard recordName.hasPrefix(prefix) else { - Logger.info("Ignoring record: \(recordName)") - return - } - } - - allRecordNames.append(recordName) - } - queryOperation.queryCompletionBlock = { (cursor, error) in - - let outcome = outcomeForCloudKitError(error: error, - remainingRetries: remainingRetries, - label: "Fetch All Records") - switch outcome { - case .success: - if let cursor = cursor { - Logger.verbose("fetching more record names \(allRecordNames.count).") - // There are more pages of results, continue fetching. - fetchAllRecordNamesStep(recipientId: recipientId, - query: query, - previousRecordNames: allRecordNames, - cursor: cursor, - remainingRetries: maxRetries, - success: success, - failure: failure) - return - } - Logger.info("fetched \(allRecordNames.count) record names.") - success(allRecordNames) - case .failureDoNotRetry(let outcomeError): - failure(outcomeError) - case .failureRetryAfterDelay(let retryDelay): - DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: { - fetchAllRecordNamesStep(recipientId: recipientId, - query: query, - previousRecordNames: allRecordNames, - cursor: cursor, - remainingRetries: remainingRetries - 1, - success: success, - failure: failure) - }) - case .failureRetryWithoutDelay: - DispatchQueue.global().async { - fetchAllRecordNamesStep(recipientId: recipientId, - query: query, - previousRecordNames: allRecordNames, - cursor: cursor, - remainingRetries: remainingRetries - 1, - success: success, - failure: failure) - } - case .unknownItem: - owsFailDebug("unexpected CloudKit response.") - failure(invalidServiceResponseError()) - } - } - database().add(queryOperation) - } - - // MARK: - Download - - @objc - public class func downloadManifestFromCloudObjc(recipientId: String) -> AnyPromise { - return AnyPromise(downloadManifestFromCloud(recipientId: recipientId)) - } - - public class func downloadManifestFromCloud(recipientId: String) -> Promise { - - let recordName = recordNameForManifest(recipientId: recipientId) - return downloadDataFromCloud(recordName: recordName) - } - - @objc - public class func downloadDataFromCloudObjc(recordName: String) -> AnyPromise { - return AnyPromise(downloadDataFromCloud(recordName: recordName)) - } - - public class func downloadDataFromCloud(recordName: String) -> Promise { - return downloadFromCloud(recordName: recordName, - remainingRetries: maxRetries) - .map { (asset) -> Data in - guard let fileURL = asset.fileURL else { - throw invalidServiceResponseError() - } - return try Data(contentsOf: fileURL) - } - } - - @objc - public class func downloadFileFromCloudObjc(recordName: String, - toFileUrl: URL) -> AnyPromise { - return AnyPromise(downloadFileFromCloud(recordName: recordName, - toFileUrl: toFileUrl)) - } - - public class func downloadFileFromCloud(recordName: String, - toFileUrl: URL) -> Promise { - - return downloadFromCloud(recordName: recordName, - remainingRetries: maxRetries) - .done { asset in - guard let fileURL = asset.fileURL else { - throw invalidServiceResponseError() - } - try FileManager.default.copyItem(at: fileURL, to: toFileUrl) - } - } - - // We return the CKAsset and not its fileUrl because - // CloudKit offers no guarantees around how long it'll - // keep around the underlying file. Presumably we can - // defer cleanup by maintaining a strong reference to - // the asset. - private class func downloadFromCloud(recordName: String, - remainingRetries: Int) -> Promise { - - Logger.verbose("downloadFromCloud \(recordName)") - - let (promise, resolver) = Promise.pending() - - let recordId = CKRecord.ID(recordName: recordName) - let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordId ]) - // Download all keys for this record. - fetchOperation.perRecordCompletionBlock = { (record, recordId, error) in - - let outcome = outcomeForCloudKitError(error: error, - remainingRetries: remainingRetries, - label: "Download Record") - switch outcome { - case .success: - guard let record = record else { - Logger.error("missing fetching record.") - resolver.reject(invalidServiceResponseError()) - return - } - guard let asset = record[payloadKey] as? CKAsset else { - Logger.error("record missing payload.") - resolver.reject(invalidServiceResponseError()) - return - } - resolver.fulfill(asset) - case .failureDoNotRetry(let outcomeError): - resolver.reject(outcomeError) - case .failureRetryAfterDelay(let retryDelay): - DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: { - downloadFromCloud(recordName: recordName, - remainingRetries: remainingRetries - 1) - .done { (asset) in - resolver.fulfill(asset) - }.catch { (error) in - resolver.reject(error) - }.retainUntilComplete() - }) - case .failureRetryWithoutDelay: - DispatchQueue.global().async { - downloadFromCloud(recordName: recordName, - remainingRetries: remainingRetries - 1) - .done { (asset) in - resolver.fulfill(asset) - }.catch { (error) in - resolver.reject(error) - }.retainUntilComplete() - } - case .unknownItem: - Logger.error("missing fetching record.") - resolver.reject(invalidServiceResponseError()) - } - } - database().add(fetchOperation) - - return promise - } - - // MARK: - Access - - @objc public enum BackupError: Int, Error { - case couldNotDetermineAccountStatus - case noAccount - case restrictedAccountStatus - } - - @objc - public class func ensureCloudKitAccessObjc() -> AnyPromise { - return AnyPromise(ensureCloudKitAccess()) - } - - public class func ensureCloudKitAccess() -> Promise { - let (promise, resolver) = Promise.pending() - CKContainer.default().accountStatus { (accountStatus, error) in - if let error = error { - Logger.error("Unknown error: \(String(describing: error)).") - resolver.reject(error) - return - } - switch accountStatus { - case .couldNotDetermine: - Logger.error("could not determine CloudKit account status: \(String(describing: error)).") - resolver.reject(BackupError.couldNotDetermineAccountStatus) - case .noAccount: - Logger.error("no CloudKit account.") - resolver.reject(BackupError.noAccount) - case .restricted: - Logger.error("restricted CloudKit account.") - resolver.reject(BackupError.restrictedAccountStatus) - case .available: - Logger.verbose("CloudKit access okay.") - resolver.fulfill(()) - default: resolver.fulfill(()) - } - } - return promise - } - - @objc - public class func errorMessage(forCloudKitAccessError error: Error) -> String { - if let backupError = error as? BackupError { - Logger.error("Backup error: \(String(describing: backupError)).") - switch backupError { - case .couldNotDetermineAccountStatus: - return NSLocalizedString("CLOUDKIT_STATUS_COULD_NOT_DETERMINE", comment: "Error indicating that the app could not determine that user's iCloud account status") - case .noAccount: - return NSLocalizedString("CLOUDKIT_STATUS_NO_ACCOUNT", comment: "Error indicating that user does not have an iCloud account.") - case .restrictedAccountStatus: - return NSLocalizedString("CLOUDKIT_STATUS_RESTRICTED", comment: "Error indicating that the app was prevented from accessing the user's iCloud account.") - } - } else { - Logger.error("Unknown error: \(String(describing: error)).") - return NSLocalizedString("CLOUDKIT_STATUS_COULD_NOT_DETERMINE", comment: "Error indicating that the app could not determine that user's iCloud account status") - } - } - - // MARK: - Retry - - private enum APIOutcome { - case success - case failureDoNotRetry(error:Error) - case failureRetryAfterDelay(retryDelay: TimeInterval) - case failureRetryWithoutDelay - // This only applies to fetches. - case unknownItem - } - - private class func outcomeForCloudKitError(error: Error?, - remainingRetries: Int, - label: String) -> APIOutcome { - if let error = error as? CKError { - if error.code == CKError.unknownItem { - // This is not always an error for our purposes. - Logger.verbose("\(label) unknown item.") - return .unknownItem - } - - Logger.error("\(label) failed: \(error)") - - if remainingRetries < 1 { - Logger.verbose("\(label) no more retries.") - return .failureDoNotRetry(error:error) - } - - if #available(iOS 11, *) { - if error.code == CKError.serverResponseLost { - Logger.verbose("\(label) retry without delay.") - return .failureRetryWithoutDelay - } - } - - switch error { - case CKError.requestRateLimited, CKError.serviceUnavailable, CKError.zoneBusy: - let retryDelay = error.retryAfterSeconds ?? 3.0 - Logger.verbose("\(label) retry with delay: \(retryDelay).") - return .failureRetryAfterDelay(retryDelay:retryDelay) - case CKError.networkFailure: - Logger.verbose("\(label) retry without delay.") - return .failureRetryWithoutDelay - default: - Logger.verbose("\(label) unknown CKError.") - return .failureDoNotRetry(error:error) - } - } else if let error = error { - Logger.error("\(label) failed: \(error)") - if remainingRetries < 1 { - Logger.verbose("\(label) no more retries.") - return .failureDoNotRetry(error:error) - } - Logger.verbose("\(label) unknown error.") - return .failureDoNotRetry(error:error) - } else { - Logger.info("\(label) succeeded.") - return .success - } - } - - // MARK: - - - @objc - public class func setup() { - cancelAllLongLivedOperations() - } - - private class func cancelAllLongLivedOperations() { - // These APIs are only available in iOS 9.3 and later. - guard #available(iOS 9.3, *) else { - return - } - - let container = CKContainer.default() - container.fetchAllLongLivedOperationIDs { (operationIds, error) in - if let error = error { - Logger.error("Could not get all long lived operations: \(error)") - return - } - guard let operationIds = operationIds else { - Logger.error("No operation ids.") - return - } - - for operationId in operationIds { - container.fetchLongLivedOperation(withID: operationId, completionHandler: { (operation, error) in - if let error = error { - Logger.error("Could not get long lived operation [\(operationId)]: \(error)") - return - } - guard let operation = operation else { - Logger.error("No operation.") - return - } - operation.cancel() - }) - } - } - } -} diff --git a/Session/Backups/OWSBackupExportJob.h b/Session/Backups/OWSBackupExportJob.h deleted file mode 100644 index 8fa9b475e..000000000 --- a/Session/Backups/OWSBackupExportJob.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupJob.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBackupExportJob : OWSBackupJob - -- (void)start; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupExportJob.m b/Session/Backups/OWSBackupExportJob.m deleted file mode 100644 index 304570ac0..000000000 --- a/Session/Backups/OWSBackupExportJob.m +++ /dev/null @@ -1,1181 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupExportJob.h" -#import "OWSBackupIO.h" -#import "OWSDatabaseMigration.h" -#import "Session-Swift.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@import CloudKit; - -NS_ASSUME_NONNULL_BEGIN - -@class OWSAttachmentExport; - -@interface OWSBackupExportItem : NSObject - -@property (nonatomic) OWSBackupEncryptedItem *encryptedItem; - -@property (nonatomic) NSString *recordName; - -// This property is optional and is only used for attachments. -@property (nonatomic, nullable) OWSAttachmentExport *attachmentExport; - -// This property is optional. -// -// See comments in `OWSBackupIO`. -@property (nonatomic, nullable) NSNumber *uncompressedDataLength; - -- (instancetype)init; - -@end - -#pragma mark - - -@implementation OWSBackupExportItem - -- (instancetype)initWithEncryptedItem:(OWSBackupEncryptedItem *)encryptedItem -{ - if (!(self = [super init])) { - return self; - } - - OWSAssertDebug(encryptedItem); - - self.encryptedItem = encryptedItem; - - return self; -} - -@end - -#pragma mark - - -// Used to serialize database snapshot contents. -// Writes db entities using protobufs into snapshot fragments. -// Snapshot fragments are compressed (they compress _very well_, -// around 20x smaller) then encrypted. Ordering matters in -// snapshot contents (entities should be restored in the same -// order they are serialized), so we are always careful to preserve -// ordering of entities within a snapshot AND ordering of snapshot -// fragments within a bakckup. -// -// This stream is used to write entities one at a time and takes -// care of sharding them into fragments, compressing and encrypting -// those fragments. Fragment size is fixed to reduce worst case -// memory usage. -@interface OWSDBExportStream : NSObject - -@property (nonatomic) OWSBackupIO *backupIO; - -@property (nonatomic) NSMutableArray *exportItems; - -@property (nonatomic, nullable) SignalIOSProtoBackupSnapshotBuilder *backupSnapshotBuilder; - -@property (nonatomic) NSUInteger cachedItemCount; - -@property (nonatomic) NSUInteger totalItemCount; - -- (instancetype)init NS_UNAVAILABLE; - -@end - -#pragma mark - - -@implementation OWSDBExportStream - -- (instancetype)initWithBackupIO:(OWSBackupIO *)backupIO -{ - if (!(self = [super init])) { - return self; - } - - OWSAssertDebug(backupIO); - - self.exportItems = [NSMutableArray new]; - self.backupIO = backupIO; - - return self; -} - - -// It isn't strictly necessary to capture the entity type (the importer doesn't -// use this state), but I think it'll be helpful to have around to future-proof -// this work, help with debugging issue, etc. -- (BOOL)writeObject:(NSObject *)object - collection:(NSString *)collection - key:(NSString *)key - entityType:(SignalIOSProtoBackupSnapshotBackupEntityType)entityType -{ - OWSAssertDebug(object); - OWSAssertDebug(collection.length > 0); - OWSAssertDebug(key.length > 0); - - NSData *_Nullable data = [NSKeyedArchiver archivedDataWithRootObject:object]; - if (!data) { - OWSFailDebug(@"couldn't serialize database object: %@", [object class]); - return NO; - } - - if (!self.backupSnapshotBuilder) { - self.backupSnapshotBuilder = [SignalIOSProtoBackupSnapshot builder]; - } - - SignalIOSProtoBackupSnapshotBackupEntityBuilder *entityBuilder = - [SignalIOSProtoBackupSnapshotBackupEntity builderWithType:entityType - entityData:data - collection:collection - key:key]; - - NSError *error; - SignalIOSProtoBackupSnapshotBackupEntity *_Nullable entity = [entityBuilder buildAndReturnError:&error]; - if (!entity || error) { - OWSFailDebug(@"couldn't build proto: %@", error); - return NO; - } - - [self.backupSnapshotBuilder addEntity:entity]; - - self.cachedItemCount = self.cachedItemCount + 1; - self.totalItemCount = self.totalItemCount + 1; - - static const int kMaxDBSnapshotSize = 1000; - if (self.cachedItemCount > kMaxDBSnapshotSize) { - @autoreleasepool { - return [self flush]; - } - } - - return YES; -} - -// Write cached data to disk, if necessary. -// -// Returns YES on success. -- (BOOL)flush -{ - if (!self.backupSnapshotBuilder) { - // No data to flush to disk. - return YES; - } - - // Try to release allocated buffers ASAP. - @autoreleasepool { - NSError *error; - NSData *_Nullable uncompressedData = [self.backupSnapshotBuilder buildSerializedDataAndReturnError:&error]; - if (!uncompressedData || error) { - OWSFailDebug(@"couldn't serialize proto: %@", error); - return NO; - } - - NSUInteger uncompressedDataLength = uncompressedData.length; - self.backupSnapshotBuilder = nil; - self.cachedItemCount = 0; - if (!uncompressedData) { - OWSFailDebug(@"couldn't convert database snapshot to data."); - return NO; - } - - NSData *compressedData = [self.backupIO compressData:uncompressedData]; - - OWSBackupEncryptedItem *_Nullable encryptedItem = [self.backupIO encryptDataAsTempFile:compressedData]; - if (!encryptedItem) { - OWSFailDebug(@"couldn't encrypt database snapshot."); - return NO; - } - - OWSBackupExportItem *exportItem = [[OWSBackupExportItem alloc] initWithEncryptedItem:encryptedItem]; - exportItem.uncompressedDataLength = @(uncompressedDataLength); - [self.exportItems addObject:exportItem]; - } - - return YES; -} - -@end - -#pragma mark - - -// This class is used to: -// -// * Lazy-encrypt and eagerly cleanup attachment uploads. -// To reduce disk footprint of backup export process, -// we only want to have one attachment export on disk -// at a time. -@interface OWSAttachmentExport : NSObject - -@property (nonatomic) OWSBackupIO *backupIO; -@property (nonatomic) NSString *attachmentId; -@property (nonatomic) NSString *attachmentFilePath; -@property (nonatomic, nullable) NSString *relativeFilePath; -@property (nonatomic) OWSBackupEncryptedItem *encryptedItem; - -- (instancetype)init NS_UNAVAILABLE; - -@end - -#pragma mark - - -@implementation OWSAttachmentExport - -- (instancetype)initWithBackupIO:(OWSBackupIO *)backupIO - attachmentId:(NSString *)attachmentId - attachmentFilePath:(NSString *)attachmentFilePath -{ - if (!(self = [super init])) { - return self; - } - - OWSAssertDebug(backupIO); - OWSAssertDebug(attachmentId.length > 0); - OWSAssertDebug(attachmentFilePath.length > 0); - - self.backupIO = backupIO; - self.attachmentId = attachmentId; - self.attachmentFilePath = attachmentFilePath; - - return self; -} - -- (void)dealloc -{ - // Surface memory leaks by logging the deallocation. - OWSLogVerbose(@"Dealloc: %@", self.class); - - [self cleanUp]; -} - -// On success, encryptedItem will be non-nil. -// -// Returns YES on success. -- (BOOL)prepareForUpload -{ - OWSAssertDebug(self.attachmentId.length > 0); - OWSAssertDebug(self.attachmentFilePath.length > 0); - - NSString *attachmentsDirPath = [TSAttachmentStream attachmentsFolder]; - if (![self.attachmentFilePath hasPrefix:attachmentsDirPath]) { - OWSFailDebug(@"attachment has unexpected path: %@", self.attachmentFilePath); - return NO; - } - NSString *relativeFilePath = [self.attachmentFilePath substringFromIndex:attachmentsDirPath.length]; - NSString *pathSeparator = @"/"; - if ([relativeFilePath hasPrefix:pathSeparator]) { - relativeFilePath = [relativeFilePath substringFromIndex:pathSeparator.length]; - } - self.relativeFilePath = relativeFilePath; - - OWSBackupEncryptedItem *_Nullable encryptedItem = [self.backupIO encryptFileAsTempFile:self.attachmentFilePath]; - if (!encryptedItem) { - OWSFailDebug(@"attachment could not be encrypted: %@", self.attachmentFilePath); - return NO; - } - self.encryptedItem = encryptedItem; - return YES; -} - -// Returns YES on success. -- (BOOL)cleanUp -{ - return [OWSFileSystem deleteFileIfExists:self.encryptedItem.filePath]; -} - -@end - -#pragma mark - - -@interface OWSBackupExportJob () - -@property (nonatomic, nullable) OWSBackgroundTask *backgroundTask; - -@property (nonatomic) OWSBackupIO *backupIO; - -@property (nonatomic) NSMutableArray *unsavedDatabaseItems; - -@property (nonatomic) NSMutableArray *unsavedAttachmentExports; - -@property (nonatomic) NSMutableArray *savedDatabaseItems; - -@property (nonatomic) NSMutableArray *savedAttachmentItems; - -@property (nonatomic, nullable) OWSBackupExportItem *localProfileAvatarItem; - -@property (nonatomic, nullable) OWSBackupExportItem *manifestItem; - -// If we are replacing an existing backup, we use some of its contents for continuity. -@property (nonatomic, nullable) NSSet *lastValidRecordNames; - -@property (nonatomic, nullable) YapDatabaseConnection *dbConnection; - -@end - -#pragma mark - - -@implementation OWSBackupExportJob - -#pragma mark - Dependencies - -- (OWSPrimaryStorage *)primaryStorage -{ - OWSAssertDebug(SSKEnvironment.shared.primaryStorage); - - return SSKEnvironment.shared.primaryStorage; -} - -- (OWSBackup *)backup -{ - OWSAssertDebug(AppEnvironment.shared.backup); - - return AppEnvironment.shared.backup; -} - -- (OWSProfileManager *)profileManager -{ - return [OWSProfileManager sharedManager]; -} - -#pragma mark - - -- (void)start -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - self.backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - - [self updateProgressWithDescription:nil progress:nil]; - - self.dbConnection = self.primaryStorage.newDatabaseConnection; - - [[self.backup ensureCloudKitAccess] - .thenInBackground(^{ - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_CONFIGURATION", - @"Indicates that the backup export is being configured.") - progress:nil]; - - return [self configureExport]; - }) - .thenInBackground(^{ - return [self fetchAllRecords]; - }) - .thenInBackground(^{ - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_EXPORT", - @"Indicates that the backup export data is being exported.") - progress:nil]; - - return [self exportDatabase]; - }) - .thenInBackground(^{ - return [self saveToCloud]; - }) - .thenInBackground(^{ - return [self cleanUp]; - }) - .thenInBackground(^{ - [self succeed]; - }) - .catch(^(NSError *error) { - OWSFailDebug(@"Backup export failed with error: %@.", error); - - [self failWithErrorDescription: - NSLocalizedString(@"BACKUP_EXPORT_ERROR_COULD_NOT_EXPORT", - @"Error indicating the backup export could not export the user's data.")]; - }) retainUntilComplete]; -} - -- (AnyPromise *)configureExport -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - if (![self ensureJobTempDir]) { - OWSFailDebug(@"Could not create jobTempDirPath."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not create jobTempDirPath.")]; - } - - self.backupIO = [[OWSBackupIO alloc] initWithJobTempDirPath:self.jobTempDirPath]; - - return [AnyPromise promiseWithValue:@(1)]; -} - -- (AnyPromise *)fetchAllRecords -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [OWSBackupAPI fetchAllRecordNamesWithRecipientId:self.recipientId - success:^(NSArray *recordNames) { - if (self.isComplete) { - return resolve(OWSBackupErrorWithDescription(@"Backup export no longer active.")); - } - self.lastValidRecordNames = [NSSet setWithArray:recordNames]; - resolve(@(1)); - } - failure:^(NSError *error) { - resolve(error); - }]; - }]; -} - -- (AnyPromise *)exportDatabase -{ - OWSAssertDebug(self.backupIO); - - OWSLogVerbose(@""); - - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - if (![self performExportDatabase]) { - NSError *error = OWSBackupErrorWithDescription(@"Backup export failed."); - return resolve(error); - } - - resolve(@(1)); - }]; -} - -- (BOOL)performExportDatabase -{ - OWSAssertDebug(self.backupIO); - - OWSLogVerbose(@""); - - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_DATABASE_EXPORT", - @"Indicates that the database data is being exported.") - progress:nil]; - - OWSDBExportStream *exportStream = [[OWSDBExportStream alloc] initWithBackupIO:self.backupIO]; - - __block BOOL aborted = NO; - typedef BOOL (^EntityFilter)(id object); - typedef NSUInteger (^ExportBlock)(YapDatabaseReadTransaction *, - NSString *, - Class, - EntityFilter _Nullable, - SignalIOSProtoBackupSnapshotBackupEntityType); - NSMutableSet *exportedCollections = [NSMutableSet new]; - ExportBlock exportEntities = ^(YapDatabaseReadTransaction *transaction, - NSString *collection, - Class expectedClass, - EntityFilter _Nullable filter, - SignalIOSProtoBackupSnapshotBackupEntityType entityType) { - [exportedCollections addObject:collection]; - - __block NSUInteger count = 0; - [transaction enumerateKeysAndObjectsInCollection:collection - usingBlock:^(NSString *key, id object, BOOL *stop) { - if (self.isComplete) { - *stop = YES; - return; - } - if (filter && !filter(object)) { - return; - } - if (![object isKindOfClass:expectedClass]) { - OWSFailDebug(@"unexpected class: %@", [object class]); - return; - } - NSObject *entity = object; - count++; - - if ([entity isKindOfClass:[TSAttachmentStream class]]) { - // Convert attachment streams to pointers, - // since we'll need to restore them. - TSAttachmentStream *attachmentStream - = (TSAttachmentStream *)entity; - TSAttachmentPointer *attachmentPointer = - [[TSAttachmentPointer alloc] - initForRestoreWithAttachmentStream:attachmentStream]; - entity = attachmentPointer; - } - - if (![exportStream writeObject:entity - collection:collection - key:key - entityType:entityType]) { - *stop = YES; - aborted = YES; - return; - } - }]; - return count; - }; - - __block NSUInteger copiedThreads = 0; - __block NSUInteger copiedInteractions = 0; - __block NSUInteger copiedAttachments = 0; - __block NSUInteger copiedMigrations = 0; - __block NSUInteger copiedMisc = 0; - self.unsavedAttachmentExports = [NSMutableArray new]; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - copiedThreads = exportEntities(transaction, - [TSThread collection], - [TSThread class], - nil, - SignalIOSProtoBackupSnapshotBackupEntityTypeThread); - if (aborted) { - return; - } - - copiedAttachments = exportEntities(transaction, - [TSAttachment collection], - [TSAttachment class], - ^(id object) { - if (![object isKindOfClass:[TSAttachmentStream class]]) { - // No need to backup the contents (e.g. the file on disk) - // of attachment pointers. - // After a restore, users will be able "tap to retry". - return YES; - } - TSAttachmentStream *attachmentStream = object; - NSString *_Nullable filePath = attachmentStream.originalFilePath; - if (!filePath || ![NSFileManager.defaultManager fileExistsAtPath:filePath]) { - OWSFailDebug(@"attachment is missing file."); - return NO; - } - - // OWSAttachmentExport is used to lazily write an encrypted copy of the - // attachment to disk. - OWSAttachmentExport *attachmentExport = - [[OWSAttachmentExport alloc] initWithBackupIO:self.backupIO - attachmentId:attachmentStream.uniqueId - attachmentFilePath:filePath]; - [self.unsavedAttachmentExports addObject:attachmentExport]; - - return YES; - }, - SignalIOSProtoBackupSnapshotBackupEntityTypeAttachment); - if (aborted) { - return; - } - - // Interactions refer to threads and attachments, so copy after them. - copiedInteractions = exportEntities(transaction, - [TSInteraction collection], - [TSInteraction class], - ^(id object) { - // Ignore disappearing messages. - if ([object isKindOfClass:[TSMessage class]]) { - TSMessage *message = object; - if (message.isExpiringMessage) { - return NO; - } - } - TSInteraction *interaction = object; - // Ignore dynamic interactions. - if (interaction.isDynamicInteraction) { - return NO; - } - return YES; - }, - SignalIOSProtoBackupSnapshotBackupEntityTypeInteraction); - if (aborted) { - return; - } - - copiedMigrations = exportEntities(transaction, - [OWSDatabaseMigration collection], - [OWSDatabaseMigration class], - nil, - SignalIOSProtoBackupSnapshotBackupEntityTypeMigration); - if (aborted) { - return; - } - - for (NSString *collection in MiscCollectionsToBackup()) { - copiedMisc += exportEntities( - transaction, collection, [NSObject class], nil, SignalIOSProtoBackupSnapshotBackupEntityTypeMisc); - if (aborted) { - return; - } - } - - for (NSString *collection in [exportedCollections.allObjects sortedArrayUsingSelector:@selector(compare:)]) { - OWSLogVerbose(@"Exported collection: %@", collection); - } - OWSLogVerbose(@"Exported collections: %lu", (unsigned long)exportedCollections.count); - - NSSet *allCollections = [NSSet setWithArray:transaction.allCollections]; - NSMutableSet *unexportedCollections = [allCollections mutableCopy]; - [unexportedCollections minusSet:exportedCollections]; - for (NSString *collection in [unexportedCollections.allObjects sortedArrayUsingSelector:@selector(compare:)]) { - OWSLogVerbose(@"Unexported collection: %@", collection); - } - OWSLogVerbose(@"Unexported collections: %lu", (unsigned long)unexportedCollections.count); - }]; - - if (aborted || self.isComplete) { - return NO; - } - - @autoreleasepool { - if (![exportStream flush]) { - OWSFailDebug(@"Could not flush database snapshots."); - return NO; - } - } - - self.unsavedDatabaseItems = [exportStream.exportItems mutableCopy]; - - // TODO: Should we do a database checkpoint? - - OWSLogInfo(@"copiedThreads: %zd", copiedThreads); - OWSLogInfo(@"copiedMessages: %zd", copiedInteractions); - OWSLogInfo(@"copiedAttachments: %zd", copiedAttachments); - OWSLogInfo(@"copiedMigrations: %zd", copiedMigrations); - OWSLogInfo(@"copiedMisc: %zd", copiedMisc); - OWSLogInfo(@"copiedEntities: %zd", exportStream.totalItemCount); - - return YES; -} - -- (AnyPromise *)saveToCloud -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - self.savedDatabaseItems = [NSMutableArray new]; - self.savedAttachmentItems = [NSMutableArray new]; - - unsigned long long totalFileSize = 0; - NSUInteger totalFileCount = 0; - { - unsigned long long databaseFileSize = 0; - for (OWSBackupExportItem *item in self.unsavedDatabaseItems) { - unsigned long long fileSize = - [OWSFileSystem fileSizeOfPath:item.encryptedItem.filePath].unsignedLongLongValue; - ows_add_overflow(databaseFileSize, fileSize, &databaseFileSize); - } - OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", - @"database items", - self.unsavedDatabaseItems.count, - databaseFileSize); - ows_add_overflow(totalFileSize, databaseFileSize, &totalFileSize); - ows_add_overflow(totalFileCount, self.unsavedDatabaseItems.count, &totalFileCount); - } - { - unsigned long long attachmentFileSize = 0; - for (OWSAttachmentExport *attachmentExport in self.unsavedAttachmentExports) { - unsigned long long fileSize = - [OWSFileSystem fileSizeOfPath:attachmentExport.attachmentFilePath].unsignedLongLongValue; - ows_add_overflow(attachmentFileSize, fileSize, &attachmentFileSize); - } - OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", - @"attachment items", - self.unsavedAttachmentExports.count, - attachmentFileSize); - ows_add_overflow(totalFileSize, attachmentFileSize, &totalFileSize); - ows_add_overflow(totalFileCount, self.unsavedAttachmentExports.count, &totalFileSize); - } - OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", @"all items", totalFileCount, totalFileSize); - - // Add one for the manifest - NSUInteger unsavedCount = (self.unsavedDatabaseItems.count + self.unsavedAttachmentExports.count + 1); - NSUInteger savedCount = (self.savedDatabaseItems.count + self.savedAttachmentItems.count); - // Ignore localProfileAvatarItem for now. - - CGFloat progress = (savedCount / (CGFloat)(unsavedCount + savedCount)); - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_UPLOAD", - @"Indicates that the backup export data is being uploaded.") - progress:@(progress)]; - - // Save attachment files _before_ anything else, since they - // are the only reusable backup records. - return [self saveAttachmentFilesToCloud] - .thenInBackground(^{ - return [self saveDatabaseFilesToCloud]; - }) - .thenInBackground(^{ - return [self saveLocalProfileAvatarToCloud]; - }) - .thenInBackground(^{ - return [self saveManifestFileToCloud]; - }); -} - -// This method returns YES IFF "work was done and there might be more work to do". -- (AnyPromise *)saveDatabaseFilesToCloud -{ - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - NSArray *items = [self.unsavedDatabaseItems copy]; - NSMutableArray *records = [NSMutableArray new]; - for (OWSBackupExportItem *item in items) { - OWSAssertDebug(item.encryptedItem.filePath.length > 0); - - NSString *recordName = - [OWSBackupAPI recordNameForEphemeralFileWithRecipientId:self.recipientId label:@"database"]; - CKRecord *record = - [OWSBackupAPI recordForFileUrl:[NSURL fileURLWithPath:item.encryptedItem.filePath] recordName:recordName]; - [records addObject:record]; - } - - // TODO: Expose progress. - return [OWSBackupAPI saveRecordsToCloudObjcWithRecords:records].thenInBackground(^{ - OWSAssertDebug(items.count == records.count); - NSUInteger count = MIN(items.count, records.count); - for (NSUInteger i = 0; i < count; i++) { - OWSBackupExportItem *item = items[i]; - CKRecord *record = records[i]; - - OWSAssertDebug(record.recordID.recordName.length > 0); - item.recordName = record.recordID.recordName; - } - - [self.savedDatabaseItems addObjectsFromArray:items]; - [self.unsavedDatabaseItems removeObjectsInArray:items]; - }); -} - -// This method returns YES IFF "work was done and there might be more work to do". -- (AnyPromise *)saveAttachmentFilesToCloud -{ - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - AnyPromise *promise = [AnyPromise promiseWithValue:@(1)]; - NSMutableArray *items = [NSMutableArray new]; - NSMutableArray *records = [NSMutableArray new]; - - for (OWSAttachmentExport *attachmentExport in self.unsavedAttachmentExports) { - if ([self tryToSkipAttachmentUpload:attachmentExport]) { - continue; - } - - promise = promise.thenInBackground(^{ - @autoreleasepool { - // OWSAttachmentExport is used to lazily write an encrypted copy of the - // attachment to disk. - if (![attachmentExport prepareForUpload]) { - // Attachment files are non-critical so any error preparing them is recoverable. - return @(1); - } - OWSAssertDebug(attachmentExport.relativeFilePath.length > 0); - OWSAssertDebug(attachmentExport.encryptedItem); - } - - NSURL *_Nullable fileUrl = ^{ - if (attachmentExport.encryptedItem.filePath.length < 1) { - OWSLogError(@"attachment export missing temp file path"); - return (NSURL *)nil; - } - if (attachmentExport.relativeFilePath.length < 1) { - OWSLogError(@"attachment export missing relative file path"); - return (NSURL *)nil; - } - return [NSURL fileURLWithPath:attachmentExport.encryptedItem.filePath]; - }(); - - if (!fileUrl) { - // Attachment files are non-critical so any error preparing them is recoverable. - return @(1); - } - - NSString *recordName = - [OWSBackupAPI recordNameForPersistentFileWithRecipientId:self.recipientId - fileId:attachmentExport.attachmentId]; - CKRecord *record = [OWSBackupAPI recordForFileUrl:fileUrl recordName:recordName]; - [records addObject:record]; - [items addObject:attachmentExport]; - return @(1); - }); - } - - void (^cleanup)(void) = ^{ - for (OWSAttachmentExport *attachmentExport in items) { - if (![attachmentExport cleanUp]) { - OWSLogError(@"couldn't clean up attachment export."); - // Attachment files are non-critical so any error uploading them is recoverable. - } - } - }; - - // TODO: Expose progress. - return promise - .thenInBackground(^{ - return [OWSBackupAPI saveRecordsToCloudObjcWithRecords:records]; - }) - .thenInBackground(^{ - OWSAssertDebug(items.count == records.count); - NSUInteger count = MIN(items.count, records.count); - for (NSUInteger i = 0; i < count; i++) { - OWSAttachmentExport *attachmentExport = items[i]; - CKRecord *record = records[i]; - NSString *recordName = record.recordID.recordName; - OWSAssertDebug(recordName.length > 0); - - OWSBackupExportItem *exportItem = [OWSBackupExportItem new]; - exportItem.encryptedItem = attachmentExport.encryptedItem; - exportItem.recordName = recordName; - exportItem.attachmentExport = attachmentExport; - [self.savedAttachmentItems addObject:exportItem]; - - // Immediately save the record metadata to facilitate export resume. - OWSBackupFragment *backupFragment = [[OWSBackupFragment alloc] initWithUniqueId:recordName]; - backupFragment.recordName = recordName; - backupFragment.encryptionKey = exportItem.encryptedItem.encryptionKey; - backupFragment.relativeFilePath = attachmentExport.relativeFilePath; - backupFragment.attachmentId = attachmentExport.attachmentId; - backupFragment.uncompressedDataLength = exportItem.uncompressedDataLength; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [backupFragment saveWithTransaction:transaction]; - }]; - - OWSLogVerbose(@"saved attachment: %@ as %@", - attachmentExport.attachmentFilePath, - attachmentExport.relativeFilePath); - } - }) - .thenInBackground(^{ - cleanup(); - }) - .catchInBackground(^(NSError *error) { - cleanup(); - - return error; - }); -} - -- (BOOL)tryToSkipAttachmentUpload:(OWSAttachmentExport *)attachmentExport -{ - if (!self.lastValidRecordNames) { - return NO; - } - - // Wherever possible, we do incremental backups and re-use fragments of the last - // backup and/or restore. - // Recycling fragments doesn't just reduce redundant network activity, - // it allows us to skip the local export work, i.e. encryption. - // To do so, we must preserve the metadata for these fragments. - // - // We check two things: - // - // * That we already know the metadata for this fragment (from a previous backup - // or restore). - // * That this record does in fact exist in our CloudKit database. - NSString *recordName = - [OWSBackupAPI recordNameForPersistentFileWithRecipientId:self.recipientId fileId:attachmentExport.attachmentId]; - OWSBackupFragment *_Nullable lastBackupFragment = [OWSBackupFragment fetchObjectWithUniqueID:recordName]; - if (!lastBackupFragment || ![self.lastValidRecordNames containsObject:recordName]) { - return NO; - } - - OWSAssertDebug(lastBackupFragment.encryptionKey.length > 0); - OWSAssertDebug(lastBackupFragment.relativeFilePath.length > 0); - - // Recycle the metadata from the last backup's manifest. - OWSBackupEncryptedItem *encryptedItem = [OWSBackupEncryptedItem new]; - encryptedItem.encryptionKey = lastBackupFragment.encryptionKey; - attachmentExport.encryptedItem = encryptedItem; - attachmentExport.relativeFilePath = lastBackupFragment.relativeFilePath; - - OWSBackupExportItem *exportItem = [OWSBackupExportItem new]; - exportItem.encryptedItem = attachmentExport.encryptedItem; - exportItem.recordName = recordName; - exportItem.attachmentExport = attachmentExport; - [self.savedAttachmentItems addObject:exportItem]; - - OWSLogVerbose( - @"recycled attachment: %@ as %@", attachmentExport.attachmentFilePath, attachmentExport.relativeFilePath); - return YES; -} - -- (AnyPromise *)saveLocalProfileAvatarToCloud -{ - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - NSData *_Nullable localProfileAvatarData = nil; // TODO: self.profileManager.localProfileAvatarData; - if (localProfileAvatarData.length < 1) { - // No profile avatar to backup. - return [AnyPromise promiseWithValue:@(1)]; - } - OWSBackupEncryptedItem *_Nullable encryptedItem = - [self.backupIO encryptDataAsTempFile:localProfileAvatarData encryptionKey:self.delegate.backupEncryptionKey]; - if (!encryptedItem) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not encrypt local profile avatar.")]; - } - - OWSBackupExportItem *exportItem = [OWSBackupExportItem new]; - exportItem.encryptedItem = encryptedItem; - - NSString *recordName = - [OWSBackupAPI recordNameForEphemeralFileWithRecipientId:self.recipientId label:@"local-profile-avatar"]; - CKRecord *record = - [OWSBackupAPI recordForFileUrl:[NSURL fileURLWithPath:encryptedItem.filePath] recordName:recordName]; - return [OWSBackupAPI saveRecordsToCloudObjcWithRecords:@[ record ]].thenInBackground(^{ - exportItem.recordName = recordName; - self.localProfileAvatarItem = exportItem; - }); -} - -- (AnyPromise *)saveManifestFileToCloud -{ - if (self.isComplete) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - OWSBackupEncryptedItem *_Nullable encryptedItem = [self writeManifestFile]; - if (!encryptedItem) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not generate manifest.")]; - } - - OWSBackupExportItem *exportItem = [OWSBackupExportItem new]; - exportItem.encryptedItem = encryptedItem; - - - NSString *recordName = [OWSBackupAPI recordNameForManifestWithRecipientId:self.recipientId]; - CKRecord *record = - [OWSBackupAPI recordForFileUrl:[NSURL fileURLWithPath:encryptedItem.filePath] recordName:recordName]; - return [OWSBackupAPI saveRecordsToCloudObjcWithRecords:@[ record ]].thenInBackground(^{ - exportItem.recordName = recordName; - self.manifestItem = exportItem; - }); -} - -- (nullable OWSBackupEncryptedItem *)writeManifestFile -{ - OWSAssertDebug(self.savedDatabaseItems.count > 0); - OWSAssertDebug(self.savedAttachmentItems); - OWSAssertDebug(self.jobTempDirPath.length > 0); - OWSAssertDebug(self.backupIO); - - NSMutableDictionary *json = [@{ - kOWSBackup_ManifestKey_DatabaseFiles : [self jsonForItems:self.savedDatabaseItems], - kOWSBackup_ManifestKey_AttachmentFiles : [self jsonForItems:self.savedAttachmentItems], - } mutableCopy]; - - NSString *_Nullable localProfileName = [[LKStorage.shared getUser] name]; - if (localProfileName.length > 0) { - json[kOWSBackup_ManifestKey_LocalProfileName] = localProfileName; - } - - if (self.localProfileAvatarItem) { - json[kOWSBackup_ManifestKey_LocalProfileAvatar] = [self jsonForItems:@[ self.localProfileAvatarItem ]]; - } - - OWSLogVerbose(@"json: %@", json); - - NSError *error; - NSData *_Nullable jsonData = - [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error]; - if (!jsonData || error) { - OWSFailDebug(@"error encoding manifest file: %@", error); - return nil; - } - return [self.backupIO encryptDataAsTempFile:jsonData encryptionKey:self.delegate.backupEncryptionKey]; -} - -- (NSArray *> *)jsonForItems:(NSArray *)items -{ - NSMutableArray *result = [NSMutableArray new]; - for (OWSBackupExportItem *item in items) { - NSMutableDictionary *itemJson = [NSMutableDictionary new]; - OWSAssertDebug(item.recordName.length > 0); - - itemJson[kOWSBackup_ManifestKey_RecordName] = item.recordName; - OWSAssertDebug(item.encryptedItem.encryptionKey.length > 0); - itemJson[kOWSBackup_ManifestKey_EncryptionKey] = item.encryptedItem.encryptionKey.base64EncodedString; - if (item.attachmentExport) { - OWSAssertDebug(item.attachmentExport.relativeFilePath.length > 0); - itemJson[kOWSBackup_ManifestKey_RelativeFilePath] = item.attachmentExport.relativeFilePath; - } - if (item.attachmentExport.attachmentId) { - OWSAssertDebug(item.attachmentExport.attachmentId.length > 0); - itemJson[kOWSBackup_ManifestKey_AttachmentId] = item.attachmentExport.attachmentId; - } - if (item.uncompressedDataLength) { - itemJson[kOWSBackup_ManifestKey_DataSize] = item.uncompressedDataLength; - } - [result addObject:itemJson]; - } - - return result; -} - -- (AnyPromise *)cleanUp -{ - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - OWSLogVerbose(@""); - - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_CLEAN_UP", - @"Indicates that the cloud is being cleaned up.") - progress:nil]; - - // Now that our backup export has successfully completed, - // we try to clean up the cloud. We can safely delete any - // records not involved in this backup export. - NSMutableSet *activeRecordNames = [NSMutableSet new]; - - OWSAssertDebug(self.savedDatabaseItems.count > 0); - for (OWSBackupExportItem *item in self.savedDatabaseItems) { - OWSAssertDebug(item.recordName.length > 0); - OWSAssertDebug(![activeRecordNames containsObject:item.recordName]); - [activeRecordNames addObject:item.recordName]; - } - for (OWSBackupExportItem *item in self.savedAttachmentItems) { - OWSAssertDebug(item.recordName.length > 0); - OWSAssertDebug(![activeRecordNames containsObject:item.recordName]); - [activeRecordNames addObject:item.recordName]; - } - if (self.localProfileAvatarItem) { - OWSBackupExportItem *item = self.localProfileAvatarItem; - OWSAssertDebug(item.recordName.length > 0); - OWSAssertDebug(![activeRecordNames containsObject:item.recordName]); - [activeRecordNames addObject:item.recordName]; - } - OWSAssertDebug(self.manifestItem.recordName.length > 0); - OWSAssertDebug(![activeRecordNames containsObject:self.manifestItem.recordName]); - [activeRecordNames addObject:self.manifestItem.recordName]; - - // Because we do "lazy attachment restores", we need to include the record names for all - // records that haven't been restored yet. - NSArray *restoringRecordNames = [OWSBackup.sharedManager attachmentRecordNamesForLazyRestore]; - [activeRecordNames addObjectsFromArray:restoringRecordNames]; - - [self cleanUpMetadataCacheWithActiveRecordNames:activeRecordNames]; - - return [self cleanUpCloudWithActiveRecordNames:activeRecordNames]; -} - -- (void)cleanUpMetadataCacheWithActiveRecordNames:(NSSet *)activeRecordNames -{ - OWSAssertDebug(activeRecordNames.count > 0); - - if (self.isComplete) { - // Job was aborted. - return; - } - - // After every successful backup export, we can (and should) cull metadata - // for any backup fragment (i.e. CloudKit record) that wasn't involved in - // the latest backup export. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - NSArray *allRecordNames = [transaction allKeysInCollection:[OWSBackupFragment collection]]; - - NSMutableSet *obsoleteRecordNames = [NSMutableSet new]; - [obsoleteRecordNames addObjectsFromArray:allRecordNames]; - [obsoleteRecordNames minusSet:activeRecordNames]; - - [transaction removeObjectsForKeys:obsoleteRecordNames.allObjects inCollection:[OWSBackupFragment collection]]; - }]; -} - -- (AnyPromise *)cleanUpCloudWithActiveRecordNames:(NSSet *)activeRecordNames -{ - OWSAssertDebug(activeRecordNames.count > 0); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup export no longer active.")]; - } - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [OWSBackupAPI fetchAllRecordNamesWithRecipientId:self.recipientId - success:^(NSArray *recordNames) { - NSMutableSet *obsoleteRecordNames = [NSMutableSet new]; - [obsoleteRecordNames addObjectsFromArray:recordNames]; - [obsoleteRecordNames minusSet:activeRecordNames]; - - OWSLogVerbose(@"recordNames: %zd - activeRecordNames: %zd = obsoleteRecordNames: %zd", - recordNames.count, - activeRecordNames.count, - obsoleteRecordNames.count); - - [self deleteRecordsFromCloud:[obsoleteRecordNames.allObjects mutableCopy] - deletedCount:0 - completion:^(NSError *_Nullable error) { - // Cloud cleanup is non-critical so any error is recoverable. - resolve(@(1)); - }]; - } - failure:^(NSError *error) { - // Cloud cleanup is non-critical so any error is recoverable. - resolve(@(1)); - }]; - }]; -} - -- (void)deleteRecordsFromCloud:(NSMutableArray *)obsoleteRecordNames - deletedCount:(NSUInteger)deletedCount - completion:(OWSBackupJobCompletion)completion -{ - OWSAssertDebug(obsoleteRecordNames); - OWSAssertDebug(completion); - - OWSLogVerbose(@""); - - if (obsoleteRecordNames.count < 1) { - // No more records to delete; cleanup is complete. - return completion(nil); - } - - if (self.isComplete) { - // Job was aborted. - return completion(nil); - } - - CGFloat progress = (obsoleteRecordNames.count / (CGFloat)(obsoleteRecordNames.count + deletedCount)); - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_EXPORT_PHASE_CLEAN_UP", - @"Indicates that the cloud is being cleaned up.") - progress:@(progress)]; - - static const NSUInteger kMaxBatchSize = 100; - NSMutableArray *batchRecordNames = [NSMutableArray new]; - while (obsoleteRecordNames.count > 0 && batchRecordNames.count < kMaxBatchSize) { - NSString *recordName = obsoleteRecordNames.lastObject; - [obsoleteRecordNames removeLastObject]; - [batchRecordNames addObject:recordName]; - } - - [OWSBackupAPI deleteRecordsFromCloudWithRecordNames:batchRecordNames - success:^{ - [self deleteRecordsFromCloud:obsoleteRecordNames - deletedCount:deletedCount + batchRecordNames.count - completion:completion]; - } - failure:^(NSError *error) { - // Cloud cleanup is non-critical so any error is recoverable. - [self deleteRecordsFromCloud:obsoleteRecordNames - deletedCount:deletedCount + batchRecordNames.count - completion:completion]; - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupIO.h b/Session/Backups/OWSBackupIO.h deleted file mode 100644 index ec9ef0575..000000000 --- a/Session/Backups/OWSBackupIO.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBackupEncryptedItem : NSObject - -@property (nonatomic) NSString *filePath; - -@property (nonatomic) NSData *encryptionKey; - -@end - -#pragma mark - - -@interface OWSBackupIO : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithJobTempDirPath:(NSString *)jobTempDirPath; - -- (NSString *)generateTempFilePath; - -- (nullable NSString *)createTempFile; - -#pragma mark - Encrypt - -- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath; - -- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath - encryptionKey:(NSData *)encryptionKey; - -- (nullable OWSBackupEncryptedItem *)encryptDataAsTempFile:(NSData *)srcData; - -- (nullable OWSBackupEncryptedItem *)encryptDataAsTempFile:(NSData *)srcData encryptionKey:(NSData *)encryptionKey; - -#pragma mark - Decrypt - -- (BOOL)decryptFileAsFile:(NSString *)srcFilePath - dstFilePath:(NSString *)dstFilePath - encryptionKey:(NSData *)encryptionKey; - -- (nullable NSData *)decryptFileAsData:(NSString *)srcFilePath encryptionKey:(NSData *)encryptionKey; - -- (nullable NSData *)decryptDataAsData:(NSData *)srcData encryptionKey:(NSData *)encryptionKey; - -#pragma mark - Compression - -- (nullable NSData *)compressData:(NSData *)srcData; - -// I'm using the (new in iOS 9) compressionlib. One of its weaknesses is that it -// requires you to pre-allocate output buffers during compression and decompression. -// During decompression this is particularly tricky since there's no way to safely -// predict how large the output will be based on the input. So, we store the -// uncompressed size for compressed backup items. -- (nullable NSData *)decompressData:(NSData *)srcData uncompressedDataLength:(NSUInteger)uncompressedDataLength; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupIO.m b/Session/Backups/OWSBackupIO.m deleted file mode 100644 index ef95733e0..000000000 --- a/Session/Backups/OWSBackupIO.m +++ /dev/null @@ -1,273 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupIO.h" -#import -#import - -@import Compression; - -NS_ASSUME_NONNULL_BEGIN - -// TODO: -static const NSUInteger kOWSBackupKeyLength = 32; - -// LZMA algorithm significantly outperforms the other compressionlib options -// for our database snapshots and is a widely adopted standard. -static const compression_algorithm SignalCompressionAlgorithm = COMPRESSION_LZMA; - -@implementation OWSBackupEncryptedItem - -@end - -#pragma mark - - -@interface OWSBackupIO () - -@property (nonatomic) NSString *jobTempDirPath; - -@end - -#pragma mark - - -@implementation OWSBackupIO - -- (instancetype)initWithJobTempDirPath:(NSString *)jobTempDirPath -{ - if (!(self = [super init])) { - return self; - } - - self.jobTempDirPath = jobTempDirPath; - - return self; -} - -- (NSString *)generateTempFilePath -{ - return [self.jobTempDirPath stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; -} - -- (nullable NSString *)createTempFile -{ - NSString *filePath = [self generateTempFilePath]; - if (![OWSFileSystem ensureFileExists:filePath]) { - OWSFailDebug(@"could not create temp file."); - return nil; - } - return filePath; -} - -#pragma mark - Encrypt - -- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath -{ - OWSAssertDebug(srcFilePath.length > 0); - - NSData *encryptionKey = [Randomness generateRandomBytes:(int)kOWSBackupKeyLength]; - - return [self encryptFileAsTempFile:srcFilePath encryptionKey:encryptionKey]; -} - -- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(srcFilePath.length > 0); - OWSAssertDebug(encryptionKey.length > 0); - - @autoreleasepool { - if (![[NSFileManager defaultManager] fileExistsAtPath:srcFilePath]) { - OWSFailDebug(@"Missing source file."); - return nil; - } - - // TODO: Encrypt the file without loading it into memory. - NSData *_Nullable srcData = [NSData dataWithContentsOfFile:srcFilePath]; - if (srcData.length < 1) { - OWSFailDebug(@"could not load file into memory for encryption."); - return nil; - } - return [self encryptDataAsTempFile:srcData encryptionKey:encryptionKey]; - } -} - -- (nullable OWSBackupEncryptedItem *)encryptDataAsTempFile:(NSData *)srcData -{ - OWSAssertDebug(srcData); - - NSData *encryptionKey = [Randomness generateRandomBytes:(int)kOWSBackupKeyLength]; - - return [self encryptDataAsTempFile:srcData encryptionKey:encryptionKey]; -} - -- (nullable OWSBackupEncryptedItem *)encryptDataAsTempFile:(NSData *)unencryptedData - encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(unencryptedData); - OWSAssertDebug(encryptionKey.length > 0); - - @autoreleasepool { - - // TODO: Encrypt the data using key; - NSData *encryptedData = unencryptedData; - - NSString *_Nullable dstFilePath = [self createTempFile]; - if (!dstFilePath) { - return nil; - } - NSError *error; - BOOL success = [encryptedData writeToFile:dstFilePath options:NSDataWritingAtomic error:&error]; - if (!success || error) { - OWSFailDebug(@"error writing encrypted data: %@", error); - return nil; - } - [OWSFileSystem protectFileOrFolderAtPath:dstFilePath]; - OWSBackupEncryptedItem *item = [OWSBackupEncryptedItem new]; - item.filePath = dstFilePath; - item.encryptionKey = encryptionKey; - return item; - } -} - -#pragma mark - Decrypt - -- (BOOL)decryptFileAsFile:(NSString *)srcFilePath - dstFilePath:(NSString *)dstFilePath - encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(srcFilePath.length > 0); - OWSAssertDebug(encryptionKey.length > 0); - - @autoreleasepool { - - // TODO: Decrypt the file without loading it into memory. - NSData *data = [self decryptFileAsData:srcFilePath encryptionKey:encryptionKey]; - - if (data.length < 1) { - return NO; - } - - NSError *error; - BOOL success = [data writeToFile:dstFilePath options:NSDataWritingAtomic error:&error]; - if (!success || error) { - OWSFailDebug(@"error writing decrypted data: %@", error); - return NO; - } - [OWSFileSystem protectFileOrFolderAtPath:dstFilePath]; - - return YES; - } -} - -- (nullable NSData *)decryptFileAsData:(NSString *)srcFilePath encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(srcFilePath.length > 0); - OWSAssertDebug(encryptionKey.length > 0); - - @autoreleasepool { - - if (![NSFileManager.defaultManager fileExistsAtPath:srcFilePath]) { - OWSLogError(@"missing downloaded file."); - return nil; - } - - NSData *_Nullable srcData = [NSData dataWithContentsOfFile:srcFilePath]; - if (srcData.length < 1) { - OWSFailDebug(@"could not load file into memory for decryption."); - return nil; - } - - NSData *_Nullable dstData = [self decryptDataAsData:srcData encryptionKey:encryptionKey]; - return dstData; - } -} - -- (nullable NSData *)decryptDataAsData:(NSData *)encryptedData encryptionKey:(NSData *)encryptionKey -{ - OWSAssertDebug(encryptedData); - OWSAssertDebug(encryptionKey.length > 0); - - @autoreleasepool { - - // TODO: Decrypt the data using key; - NSData *unencryptedData = encryptedData; - - return unencryptedData; - } -} - -#pragma mark - Compression - -- (nullable NSData *)compressData:(NSData *)srcData -{ - OWSAssertDebug(srcData); - - @autoreleasepool { - - if (!srcData) { - OWSFailDebug(@"missing unencrypted data."); - return nil; - } - - size_t srcLength = [srcData length]; - - // This assumes that dst will always be smaller than src. - // - // We slightly pad the buffer size to account for the worst case. - size_t dstBufferLength = srcLength + 64 * 1024; - NSMutableData *dstBufferData = [NSMutableData dataWithLength:dstBufferLength]; - if (!dstBufferData) { - OWSFailDebug(@"Failed to allocate buffer."); - return nil; - } - - size_t dstLength = compression_encode_buffer( - dstBufferData.mutableBytes, dstBufferLength, srcData.bytes, srcLength, NULL, SignalCompressionAlgorithm); - NSData *compressedData = [dstBufferData subdataWithRange:NSMakeRange(0, dstLength)]; - - OWSLogVerbose(@"compressed %zd -> %zd = %0.2f", - srcLength, - dstLength, - (srcLength > 0 ? (dstLength / (CGFloat)srcLength) : 0)); - - return compressedData; - } -} - -- (nullable NSData *)decompressData:(NSData *)srcData uncompressedDataLength:(NSUInteger)uncompressedDataLength -{ - OWSAssertDebug(srcData); - - @autoreleasepool { - - if (!srcData) { - OWSFailDebug(@"missing unencrypted data."); - return nil; - } - - size_t srcLength = [srcData length]; - - // We pad the buffer to be defensive. - size_t dstBufferLength = uncompressedDataLength + 1024; - NSMutableData *dstBufferData = [NSMutableData dataWithLength:dstBufferLength]; - if (!dstBufferData) { - OWSFailDebug(@"Failed to allocate buffer."); - return nil; - } - - size_t dstLength = compression_decode_buffer( - dstBufferData.mutableBytes, dstBufferLength, srcData.bytes, srcLength, NULL, SignalCompressionAlgorithm); - NSData *decompressedData = [dstBufferData subdataWithRange:NSMakeRange(0, dstLength)]; - OWSAssertDebug(decompressedData.length == uncompressedDataLength); - OWSLogVerbose(@"decompressed %zd -> %zd = %0.2f", - srcLength, - dstLength, - (dstLength > 0 ? (srcLength / (CGFloat)dstLength) : 0)); - - return decompressedData; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupImportJob.h b/Session/Backups/OWSBackupImportJob.h deleted file mode 100644 index 969e2c668..000000000 --- a/Session/Backups/OWSBackupImportJob.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupJob.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBackupImportJob : OWSBackupJob - -- (void)start; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupImportJob.m b/Session/Backups/OWSBackupImportJob.m deleted file mode 100644 index 46233da19..000000000 --- a/Session/Backups/OWSBackupImportJob.m +++ /dev/null @@ -1,635 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupImportJob.h" -#import "OWSBackupIO.h" -#import "OWSDatabaseMigration.h" -#import "OWSDatabaseMigrationRunner.h" -#import "Session-Swift.h" -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKeySpec"; - -#pragma mark - - -@interface OWSBackupImportJob () - -@property (nonatomic, nullable) OWSBackgroundTask *backgroundTask; - -@property (nonatomic) OWSBackupIO *backupIO; - -@property (nonatomic) OWSBackupManifestContents *manifest; - -@property (nonatomic, nullable) YapDatabaseConnection *dbConnection; - -@end - -#pragma mark - - -@implementation OWSBackupImportJob - -#pragma mark - Dependencies - -- (OWSPrimaryStorage *)primaryStorage -{ - OWSAssertDebug(SSKEnvironment.shared.primaryStorage); - - return SSKEnvironment.shared.primaryStorage; -} - -- (OWSProfileManager *)profileManager -{ - return [OWSProfileManager sharedManager]; -} - -- (TSAccountManager *)tsAccountManager -{ - OWSAssertDebug(SSKEnvironment.shared.tsAccountManager); - - return SSKEnvironment.shared.tsAccountManager; -} - -- (OWSBackup *)backup -{ - OWSAssertDebug(AppEnvironment.shared.backup); - - return AppEnvironment.shared.backup; -} - -- (OWSBackupLazyRestore *)backupLazyRestore -{ - return AppEnvironment.shared.backupLazyRestore; -} - -#pragma mark - - -- (NSArray *)databaseItems -{ - OWSAssertDebug(self.manifest); - - return self.manifest.databaseItems; -} - -- (NSArray *)attachmentsItems -{ - OWSAssertDebug(self.manifest); - - return self.manifest.attachmentsItems; -} - -- (void)start -{ - OWSAssertIsOnMainThread(); - - OWSLogInfo(@""); - - self.backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; - - self.dbConnection = self.primaryStorage.newDatabaseConnection; - - [self updateProgressWithDescription:nil progress:nil]; - - [[self.backup ensureCloudKitAccess] - .thenInBackground(^{ - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_CONFIGURATION", - @"Indicates that the backup import is being configured.") - progress:nil]; - - return [self configureImport]; - }) - .thenInBackground(^{ - if (self.isComplete) { - return - [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_IMPORT", - @"Indicates that the backup import data is being imported.") - progress:nil]; - - return [self downloadAndProcessManifestWithBackupIO:self.backupIO]; - }) - .thenInBackground(^(OWSBackupManifestContents *manifest) { - OWSCAssertDebug(manifest.databaseItems.count > 0); - OWSCAssertDebug(manifest.attachmentsItems); - - self.manifest = manifest; - - return [self downloadAndProcessImport]; - }) - .catch(^(NSError *error) { - [self failWithErrorDescription: - NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT", - @"Error indicating the backup import could not import the user's data.")]; - }) retainUntilComplete]; -} - -- (AnyPromise *)downloadAndProcessImport -{ - OWSAssertDebug(self.databaseItems); - OWSAssertDebug(self.attachmentsItems); - - // These items should be downloaded immediately. - NSMutableArray *allItems = [NSMutableArray new]; - [allItems addObjectsFromArray:self.databaseItems]; - - // Make a copy of the blockingItems before we add - // the optional items. - NSArray *blockingItems = [allItems copy]; - - // Local profile avatars are optional in the sense that if their - // download fails, we want to proceed with the import. - if (self.manifest.localProfileAvatarItem) { - [allItems addObject:self.manifest.localProfileAvatarItem]; - } - - // Attachment items can be downloaded later; - // they will can be lazy-restored. - [allItems addObjectsFromArray:self.attachmentsItems]; - - // Record metadata for all items, so that we can re-use them in incremental backups after the restore. - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (OWSBackupFragment *item in allItems) { - [item saveWithTransaction:transaction]; - } - }]; - - return [self downloadFilesFromCloud:blockingItems] - .thenInBackground(^{ - return [self restoreDatabase]; - }) - .thenInBackground(^{ - return [self ensureMigrations]; - }) - .thenInBackground(^{ - return [self restoreLocalProfile]; - }) - .thenInBackground(^{ - return [self restoreAttachmentFiles]; - }) - .then(^{ - // Kick off lazy restore on main thread. - [self.backupLazyRestore clearCompleteAndRunIfNecessary]; - - // Make sure backup is enabled once we complete - // a backup restore. - [OWSBackup.sharedManager setIsBackupEnabled:YES]; - }) - .thenInBackground(^{ - return [self.tsAccountManager updateAccountAttributes]; - }) - .thenInBackground(^{ - [self succeed]; - }); -} - -- (AnyPromise *)configureImport -{ - OWSLogVerbose(@""); - - if (![self ensureJobTempDir]) { - OWSFailDebug(@"Could not create jobTempDirPath."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not create jobTempDirPath.")]; - } - - self.backupIO = [[OWSBackupIO alloc] initWithJobTempDirPath:self.jobTempDirPath]; - - return [AnyPromise promiseWithValue:@(1)]; -} - -- (AnyPromise *)downloadFilesFromCloud:(NSArray *)items -{ - OWSAssertDebug(items.count > 0); - - OWSLogVerbose(@""); - - NSUInteger recordCount = items.count; - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - if (items.count < 1) { - // All downloads are complete; exit. - return [AnyPromise promiseWithValue:@(1)]; - } - - AnyPromise *promise = [AnyPromise promiseWithValue:@(1)]; - for (OWSBackupFragment *item in items) { - promise = promise - .thenInBackground(^{ - CGFloat progress - = (recordCount > 0 ? ((recordCount - items.count) / (CGFloat)recordCount) : 0.f); - [self updateProgressWithDescription: - NSLocalizedString(@"BACKUP_IMPORT_PHASE_DOWNLOAD", - @"Indicates that the backup import data is being downloaded.") - progress:@(progress)]; - }) - .thenInBackground(^{ - return [self downloadFileFromCloud:item]; - }); - } - - return promise; -} - -- (AnyPromise *)downloadFileFromCloud:(OWSBackupFragment *)item -{ - OWSAssertDebug(item); - - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - // TODO: Use a predictable file path so that multiple "import backup" attempts - // will leverage successful file downloads from previous attempts. - // - // TODO: This will also require imports using a predictable jobTempDirPath. - NSString *tempFilePath = [self.jobTempDirPath stringByAppendingPathComponent:item.recordName]; - - // Skip redundant file download. - if ([NSFileManager.defaultManager fileExistsAtPath:tempFilePath]) { - [OWSFileSystem protectFileOrFolderAtPath:tempFilePath]; - - item.downloadFilePath = tempFilePath; - - return resolve(@(1)); - } - - [OWSBackupAPI downloadFileFromCloudObjcWithRecordName:item.recordName - toFileUrl:[NSURL fileURLWithPath:tempFilePath]] - .thenInBackground(^{ - [OWSFileSystem protectFileOrFolderAtPath:tempFilePath]; - item.downloadFilePath = tempFilePath; - - resolve(@(1)); - }) - .catchInBackground(^(NSError *error) { - resolve(error); - }); - }]; -} - -- (AnyPromise *)restoreLocalProfile -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - AnyPromise *promise = [AnyPromise promiseWithValue:@(1)]; - - if (self.manifest.localProfileAvatarItem) { - promise = promise.thenInBackground(^{ - return - [self downloadFileFromCloud:self.manifest.localProfileAvatarItem].catchInBackground(^(NSError *error) { - OWSLogInfo(@"Ignoring error; profiles are optional: %@", error); - }); - }); - } - - promise = promise.thenInBackground(^{ - return [self applyLocalProfile]; - }); - return promise; -} - -- (AnyPromise *)applyLocalProfile -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - NSString *_Nullable localProfileName = self.manifest.localProfileName; - UIImage *_Nullable localProfileAvatar = [self tryToLoadLocalProfileAvatar]; - - OWSLogVerbose(@"local profile name: %@, avatar: %d", localProfileName, localProfileAvatar != nil); - - if (localProfileName.length < 1 && !localProfileAvatar) { - return [AnyPromise promiseWithValue:@(1)]; - } - - return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [self.profileManager updateLocalProfileName:localProfileName - avatarImage:localProfileAvatar - success:^{ - resolve(@(1)); - } - failure:^(NSError *error) { - // Ignore errors related to local profile. - resolve(@(1)); - } - requiresSync:YES]; - }]; -} - -- (nullable UIImage *)tryToLoadLocalProfileAvatar -{ - if (!self.manifest.localProfileAvatarItem) { - return nil; - } - if (!self.manifest.localProfileAvatarItem.downloadFilePath) { - // Download of the avatar failed. - // We can safely ignore errors related to local profile. - OWSLogError(@"local profile avatar was not downloaded."); - return nil; - } - OWSBackupFragment *item = self.manifest.localProfileAvatarItem; - if (item.recordName.length < 1) { - OWSFailDebug(@"item missing record name."); - return nil; - } - - @autoreleasepool { - NSData *_Nullable data = - [self.backupIO decryptFileAsData:item.downloadFilePath encryptionKey:item.encryptionKey]; - if (!data) { - OWSLogError(@"could not decrypt local profile avatar."); - // Ignore errors related to local profile. - return nil; - } - // TODO: Verify that we're not compressing the profile avatar data. - UIImage *_Nullable image = [UIImage imageWithData:data]; - if (!image) { - OWSLogError(@"could not decrypt local profile avatar."); - // Ignore errors related to local profile. - return nil; - } - return image; - } -} - -- (AnyPromise *)restoreAttachmentFiles -{ - OWSLogVerbose(@": %zd", self.attachmentsItems.count); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - __block NSUInteger count = 0; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (OWSBackupFragment *item in self.attachmentsItems) { - if (self.isComplete) { - return; - } - if (item.recordName.length < 1) { - OWSLogError(@"attachment was not downloaded."); - // Attachment-related errors are recoverable and can be ignored. - continue; - } - if (item.attachmentId.length < 1) { - OWSLogError(@"attachment missing attachment id."); - // Attachment-related errors are recoverable and can be ignored. - continue; - } - if (item.relativeFilePath.length < 1) { - OWSLogError(@"attachment missing relative file path."); - // Attachment-related errors are recoverable and can be ignored. - continue; - } - TSAttachmentPointer *_Nullable attachment = - [TSAttachmentPointer fetchObjectWithUniqueID:item.attachmentId transaction:transaction]; - if (!attachment) { - OWSLogError(@"attachment to restore could not be found."); - // Attachment-related errors are recoverable and can be ignored. - continue; - } - if (![attachment isKindOfClass:[TSAttachmentPointer class]]) { - OWSFailDebug(@"attachment has unexpected type: %@.", attachment.class); - // Attachment-related errors are recoverable and can be ignored. - continue; - } - [attachment markForLazyRestoreWithFragment:item transaction:transaction]; - count++; - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_RESTORING_FILES", - @"Indicates that the backup import data is being restored.") - progress:@(count / (CGFloat)self.attachmentsItems.count)]; - } - }]; - - OWSLogError(@"enqueued lazy restore of %zd files.", count); - - return [AnyPromise promiseWithValue:@(1)]; -} - -- (AnyPromise *)restoreDatabase -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - // Order matters here. - NSArray *collectionsToRestore = @[ - [TSThread collection], - [TSAttachment collection], - // Interactions refer to threads and attachments, - // so copy them afterward. - [TSInteraction collection], - [OWSDatabaseMigration collection], - ]; - NSMutableDictionary *restoredEntityCounts = [NSMutableDictionary new]; - __block unsigned long long copiedEntities = 0; - __block BOOL aborted = NO; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (NSString *collection in collectionsToRestore) { - if ([collection isEqualToString:[OWSDatabaseMigration collection]]) { - // It's okay if there are existing migrations; we'll clear those - // before restoring. - continue; - } - if ([transaction numberOfKeysInCollection:collection] > 0) { - OWSLogError(@"unexpected contents in database (%@).", collection); - } - } - - // Clear existing database contents. - // - // This should be safe since we only ever import into an empty database. - // - // Note that if the app receives a message after registering and before restoring - // backup, it will be lost. - // - // Note that this will clear all migrations. - for (NSString *collection in collectionsToRestore) { - [transaction removeAllObjectsInCollection:collection]; - } - - NSUInteger count = 0; - for (OWSBackupFragment *item in self.databaseItems) { - if (self.isComplete) { - return; - } - if (item.recordName.length < 1) { - OWSLogError(@"database snapshot was not downloaded."); - // Attachment-related errors are recoverable and can be ignored. - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - if (!item.uncompressedDataLength || item.uncompressedDataLength.unsignedIntValue < 1) { - OWSLogError(@"database snapshot missing size."); - // Attachment-related errors are recoverable and can be ignored. - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - - count++; - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_RESTORING_DATABASE", - @"Indicates that the backup database is being restored.") - progress:@(count / (CGFloat)self.databaseItems.count)]; - - @autoreleasepool { - NSData *_Nullable compressedData = - [self.backupIO decryptFileAsData:item.downloadFilePath encryptionKey:item.encryptionKey]; - if (!compressedData) { - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - NSData *_Nullable uncompressedData = - [self.backupIO decompressData:compressedData - uncompressedDataLength:item.uncompressedDataLength.unsignedIntValue]; - if (!uncompressedData) { - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - NSError *error; - SignalIOSProtoBackupSnapshot *_Nullable entities = - [SignalIOSProtoBackupSnapshot parseData:uncompressedData error:&error]; - if (!entities || error) { - OWSLogError(@"could not parse proto: %@.", error); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - if (!entities || entities.entity.count < 1) { - OWSLogError(@"missing entities."); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - for (SignalIOSProtoBackupSnapshotBackupEntity *entity in entities.entity) { - NSData *_Nullable entityData = entity.entityData; - if (entityData.length < 1) { - OWSLogError(@"missing entity data."); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - - NSString *_Nullable collection = entity.collection; - if (collection.length < 1) { - OWSLogError(@"missing collection."); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - - NSString *_Nullable key = entity.key; - if (key.length < 1) { - OWSLogError(@"missing key."); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - - __block NSObject *object = nil; - @try { - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:entityData]; - object = [unarchiver decodeObjectForKey:@"root"]; - if (![object isKindOfClass:[object class]]) { - OWSLogError(@"invalid decoded entity: %@.", [object class]); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - } @catch (NSException *exception) { - OWSLogError(@"could not decode entity."); - // Database-related errors are unrecoverable. - aborted = YES; - return; - } - - [transaction setObject:object forKey:key inCollection:collection]; - copiedEntities++; - NSUInteger restoredEntityCount = restoredEntityCounts[collection].unsignedIntValue; - restoredEntityCounts[collection] = @(restoredEntityCount + 1); - } - } - } - }]; - - if (aborted) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import failed.")]; - } - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - for (NSString *collection in restoredEntityCounts) { - OWSLogInfo(@"copied %@: %@", collection, restoredEntityCounts[collection]); - } - OWSLogInfo(@"copiedEntities: %llu", copiedEntities); - - [self.primaryStorage logFileSizes]; - - return [AnyPromise promiseWithValue:@(1)]; -} - -- (AnyPromise *)ensureMigrations -{ - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")]; - } - - [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_FINALIZING", - @"Indicates that the backup import data is being finalized.") - progress:nil]; - - - // It's okay that we do this in a separate transaction from the - // restoration of backup contents. If some of migrations don't - // complete, they'll be run the next time the app launches. - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[[OWSDatabaseMigrationRunner alloc] init] runAllOutstandingWithCompletion:^{ - resolve(@(1)); - }]; - }); - }]; - return promise; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupJob.h b/Session/Backups/OWSBackupJob.h deleted file mode 100644 index a426dea57..000000000 --- a/Session/Backups/OWSBackupJob.h +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "TSYapDatabaseObject.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const kOWSBackup_ManifestKey_DatabaseFiles; -extern NSString *const kOWSBackup_ManifestKey_AttachmentFiles; -extern NSString *const kOWSBackup_ManifestKey_RecordName; -extern NSString *const kOWSBackup_ManifestKey_EncryptionKey; -extern NSString *const kOWSBackup_ManifestKey_RelativeFilePath; -extern NSString *const kOWSBackup_ManifestKey_AttachmentId; -extern NSString *const kOWSBackup_ManifestKey_DataSize; -extern NSString *const kOWSBackup_ManifestKey_LocalProfileAvatar; -extern NSString *const kOWSBackup_ManifestKey_LocalProfileName; - -@class AnyPromise; -@class OWSBackupIO; -@class OWSBackupJob; -@class OWSBackupManifestContents; - -typedef void (^OWSBackupJobBoolCompletion)(BOOL success); -typedef void (^OWSBackupJobCompletion)(NSError *_Nullable error); -typedef void (^OWSBackupJobManifestSuccess)(OWSBackupManifestContents *manifest); -typedef void (^OWSBackupJobManifestFailure)(NSError *error); - -@interface OWSBackupManifestContents : NSObject - -@property (nonatomic) NSArray *databaseItems; -@property (nonatomic) NSArray *attachmentsItems; -@property (nonatomic, nullable) OWSBackupFragment *localProfileAvatarItem; -@property (nonatomic, nullable) NSString *localProfileName; - -@end - -#pragma mark - - -@protocol OWSBackupJobDelegate - -- (nullable NSData *)backupEncryptionKey; - -// Either backupJobDidSucceed:... or backupJobDidFail:... will -// be called exactly once on the main thread UNLESS: -// -// * The job was never started. -// * The job was cancelled. -- (void)backupJobDidSucceed:(OWSBackupJob *)backupJob; -- (void)backupJobDidFail:(OWSBackupJob *)backupJob error:(NSError *)error; - -- (void)backupJobDidUpdate:(OWSBackupJob *)backupJob - description:(nullable NSString *)description - progress:(nullable NSNumber *)progress; - -@end - -#pragma mark - - -@interface OWSBackupJob : NSObject - -@property (nonatomic, weak, readonly) id delegate; - -@property (nonatomic, readonly) NSString *recipientId; - -// Indicates that the backup succeeded, failed or was cancelled. -@property (atomic, readonly) BOOL isComplete; - -@property (nonatomic, readonly) NSString *jobTempDirPath; - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithDelegate:(id)delegate recipientId:(NSString *)recipientId; - -#pragma mark - Private - -- (BOOL)ensureJobTempDir; - -- (void)cancel; -- (void)succeed; -- (void)failWithErrorDescription:(NSString *)description; -- (void)failWithError:(NSError *)error; -- (void)updateProgressWithDescription:(nullable NSString *)description progress:(nullable NSNumber *)progress; - -#pragma mark - Manifest - -- (AnyPromise *)downloadAndProcessManifestWithBackupIO:(OWSBackupIO *)backupIO __attribute__((warn_unused_result)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupJob.m b/Session/Backups/OWSBackupJob.m deleted file mode 100644 index 92aab7714..000000000 --- a/Session/Backups/OWSBackupJob.m +++ /dev/null @@ -1,316 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupJob.h" -#import "OWSBackupIO.h" -#import "Session-Swift.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const kOWSBackup_ManifestKey_DatabaseFiles = @"database_files"; -NSString *const kOWSBackup_ManifestKey_AttachmentFiles = @"attachment_files"; -NSString *const kOWSBackup_ManifestKey_RecordName = @"record_name"; -NSString *const kOWSBackup_ManifestKey_EncryptionKey = @"encryption_key"; -NSString *const kOWSBackup_ManifestKey_RelativeFilePath = @"relative_file_path"; -NSString *const kOWSBackup_ManifestKey_AttachmentId = @"attachment_id"; -NSString *const kOWSBackup_ManifestKey_DataSize = @"data_size"; -NSString *const kOWSBackup_ManifestKey_LocalProfileAvatar = @"local_profile_avatar"; -NSString *const kOWSBackup_ManifestKey_LocalProfileName = @"local_profile_name"; - -NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService"; - -@implementation OWSBackupManifestContents - -@end - -#pragma mark - - -@interface OWSBackupJob () - -@property (nonatomic, weak) id delegate; - -@property (nonatomic) NSString *recipientId; - -@property (atomic) BOOL isComplete; -@property (atomic) BOOL hasSucceeded; - -@property (nonatomic) NSString *jobTempDirPath; - -@end - -#pragma mark - - -@implementation OWSBackupJob - -- (instancetype)initWithDelegate:(id)delegate recipientId:(NSString *)recipientId -{ - self = [super init]; - - if (!self) { - return self; - } - - OWSAssertDebug(recipientId.length > 0); - OWSAssertDebug([OWSStorage isStorageReady]); - - self.delegate = delegate; - self.recipientId = recipientId; - - return self; -} - -- (void)dealloc -{ - // Surface memory leaks by logging the deallocation. - OWSLogVerbose(@"Dealloc: %@", self.class); - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - if (self.jobTempDirPath) { - [OWSFileSystem deleteFileIfExists:self.jobTempDirPath]; - } -} - -- (BOOL)ensureJobTempDir -{ - OWSLogVerbose(@""); - - // TODO: Exports should use a new directory each time, but imports - // might want to use a predictable directory so that repeated - // import attempts can reuse downloads from previous attempts. - NSString *temporaryDirectory = OWSTemporaryDirectory(); - self.jobTempDirPath = [temporaryDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; - - if (![OWSFileSystem ensureDirectoryExists:self.jobTempDirPath]) { - OWSFailDebug(@"Could not create jobTempDirPath."); - return NO; - } - return YES; -} - -#pragma mark - - -- (void)cancel -{ - OWSAssertIsOnMainThread(); - - self.isComplete = YES; -} - -- (void)succeed -{ - OWSLogInfo(@""); - - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.isComplete) { - OWSAssertDebug(!self.hasSucceeded); - return; - } - self.isComplete = YES; - - // There's a lot of asynchrony in these backup jobs; - // ensure we only end up finishing these jobs once. - OWSAssertDebug(!self.hasSucceeded); - self.hasSucceeded = YES; - - [self.delegate backupJobDidSucceed:self]; - }); -} - -- (void)failWithErrorDescription:(NSString *)description -{ - [self failWithError:OWSErrorWithCodeDescription(OWSErrorCodeImportBackupFailed, description)]; -} - -- (void)failWithError:(NSError *)error -{ - OWSFailDebug(@"%@", error); - - dispatch_async(dispatch_get_main_queue(), ^{ - OWSAssertDebug(!self.hasSucceeded); - if (self.isComplete) { - return; - } - self.isComplete = YES; - [self.delegate backupJobDidFail:self error:error]; - }); -} - -- (void)updateProgressWithDescription:(nullable NSString *)description progress:(nullable NSNumber *)progress -{ - OWSLogInfo(@""); - - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.isComplete) { - return; - } - [self.delegate backupJobDidUpdate:self description:description progress:progress]; - }); -} - -#pragma mark - Manifest - -- (AnyPromise *)downloadAndProcessManifestWithBackupIO:(OWSBackupIO *)backupIO -{ - OWSAssertDebug(backupIO); - - OWSLogVerbose(@""); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup job no longer active.")]; - } - - return - [OWSBackupAPI downloadManifestFromCloudObjcWithRecipientId:self.recipientId].thenInBackground(^(NSData *data) { - return [self processManifest:data backupIO:backupIO]; - }); -} - -- (AnyPromise *)processManifest:(NSData *)manifestDataEncrypted backupIO:(OWSBackupIO *)backupIO -{ - OWSAssertDebug(manifestDataEncrypted.length > 0); - OWSAssertDebug(backupIO); - - if (self.isComplete) { - // Job was aborted. - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup job no longer active.")]; - } - - OWSLogVerbose(@""); - - NSData *_Nullable manifestDataDecrypted = - [backupIO decryptDataAsData:manifestDataEncrypted encryptionKey:self.delegate.backupEncryptionKey]; - if (!manifestDataDecrypted) { - OWSFailDebug(@"Could not decrypt manifest."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not decrypt manifest.")]; - } - - NSError *error; - NSDictionary *_Nullable json = - [NSJSONSerialization JSONObjectWithData:manifestDataDecrypted options:0 error:&error]; - if (![json isKindOfClass:[NSDictionary class]]) { - OWSFailDebug(@"Could not download manifest."); - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Could not download manifest.")]; - } - - OWSLogVerbose(@"json: %@", json); - - NSArray *_Nullable databaseItems = - [self parseManifestItems:json key:kOWSBackup_ManifestKey_DatabaseFiles]; - if (!databaseItems) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"No database items in manifest.")]; - } - NSArray *_Nullable attachmentsItems = - [self parseManifestItems:json key:kOWSBackup_ManifestKey_AttachmentFiles]; - if (!attachmentsItems) { - return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"No attachment items in manifest.")]; - } - - NSArray *_Nullable localProfileAvatarItems; - if ([self parseManifestItem:json key:kOWSBackup_ManifestKey_LocalProfileAvatar]) { - localProfileAvatarItems = [self parseManifestItems:json key:kOWSBackup_ManifestKey_LocalProfileAvatar]; - } - - NSString *_Nullable localProfileName = [self parseManifestItem:json key:kOWSBackup_ManifestKey_LocalProfileName]; - - OWSBackupManifestContents *contents = [OWSBackupManifestContents new]; - contents.databaseItems = databaseItems; - contents.attachmentsItems = attachmentsItems; - contents.localProfileAvatarItem = localProfileAvatarItems.firstObject; - if ([localProfileName isKindOfClass:[NSString class]]) { - contents.localProfileName = localProfileName; - } else if (localProfileName) { - OWSFailDebug(@"Invalid localProfileName: %@", [localProfileName class]); - } - - return [AnyPromise promiseWithValue:contents]; -} - -- (nullable id)parseManifestItem:(id)json key:(NSString *)key -{ - OWSAssertDebug(json); - OWSAssertDebug(key.length); - - if (![json isKindOfClass:[NSDictionary class]]) { - OWSFailDebug(@"manifest has invalid data."); - return nil; - } - id _Nullable value = json[key]; - return value; -} - -- (nullable NSArray *)parseManifestItems:(id)json key:(NSString *)key -{ - OWSAssertDebug(json); - OWSAssertDebug(key.length); - - if (![json isKindOfClass:[NSDictionary class]]) { - OWSFailDebug(@"manifest has invalid data."); - return nil; - } - NSArray *itemMaps = json[key]; - if (![itemMaps isKindOfClass:[NSArray class]]) { - OWSFailDebug(@"manifest has invalid data."); - return nil; - } - NSMutableArray *items = [NSMutableArray new]; - for (NSDictionary *itemMap in itemMaps) { - if (![itemMap isKindOfClass:[NSDictionary class]]) { - OWSFailDebug(@"manifest has invalid item."); - return nil; - } - NSString *_Nullable recordName = itemMap[kOWSBackup_ManifestKey_RecordName]; - NSString *_Nullable encryptionKeyString = itemMap[kOWSBackup_ManifestKey_EncryptionKey]; - NSString *_Nullable relativeFilePath = itemMap[kOWSBackup_ManifestKey_RelativeFilePath]; - NSString *_Nullable attachmentId = itemMap[kOWSBackup_ManifestKey_AttachmentId]; - NSNumber *_Nullable uncompressedDataLength = itemMap[kOWSBackup_ManifestKey_DataSize]; - if (![recordName isKindOfClass:[NSString class]]) { - OWSFailDebug(@"manifest has invalid recordName: %@.", recordName); - return nil; - } - if (![encryptionKeyString isKindOfClass:[NSString class]]) { - OWSFailDebug(@"manifest has invalid encryptionKey."); - return nil; - } - // relativeFilePath is an optional field. - if (relativeFilePath && ![relativeFilePath isKindOfClass:[NSString class]]) { - OWSLogDebug(@"manifest has invalid relativeFilePath: %@.", relativeFilePath); - OWSFailDebug(@"manifest has invalid relativeFilePath"); - return nil; - } - // attachmentId is an optional field. - if (attachmentId && ![attachmentId isKindOfClass:[NSString class]]) { - OWSLogDebug(@"manifest has invalid attachmentId: %@.", attachmentId); - OWSFailDebug(@"manifest has invalid attachmentId"); - return nil; - } - NSData *_Nullable encryptionKey = [NSData dataFromBase64String:encryptionKeyString]; - if (!encryptionKey) { - OWSFailDebug(@"manifest has corrupt encryptionKey"); - return nil; - } - // uncompressedDataLength is an optional field. - if (uncompressedDataLength && ![uncompressedDataLength isKindOfClass:[NSNumber class]]) { - OWSFailDebug(@"manifest has invalid uncompressedDataLength: %@.", uncompressedDataLength); - return nil; - } - - OWSBackupFragment *item = [[OWSBackupFragment alloc] initWithUniqueId:recordName]; - item.recordName = recordName; - item.encryptionKey = encryptionKey; - item.relativeFilePath = relativeFilePath; - item.attachmentId = attachmentId; - item.uncompressedDataLength = uncompressedDataLength; - [items addObject:item]; - } - return items; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupLazyRestore.swift b/Session/Backups/OWSBackupLazyRestore.swift deleted file mode 100644 index d29c6a72d..000000000 --- a/Session/Backups/OWSBackupLazyRestore.swift +++ /dev/null @@ -1,176 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit -import SignalUtilitiesKit - -@objc(OWSBackupLazyRestore) -public class BackupLazyRestore: NSObject { - - // MARK: - Dependencies - - private var backup: OWSBackup { - return AppEnvironment.shared.backup - } - - private var primaryStorage: OWSPrimaryStorage { - return SSKEnvironment.shared.primaryStorage - } - - private var tsAccountManager: TSAccountManager { - return TSAccountManager.sharedInstance() - } - - // MARK: - - - private var isRunning = false - private var isComplete = false - - @objc - public required override init() { - super.init() - - SwiftSingletons.register(self) - - AppReadiness.runNowOrWhenAppDidBecomeReady { - self.runIfNecessary() - } - - NotificationCenter.default.addObserver(forName: .OWSApplicationDidBecomeActive, object: nil, queue: nil) { _ in - self.runIfNecessary() - } - NotificationCenter.default.addObserver(forName: .RegistrationStateDidChange, object: nil, queue: nil) { _ in - self.runIfNecessary() - } - NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in - self.runIfNecessary() - } - NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in - self.runIfNecessary() - } - NotificationCenter.default.addObserver(forName: NSNotification.Name(NSNotificationNameBackupStateDidChange), object: nil, queue: nil) { _ in - self.runIfNecessary() - } - } - - // MARK: - - - private let backgroundQueue = DispatchQueue.global(qos: .background) - - @objc - public func clearCompleteAndRunIfNecessary() { - AssertIsOnMainThread() - - isComplete = false - - runIfNecessary() - } - - @objc - public func isBackupImportInProgress() -> Bool { - return backup.backupImportState == .inProgress - } - - @objc - public func runIfNecessary() { - AssertIsOnMainThread() - - guard !CurrentAppContext().isRunningTests else { - return - } - guard AppReadiness.isAppReady() else { - return - } - guard CurrentAppContext().isMainAppAndActive else { - return - } - guard tsAccountManager.isRegisteredAndReady() else { - return - } - guard !isBackupImportInProgress() else { - return - } - guard !isRunning, !isComplete else { - return - } - - isRunning = true - - backgroundQueue.async { - self.restoreAttachments() - } - } - - private func restoreAttachments() { - let temporaryDirectory = OWSTemporaryDirectory() - let jobTempDirPath = (temporaryDirectory as NSString).appendingPathComponent(NSUUID().uuidString) - - guard OWSFileSystem.ensureDirectoryExists(jobTempDirPath) else { - Logger.error("could not create temp directory.") - complete(errorCount: 1) - return - } - - let backupIO = OWSBackupIO(jobTempDirPath: jobTempDirPath) - - let attachmentIds = backup.attachmentIdsForLazyRestore() - guard attachmentIds.count > 0 else { - Logger.info("No attachments need lazy restore.") - complete(errorCount: 0) - return - } - Logger.info("Lazy restoring \(attachmentIds.count) attachments.") - tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: 0, backupIO: backupIO) - } - - private func tryToRestoreNextAttachment(attachmentIds: [String], errorCount: UInt, backupIO: OWSBackupIO) { - guard !isBackupImportInProgress() else { - Logger.verbose("A backup import is in progress; abort.") - complete(errorCount: errorCount + 1) - return - } - - var attachmentIdsCopy = attachmentIds - guard let attachmentId = attachmentIdsCopy.popLast() else { - // This job is done. - Logger.verbose("job is done.") - complete(errorCount: errorCount) - return - } - guard let attachmentPointer = TSAttachment.fetch(uniqueId: attachmentId) as? TSAttachmentPointer else { - Logger.warn("could not load attachment.") - // Not necessarily an error. - // The attachment might have been deleted since the job began. - // Continue trying to restore the other attachments. - tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: errorCount + 1, backupIO: backupIO) - return - } - backup.lazyRestoreAttachment(attachmentPointer, - backupIO: backupIO) - .done(on: self.backgroundQueue) { _ in - Logger.info("Restored attachment.") - - // Continue trying to restore the other attachments. - self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount, backupIO: backupIO) - }.catch(on: self.backgroundQueue) { (error) in - Logger.error("Could not restore attachment: \(error)") - - // Continue trying to restore the other attachments. - self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount + 1, backupIO: backupIO) - }.retainUntilComplete() - } - - private func complete(errorCount: UInt) { - Logger.verbose("") - - DispatchQueue.main.async { - self.isRunning = false - - if errorCount == 0 { - self.isComplete = true - } - } - } -} diff --git a/Session/Backups/OWSBackupSettingsViewController.h b/Session/Backups/OWSBackupSettingsViewController.h deleted file mode 100644 index 34b03c1ab..000000000 --- a/Session/Backups/OWSBackupSettingsViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBackupSettingsViewController : OWSTableViewController - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Backups/OWSBackupSettingsViewController.m b/Session/Backups/OWSBackupSettingsViewController.m deleted file mode 100644 index 575d4a281..000000000 --- a/Session/Backups/OWSBackupSettingsViewController.m +++ /dev/null @@ -1,214 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSBackupSettingsViewController.h" -#import "OWSBackup.h" -#import "Session-Swift.h" - -#import -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSBackupSettingsViewController () - -@property (nonatomic, nullable) NSError *iCloudError; - -@end - -#pragma mark - - -@implementation OWSBackupSettingsViewController - -#pragma mark - Dependencies - -- (OWSBackup *)backup -{ - OWSAssertDebug(AppEnvironment.shared.backup); - - return AppEnvironment.shared.backup; -} - -#pragma mark - - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.title = NSLocalizedString(@"SETTINGS_BACKUP", @"Label for the backup view in app settings."); - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(backupStateDidChange:) - name:NSNotificationNameBackupStateDidChange - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive:) - name:OWSApplicationDidBecomeActiveNotification - object:nil]; - - [self updateTableContents]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - - [self updateTableContents]; - [self updateICloudStatus]; -} - -- (void)updateICloudStatus -{ - __weak OWSBackupSettingsViewController *weakSelf = self; - [[self.backup ensureCloudKitAccess] - .then(^{ - OWSAssertIsOnMainThread(); - - weakSelf.iCloudError = nil; - [weakSelf updateTableContents]; - }) - .catch(^(NSError *error) { - OWSAssertIsOnMainThread(); - - weakSelf.iCloudError = error; - [weakSelf updateTableContents]; - }) retainUntilComplete]; -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - - BOOL isBackupEnabled = [OWSBackup.sharedManager isBackupEnabled]; - - if (self.iCloudError) { - OWSTableSection *iCloudSection = [OWSTableSection new]; - iCloudSection.headerTitle = NSLocalizedString( - @"SETTINGS_BACKUP_ICLOUD_STATUS", @"Label for iCloud status row in the in the backup settings view."); - [iCloudSection - addItem:[OWSTableItem - longDisclosureItemWithText:[OWSBackupAPI errorMessageForCloudKitAccessError:self.iCloudError] - actionBlock:^{ - [[UIApplication sharedApplication] - openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; - }]]; - [contents addSection:iCloudSection]; - } - - // TODO: This UI is temporary. - // Enabling backup will involve entering and registering a PIN. - OWSTableSection *enableSection = [OWSTableSection new]; - enableSection.headerTitle = NSLocalizedString(@"SETTINGS_BACKUP", @"Label for the backup view in app settings."); - [enableSection - addItem:[OWSTableItem switchItemWithText: - NSLocalizedString(@"SETTINGS_BACKUP_ENABLING_SWITCH", - @"Label for switch in settings that controls whether or not backup is enabled.") - isOnBlock:^{ - return [OWSBackup.sharedManager isBackupEnabled]; - } - target:self - selector:@selector(isBackupEnabledDidChange:)]]; - [contents addSection:enableSection]; - - if (isBackupEnabled) { - // TODO: This UI is temporary. - // Enabling backup will involve entering and registering a PIN. - OWSTableSection *progressSection = [OWSTableSection new]; - [progressSection - addItem:[OWSTableItem - labelItemWithText:NSLocalizedString(@"SETTINGS_BACKUP_STATUS", - @"Label for backup status row in the in the backup settings view.") - accessoryText:NSStringForBackupExportState(OWSBackup.sharedManager.backupExportState)]]; - if (OWSBackup.sharedManager.backupExportState == OWSBackupState_InProgress) { - if (OWSBackup.sharedManager.backupExportDescription) { - [progressSection - addItem:[OWSTableItem - labelItemWithText:NSLocalizedString(@"SETTINGS_BACKUP_PHASE", - @"Label for phase row in the in the backup settings view.") - accessoryText:OWSBackup.sharedManager.backupExportDescription]]; - if (OWSBackup.sharedManager.backupExportProgress) { - NSUInteger progressPercent - = (NSUInteger)round(OWSBackup.sharedManager.backupExportProgress.floatValue * 100); - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - [numberFormatter setNumberStyle:NSNumberFormatterPercentStyle]; - [numberFormatter setMaximumFractionDigits:0]; - [numberFormatter setMultiplier:@1]; - NSString *progressString = [numberFormatter stringFromNumber:@(progressPercent)]; - [progressSection - addItem:[OWSTableItem - labelItemWithText:NSLocalizedString(@"SETTINGS_BACKUP_PROGRESS", - @"Label for phase row in the in the backup settings view.") - accessoryText:progressString]]; - } - } - } - - switch (OWSBackup.sharedManager.backupExportState) { - case OWSBackupState_Idle: - case OWSBackupState_Failed: - case OWSBackupState_Succeeded: - [progressSection - addItem:[OWSTableItem disclosureItemWithText: - NSLocalizedString(@"SETTINGS_BACKUP_BACKUP_NOW", - @"Label for 'backup now' button in the backup settings view.") - actionBlock:^{ - [OWSBackup.sharedManager tryToExportBackup]; - }]]; - break; - case OWSBackupState_InProgress: - [progressSection - addItem:[OWSTableItem disclosureItemWithText: - NSLocalizedString(@"SETTINGS_BACKUP_CANCEL_BACKUP", - @"Label for 'cancel backup' button in the backup settings view.") - actionBlock:^{ - [OWSBackup.sharedManager cancelExportBackup]; - }]]; - break; - } - - [contents addSection:progressSection]; - } - - self.contents = contents; -} - -- (void)isBackupEnabledDidChange:(UISwitch *)sender -{ - [OWSBackup.sharedManager setIsBackupEnabled:sender.isOn]; - - [self updateTableContents]; -} - -#pragma mark - Events - -- (void)backupStateDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateTableContents]; -} - -- (void)applicationDidBecomeActive:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self updateICloudStatus]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Meta/AppDelegate.m b/Session/Meta/AppDelegate.m index 5219167c1..f0199f467 100644 --- a/Session/Meta/AppDelegate.m +++ b/Session/Meta/AppDelegate.m @@ -4,7 +4,6 @@ #import "AppDelegate.h" #import "MainAppContext.h" -#import "OWSBackup.h" #import "OWSOrphanDataCleaner.h" #import "OWSScreenLockUI.h" #import "Session-Swift.h" @@ -101,11 +100,6 @@ static NSTimeInterval launchStartedAt; return Environment.shared.windowManager; } -- (OWSBackup *)backup -{ - return AppEnvironment.shared.backup; -} - - (OWSNotificationPresenter *)notificationPresenter { return AppEnvironment.shared.notificationPresenter; @@ -552,11 +546,7 @@ static NSTimeInterval launchStartedAt; UIViewController *rootViewController; BOOL navigationBarHidden = NO; if ([self.tsAccountManager isRegistered]) { - if (self.backup.hasPendingRestoreDecision) { - rootViewController = [BackupRestoreViewController new]; - } else { - rootViewController = [HomeVC new]; - } + rootViewController = [HomeVC new]; } else { rootViewController = [LandingVC new]; navigationBarHidden = NO; diff --git a/Session/Meta/AppEnvironment.swift b/Session/Meta/AppEnvironment.swift index 805df0d81..ec6a1123b 100644 --- a/Session/Meta/AppEnvironment.swift +++ b/Session/Meta/AppEnvironment.swift @@ -34,9 +34,6 @@ import SignalUtilitiesKit @objc public var pushRegistrationManager: PushRegistrationManager - @objc - public var backup: OWSBackup - @objc public var fileLogger: DDFileLogger @@ -49,15 +46,10 @@ import SignalUtilitiesKit return _userNotificationActionHandler as! UserNotificationActionHandler } - @objc - public var backupLazyRestore: BackupLazyRestore - private override init() { self.accountManager = AccountManager() self.notificationPresenter = NotificationPresenter() self.pushRegistrationManager = PushRegistrationManager() - self.backup = OWSBackup() - self.backupLazyRestore = BackupLazyRestore() self._userNotificationActionHandler = UserNotificationActionHandler() self.fileLogger = DDFileLogger() diff --git a/Session/Meta/Signal-Bridging-Header.h b/Session/Meta/Signal-Bridging-Header.h index 956c13505..1b5f636b7 100644 --- a/Session/Meta/Signal-Bridging-Header.h +++ b/Session/Meta/Signal-Bridging-Header.h @@ -19,8 +19,6 @@ #import "NotificationSettingsViewController.h" #import "OWSAnyTouchGestureRecognizer.h" #import "OWSAudioPlayer.h" -#import "OWSBackup.h" -#import "OWSBackupIO.h" #import "OWSBezierPathView.h" #import "OWSConversationSettingsViewController.h" #import "OWSDatabaseMigration.h" diff --git a/SessionMessagingKit/To Do/TSAccountManager.h b/SessionMessagingKit/To Do/TSAccountManager.h index fae863d46..9c6013bf9 100644 --- a/SessionMessagingKit/To Do/TSAccountManager.h +++ b/SessionMessagingKit/To Do/TSAccountManager.h @@ -140,9 +140,6 @@ typedef NS_ENUM(NSUInteger, OWSRegistrationState) { - (BOOL)isDeregistered; - (void)setIsDeregistered:(BOOL)isDeregistered; -- (BOOL)hasPendingBackupRestoreDecision; -- (void)setHasPendingBackupRestoreDecision:(BOOL)value; - #pragma mark - Re-registration // Re-registration is the process of re-registering _with the same phone number_. diff --git a/SessionMessagingKit/To Do/TSAccountManager.m b/SessionMessagingKit/To Do/TSAccountManager.m index 03c1cfcc4..eed562917 100644 --- a/SessionMessagingKit/To Do/TSAccountManager.m +++ b/SessionMessagingKit/To Do/TSAccountManager.m @@ -418,22 +418,6 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa inCollection:TSAccountManager_UserAccountCollection]; } -- (BOOL)hasPendingBackupRestoreDecision -{ - return [self.dbConnection boolForKey:TSAccountManager_HasPendingRestoreDecisionKey - inCollection:TSAccountManager_UserAccountCollection - defaultValue:NO]; -} - -- (void)setHasPendingBackupRestoreDecision:(BOOL)value -{ - [self.dbConnection setBool:value - forKey:TSAccountManager_HasPendingRestoreDecisionKey - inCollection:TSAccountManager_UserAccountCollection]; - - [self postRegistrationStateDidChangeNotification]; -} - - (BOOL)isManualMessageFetchEnabled { return [self.dbConnection boolForKey:TSAccountManager_ManualMessageFetchKey From 0842dbff1ed34f8a417649128f3ce4b7c3864ad6 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Wed, 30 Mar 2022 09:46:08 +1100 Subject: [PATCH 2/4] Removed a bunch of unused code Removed the legacy Theme code (replaced with SessionUIKit equivalents) Removed the OWSOrphanDataCleaner (unused) Removed the OWSReceiptType_Delivery from the OWSOutgoingReceiptManager (unused) Removed the TSStorageKeys and TSStorageHeaders files (unused) Removed the LKMessageIDCollection (unused - only had methods for deleting data from the collection) Removed the OWSPrimaryStorageTrustedKeysCollection and OWSIdentityManager_QueuedVerificationStateSyncMessages (unused) Removed collections and notifications from OWSProfileManager (unused) --- Session.xcodeproj/project.pbxproj | 42 - Session/Conversations/ConversationViewModel.m | 12 - .../LongTextViewController.swift | 2 +- .../Content Views/MediaView.swift | 5 +- .../GIFs/GifPickerCell.swift | 2 +- .../GIFs/GifPickerViewController.swift | 4 +- .../MediaDetailViewController.m | 7 +- .../MediaPageViewController.swift | 9 +- .../MediaTileViewController.swift | 12 +- .../PhotoGridViewCell.swift | 5 +- Session/Meta/AppDelegate.m | 1 - Session/Shared/DismissableTextField.swift | 2 +- Session/Shared/ReminderView.swift | 7 +- Session/Utilities/OWSOrphanDataCleaner.h | 23 - Session/Utilities/OWSOrphanDataCleaner.m | 737 ------------------ .../Open Groups/OpenGroupManagerV2.swift | 1 - .../Read Tracking/OWSOutgoingReceiptManager.m | 56 +- SessionMessagingKit/Storage.swift | 4 +- .../Utilities/OWSIdentityManager.h | 14 - .../Utilities/OWSIdentityManager.m | 129 --- SessionShareExtension/ShareVC.swift | 1 - .../Database/Storage+Conformances.swift | 8 - .../Database/TSStorageHeaders.h | 11 - SignalUtilitiesKit/Database/TSStorageKeys.h | 32 - .../AttachmentCaptionViewController.swift | 5 +- .../MessageApprovalViewController.swift | 19 +- SignalUtilitiesKit/Meta/SignalUtilitiesKit.h | 5 - .../OWSNavigationController.m | 14 - .../OWSTableViewController.m | 22 +- .../OWSViewController.m | 4 +- .../SheetViewController.swift | 10 +- .../Shared Views/OWSNavigationBar.swift | 22 +- .../Shared Views/OWSSearchBar.h | 13 - .../Shared Views/OWSSearchBar.m | 118 --- .../Shared Views/OWSTextField.m | 1 - SignalUtilitiesKit/Shared Views/OWSTextView.m | 1 - SignalUtilitiesKit/Shared Views/Toast.swift | 5 +- .../Sharing/SelectRecipientViewController.m | 11 +- .../Sharing/SelectThreadViewController.m | 1 - SignalUtilitiesKit/To Do/ContactCellView.m | 8 +- .../To Do/OWSPrimaryStorage+Loki.h | 13 - .../To Do/OWSPrimaryStorage+Loki.m | 26 - SignalUtilitiesKit/To Do/OWSProfileManager.h | 3 - SignalUtilitiesKit/To Do/OWSProfileManager.m | 8 - SignalUtilitiesKit/Utilities/Theme.h | 68 -- SignalUtilitiesKit/Utilities/Theme.m | 217 ------ SignalUtilitiesKit/Utilities/UIUtil.m | 1 - .../Utilities/UIViewController+OWS.m | 3 +- .../Utilities/VersionMigrations.m | 1 - 49 files changed, 90 insertions(+), 1635 deletions(-) delete mode 100644 Session/Utilities/OWSOrphanDataCleaner.h delete mode 100644 Session/Utilities/OWSOrphanDataCleaner.m delete mode 100644 SignalUtilitiesKit/Database/Storage+Conformances.swift delete mode 100644 SignalUtilitiesKit/Database/TSStorageHeaders.h delete mode 100644 SignalUtilitiesKit/Database/TSStorageKeys.h delete mode 100644 SignalUtilitiesKit/Shared Views/OWSSearchBar.h delete mode 100644 SignalUtilitiesKit/Shared Views/OWSSearchBar.m delete mode 100644 SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.h delete mode 100644 SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.m delete mode 100644 SignalUtilitiesKit/Utilities/Theme.h delete mode 100644 SignalUtilitiesKit/Utilities/Theme.m diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 179733f8b..58947be29 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; }; 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; }; 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; }; - 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */; }; 346129991FD1E4DA00532771 /* SignalApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129971FD1E4D900532771 /* SignalApp.m */; }; 34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; }; 346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; }; @@ -454,24 +453,20 @@ C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */; }; C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB49255A580C00E217F9 /* WeakTimer.swift */; }; C33FDD06255A582000E217F9 /* AppVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB4C255A580D00E217F9 /* AppVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD12255A582000E217F9 /* OWSPrimaryStorage+Loki.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */; }; C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB59255A580E00E217F9 /* OWSFailedAttachmentDownloadsJob.m */; }; C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; }; C33FDD32255A582000E217F9 /* OWSOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB78255A581000E217F9 /* OWSOperation.m */; }; C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; }; C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; }; C33FDD53255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */; }; - C33FDD5A255A582000E217F9 /* TSStorageHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA1255A581400E217F9 /* OWSOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD68255A582000E217F9 /* SignalAccount.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBAE255A581500E217F9 /* SignalAccount.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD6E255A582000E217F9 /* NSURLSessionDataTask+StatusCode.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB4255A581600E217F9 /* NSURLSessionDataTask+StatusCode.m */; }; C33FDD74255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C33FDD75255A582000E217F9 /* OWSPrimaryStorage+Loki.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC2255A581700E217F9 /* SSKAsserts.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDD8D255A582000E217F9 /* OWSSignalAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */; }; C33FDD91255A582000E217F9 /* OWSMessageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD7255A581900E217F9 /* OWSMessageUtils.m */; }; C33FDD92255A582000E217F9 /* SignalIOS.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD8255A581900E217F9 /* SignalIOS.pb.swift */; }; - C33FDDA9255A582000E217F9 /* TSStorageKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB0255A582000E217F9 /* NSURLSessionDataTask+StatusCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB2255A582000E217F9 /* NSArray+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDDB3255A582000E217F9 /* OWSError.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF9255A581C00E217F9 /* OWSError.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -518,8 +513,6 @@ C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */; }; C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; }; - C38EF216255B6D3B007E1867 /* Theme.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF212255B6D3A007E1867 /* Theme.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF218255B6D3B007E1867 /* Theme.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF214255B6D3A007E1867 /* Theme.m */; }; C38EF228255B6D5D007E1867 /* AttachmentSharing.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF223255B6D5D007E1867 /* AttachmentSharing.m */; }; C38EF22A255B6D5D007E1867 /* AttachmentSharing.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF225255B6D5D007E1867 /* AttachmentSharing.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF22B255B6D5D007E1867 /* ShareViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF226255B6D5D007E1867 /* ShareViewDelegate.swift */; }; @@ -614,7 +607,6 @@ C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */; }; C38EF3EF255B6DF7007E1867 /* ThreadViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */; }; C38EF3F0255B6DF7007E1867 /* ThreadViewHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C38EF3F1255B6DF7007E1867 /* OWSSearchBar.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF3F2255B6DF7007E1867 /* DisappearingTimerConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */; }; C38EF3F4255B6DF7007E1867 /* ContactCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3D6255B6DEF007E1867 /* ContactCellView.m */; }; C38EF3F5255B6DF7007E1867 /* OWSTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3D7255B6DF0007E1867 /* OWSTextField.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -633,7 +625,6 @@ C38EF404255B6DF7007E1867 /* ContactTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E7255B6DF5007E1867 /* OWSButton.swift */; }; C38EF407255B6DF7007E1867 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3E9255B6DF6007E1867 /* Toast.swift */; }; - C38EF408255B6DF7007E1867 /* OWSSearchBar.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */; }; C38EF409255B6DF7007E1867 /* ContactTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */; }; C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */; }; C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */; }; @@ -727,7 +718,6 @@ C3D9E3C025676AD70040E4F3 /* TSAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAC2255A580200E217F9 /* TSAttachment.m */; }; C3D9E3C925676AF30040E4F3 /* TSYapDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */; }; C3D9E3FA25676BCE0040E4F3 /* TSYapDatabaseObject.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA1255A57FF00E217F9 /* TSYapDatabaseObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C3D9E40C25676C100040E4F3 /* Storage+Conformances.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D9E40B25676C100040E4F3 /* Storage+Conformances.swift */; }; C3D9E41525676C320040E4F3 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB36255A580B00E217F9 /* Storage.swift */; }; C3D9E41F25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D9E41E25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift */; }; C3D9E43125676D3D0040E4F3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D9E43025676D3D0040E4F3 /* Configuration.swift */; }; @@ -970,8 +960,6 @@ 34386A53207D271C009F5D9C /* NeverClearView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeverClearView.swift; sourceTree = ""; }; 34480B371FD092A900BC14EF /* SignalShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SignalShareExtension-Bridging-Header.h"; sourceTree = ""; }; 34480B381FD092E300BC14EF /* SessionShareExtension-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SessionShareExtension-Prefix.pch"; sourceTree = ""; }; - 344825C4211390C700DB4BD8 /* OWSOrphanDataCleaner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOrphanDataCleaner.h; sourceTree = ""; }; - 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanDataCleaner.m; sourceTree = ""; }; 346129971FD1E4D900532771 /* SignalApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalApp.m; sourceTree = ""; }; 346129981FD1E4DA00532771 /* SignalApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalApp.h; sourceTree = ""; }; 34661FB720C1C0D60056EDD6 /* message_sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = message_sent.aiff; path = Session/Meta/AudioFiles/message_sent.aiff; sourceTree = SOURCE_ROOT; }; @@ -1418,7 +1406,6 @@ C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+OWS.h"; sourceTree = ""; }; C33FDB54255A580D00E217F9 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = ""; }; C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSOutgoingMessage.m; sourceTree = ""; }; - C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+Loki.m"; sourceTree = ""; }; C33FDB59255A580E00E217F9 /* OWSFailedAttachmentDownloadsJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFailedAttachmentDownloadsJob.m; sourceTree = ""; }; C33FDB5B255A580E00E217F9 /* YapDatabaseTransaction+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YapDatabaseTransaction+OWS.m"; sourceTree = ""; }; C33FDB5C255A580E00E217F9 /* NSArray+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Functional.h"; sourceTree = ""; }; @@ -1449,7 +1436,6 @@ C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+keyFromIntLong.m"; sourceTree = ""; }; C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSIncomingMessage.h; sourceTree = ""; }; C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentPointer.m; sourceTree = ""; }; - C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageHeaders.h; sourceTree = ""; }; C33FDBA1255A581400E217F9 /* OWSOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOperation.h; sourceTree = ""; }; C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesConfiguration.m; sourceTree = ""; }; C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSLinkPreview.swift; sourceTree = ""; }; @@ -1462,7 +1448,6 @@ C33FDBB8255A581600E217F9 /* TSThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThread.m; sourceTree = ""; }; C33FDBB9255A581600E217F9 /* ProfileManagerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileManagerProtocol.h; sourceTree = ""; }; C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+keyFromIntLong.h"; sourceTree = ""; }; - C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSPrimaryStorage+Loki.h"; sourceTree = ""; }; C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKKeychainStorage.swift; sourceTree = ""; }; C33FDBC1255A581700E217F9 /* General.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = ""; }; C33FDBC2255A581700E217F9 /* SSKAsserts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKAsserts.h; sourceTree = ""; }; @@ -1476,7 +1461,6 @@ C33FDBE9255A581A00E217F9 /* TSInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInteraction.m; sourceTree = ""; }; C33FDBEB255A581B00E217F9 /* OWSBlockingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBlockingManager.h; sourceTree = ""; }; C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRecipientIdentity.m; sourceTree = ""; }; - C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageKeys.h; sourceTree = ""; }; C33FDBF1255A581B00E217F9 /* OWSIdentityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIdentityManager.h; sourceTree = ""; }; C33FDBF6255A581C00E217F9 /* NSURLSessionDataTask+StatusCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionDataTask+StatusCode.h"; sourceTree = ""; }; C33FDBF8255A581C00E217F9 /* NSArray+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+OWS.h"; sourceTree = ""; }; @@ -1529,8 +1513,6 @@ C37F5402255BA9ED002AEA92 /* Environment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Environment.m; sourceTree = ""; }; C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+ClosedGroups.swift"; sourceTree = ""; }; C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SNProtoEnvelope+Conversion.swift"; sourceTree = ""; }; - C38EF212255B6D3A007E1867 /* Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Theme.h; path = SignalUtilitiesKit/Utilities/Theme.h; sourceTree = SOURCE_ROOT; }; - C38EF214255B6D3A007E1867 /* Theme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Theme.m; path = SignalUtilitiesKit/Utilities/Theme.m; sourceTree = SOURCE_ROOT; }; C38EF223255B6D5D007E1867 /* AttachmentSharing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AttachmentSharing.m; path = "SignalUtilitiesKit/Media Viewing & Editing/AttachmentSharing.m"; sourceTree = SOURCE_ROOT; }; C38EF224255B6D5D007E1867 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalAttachment.swift; path = "SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift"; sourceTree = SOURCE_ROOT; }; C38EF225255B6D5D007E1867 /* AttachmentSharing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentSharing.h; path = "SignalUtilitiesKit/Media Viewing & Editing/AttachmentSharing.h"; sourceTree = SOURCE_ROOT; }; @@ -1647,7 +1629,6 @@ C38EF3B7255B6DE6007E1867 /* ImageEditorCanvasView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageEditorCanvasView.swift; path = "SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorCanvasView.swift"; sourceTree = SOURCE_ROOT; }; C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ThreadViewHelper.m; path = SignalUtilitiesKit/Database/ThreadViewHelper.m; sourceTree = SOURCE_ROOT; }; C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadViewHelper.h; path = SignalUtilitiesKit/Database/ThreadViewHelper.h; sourceTree = SOURCE_ROOT; }; - C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSSearchBar.h; path = "SignalUtilitiesKit/Shared Views/OWSSearchBar.h"; sourceTree = SOURCE_ROOT; }; C38EF3D4255B6DEE007E1867 /* DisappearingTimerConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisappearingTimerConfigurationView.swift; path = SignalUtilitiesKit/Messaging/DisappearingTimerConfigurationView.swift; sourceTree = SOURCE_ROOT; }; C38EF3D6255B6DEF007E1867 /* ContactCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactCellView.m; path = "SignalUtilitiesKit/To Do/ContactCellView.m"; sourceTree = SOURCE_ROOT; }; C38EF3D7255B6DF0007E1867 /* OWSTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSTextField.h; path = "SignalUtilitiesKit/Shared Views/OWSTextField.h"; sourceTree = SOURCE_ROOT; }; @@ -1666,7 +1647,6 @@ C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContactTableViewCell.h; path = "SignalUtilitiesKit/To Do/ContactTableViewCell.h"; sourceTree = SOURCE_ROOT; }; C38EF3E7255B6DF5007E1867 /* OWSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSButton.swift; path = "SignalUtilitiesKit/Shared Views/OWSButton.swift"; sourceTree = SOURCE_ROOT; }; C38EF3E9255B6DF6007E1867 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = "SignalUtilitiesKit/Shared Views/Toast.swift"; sourceTree = SOURCE_ROOT; }; - C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSSearchBar.m; path = "SignalUtilitiesKit/Shared Views/OWSSearchBar.m"; sourceTree = SOURCE_ROOT; }; C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContactTableViewCell.m; path = "SignalUtilitiesKit/To Do/ContactTableViewCell.m"; sourceTree = SOURCE_ROOT; }; C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = "SignalUtilitiesKit/Shared Views/OWSFlatButton.swift"; sourceTree = SOURCE_ROOT; }; C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TappableStackView.swift; path = "SignalUtilitiesKit/Shared Views/TappableStackView.swift"; sourceTree = SOURCE_ROOT; }; @@ -1747,7 +1727,6 @@ C3CA3ABD255CDB0D00F4C6D4 /* portuguese.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = portuguese.txt; sourceTree = ""; }; C3CA3AC7255CDB2900F4C6D4 /* spanish.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = spanish.txt; sourceTree = ""; }; C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundPoller.swift; sourceTree = ""; }; - C3D9E40B25676C100040E4F3 /* Storage+Conformances.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Conformances.swift"; sourceTree = ""; }; C3D9E41E25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSPrimaryStorageProtocol.swift; sourceTree = ""; }; C3D9E43025676D3D0040E4F3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMessage.swift; sourceTree = ""; }; @@ -2023,8 +2002,6 @@ 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */, B90418E4183E9DD40038554A /* DateUtil.h */, B90418E5183E9DD40038554A /* DateUtil.m */, - 344825C4211390C700DB4BD8 /* OWSOrphanDataCleaner.h */, - 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */, 4C21D5D5223A9DC500EF8A77 /* UIAlerts+iOS9.m */, 45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */, 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */, @@ -3079,8 +3056,6 @@ C38EF3EC255B6DF6007E1867 /* OWSFlatButton.swift */, C38EF3DB255B6DF1007E1867 /* OWSLayerView.swift */, C38EF3D9255B6DF1007E1867 /* OWSNavigationBar.swift */, - C38EF3D3255B6DEE007E1867 /* OWSSearchBar.h */, - C38EF3EA255B6DF6007E1867 /* OWSSearchBar.m */, C38EF3D7255B6DF0007E1867 /* OWSTextField.h */, C38EF3E0255B6DF3007E1867 /* OWSTextField.m */, C38EF3D8255B6DF0007E1867 /* OWSTextView.h */, @@ -3104,8 +3079,6 @@ C38EF3EB255B6DF6007E1867 /* ContactTableViewCell.m */, C38EF2D2255B6DAF007E1867 /* OWSProfileManager.h */, C38EF2CF255B6DAE007E1867 /* OWSProfileManager.m */, - C33FDBBB255A581600E217F9 /* OWSPrimaryStorage+Loki.h */, - C33FDB58255A580E00E217F9 /* OWSPrimaryStorage+Loki.m */, ); path = "To Do"; sourceTree = ""; @@ -3139,11 +3112,8 @@ C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */, C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */, C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */, - C3D9E40B25676C100040E4F3 /* Storage+Conformances.swift */, C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */, C38EF3D1255B6DEE007E1867 /* ThreadViewHelper.m */, - C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */, - C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */, C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */, ); path = Database; @@ -3356,8 +3326,6 @@ C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */, C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */, C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */, - C38EF212255B6D3A007E1867 /* Theme.h */, - C38EF214255B6D3A007E1867 /* Theme.m */, C38EF3DC255B6DF1007E1867 /* DirectionalPanGestureRecognizer.swift */, C38EF240255B6D67007E1867 /* UIView+OWS.swift */, C38EF236255B6D65007E1867 /* UIViewController+OWS.h */, @@ -3627,7 +3595,6 @@ C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */, C38EF313255B6DBF007E1867 /* OWSUnreadIndicator.h in Headers */, C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */, - C33FDDA9255A582000E217F9 /* TSStorageKeys.h in Headers */, C38EF3F6255B6DF7007E1867 /* OWSTextView.h in Headers */, C38EF24C255B6D67007E1867 /* NSAttributedString+OWS.h in Headers */, C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */, @@ -3640,12 +3607,9 @@ C38EF36D255B6DCC007E1867 /* SharingThreadPickerViewController.h in Headers */, C38EF35D255B6DCC007E1867 /* OWSNavigationController.h in Headers */, C38EF249255B6D67007E1867 /* UIColor+OWS.h in Headers */, - C38EF216255B6D3B007E1867 /* Theme.h in Headers */, C38EF3F0255B6DF7007E1867 /* ThreadViewHelper.h in Headers */, C38EF274255B6D7A007E1867 /* OWSResaveCollectionDBMigration.h in Headers */, C33FDC95255A582000E217F9 /* OWSFailedMessagesJob.h in Headers */, - C38EF3F1255B6DF7007E1867 /* OWSSearchBar.h in Headers */, - C33FDD75255A582000E217F9 /* OWSPrimaryStorage+Loki.h in Headers */, C38EF277255B6D7A007E1867 /* OWSDatabaseMigration.h in Headers */, C38EF3F5255B6DF7007E1867 /* OWSTextField.h in Headers */, C38EF275255B6D7A007E1867 /* OWSDatabaseMigrationRunner.h in Headers */, @@ -3668,7 +3632,6 @@ C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */, C33FDC50255A582000E217F9 /* OWSDispatch.h in Headers */, C33FDD06255A582000E217F9 /* AppVersion.h in Headers */, - C33FDD5A255A582000E217F9 /* TSStorageHeaders.h in Headers */, C33FDCA2255A582000E217F9 /* OWSMessageUtils.h in Headers */, B8F5F54E25EC50A5003BF8D4 /* BlockListUIUtils.h in Headers */, C38EF28F255B6D86007E1867 /* VersionMigrations.h in Headers */, @@ -4456,8 +4419,6 @@ C38EF39B255B6DDA007E1867 /* ThreadViewModel.swift in Sources */, C38EF2A5255B6D93007E1867 /* Identicon+ObjC.swift in Sources */, C38EF273255B6D7A007E1867 /* OWSDatabaseMigrationRunner.m in Sources */, - C33FDD12255A582000E217F9 /* OWSPrimaryStorage+Loki.m in Sources */, - C3D9E40C25676C100040E4F3 /* Storage+Conformances.swift in Sources */, C38EF31A255B6DBF007E1867 /* OWSAnyTouchGestureRecognizer.m in Sources */, C38EF36C255B6DCC007E1867 /* SharingThreadPickerViewController.m in Sources */, C38EF385255B6DD2007E1867 /* AttachmentTextToolbar.swift in Sources */, @@ -4503,7 +4464,6 @@ C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */, C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */, C38EF32F255B6DBF007E1867 /* OWSFormat.m in Sources */, - C38EF218255B6D3B007E1867 /* Theme.m in Sources */, C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */, C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */, C38EF2D4255B6DAF007E1867 /* OWSProfileManager.m in Sources */, @@ -4517,7 +4477,6 @@ C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */, C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */, C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */, - C38EF408255B6DF7007E1867 /* OWSSearchBar.m in Sources */, C38EF38B255B6DD2007E1867 /* AttachmentPrepViewController.swift in Sources */, C33FDC7B255A582000E217F9 /* NSSet+Functional.m in Sources */, C38EF405255B6DF7007E1867 /* OWSButton.swift in Sources */, @@ -4890,7 +4849,6 @@ 7BA9057E27911C5800998B3C /* GlobalSearchViewController.swift in Sources */, 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */, 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */, - 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */, C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */, 4542DF54208D40AC007B4E76 /* LoadingViewController.swift in Sources */, 34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */, diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index ac9ba0ccc..3c2b632dc 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -281,10 +281,6 @@ NS_ASSUME_NONNULL_BEGIN selector:@selector(typingIndicatorStateDidChange:) name:[OWSTypingIndicatorsImpl typingIndicatorStateDidChange] object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(profileWhitelistDidChange:) - name:kNSNotificationName_ProfileWhitelistDidChange - object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(blockListDidChange:) name:kNSNotificationName_BlockListDidChange @@ -295,14 +291,6 @@ NS_ASSUME_NONNULL_BEGIN object:nil]; } -- (void)profileWhitelistDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - self.conversationProfileState = nil; - [self updateForTransientItems]; -} - - (void)localProfileDidChange:(NSNotification *)notification { OWSAssertIsOnMainThread(); diff --git a/Session/Conversations/LongTextViewController.swift b/Session/Conversations/LongTextViewController.swift index ed8e043f8..ecb9ac5bf 100644 --- a/Session/Conversations/LongTextViewController.swift +++ b/Session/Conversations/LongTextViewController.swift @@ -113,7 +113,7 @@ public class LongTextViewController: OWSViewController { // MARK: - Create Views private func createViews() { - view.backgroundColor = Theme.backgroundColor + view.backgroundColor = Colors.navigationBarBackground let messageTextView = OWSTextView() self.messageTextView = messageTextView diff --git a/Session/Conversations/Message Cells/Content Views/MediaView.swift b/Session/Conversations/Message Cells/Content Views/MediaView.swift index 9c9e7b77b..15ff3b413 100644 --- a/Session/Conversations/Message Cells/Content Views/MediaView.swift +++ b/Session/Conversations/Message Cells/Content Views/MediaView.swift @@ -3,6 +3,7 @@ // import Foundation +import SessionUIKit @objc(OWSMediaView) public class MediaView: UIView { @@ -149,7 +150,7 @@ public class MediaView: UIView { configure(forError: .missing) return } - backgroundColor = (Theme.isDarkThemeEnabled ? .ows_gray90 : .ows_gray05) + backgroundColor = (isDarkMode ? .ows_gray90 : .ows_gray05) let loader = MediaLoaderView() addSubview(loader) loader.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.bottom, UIView.HorizontalEdge.right ], to: self) @@ -352,7 +353,7 @@ public class MediaView: UIView { } private func configure(forError error: MediaError) { - backgroundColor = (Theme.isDarkThemeEnabled ? .ows_gray90 : .ows_gray05) + backgroundColor = (isDarkMode ? .ows_gray90 : .ows_gray05) let icon: UIImage switch error { case .failed: diff --git a/Session/Media Viewing & Editing/GIFs/GifPickerCell.swift b/Session/Media Viewing & Editing/GIFs/GifPickerCell.swift index 1cfcf101c..ccd544701 100644 --- a/Session/Media Viewing & Editing/GIFs/GifPickerCell.swift +++ b/Session/Media Viewing & Editing/GIFs/GifPickerCell.swift @@ -270,7 +270,7 @@ class GifPickerCell: UICollectionViewCell { private func clearViewState() { imageView?.image = nil - self.backgroundColor = (Theme.isDarkThemeEnabled + self.backgroundColor = (isDarkMode ? UIColor(white: 0.25, alpha: 1.0) : UIColor(white: 0.95, alpha: 1.0)) } diff --git a/Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift b/Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift index 350ecf378..f667a47b7 100644 --- a/Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift +++ b/Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift @@ -3,10 +3,10 @@ // import Foundation -import SignalUtilitiesKit import Reachability import SignalUtilitiesKit import PromiseKit +import SessionUIKit @objc protocol GifPickerViewControllerDelegate: class { @@ -234,7 +234,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect private func createErrorLabel(text: String) -> UILabel { let label = UILabel() label.text = text - label.textColor = Theme.primaryColor + label.textColor = Colors.text label.font = UIFont.ows_mediumFont(withSize: 20) label.textAlignment = .center label.numberOfLines = 0 diff --git a/Session/Media Viewing & Editing/MediaDetailViewController.m b/Session/Media Viewing & Editing/MediaDetailViewController.m index 446000b80..ece98e334 100644 --- a/Session/Media Viewing & Editing/MediaDetailViewController.m +++ b/Session/Media Viewing & Editing/MediaDetailViewController.m @@ -16,6 +16,7 @@ #import #import #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -201,18 +202,18 @@ NS_ASSUME_NONNULL_BEGIN self.mediaView = animatedView; } else { self.mediaView = [UIView new]; - self.mediaView.backgroundColor = Theme.offBackgroundColor; + self.mediaView.backgroundColor = LKColors.unimportant; } } else if (!self.image) { // Still loading thumbnail. self.mediaView = [UIView new]; - self.mediaView.backgroundColor = Theme.offBackgroundColor; + self.mediaView.backgroundColor = LKColors.unimportant; } else if (self.isVideo) { if (self.attachmentStream.isValidVideo) { self.mediaView = [self buildVideoPlayerView]; } else { self.mediaView = [UIView new]; - self.mediaView.backgroundColor = Theme.offBackgroundColor; + self.mediaView.backgroundColor = LKColors.unimportant; } } else { // Present the static image using standard UIImageView diff --git a/Session/Media Viewing & Editing/MediaPageViewController.swift b/Session/Media Viewing & Editing/MediaPageViewController.swift index 9301d4f57..7f52cd17f 100644 --- a/Session/Media Viewing & Editing/MediaPageViewController.swift +++ b/Session/Media Viewing & Editing/MediaPageViewController.swift @@ -4,6 +4,7 @@ import UIKit import PromiseKit +import SessionUIKit // Objc wrapper for the MediaGalleryItem struct @objc @@ -280,7 +281,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou lazy var shareBarButton: UIBarButtonItem = { let shareBarButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(didPressShare)) - shareBarButton.tintColor = Theme.darkThemePrimaryColor + shareBarButton.tintColor = Colors.text return shareBarButton }() @@ -288,7 +289,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou let deleteBarButton = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(didPressDelete)) - deleteBarButton.tintColor = Theme.darkThemePrimaryColor + deleteBarButton.tintColor = Colors.text return deleteBarButton }() @@ -298,14 +299,14 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou lazy var videoPlayBarButton: UIBarButtonItem = { let videoPlayBarButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(didPressPlayBarButton)) - videoPlayBarButton.tintColor = Theme.darkThemePrimaryColor + videoPlayBarButton.tintColor = Colors.text return videoPlayBarButton }() lazy var videoPauseBarButton: UIBarButtonItem = { let videoPauseBarButton = UIBarButtonItem(barButtonSystemItem: .pause, target: self, action: #selector(didPressPauseBarButton)) - videoPauseBarButton.tintColor = Theme.darkThemePrimaryColor + videoPauseBarButton.tintColor = Colors.text return videoPauseBarButton }() diff --git a/Session/Media Viewing & Editing/MediaTileViewController.swift b/Session/Media Viewing & Editing/MediaTileViewController.swift index 2c6a82972..6958a2c34 100644 --- a/Session/Media Viewing & Editing/MediaTileViewController.swift +++ b/Session/Media Viewing & Editing/MediaTileViewController.swift @@ -3,6 +3,8 @@ // import Foundation +import SessionUIKit +import UIKit public protocol MediaTileViewControllerDelegate: class { func mediaTileViewController(_ viewController: MediaTileViewController, didTapView tappedView: UIView, mediaGalleryItem: MediaGalleryItem) @@ -75,7 +77,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryDa let deleteButton = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(didPressDelete)) - deleteButton.tintColor = Theme.darkThemeNavbarIconColor + deleteButton.tintColor = Colors.text return deleteButton }() @@ -823,16 +825,16 @@ private class MediaGallerySectionHeader: UICollectionReusableView { override init(frame: CGRect) { label = UILabel() - label.textColor = Theme.darkThemePrimaryColor + label.textColor = Colors.text - let blurEffect = Theme.darkThemeBarBlurEffect + let blurEffect = UIBlurEffect(style: .dark) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] super.init(frame: frame) - self.backgroundColor = isLightMode ? Colors.cellBackground : Theme.darkThemeNavbarBackgroundColor.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor) + self.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor) self.addSubview(blurEffectView) self.addSubview(label) @@ -871,7 +873,7 @@ private class MediaGalleryStaticHeader: UICollectionViewCell { addSubview(label) - label.textColor = Theme.darkThemePrimaryColor + label.textColor = Colors.text label.textAlignment = .center label.numberOfLines = 0 label.autoPinEdgesToSuperviewMargins(with: UIEdgeInsets(top: 0, leading: Values.largeSpacing, bottom: 0, trailing: Values.largeSpacing)) diff --git a/Session/Media Viewing & Editing/PhotoGridViewCell.swift b/Session/Media Viewing & Editing/PhotoGridViewCell.swift index 9af59c17f..4b2470c1c 100644 --- a/Session/Media Viewing & Editing/PhotoGridViewCell.swift +++ b/Session/Media Viewing & Editing/PhotoGridViewCell.swift @@ -2,6 +2,9 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +import UIKit +import SessionUIKit + public enum PhotoGridItemType { case photo, animated, video } @@ -29,7 +32,7 @@ public class PhotoGridViewCell: UICollectionViewCell { private static let animatedBadgeImage = #imageLiteral(resourceName: "ic_gallery_badge_gif") private static let selectedBadgeImage = #imageLiteral(resourceName: "selected_blue_circle") - public var loadingColor = Theme.offBackgroundColor + public var loadingColor = Colors.unimportant override public var isSelected: Bool { didSet { diff --git a/Session/Meta/AppDelegate.m b/Session/Meta/AppDelegate.m index f0199f467..134598e5c 100644 --- a/Session/Meta/AppDelegate.m +++ b/Session/Meta/AppDelegate.m @@ -4,7 +4,6 @@ #import "AppDelegate.h" #import "MainAppContext.h" -#import "OWSOrphanDataCleaner.h" #import "OWSScreenLockUI.h" #import "Session-Swift.h" #import "SignalApp.h" diff --git a/Session/Shared/DismissableTextField.swift b/Session/Shared/DismissableTextField.swift index b01ca40cb..265b8f5b7 100644 --- a/Session/Shared/DismissableTextField.swift +++ b/Session/Shared/DismissableTextField.swift @@ -23,7 +23,7 @@ class DismissInputBar: UIToolbar { self.items = [spacer, dismissButton] self.isTranslucent = false self.isOpaque = true - self.barTintColor = Theme.toolbarBackgroundColor + self.barTintColor = UIColor.lokiDarkestGray() self.autoresizingMask = .flexibleHeight self.translatesAutoresizingMaskIntoConstraints = false diff --git a/Session/Shared/ReminderView.swift b/Session/Shared/ReminderView.swift index 83cc1738f..4ae32143e 100644 --- a/Session/Shared/ReminderView.swift +++ b/Session/Shared/ReminderView.swift @@ -3,6 +3,7 @@ // import Foundation +import SessionUIKit class ReminderView: UIView { @@ -70,9 +71,9 @@ class ReminderView: UIView { iconColor = UIColor.ows_gray60 case .explanation: // TODO: Theme, review with design. - self.backgroundColor = Theme.offBackgroundColor - textColor = Theme.primaryColor - iconColor = Theme.secondaryColor + self.backgroundColor = Colors.unimportant + textColor = Colors.text + iconColor = Colors.separator } self.clipsToBounds = true diff --git a/Session/Utilities/OWSOrphanDataCleaner.h b/Session/Utilities/OWSOrphanDataCleaner.h deleted file mode 100644 index e77ad8bff..000000000 --- a/Session/Utilities/OWSOrphanDataCleaner.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -// Notes: -// -// * On disk, we only bother cleaning up files, not directories. -@interface OWSOrphanDataCleaner : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -// This is exposed for the debug UI. -+ (void)auditAndCleanup:(BOOL)shouldCleanup; -// This is exposed for the tests. -+ (void)auditAndCleanup:(BOOL)shouldCleanup completion:(dispatch_block_t)completion; - -+ (void)auditOnLaunchIfNecessary; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Utilities/OWSOrphanDataCleaner.m b/Session/Utilities/OWSOrphanDataCleaner.m deleted file mode 100644 index b494abb91..000000000 --- a/Session/Utilities/OWSOrphanDataCleaner.m +++ /dev/null @@ -1,737 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSOrphanDataCleaner.h" -#import "DateUtil.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -// LOG_ALL_FILE_PATHS can be used to determine if there are other kinds of files -// that we're not cleaning up. -//#define LOG_ALL_FILE_PATHS - -#define ENABLE_ORPHAN_DATA_CLEANER - -NSString *const OWSOrphanDataCleaner_Collection = @"OWSOrphanDataCleaner_Collection"; -NSString *const OWSOrphanDataCleaner_LastCleaningVersionKey = @"OWSOrphanDataCleaner_LastCleaningVersionKey"; -NSString *const OWSOrphanDataCleaner_LastCleaningDateKey = @"OWSOrphanDataCleaner_LastCleaningDateKey"; - -@interface OWSOrphanData : NSObject - -@property (nonatomic) NSSet *interactionIds; -@property (nonatomic) NSSet *attachmentIds; -@property (nonatomic) NSSet *filePaths; - -@end - -#pragma mark - - -@implementation OWSOrphanData - -@end - -#pragma mark - - -typedef void (^OrphanDataBlock)(OWSOrphanData *); - -@implementation OWSOrphanDataCleaner - -// Unlike CurrentAppContext().isMainAppAndActive, this method can be safely -// invoked off the main thread. -+ (BOOL)isMainAppAndActive -{ - return CurrentAppContext().reportedApplicationState == UIApplicationStateActive; -} - -+ (void)printPaths:(NSArray *)paths label:(NSString *)label -{ - for (NSString *path in [paths sortedArrayUsingSelector:@selector(compare:)]) { - OWSLogDebug(@"%@: %@", label, path); - } -} - -+ (long long)fileSizeOfFilePath:(NSString *)filePath -{ - NSError *error; - NSNumber *fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error][NSFileSize]; - if (error) { - if ([error.domain isEqualToString:NSCocoaErrorDomain] && error.code == 260) { - OWSLogWarn(@"can't find size of missing file."); - OWSLogDebug(@"can't find size of missing file: %@", filePath); - } else { - OWSFailDebug(@"attributesOfItemAtPath: %@ error: %@", filePath, error); - } - return 0; - } - return fileSize.longLongValue; -} - -+ (nullable NSNumber *)fileSizeOfFilePathsSafe:(NSArray *)filePaths -{ - long long result = 0; - for (NSString *filePath in filePaths) { - if (!self.isMainAppAndActive) { - return nil; - } - result += [self fileSizeOfFilePath:filePath]; - } - return @(result); -} - -+ (nullable NSSet *)filePathsInDirectorySafe:(NSString *)dirPath -{ - NSMutableSet *filePaths = [NSMutableSet new]; - if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) { - return filePaths; - } - NSError *error; - NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error]; - if (error) { - OWSFailDebug(@"contentsOfDirectoryAtPath error: %@", error); - return [NSSet new]; - } - for (NSString *fileName in fileNames) { - if (!self.isMainAppAndActive) { - return nil; - } - NSString *filePath = [dirPath stringByAppendingPathComponent:fileName]; - BOOL isDirectory; - [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory]; - if (isDirectory) { - NSSet *_Nullable dirPaths = [self filePathsInDirectorySafe:filePath]; - if (!dirPaths) { - return nil; - } - [filePaths unionSet:dirPaths]; - } else { - [filePaths addObject:filePath]; - } - } - return filePaths; -} - -// This method finds (but does not delete): -// -// * Orphan TSInteractions (with no thread). -// * Orphan TSAttachments (with no message). -// * Orphan attachment files (with no corresponding TSAttachment). -// * Orphan profile avatars. -// * Temporary files (all). -// -// It also finds (we don't clean these up). -// -// * Missing attachment files (cannot be cleaned up). -// These are attachments which have no file on disk. They should be extremely rare - -// the only cases I have seen are probably due to debugging. -// They can't be cleaned up - we don't want to delete the TSAttachmentStream or -// its corresponding message. Better that the broken message shows up in the -// conversation view. -+ (void)findOrphanDataWithRetries:(NSInteger)remainingRetries - databaseConnection:(YapDatabaseConnection *)databaseConnection - success:(OrphanDataBlock)success - failure:(dispatch_block_t)failure -{ - OWSAssertDebug(databaseConnection); - - if (remainingRetries < 1) { - OWSLogInfo(@"Aborting orphan data search."); - dispatch_async(self.workQueue, ^{ - failure(); - }); - return; - } - - // Wait until the app is active... - [CurrentAppContext() runNowOrWhenMainAppIsActive:^{ - // ...but perform the work off the main thread. - dispatch_async(self.workQueue, ^{ - OWSOrphanData *_Nullable orphanData = [self findOrphanDataSync:databaseConnection]; - if (orphanData) { - success(orphanData); - } else { - [self findOrphanDataWithRetries:remainingRetries - 1 - databaseConnection:databaseConnection - success:success - failure:failure]; - } - }); - }]; -} - -// Returns nil on failure, usually indicating that the search -// aborted due to the app resigning active. This method is extremely careful to -// abort if the app resigns active, in order to avoid 0xdead10cc crashes. -+ (nullable OWSOrphanData *)findOrphanDataSync:(YapDatabaseConnection *)databaseConnection -{ - OWSAssertDebug(databaseConnection); - - __block BOOL shouldAbort = NO; - -#ifdef LOG_ALL_FILE_PATHS - { - NSString *documentDirPath = [OWSFileSystem appDocumentDirectoryPath]; - NSArray *_Nullable allDocumentFilePaths = - [self filePathsInDirectorySafe:documentDirPath].allObjects; - allDocumentFilePaths = [allDocumentFilePaths sortedArrayUsingSelector:@selector(compare:)]; - NSString *attachmentsFolder = [TSAttachmentStream attachmentsFolder]; - for (NSString *filePath in allDocumentFilePaths) { - if ([filePath hasPrefix:attachmentsFolder]) { - continue; - } - OWSLogVerbose(@"non-attachment file: %@", filePath); - } - } - { - NSString *documentDirPath = [OWSFileSystem appSharedDataDirectoryPath]; - NSArray *_Nullable allDocumentFilePaths = - [self filePathsInDirectorySafe:documentDirPath].allObjects; - allDocumentFilePaths = [allDocumentFilePaths sortedArrayUsingSelector:@selector(compare:)]; - NSString *attachmentsFolder = [TSAttachmentStream attachmentsFolder]; - for (NSString *filePath in allDocumentFilePaths) { - if ([filePath hasPrefix:attachmentsFolder]) { - continue; - } - OWSLogVerbose(@"non-attachment file: %@", filePath); - } - } -#endif - - // We treat _all_ temp files as orphan files. This is safe - // because temp files only need to be retained for the - // a single launch of the app. Since our "date threshold" - // for deletion is relative to the current launch time, - // all temp files currently in use should be safe. - NSArray *_Nullable tempFilePaths = [self getTempFilePaths]; - if (!tempFilePaths || !self.isMainAppAndActive) { - return nil; - } - -#ifdef LOG_ALL_FILE_PATHS - { - NSDateFormatter *dateFormatter = [NSDateFormatter new]; - [dateFormatter setDateStyle:NSDateFormatterLongStyle]; - [dateFormatter setTimeStyle:NSDateFormatterLongStyle]; - - tempFilePaths = [tempFilePaths sortedArrayUsingSelector:@selector(compare:)]; - for (NSString *filePath in tempFilePaths) { - NSError *error; - NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error]; - if (!attributes || error) { - OWSLogDebug(@"Could not get attributes of file at: %@", filePath); - OWSFailDebug(@"Could not get attributes of file"); - continue; - } - OWSLogVerbose( - @"temp file: %@, %@", filePath, [dateFormatter stringFromDate:attributes.fileModificationDate]); - } - } -#endif - - NSString *legacyAttachmentsDirPath = TSAttachmentStream.legacyAttachmentsDirPath; - NSString *sharedDataAttachmentsDirPath = TSAttachmentStream.sharedDataAttachmentsDirPath; - NSSet *_Nullable legacyAttachmentFilePaths = [self filePathsInDirectorySafe:legacyAttachmentsDirPath]; - if (!legacyAttachmentFilePaths || !self.isMainAppAndActive) { - return nil; - } - NSSet *_Nullable sharedDataAttachmentFilePaths = - [self filePathsInDirectorySafe:sharedDataAttachmentsDirPath]; - if (!sharedDataAttachmentFilePaths || !self.isMainAppAndActive) { - return nil; - } - - NSString *legacyProfileAvatarsDirPath = OWSUserProfile.legacyProfileAvatarsDirPath; - NSString *sharedDataProfileAvatarsDirPath = OWSUserProfile.sharedDataProfileAvatarsDirPath; - NSSet *_Nullable legacyProfileAvatarsFilePaths = - [self filePathsInDirectorySafe:legacyProfileAvatarsDirPath]; - if (!legacyProfileAvatarsFilePaths || !self.isMainAppAndActive) { - return nil; - } - NSSet *_Nullable sharedDataProfileAvatarFilePaths = - [self filePathsInDirectorySafe:sharedDataProfileAvatarsDirPath]; - if (!sharedDataProfileAvatarFilePaths || !self.isMainAppAndActive) { - return nil; - } - - NSMutableSet *allOnDiskFilePaths = [NSMutableSet new]; - [allOnDiskFilePaths unionSet:legacyAttachmentFilePaths]; - [allOnDiskFilePaths unionSet:sharedDataAttachmentFilePaths]; - [allOnDiskFilePaths unionSet:legacyProfileAvatarsFilePaths]; - [allOnDiskFilePaths unionSet:sharedDataProfileAvatarFilePaths]; - [allOnDiskFilePaths addObjectsFromArray:tempFilePaths]; - - NSSet *profileAvatarFilePaths = [OWSUserProfile allProfileAvatarFilePaths]; - - if (!self.isMainAppAndActive) { - return nil; - } - - NSNumber *_Nullable totalFileSize = [self fileSizeOfFilePathsSafe:allOnDiskFilePaths.allObjects]; - - if (!totalFileSize || !self.isMainAppAndActive) { - return nil; - } - - NSUInteger fileCount = allOnDiskFilePaths.count; - - // Attachments - __block int attachmentStreamCount = 0; - NSMutableSet *allAttachmentFilePaths = [NSMutableSet new]; - NSMutableSet *allAttachmentIds = [NSMutableSet new]; - // Threads - __block NSSet *threadIds; - // Messages - NSMutableSet *orphanInteractionIds = [NSMutableSet new]; - NSMutableSet *allMessageAttachmentIds = [NSMutableSet new]; - [databaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - [transaction - enumerateKeysAndObjectsInCollection:TSAttachmentStream.collection - usingBlock:^(NSString *key, TSAttachment *attachment, BOOL *stop) { - if (!self.isMainAppAndActive) { - shouldAbort = YES; - *stop = YES; - return; - } - if (![attachment isKindOfClass:[TSAttachmentStream class]]) { - return; - } - [allAttachmentIds addObject:attachment.uniqueId]; - - TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment; - attachmentStreamCount++; - NSString *_Nullable filePath = [attachmentStream originalFilePath]; - if (filePath) { - [allAttachmentFilePaths addObject:filePath]; - } else { - OWSFailDebug(@"attachment has no file path."); - } - - [allAttachmentFilePaths - addObjectsFromArray:attachmentStream.allThumbnailPaths]; - }]; - - if (shouldAbort) { - return; - } - - threadIds = [NSSet setWithArray:[transaction allKeysInCollection:TSThread.collection]]; - - [transaction - enumerateKeysAndObjectsInCollection:TSMessage.collection - usingBlock:^(NSString *key, TSInteraction *interaction, BOOL *stop) { - if (!self.isMainAppAndActive) { - shouldAbort = YES; - *stop = YES; - return; - } - if (interaction.uniqueThreadId.length < 1 - || ![threadIds containsObject:interaction.uniqueThreadId]) { - [orphanInteractionIds addObject:interaction.uniqueId]; - } - - if (![interaction isKindOfClass:[TSMessage class]]) { - return; - } - - TSMessage *message = (TSMessage *)interaction; - [allMessageAttachmentIds addObjectsFromArray:message.allAttachmentIds]; - }]; - }]; - if (shouldAbort) { - return nil; - } - - OWSLogDebug(@"fileCount: %zu", fileCount); - OWSLogDebug(@"totalFileSize: %lld", totalFileSize.longLongValue); - OWSLogDebug(@"attachmentStreams: %d", attachmentStreamCount); - OWSLogDebug(@"attachmentStreams with file paths: %zu", allAttachmentFilePaths.count); - - NSMutableSet *orphanFilePaths = [allOnDiskFilePaths mutableCopy]; - [orphanFilePaths minusSet:allAttachmentFilePaths]; - [orphanFilePaths minusSet:profileAvatarFilePaths]; - NSMutableSet *missingAttachmentFilePaths = [allAttachmentFilePaths mutableCopy]; - [missingAttachmentFilePaths minusSet:allOnDiskFilePaths]; - - OWSLogDebug(@"orphan file paths: %zu", orphanFilePaths.count); - OWSLogDebug(@"missing attachment file paths: %zu", missingAttachmentFilePaths.count); - - [self printPaths:orphanFilePaths.allObjects label:@"orphan file paths"]; - [self printPaths:missingAttachmentFilePaths.allObjects label:@"missing attachment file paths"]; - - OWSLogDebug(@"attachmentIds: %zu", allAttachmentIds.count); - OWSLogDebug(@"allMessageAttachmentIds: %zu", allMessageAttachmentIds.count); - - NSMutableSet *orphanAttachmentIds = [allAttachmentIds mutableCopy]; - [orphanAttachmentIds minusSet:allMessageAttachmentIds]; - NSMutableSet *missingAttachmentIds = [allMessageAttachmentIds mutableCopy]; - [missingAttachmentIds minusSet:allAttachmentIds]; - - OWSLogDebug(@"orphan attachmentIds: %zu", orphanAttachmentIds.count); - OWSLogDebug(@"missing attachmentIds: %zu", missingAttachmentIds.count); - OWSLogDebug(@"orphan interactions: %zu", orphanInteractionIds.count); - - OWSOrphanData *result = [OWSOrphanData new]; - result.interactionIds = [orphanInteractionIds copy]; - result.attachmentIds = [orphanAttachmentIds copy]; - result.filePaths = [orphanFilePaths copy]; - return result; -} - -+ (BOOL)shouldAuditOnLaunch:(YapDatabaseConnection *)databaseConnection { - OWSAssertIsOnMainThread(); - -#ifndef ENABLE_ORPHAN_DATA_CLEANER - return NO; -#endif - - __block NSString *_Nullable lastCleaningVersion; - __block NSDate *_Nullable lastCleaningDate; - [databaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - lastCleaningVersion = [transaction stringForKey:OWSOrphanDataCleaner_LastCleaningVersionKey - inCollection:OWSOrphanDataCleaner_Collection]; - lastCleaningDate = [transaction dateForKey:OWSOrphanDataCleaner_LastCleaningDateKey - inCollection:OWSOrphanDataCleaner_Collection]; - }]; - - // Clean up once per app version. - NSString *currentAppVersion = AppVersion.sharedInstance.currentAppVersion; - if (!lastCleaningVersion || ![lastCleaningVersion isEqualToString:currentAppVersion]) { - OWSLogVerbose(@"Performing orphan data cleanup; new version: %@.", currentAppVersion); - return YES; - } - - // Clean up once per N days. - if (lastCleaningDate) { -#ifdef DEBUG - BOOL shouldAudit = [DateUtil dateIsOlderThanToday:lastCleaningDate]; -#else - BOOL shouldAudit = [DateUtil dateIsOlderThanOneWeek:lastCleaningDate]; -#endif - - if (shouldAudit) { - OWSLogVerbose(@"Performing orphan data cleanup; time has passed."); - } - return shouldAudit; - } - - // Has never audited before. - return NO; -} - -+ (void)auditOnLaunchIfNecessary { - OWSAssertIsOnMainThread(); - - OWSPrimaryStorage *primaryStorage = [OWSPrimaryStorage sharedManager]; - YapDatabaseConnection *databaseConnection = [primaryStorage newDatabaseConnection]; - - if (![self shouldAuditOnLaunch:databaseConnection]) { - return; - } - - // If we want to be cautious, we can disable orphan deletion using - // flag - the cleanup will just be a dry run with logging. - BOOL shouldRemoveOrphans = YES; - [self auditAndCleanup:shouldRemoveOrphans databaseConnection:databaseConnection completion:nil]; -} - -+ (void)auditAndCleanup:(BOOL)shouldRemoveOrphans -{ - [self auditAndCleanup:shouldRemoveOrphans - completion:^ { - }]; -} - -+ (void)auditAndCleanup:(BOOL)shouldRemoveOrphans completion:(dispatch_block_t)completion -{ - OWSPrimaryStorage *primaryStorage = [OWSPrimaryStorage sharedManager]; - YapDatabaseConnection *databaseConnection = [primaryStorage newDatabaseConnection]; - - [self auditAndCleanup:shouldRemoveOrphans databaseConnection:databaseConnection completion:completion]; -} - -// We use the lowest priority possible. -+ (dispatch_queue_t)workQueue -{ - return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); -} - -+ (void)auditAndCleanup:(BOOL)shouldRemoveOrphans - databaseConnection:(YapDatabaseConnection *)databaseConnection - completion:(nullable dispatch_block_t)completion -{ - OWSAssertIsOnMainThread(); - OWSAssertDebug(databaseConnection); - - if (!AppReadiness.isAppReady) { - OWSFailDebug(@"can't audit orphan data until app is ready."); - return; - } - if (!CurrentAppContext().isMainApp) { - OWSFailDebug(@"can't audit orphan data in app extensions."); - return; - } - if (CurrentAppContext().isRunningTests) { - OWSLogVerbose(@"Ignoring audit orphan data in tests."); - return; - } - - // Orphan cleanup has two risks: - // - // * As a long-running process that involves access to the - // shared data container, it could cause 0xdead10cc. - // * It could accidentally delete data still in use, - // e.g. a profile avatar which has been saved to disk - // but whose OWSUserProfile hasn't been saved yet. - // - // To prevent 0xdead10cc, the cleaner continually checks - // whether the app has resigned active. If so, it aborts. - // Each phase (search, re-search, processing) retries N times, - // then gives up until the next app launch. - // - // To prevent accidental data deletion, we take the following - // measures: - // - // * Only cleanup data of the following types (which should - // include all relevant app data): profile avatar, - // attachment, temporary files (including temporary - // attachments). - // * We don't delete any data created more recently than N seconds - // _before_ when the app launched. This prevents any stray data - // currently in use by the app from being accidentally cleaned - // up. - const NSInteger kMaxRetries = 3; - [self findOrphanDataWithRetries:kMaxRetries - databaseConnection:databaseConnection - success:^(OWSOrphanData *orphanData) { - [self processOrphans:orphanData - remainingRetries:kMaxRetries - databaseConnection:databaseConnection - shouldRemoveOrphans:shouldRemoveOrphans - success:^{ - OWSLogInfo(@"Completed orphan data cleanup."); - - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [transaction setObject:AppVersion.sharedInstance.currentAppVersion - forKey:OWSOrphanDataCleaner_LastCleaningVersionKey - inCollection:OWSOrphanDataCleaner_Collection]; - [transaction setDate:[NSDate new] - forKey:OWSOrphanDataCleaner_LastCleaningDateKey - inCollection:OWSOrphanDataCleaner_Collection]; - }]; - - if (completion) { - completion(); - } - } - failure:^{ - OWSLogInfo(@"Aborting orphan data cleanup."); - if (completion) { - completion(); - } - }]; - } - failure:^{ - OWSLogInfo(@"Aborting orphan data cleanup."); - if (completion) { - completion(); - } - }]; -} - -// Returns NO on failure, usually indicating that orphan processing -// aborted due to the app resigning active. This method is extremely careful to -// abort if the app resigns active, in order to avoid 0xdead10cc crashes. -+ (void)processOrphans:(OWSOrphanData *)orphanData - remainingRetries:(NSInteger)remainingRetries - databaseConnection:(YapDatabaseConnection *)databaseConnection - shouldRemoveOrphans:(BOOL)shouldRemoveOrphans - success:(dispatch_block_t)success - failure:(dispatch_block_t)failure -{ - OWSAssertDebug(databaseConnection); - OWSAssertDebug(orphanData); - - if (remainingRetries < 1) { - OWSLogInfo(@"Aborting orphan data audit."); - dispatch_async(self.workQueue, ^{ - failure(); - }); - return; - } - - // Wait until the app is active... - [CurrentAppContext() runNowOrWhenMainAppIsActive:^{ - // ...but perform the work off the main thread. - dispatch_async(self.workQueue, ^{ - if ([self processOrphansSync:orphanData - databaseConnection:databaseConnection - shouldRemoveOrphans:shouldRemoveOrphans]) { - success(); - return; - } else { - [self processOrphans:orphanData - remainingRetries:remainingRetries - 1 - databaseConnection:databaseConnection - shouldRemoveOrphans:shouldRemoveOrphans - success:success - failure:failure]; - } - }); - }]; -} - -// Returns NO on failure, usually indicating that orphan processing -// aborted due to the app resigning active. This method is extremely careful to -// abort if the app resigns active, in order to avoid 0xdead10cc crashes. -+ (BOOL)processOrphansSync:(OWSOrphanData *)orphanData - databaseConnection:(YapDatabaseConnection *)databaseConnection - shouldRemoveOrphans:(BOOL)shouldRemoveOrphans -{ - OWSAssertDebug(databaseConnection); - OWSAssertDebug(orphanData); - - __block BOOL shouldAbort = NO; - - // We need to avoid cleaning up new files that are still in the process of - // being created/written, so we don't clean up anything recent. - const NSTimeInterval kMinimumOrphanAgeSeconds = CurrentAppContext().isRunningTests ? 0.f : 15 * kMinuteInterval; - NSDate *appLaunchTime = CurrentAppContext().appLaunchTime; - NSTimeInterval thresholdTimestamp = appLaunchTime.timeIntervalSince1970 - kMinimumOrphanAgeSeconds; - NSDate *thresholdDate = [NSDate dateWithTimeIntervalSince1970:thresholdTimestamp]; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - NSUInteger interactionsRemoved = 0; - for (NSString *interactionId in orphanData.interactionIds) { - if (!self.isMainAppAndActive) { - shouldAbort = YES; - return; - } - TSInteraction *_Nullable interaction = - [TSInteraction fetchObjectWithUniqueID:interactionId transaction:transaction]; - if (!interaction) { - // This could just be a race condition, but it should be very unlikely. - OWSLogWarn(@"Could not load interaction: %@", interactionId); - continue; - } - // Don't delete interactions which were created in the last N minutes. - NSDate *creationDate = [NSDate ows_dateWithMillisecondsSince1970:interaction.timestamp]; - if ([creationDate isAfterDate:thresholdDate]) { - OWSLogInfo(@"Skipping orphan interaction due to age: %f", fabs(creationDate.timeIntervalSinceNow)); - continue; - } - OWSLogInfo(@"Removing orphan message: %@", interaction.uniqueId); - interactionsRemoved++; - if (!shouldRemoveOrphans) { - continue; - } - [interaction removeWithTransaction:transaction]; - } - OWSLogInfo(@"Deleted orphan interactions: %zu", interactionsRemoved); - - NSUInteger attachmentsRemoved = 0; - for (NSString *attachmentId in orphanData.attachmentIds) { - if (!self.isMainAppAndActive) { - shouldAbort = YES; - return; - } - TSAttachment *_Nullable attachment = - [TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction]; - if (!attachment) { - // This can happen on launch since we sync contacts/groups, especially if you have a lot of attachments - // to churn through, it's likely it's been deleted since starting this job. - OWSLogWarn(@"Could not load attachment: %@", attachmentId); - continue; - } - if (![attachment isKindOfClass:[TSAttachmentStream class]]) { - continue; - } - TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment; - // Don't delete attachments which were created in the last N minutes. - NSDate *creationDate = attachmentStream.creationTimestamp; - if ([creationDate isAfterDate:thresholdDate]) { - OWSLogInfo(@"Skipping orphan attachment due to age: %f", fabs(creationDate.timeIntervalSinceNow)); - continue; - } - OWSLogInfo(@"Removing orphan attachmentStream: %@", attachmentStream.uniqueId); - attachmentsRemoved++; - if (!shouldRemoveOrphans) { - continue; - } - [attachmentStream removeWithTransaction:transaction]; - } - OWSLogInfo(@"Deleted orphan attachments: %zu", attachmentsRemoved); - }]; - - if (shouldAbort) { - return nil; - } - - NSUInteger filesRemoved = 0; - NSArray *filePaths = [orphanData.filePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; - for (NSString *filePath in filePaths) { - if (!self.isMainAppAndActive) { - return nil; - } - - NSError *error; - NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error]; - if (!attributes || error) { - // This is fine; the file may have been deleted since we found it. - OWSLogWarn(@"Could not get attributes of file at: %@", filePath); - continue; - } - // Don't delete files which were created in the last N minutes. - NSDate *creationDate = attributes.fileModificationDate; - if ([creationDate isAfterDate:thresholdDate]) { - OWSLogInfo(@"Skipping file due to age: %f", fabs([creationDate timeIntervalSinceNow])); - continue; - } - OWSLogInfo(@"Deleting file: %@", filePath); - filesRemoved++; - if (!shouldRemoveOrphans) { - continue; - } - [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; - if (error) { - OWSLogDebug(@"Could not remove orphan file at: %@", filePath); - OWSFailDebug(@"Could not remove orphan file"); - } - } - OWSLogInfo(@"Deleted orphan files: %zu", filesRemoved); - - return YES; -} - -+ (nullable NSArray *)getTempFilePaths -{ - NSString *dir1 = OWSTemporaryDirectory(); - NSArray *_Nullable paths1 = [[self filePathsInDirectorySafe:dir1].allObjects mutableCopy]; - - NSString *dir2 = OWSTemporaryDirectoryAccessibleAfterFirstAuth(); - NSArray *_Nullable paths2 = [[self filePathsInDirectorySafe:dir2].allObjects mutableCopy]; - - if (paths1 && paths2) { - return [paths1 arrayByAddingObjectsFromArray:paths2]; - } else { - return nil; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift b/SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift index dff1a97e6..66547950d 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift @@ -150,7 +150,6 @@ public final class OpenGroupManagerV2 : NSObject { messageIDs.insert(interaction.uniqueId!) messageTimestamps.insert(interaction.timestamp) } - storage.updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, using: transaction) Storage.shared.removeReceivedMessageTimestamps(messageTimestamps, using: transaction) Storage.shared.removeLastMessageServerID(for: openGroup.room, on: openGroup.server, using: transaction) Storage.shared.removeLastDeletionServerID(for: openGroup.room, on: openGroup.server, using: transaction) diff --git a/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSOutgoingReceiptManager.m b/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSOutgoingReceiptManager.m index 7df3fa741..b639e8008 100644 --- a/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSOutgoingReceiptManager.m +++ b/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSOutgoingReceiptManager.m @@ -16,12 +16,6 @@ NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSUInteger, OWSReceiptType) { - OWSReceiptType_Delivery, - OWSReceiptType_Read, -}; - -NSString *const kOutgoingDeliveryReceiptManagerCollection = @"kOutgoingDeliveryReceiptManagerCollection"; NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptManagerCollection"; @interface OWSOutgoingReceiptManager () @@ -103,7 +97,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa } NSMutableArray *sendPromises = [NSMutableArray array]; - [sendPromises addObjectsFromArray:[self sendReceiptsForReceiptType:OWSReceiptType_Read]]; + [sendPromises addObjectsFromArray:[self sendReceipts]]; if (sendPromises.count < 1) { // No work to do; abort. @@ -132,10 +126,8 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa }); } -- (NSArray *)sendReceiptsForReceiptType:(OWSReceiptType)receiptType { - if (receiptType == OWSReceiptType_Delivery) { return @[]; } // Don't send delivery receipts - - NSString *collection = [self collectionForReceiptType:receiptType]; +- (NSArray *)sendReceipts { + NSString *collection = kOutgoingReadReceiptManagerCollection; NSMutableDictionary *> *queuedReceiptMap = [NSMutableDictionary new]; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { @@ -170,7 +162,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { AnyPromise *promise = [SNMessageSender sendNonDurably:readReceipt inThread:thread usingTransaction:transaction] .thenOn(self.serialQueue, ^(id object) { - [self dequeueReceiptsWithRecipientId:recipientId timestamps:timestampsAsSet receiptType:OWSReceiptType_Read]; + [self dequeueReceiptsWithRecipientId:recipientId timestamps:timestampsAsSet]; }); [sendPromises addObject:promise]; }]; @@ -179,22 +171,11 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa return [sendPromises copy]; } -- (void)enqueueDeliveryReceiptForEnvelope:(SNProtoEnvelope *)envelope -{ - [self enqueueReceiptWithRecipientId:envelope.source - timestamp:envelope.timestamp - receiptType:OWSReceiptType_Delivery]; -} - - (void)enqueueReadReceiptForEnvelope:(NSString *)messageAuthorId timestamp:(uint64_t)timestamp { - [self enqueueReceiptWithRecipientId:messageAuthorId timestamp:timestamp receiptType:OWSReceiptType_Read]; + [self enqueueReceiptWithRecipientId:messageAuthorId timestamp:timestamp]; } -- (void)enqueueReceiptWithRecipientId:(NSString *)recipientId - timestamp:(uint64_t)timestamp - receiptType:(OWSReceiptType)receiptType { - NSString *collection = [self collectionForReceiptType:receiptType]; - +- (void)enqueueReceiptWithRecipientId:(NSString *)recipientId timestamp:(uint64_t)timestamp { if (recipientId.length < 1) { return; } @@ -203,23 +184,19 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa } dispatch_async(self.serialQueue, ^{ [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - NSSet *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:collection]; + NSSet *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:kOutgoingReadReceiptManagerCollection]; NSMutableSet *newTimestamps = (oldTimestamps ? [oldTimestamps mutableCopy] : [NSMutableSet new]); [newTimestamps addObject:@(timestamp)]; - [transaction setObject:newTimestamps forKey:recipientId inCollection:collection]; + [transaction setObject:newTimestamps forKey:recipientId inCollection:kOutgoingReadReceiptManagerCollection]; }]; [self process]; }); } -- (void)dequeueReceiptsWithRecipientId:(NSString *)recipientId - timestamps:(NSSet *)timestamps - receiptType:(OWSReceiptType)receiptType { - NSString *collection = [self collectionForReceiptType:receiptType]; - +- (void)dequeueReceiptsWithRecipientId:(NSString *)recipientId timestamps:(NSSet *)timestamps { if (recipientId.length < 1) { return; } @@ -228,15 +205,15 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa } dispatch_async(self.serialQueue, ^{ [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - NSSet *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:collection]; + NSSet *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:kOutgoingReadReceiptManagerCollection]; NSMutableSet *newTimestamps = (oldTimestamps ? [oldTimestamps mutableCopy] : [NSMutableSet new]); [newTimestamps minusSet:timestamps]; if (newTimestamps.count > 0) { - [transaction setObject:newTimestamps forKey:recipientId inCollection:collection]; + [transaction setObject:newTimestamps forKey:recipientId inCollection:kOutgoingReadReceiptManagerCollection]; } else { - [transaction removeObjectForKey:recipientId inCollection:collection]; + [transaction removeObjectForKey:recipientId inCollection:kOutgoingReadReceiptManagerCollection]; } }]; }); @@ -247,15 +224,6 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa [self process]; } -- (NSString *)collectionForReceiptType:(OWSReceiptType)receiptType { - switch (receiptType) { - case OWSReceiptType_Delivery: - return kOutgoingDeliveryReceiptManagerCollection; - case OWSReceiptType_Read: - return kOutgoingReadReceiptManagerCollection; - } -} - @end NS_ASSUME_NONNULL_END diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index 6de688097..43508d40b 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -1,5 +1,6 @@ import PromiseKit import Sodium +import SessionSnodeKit public protocol SessionMessagingKitStorageProtocol { @@ -52,7 +53,6 @@ public protocol SessionMessagingKitStorageProtocol { func getAllV2OpenGroups() -> [String:OpenGroupV2] func getV2OpenGroup(for threadID: String) -> OpenGroupV2? func v2GetThreadID(for v2OpenGroupID: String) -> String? - func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set, using transaction: Any) // MARK: - Open Group Public Keys @@ -97,3 +97,5 @@ public protocol SessionMessagingKitStorageProtocol { /// Also touches the associated message. func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any) } + +extension Storage: SessionMessagingKitStorageProtocol, SessionSnodeKitStorageProtocol {} diff --git a/SessionMessagingKit/Utilities/OWSIdentityManager.h b/SessionMessagingKit/Utilities/OWSIdentityManager.h index 9972facbc..d283170d6 100644 --- a/SessionMessagingKit/Utilities/OWSIdentityManager.h +++ b/SessionMessagingKit/Utilities/OWSIdentityManager.h @@ -15,8 +15,6 @@ extern NSString *const LKED25519SecretKey; extern NSString *const LKED25519PublicKey; extern NSString *const OWSPrimaryStorageIdentityKeyStoreCollection; -extern NSString *const OWSPrimaryStorageTrustedKeysCollection; - // This notification will be fired whenever identities are created // or their verification state changes. extern NSString *const kNSNotificationName_IdentityStateDidChange; @@ -55,20 +53,8 @@ extern const NSUInteger kStoredIdentityKeyLength; */ - (nullable OWSRecipientIdentity *)untrustedIdentityForSendingToRecipientId:(NSString *)recipientId; -- (BOOL)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString *)recipientId; - - (nullable ECKeyPair *)identityKeyPair; -#pragma mark - Debug - -#if DEBUG -// Clears everything except the local identity key. -- (void)clearIdentityState:(YapDatabaseReadWriteTransaction *)transaction; - -- (void)snapshotIdentityState:(YapDatabaseReadWriteTransaction *)transaction; -- (void)restoreIdentityState:(YapDatabaseReadWriteTransaction *)transaction; -#endif - @end NS_ASSUME_NONNULL_END diff --git a/SessionMessagingKit/Utilities/OWSIdentityManager.m b/SessionMessagingKit/Utilities/OWSIdentityManager.m index a4a017baf..82b9b3c26 100644 --- a/SessionMessagingKit/Utilities/OWSIdentityManager.m +++ b/SessionMessagingKit/Utilities/OWSIdentityManager.m @@ -33,12 +33,6 @@ NSString *const LKED25519SecretKey = @"LKED25519SecretKey"; NSString *const LKED25519PublicKey = @"LKED25519PublicKey"; NSString *const OWSPrimaryStorageIdentityKeyStoreCollection = @"TSStorageManagerIdentityKeyStoreCollection"; -// Storing recipients identity keys -NSString *const OWSPrimaryStorageTrustedKeysCollection = @"TSStorageManagerTrustedKeysCollection"; - -NSString *const OWSIdentityManager_QueuedVerificationStateSyncMessages = - @"OWSIdentityManager_QueuedVerificationStateSyncMessages"; - // Don't trust an identity for sending to unless they've been around for at least this long const NSTimeInterval kIdentityKeyStoreNonBlockingSecondsThreshold = 5.0; @@ -165,70 +159,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa return (int)[TSAccountManager getOrGenerateRegistrationId:transaction]; } -- (BOOL)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString *)recipientId -{ - __block BOOL result; - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - result = [self saveRemoteIdentity:identityKey recipientId:recipientId protocolContext:transaction]; - }]; - - return result; -} - -- (BOOL)saveRemoteIdentity:(NSData *)identityKey - recipientId:(NSString *)recipientId - protocolContext:(nullable id)protocolContext -{ - YapDatabaseReadWriteTransaction *transaction = protocolContext; - - // Deprecated. We actually no longer use the OWSPrimaryStorageTrustedKeysCollection for trust - // decisions, but it's desirable to try to keep it up to date with our trusted identitys - // while we're switching between versions, e.g. so we don't get into a state where we have a - // session for an identity not in our key store. - [transaction setObject:identityKey forKey:recipientId inCollection:OWSPrimaryStorageTrustedKeysCollection]; - - OWSRecipientIdentity *existingIdentity = - [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId transaction:transaction]; - - if (existingIdentity == nil) { - [[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId - identityKey:identityKey - isFirstKnownKey:YES - createdAt:[NSDate new] - verificationState:OWSVerificationStateDefault] - saveWithTransaction:transaction]; - - [self fireIdentityStateChangeNotification]; - - return NO; - } - - if (![existingIdentity.identityKey isEqual:identityKey]) { - OWSVerificationState verificationState; - switch (existingIdentity.verificationState) { - case OWSVerificationStateDefault: - verificationState = OWSVerificationStateDefault; - break; - case OWSVerificationStateVerified: - case OWSVerificationStateNoLongerVerified: - verificationState = OWSVerificationStateNoLongerVerified; - break; - } - - [[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId - identityKey:identityKey - isFirstKnownKey:NO - createdAt:[NSDate new] - verificationState:verificationState] saveWithTransaction:transaction]; - - [self fireIdentityStateChangeNotification]; - - return YES; - } - - return NO; -} - - (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId { __block OWSRecipientIdentity *_Nullable result; @@ -341,65 +271,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa } } -#pragma mark - Debug - -#if DEBUG -- (void)clearIdentityState:(YapDatabaseReadWriteTransaction *)transaction -{ - NSMutableArray *identityKeysToRemove = [NSMutableArray new]; - [transaction enumerateKeysInCollection:OWSPrimaryStorageIdentityKeyStoreCollection - usingBlock:^(NSString *_Nonnull key, BOOL *_Nonnull stop) { - if ([key isEqualToString:OWSPrimaryStorageIdentityKeyStoreIdentityKey]) { - // Don't delete our own key. - return; - } - [identityKeysToRemove addObject:key]; - }]; - for (NSString *key in identityKeysToRemove) { - [transaction removeObjectForKey:key inCollection:OWSPrimaryStorageIdentityKeyStoreCollection]; - } - [transaction removeAllObjectsInCollection:OWSPrimaryStorageTrustedKeysCollection]; -} - -- (NSString *)identityKeySnapshotFilePath -{ - // Prefix name with period "." so that backups will ignore these snapshots. - NSString *dirPath = [OWSFileSystem appDocumentDirectoryPath]; - return [dirPath stringByAppendingPathComponent:@".identity-key-snapshot"]; -} - -- (NSString *)trustedKeySnapshotFilePath -{ - // Prefix name with period "." so that backups will ignore these snapshots. - NSString *dirPath = [OWSFileSystem appDocumentDirectoryPath]; - return [dirPath stringByAppendingPathComponent:@".trusted-key-snapshot"]; -} - -- (void)snapshotIdentityState:(YapDatabaseReadWriteTransaction *)transaction -{ - [transaction snapshotCollection:OWSPrimaryStorageIdentityKeyStoreCollection - snapshotFilePath:self.identityKeySnapshotFilePath]; - [transaction snapshotCollection:OWSPrimaryStorageTrustedKeysCollection - snapshotFilePath:self.trustedKeySnapshotFilePath]; -} - -- (void)restoreIdentityState:(YapDatabaseReadWriteTransaction *)transaction -{ - [transaction restoreSnapshotOfCollection:OWSPrimaryStorageIdentityKeyStoreCollection - snapshotFilePath:self.identityKeySnapshotFilePath]; - [transaction restoreSnapshotOfCollection:OWSPrimaryStorageTrustedKeysCollection - snapshotFilePath:self.trustedKeySnapshotFilePath]; -} - -#endif - -#pragma mark - Notifications - -- (void)applicationDidBecomeActive:(NSNotification *)notification -{ - -} - @end NS_ASSUME_NONNULL_END diff --git a/SessionShareExtension/ShareVC.swift b/SessionShareExtension/ShareVC.swift index 34716c706..9404db53e 100644 --- a/SessionShareExtension/ShareVC.swift +++ b/SessionShareExtension/ShareVC.swift @@ -123,7 +123,6 @@ final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerD // We don't need to use OWSMessageReceiver in the SAE. // We don't need to use OWSBatchMessageProcessor in the SAE. - // We don't need to use OWSOrphanDataCleaner in the SAE. // We don't need to fetch the local profile in the SAE OWSReadReceiptManager.shared().prepareCachedValues() diff --git a/SignalUtilitiesKit/Database/Storage+Conformances.swift b/SignalUtilitiesKit/Database/Storage+Conformances.swift deleted file mode 100644 index 21c74ee43..000000000 --- a/SignalUtilitiesKit/Database/Storage+Conformances.swift +++ /dev/null @@ -1,8 +0,0 @@ - -extension Storage : SessionMessagingKitStorageProtocol, SessionSnodeKitStorageProtocol { - - public func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set, using transaction: Any) { - let transaction = transaction as! YapDatabaseReadWriteTransaction - OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction) - } -} diff --git a/SignalUtilitiesKit/Database/TSStorageHeaders.h b/SignalUtilitiesKit/Database/TSStorageHeaders.h deleted file mode 100644 index 9f7894e8e..000000000 --- a/SignalUtilitiesKit/Database/TSStorageHeaders.h +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#ifndef Signal_TSStorageHeaders_h -#define Signal_TSStorageHeaders_h -#import -#import -#import - -#endif diff --git a/SignalUtilitiesKit/Database/TSStorageKeys.h b/SignalUtilitiesKit/Database/TSStorageKeys.h deleted file mode 100644 index 89834b775..000000000 --- a/SignalUtilitiesKit/Database/TSStorageKeys.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -#ifndef TextSecureKit_TSStorageKeys_h -#define TextSecureKit_TSStorageKeys_h - -/** - * Preferences exposed to the user - */ - -#pragma mark User Preferences - -#define TSStorageUserPreferencesCollection @"TSStorageUserPreferencesCollection" - - -/** - * Internal settings of the application, not exposed to the user. - */ - -#pragma mark Internal Settings - -#define TSStorageInternalSettingsCollection @"TSStorageInternalSettingsCollection" -#define TSStorageInternalSettingsVersion @"TSLastLaunchedVersion" - -#endif - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionViewController.swift b/SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionViewController.swift index 6d33c73bb..d792e433d 100644 --- a/SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionViewController.swift +++ b/SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentCaptionViewController.swift @@ -3,6 +3,7 @@ // import UIKit +import SessionUIKit protocol AttachmentCaptionDelegate: class { func captionView(_ captionView: AttachmentCaptionViewController, didChangeCaptionText captionText: String?, attachmentItem: SignalAttachmentItem) @@ -217,8 +218,8 @@ class AttachmentCaptionViewController: OWSViewController { placeholderTextView.backgroundColor = .clear placeholderTextView.font = UIFont.ows_dynamicTypeBody - placeholderTextView.textColor = Theme.darkThemePrimaryColor - placeholderTextView.tintColor = Theme.darkThemePrimaryColor + placeholderTextView.textColor = Colors.text + placeholderTextView.tintColor = Colors.text placeholderTextView.returnKeyType = .done return placeholderTextView diff --git a/SignalUtilitiesKit/Media Viewing & Editing/MessageApprovalViewController.swift b/SignalUtilitiesKit/Media Viewing & Editing/MessageApprovalViewController.swift index 8c83c0510..5cea0fcd6 100644 --- a/SignalUtilitiesKit/Media Viewing & Editing/MessageApprovalViewController.swift +++ b/SignalUtilitiesKit/Media Viewing & Editing/MessageApprovalViewController.swift @@ -3,6 +3,7 @@ // import Foundation +import SessionUIKit @objc public protocol MessageApprovalViewControllerDelegate: class { @@ -70,7 +71,7 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat public override func loadView() { self.view = UIView.container() - self.view.backgroundColor = Theme.backgroundColor + self.view.backgroundColor = Colors.navigationBarBackground // Recipient Row let recipientRow = createRecipientRow() @@ -82,8 +83,8 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat // Text View textView = OWSTextView() textView.delegate = self - textView.backgroundColor = Theme.backgroundColor - textView.textColor = Theme.primaryColor + textView.backgroundColor = Colors.navigationBarBackground + textView.textColor = Colors.text textView.font = UIFont.ows_dynamicTypeBody textView.text = self.initialMessageText textView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) @@ -97,11 +98,11 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat private func createRecipientRow() -> UIView { let recipientRow = UIView.container() - recipientRow.backgroundColor = Theme.toolbarBackgroundColor + recipientRow.backgroundColor = UIColor.lokiDarkestGray() // Hairline borders should be 1 pixel, not 1 point. let borderThickness = 1.0 / UIScreen.main.scale - let borderColor = Theme.middleGrayColor + let borderColor = UIColor(white: 0.5, alpha: 1) let topBorder = UIView.container() topBorder.backgroundColor = borderColor @@ -126,12 +127,12 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat let toLabel = UILabel() toLabel.text = NSLocalizedString("MESSAGE_APPROVAL_RECIPIENT_LABEL", comment: "Label for the recipient name in the 'message approval' dialog.") - toLabel.textColor = Theme.secondaryColor + toLabel.textColor = Colors.separator toLabel.font = font recipientRow.addSubview(toLabel) let nameLabel = UILabel() - nameLabel.textColor = Theme.primaryColor + nameLabel.textColor = Colors.text nameLabel.font = font nameLabel.lineBreakMode = .byTruncatingTail recipientRow.addSubview(nameLabel) @@ -164,12 +165,12 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat let publicKey = contactThread.contactSessionID() nameLabel.text = Storage.shared.getContact(with: publicKey)?.displayName(for: .regular) ?? publicKey - nameLabel.textColor = Theme.primaryColor + nameLabel.textColor = Colors.text if let profileName = self.profileName(contactThread: contactThread) { // If there's a profile name worth showing, add it as a second line below the name. let profileNameLabel = UILabel() - profileNameLabel.textColor = Theme.secondaryColor + profileNameLabel.textColor = Colors.separator profileNameLabel.font = font profileNameLabel.text = profileName profileNameLabel.lineBreakMode = .byTruncatingTail diff --git a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h index 1d2cedbc2..8ee353b51 100644 --- a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h +++ b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h @@ -32,11 +32,9 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import #import #import #import -#import #import #import #import @@ -47,12 +45,9 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; #import #import #import -#import #import #import #import -#import -#import #import #import #import diff --git a/SignalUtilitiesKit/Shared View Controllers/OWSNavigationController.m b/SignalUtilitiesKit/Shared View Controllers/OWSNavigationController.m index 13f453fcf..6921a9001 100644 --- a/SignalUtilitiesKit/Shared View Controllers/OWSNavigationController.m +++ b/SignalUtilitiesKit/Shared View Controllers/OWSNavigationController.m @@ -55,15 +55,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -- (void)themeDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - self.navigationBar.barTintColor = [UINavigationBar appearance].barTintColor; - self.navigationBar.tintColor = [UINavigationBar appearance].tintColor; - self.navigationBar.titleTextAttributes = [UINavigationBar appearance].titleTextAttributes; -} - - (void)viewDidLoad { [super viewDidLoad]; @@ -90,11 +81,6 @@ NS_ASSUME_NONNULL_BEGIN OWSNavigationBar *navbar = (OWSNavigationBar *)self.navigationBar; navbar.navBarLayoutDelegate = self; [self updateLayoutForNavbar:navbar]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(themeDidChange:) - name:ThemeDidChangeNotification - object:nil]; } // All OWSNavigationController serve as the UINavigationBarDelegate for their navbar. diff --git a/SignalUtilitiesKit/Shared View Controllers/OWSTableViewController.m b/SignalUtilitiesKit/Shared View Controllers/OWSTableViewController.m index 0882eca30..0a6b1915f 100644 --- a/SignalUtilitiesKit/Shared View Controllers/OWSTableViewController.m +++ b/SignalUtilitiesKit/Shared View Controllers/OWSTableViewController.m @@ -4,7 +4,6 @@ #import "OWSTableViewController.h" #import "OWSNavigationController.h" -#import "Theme.h" #import "UIColor+OWS.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" @@ -594,11 +593,6 @@ NSString *const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kOWSTableCellIdentifier]; [self applyTheme]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(themeDidChange:) - name:ThemeDidChangeNotification - object:nil]; } - (void)dealloc @@ -816,23 +810,13 @@ NSString *const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; [self.delegate tableViewWillBeginDragging]; } -#pragma mark - Theme - -- (void)themeDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self applyTheme]; - [self.tableView reloadData]; -} - - (void)applyTheme { OWSAssertIsOnMainThread(); - self.view.backgroundColor = Theme.backgroundColor; - self.tableView.backgroundColor = Theme.backgroundColor; - self.tableView.separatorColor = Theme.cellSeparatorColor; + self.view.backgroundColor = LKColors.navigationBarBackground; + self.tableView.backgroundColor = LKColors.navigationBarBackground; + self.tableView.separatorColor = LKColors.separator; } @end diff --git a/SignalUtilitiesKit/Shared View Controllers/OWSViewController.m b/SignalUtilitiesKit/Shared View Controllers/OWSViewController.m index 0e5ec8c6a..4ed5a5c87 100644 --- a/SignalUtilitiesKit/Shared View Controllers/OWSViewController.m +++ b/SignalUtilitiesKit/Shared View Controllers/OWSViewController.m @@ -4,7 +4,7 @@ #import "OWSViewController.h" #import "UIView+OWS.h" -#import +#import #import "AppContext.h" NS_ASSUME_NONNULL_BEGIN @@ -87,7 +87,7 @@ UIInterfaceOrientationMask DefaultUIInterfaceOrientationMask(void) [super viewDidLoad]; if (self.shouldUseTheme) { - self.view.backgroundColor = Theme.backgroundColor; + self.view.backgroundColor = [LKColors navigationBarBackground]; } } diff --git a/SignalUtilitiesKit/Shared View Controllers/SheetViewController.swift b/SignalUtilitiesKit/Shared View Controllers/SheetViewController.swift index 1a7d6510f..a8a142acf 100644 --- a/SignalUtilitiesKit/Shared View Controllers/SheetViewController.swift +++ b/SignalUtilitiesKit/Shared View Controllers/SheetViewController.swift @@ -3,6 +3,7 @@ // import Foundation +import SessionUIKit @objc(OWSSheetViewControllerDelegate) public protocol SheetViewControllerDelegate: class { @@ -54,8 +55,8 @@ public class SheetViewController: UIViewController { sheetView.setContentHuggingVerticalHigh() sheetView.setCompressionResistanceHigh() self.sheetViewVerticalConstraint = sheetView.autoPinEdge(.top, to: .bottom, of: self.view) - - handleView.backgroundColor = Theme.isDarkThemeEnabled ? UIColor.ows_white : UIColor.ows_gray05 + + handleView.backgroundColor = isDarkMode ? UIColor.ows_white : UIColor.ows_gray05 let kHandleViewHeight: CGFloat = 5 handleView.autoSetDimensions(to: CGSize(width: 40, height: kHandleViewHeight)) handleView.layer.cornerRadius = kHandleViewHeight / 2 @@ -82,7 +83,7 @@ public class SheetViewController: UIViewController { let backgroundDuration: TimeInterval = 0.1 UIView.animate(withDuration: backgroundDuration) { - let alpha: CGFloat = Theme.isDarkThemeEnabled ? 0.7 : 0.6 + let alpha: CGFloat = isDarkMode ? 0.7 : 0.6 self.view.backgroundColor = UIColor.black.withAlphaComponent(alpha) } @@ -199,8 +200,7 @@ private class SheetView: UIView { override init(frame: CGRect) { super.init(frame: frame) - self.backgroundColor = Theme.isDarkThemeEnabled ? UIColor.ows_gray90 - : UIColor.ows_gray05 + self.backgroundColor = isDarkMode ? UIColor.ows_gray90 : UIColor.ows_gray05 } required init?(coder aDecoder: NSCoder) { diff --git a/SignalUtilitiesKit/Shared Views/OWSNavigationBar.swift b/SignalUtilitiesKit/Shared Views/OWSNavigationBar.swift index 707023a3b..ef81497b4 100644 --- a/SignalUtilitiesKit/Shared Views/OWSNavigationBar.swift +++ b/SignalUtilitiesKit/Shared Views/OWSNavigationBar.swift @@ -50,10 +50,6 @@ public class OWSNavigationBar: UINavigationBar { NotificationCenter.default.addObserver(self, selector: #selector(callDidChange), name: .OWSWindowManagerCallDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didChangeStatusBarFrame), name: UIApplication.didChangeStatusBarFrameNotification, object: nil) - NotificationCenter.default.addObserver(self, - selector: #selector(themeDidChange), - name: .ThemeDidChange, - object: nil) } // MARK: FirstResponder Stubbing @@ -82,13 +78,13 @@ public class OWSNavigationBar: UINavigationBar { if UIAccessibility.isReduceTransparencyEnabled { blurEffectView?.isHidden = true - let color = Theme.navbarBackgroundColor + let color = UIColor.lokiDarkestGray() let backgroundImage = UIImage(color: color) self.setBackgroundImage(backgroundImage, for: .default) } else { // Make navbar more translucent than default. Navbars remove alpha from any assigned backgroundColor, so // to achieve transparency, we have to assign a transparent image. - let color = Theme.navbarBackgroundColor + let color = UIColor.lokiDarkestGray() let backgroundImage = UIImage(color: color) self.setBackgroundImage(backgroundImage, for: .default) @@ -97,16 +93,10 @@ public class OWSNavigationBar: UINavigationBar { } } - @objc - public func themeDidChange() { - Logger.debug("") - applyTheme() - } - @objc public var respectsTheme: Bool = true { didSet { - themeDidChange() + applyTheme() } } @@ -191,9 +181,9 @@ public class OWSNavigationBar: UINavigationBar { respectsTheme = false barStyle = .black - titleTextAttributes = [NSAttributedString.Key.foregroundColor: Theme.darkThemePrimaryColor] - barTintColor = Theme.darkThemeBackgroundColor.withAlphaComponent(0.6) - tintColor = Theme.darkThemePrimaryColor + titleTextAttributes = [NSAttributedString.Key.foregroundColor: Colors.text] + barTintColor = Colors.navigationBarBackground.withAlphaComponent(0.6) + tintColor = Colors.text switch type { case .clear: diff --git a/SignalUtilitiesKit/Shared Views/OWSSearchBar.h b/SignalUtilitiesKit/Shared Views/OWSSearchBar.h deleted file mode 100644 index bbac537f4..000000000 --- a/SignalUtilitiesKit/Shared Views/OWSSearchBar.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSSearchBar : UISearchBar - -+ (void)applyThemeToSearchBar:(UISearchBar *)searchBar; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Shared Views/OWSSearchBar.m b/SignalUtilitiesKit/Shared Views/OWSSearchBar.m deleted file mode 100644 index aa82a0e00..000000000 --- a/SignalUtilitiesKit/Shared Views/OWSSearchBar.m +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "OWSSearchBar.h" -#import "Theme.h" -#import "UIView+OWS.h" -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation OWSSearchBar - -- (instancetype)init -{ - if (self = [super init]) { - [self ows_configure]; - } - - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - [self ows_configure]; - } - - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder -{ - if (self = [super initWithCoder:aDecoder]) { - [self ows_configure]; - } - - return self; -} - -- (void)ows_configure -{ - [self ows_applyTheme]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(themeDidChange:) - name:ThemeDidChangeNotification - object:nil]; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)ows_applyTheme -{ - [self.class applyThemeToSearchBar:self]; -} - -+ (void)applyThemeToSearchBar:(UISearchBar *)searchBar -{ - OWSAssertIsOnMainThread(); - - UIColor *foregroundColor = UIColor.lokiLightestGray; - searchBar.barTintColor = Theme.backgroundColor; - searchBar.barStyle = Theme.barStyle; - searchBar.tintColor = UIColor.lokiGreen; - - // Hide searchBar border. - // Alternatively we could hide the border by using `UISearchBarStyleMinimal`, but that causes an issue when toggling - // from light -> dark -> light theme wherein the textField background color appears darker than it should - // (regardless of our re-setting textfield.backgroundColor below). - searchBar.backgroundImage = [UIImage new]; - - if (Theme.isDarkThemeEnabled) { - UIImage *clearImage = [UIImage imageNamed:@"searchbar_clear"]; - [searchBar setImage:[clearImage asTintedImageWithColor:foregroundColor] - forSearchBarIcon:UISearchBarIconClear - state:UIControlStateNormal]; - - UIImage *searchImage = [UIImage imageNamed:@"searchbar_search"]; - [searchBar setImage:[searchImage asTintedImageWithColor:foregroundColor] - forSearchBarIcon:UISearchBarIconSearch - state:UIControlStateNormal]; - } else { - [searchBar setImage:nil forSearchBarIcon:UISearchBarIconClear state:UIControlStateNormal]; - - [searchBar setImage:nil forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal]; - } - - [searchBar traverseViewHierarchyWithVisitor:^(UIView *view) { - if ([view isKindOfClass:[UITextField class]]) { - UITextField *textField = (UITextField *)view; - textField.backgroundColor = Theme.searchFieldBackgroundColor; - textField.textColor = Theme.primaryColor; - NSString *placeholder = textField.placeholder; - if (placeholder != nil) { - NSMutableAttributedString *attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:placeholder]; - [attributedPlaceholder addAttribute:NSForegroundColorAttributeName value:foregroundColor range:NSMakeRange(0, placeholder.length)]; - textField.attributedPlaceholder = attributedPlaceholder; - } - textField.keyboardAppearance = LKAppModeUtilities.isLightMode ? UIKeyboardAppearanceDefault : UIKeyboardAppearanceDark; - } - }]; -} - -- (void)themeDidChange:(NSNotification *)notification -{ - OWSAssertIsOnMainThread(); - - [self ows_applyTheme]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Shared Views/OWSTextField.m b/SignalUtilitiesKit/Shared Views/OWSTextField.m index a311ae255..e2e390ff9 100644 --- a/SignalUtilitiesKit/Shared Views/OWSTextField.m +++ b/SignalUtilitiesKit/Shared Views/OWSTextField.m @@ -3,7 +3,6 @@ // #import "OWSTextField.h" -#import "Theme.h" #import diff --git a/SignalUtilitiesKit/Shared Views/OWSTextView.m b/SignalUtilitiesKit/Shared Views/OWSTextView.m index 825507766..03876d3ff 100644 --- a/SignalUtilitiesKit/Shared Views/OWSTextView.m +++ b/SignalUtilitiesKit/Shared Views/OWSTextView.m @@ -3,7 +3,6 @@ // #import "OWSTextView.h" -#import "Theme.h" #import diff --git a/SignalUtilitiesKit/Shared Views/Toast.swift b/SignalUtilitiesKit/Shared Views/Toast.swift index 3500aaf12..28e6995c7 100644 --- a/SignalUtilitiesKit/Shared Views/Toast.swift +++ b/SignalUtilitiesKit/Shared Views/Toast.swift @@ -3,6 +3,7 @@ // import Foundation +import SessionUIKit @objc public class ToastController: NSObject, ToastViewDelegate { @@ -116,11 +117,11 @@ class ToastView: UIView { super.init(frame: frame) self.layer.cornerRadius = 4 - self.backgroundColor = Theme.toastBackgroundColor + self.backgroundColor = (isDarkMode ? UIColor.ows_gray75 : UIColor.ows_gray60) self.layoutMargins = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) label.textAlignment = .center - label.textColor = Theme.toastForegroundColor + label.textColor = (isDarkMode ? UIColor.ows_white : UIColor.ows_white) label.font = UIFont.ows_dynamicTypeBody label.numberOfLines = 0 self.addSubview(label) diff --git a/SignalUtilitiesKit/Sharing/SelectRecipientViewController.m b/SignalUtilitiesKit/Sharing/SelectRecipientViewController.m index cf18cc5c4..a6fdfa561 100644 --- a/SignalUtilitiesKit/Sharing/SelectRecipientViewController.m +++ b/SignalUtilitiesKit/Sharing/SelectRecipientViewController.m @@ -14,6 +14,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -47,7 +48,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien { [super loadView]; - self.view.backgroundColor = [Theme backgroundColor]; + self.view.backgroundColor = LKColors.navigationBarBackground; [self createViews]; @@ -86,7 +87,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien [_tableViewController.view autoPinEdgeToSuperviewEdge:ALEdgeBottom]; self.tableViewController.tableView.rowHeight = UITableViewAutomaticDimension; self.tableViewController.tableView.estimatedRowHeight = 60; - _tableViewController.view.backgroundColor = [Theme backgroundColor]; + _tableViewController.view.backgroundColor = LKColors.navigationBarBackground; [self updateTableContents]; } @@ -95,7 +96,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien { UILabel *countryCodeLabel = [UILabel new]; countryCodeLabel.font = [UIFont ows_mediumFontWithSize:18.f]; - countryCodeLabel.textColor = [Theme primaryColor]; + countryCodeLabel.textColor = LKColors.text; countryCodeLabel.text = NSLocalizedString(@"REGISTRATION_DEFAULT_COUNTRY_NAME", @"Label for the country code field"); return countryCodeLabel; @@ -121,7 +122,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien { UILabel *phoneNumberLabel = [UILabel new]; phoneNumberLabel.font = [UIFont ows_mediumFontWithSize:18.f]; - phoneNumberLabel.textColor = [Theme primaryColor]; + phoneNumberLabel.textColor = LKColors.text; phoneNumberLabel.text = NSLocalizedString(@"REGISTRATION_PHONENUMBER_BUTTON", @"Label for the phone number textfield"); return phoneNumberLabel; @@ -137,7 +138,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien if (!_examplePhoneNumberLabel) { _examplePhoneNumberLabel = [UILabel new]; _examplePhoneNumberLabel.font = [self examplePhoneNumberFont]; - _examplePhoneNumberLabel.textColor = [Theme secondaryColor]; + _examplePhoneNumberLabel.textColor = LKColors.separator; SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _examplePhoneNumberLabel); } diff --git a/SignalUtilitiesKit/Sharing/SelectThreadViewController.m b/SignalUtilitiesKit/Sharing/SelectThreadViewController.m index 0a2f5ffe8..0ae237de3 100644 --- a/SignalUtilitiesKit/Sharing/SelectThreadViewController.m +++ b/SignalUtilitiesKit/Sharing/SelectThreadViewController.m @@ -4,7 +4,6 @@ #import "SelectThreadViewController.h" #import "ContactTableViewCell.h" -#import "OWSSearchBar.h" #import "OWSTableViewController.h" #import "ThreadViewHelper.h" #import "UIColor+OWS.h" diff --git a/SignalUtilitiesKit/To Do/ContactCellView.m b/SignalUtilitiesKit/To Do/ContactCellView.m index 7190ae587..8ebbbbf69 100644 --- a/SignalUtilitiesKit/To Do/ContactCellView.m +++ b/SignalUtilitiesKit/To Do/ContactCellView.m @@ -114,10 +114,10 @@ const CGFloat kContactCellAvatarTextMargin = 12; self.subtitleLabel.font = [UIFont ows_regularFontWithSize:11.f]; self.accessoryLabel.font = [UIFont ows_mediumFontWithSize:13.f]; - self.nameLabel.textColor = [Theme primaryColor]; - self.profileNameLabel.textColor = [Theme secondaryColor]; - self.subtitleLabel.textColor = [Theme secondaryColor]; - self.accessoryLabel.textColor = Theme.middleGrayColor; + self.nameLabel.textColor = LKColors.text; + self.profileNameLabel.textColor = LKColors.separator; + self.subtitleLabel.textColor = LKColors.separator; + self.accessoryLabel.textColor = [UIColor colorWithWhite:0.5f alpha:1.f]; } - (void)configureWithRecipientId:(NSString *)recipientId diff --git a/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.h b/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.h deleted file mode 100644 index 358213958..000000000 --- a/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.h +++ /dev/null @@ -1,13 +0,0 @@ -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface OWSPrimaryStorage (Loki) - -- (void)updateMessageIDCollectionByPruningMessagesWithIDs:(NSSet *)targetMessageIDs in:(YapDatabaseReadWriteTransaction *)transaction NS_SWIFT_NAME(updateMessageIDCollectionByPruningMessagesWithIDs(_:in:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.m b/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.m deleted file mode 100644 index 97e2f4e8a..000000000 --- a/SignalUtilitiesKit/To Do/OWSPrimaryStorage+Loki.m +++ /dev/null @@ -1,26 +0,0 @@ -#import "OWSPrimaryStorage+Loki.h" -#import "OWSPrimaryStorage+keyFromIntLong.h" -#import "OWSIdentityManager.h" -#import "NSDate+OWS.h" -#import "TSAccountManager.h" -#import "YapDatabaseConnection+OWS.h" -#import "YapDatabaseTransaction+OWS.h" -#import "NSObject+Casting.h" -#import - -#define LKMessageIDCollection @"LKMessageIDCollection" - -@implementation OWSPrimaryStorage (Loki) - -- (void)updateMessageIDCollectionByPruningMessagesWithIDs:(NSSet *)targetMessageIDs in:(YapDatabaseReadWriteTransaction *)transaction { - NSMutableArray *serverIDs = [NSMutableArray new]; - [transaction enumerateRowsInCollection:LKMessageIDCollection usingBlock:^(NSString *key, id object, id metadata, BOOL *stop) { - if (![object isKindOfClass:NSString.class]) { return; } - NSString *messageID = (NSString *)object; - if (![targetMessageIDs containsObject:messageID]) { return; } - [serverIDs addObject:key]; - }]; - [transaction removeObjectsForKeys:serverIDs inCollection:LKMessageIDCollection]; -} - -@end diff --git a/SignalUtilitiesKit/To Do/OWSProfileManager.h b/SignalUtilitiesKit/To Do/OWSProfileManager.h index 6df6b5c70..d584604ec 100644 --- a/SignalUtilitiesKit/To Do/OWSProfileManager.h +++ b/SignalUtilitiesKit/To Do/OWSProfileManager.h @@ -6,9 +6,6 @@ NS_ASSUME_NONNULL_BEGIN -extern NSString *const kNSNotificationName_ProfileWhitelistDidChange; -extern NSString *const kNSNotificationName_ProfileKeyDidChange; - extern const NSUInteger kOWSProfileManager_NameDataLength; extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter; diff --git a/SignalUtilitiesKit/To Do/OWSProfileManager.m b/SignalUtilitiesKit/To Do/OWSProfileManager.m index 729049c36..d37b78be1 100644 --- a/SignalUtilitiesKit/To Do/OWSProfileManager.m +++ b/SignalUtilitiesKit/To Do/OWSProfileManager.m @@ -16,7 +16,6 @@ #import #import #import -#import #import #import #import @@ -27,13 +26,6 @@ NS_ASSUME_NONNULL_BEGIN -NSString *const kNSNotificationName_ProfileWhitelistDidChange = @"kNSNotificationName_ProfileWhitelistDidChange"; - -NSString *const kOWSProfileManager_UserWhitelistCollection = @"kOWSProfileManager_UserWhitelistCollection"; -NSString *const kOWSProfileManager_GroupWhitelistCollection = @"kOWSProfileManager_GroupWhitelistCollection"; - -NSString *const kNSNotificationName_ProfileKeyDidChange = @"kNSNotificationName_ProfileKeyDidChange"; - // The max bytes for a user's profile name, encoded in UTF8. // Before encrypting and submitting we NULL pad the name data to this length. const NSUInteger kOWSProfileManager_NameDataLength = 26; diff --git a/SignalUtilitiesKit/Utilities/Theme.h b/SignalUtilitiesKit/Utilities/Theme.h deleted file mode 100644 index 7a5e82e9b..000000000 --- a/SignalUtilitiesKit/Utilities/Theme.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -extern NSString *const ThemeDidChangeNotification; - -@interface Theme : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -@property (class, readonly, atomic) BOOL isDarkThemeEnabled; - -+ (void)setIsDarkThemeEnabled:(BOOL)value; - -@property (class, readonly, nonatomic) UIColor *backgroundColor; -@property (class, readonly, nonatomic) UIColor *primaryColor; -@property (class, readonly, nonatomic) UIColor *secondaryColor; -@property (class, readonly, nonatomic) UIColor *boldColor; -@property (class, readonly, nonatomic) UIColor *offBackgroundColor; -@property (class, readonly, nonatomic) UIColor *middleGrayColor; -@property (class, readonly, nonatomic) UIColor *placeholderColor; -@property (class, readonly, nonatomic) UIColor *hairlineColor; - -#pragma mark - Global App Colors - -@property (class, readonly, nonatomic) UIColor *navbarBackgroundColor; -@property (class, readonly, nonatomic) UIColor *navbarIconColor; -@property (class, readonly, nonatomic) UIColor *navbarTitleColor; - -@property (class, readonly, nonatomic) UIColor *toolbarBackgroundColor; - -@property (class, readonly, nonatomic) UIColor *conversationButtonBackgroundColor; - -@property (class, readonly, nonatomic) UIColor *cellSelectedColor; -@property (class, readonly, nonatomic) UIColor *cellSeparatorColor; - -// In some contexts, e.g. media viewing/sending, we always use "dark theme" UI regardless of the -// users chosen theme. -@property (class, readonly, nonatomic) UIColor *darkThemeNavbarIconColor; -@property (class, readonly, nonatomic) UIColor *darkThemeNavbarBackgroundColor; -@property (class, readonly, nonatomic) UIColor *darkThemeBackgroundColor; -@property (class, readonly, nonatomic) UIColor *darkThemePrimaryColor; -@property (class, readonly, nonatomic) UIBlurEffect *darkThemeBarBlurEffect; -@property (class, readonly, nonatomic) UIColor *galleryHighlightColor; - -#pragma mark - - -@property (class, readonly, nonatomic) UIBarStyle barStyle; -@property (class, readonly, nonatomic) UIColor *searchFieldBackgroundColor; -@property (class, readonly, nonatomic) UIBlurEffect *barBlurEffect; -@property (class, readonly, nonatomic) UIKeyboardAppearance keyboardAppearance; -@property (class, readonly, nonatomic) UIKeyboardAppearance darkThemeKeyboardAppearance; - -#pragma mark - - -@property (class, readonly, nonatomic) UIColor *toastForegroundColor; -@property (class, readonly, nonatomic) UIColor *toastBackgroundColor; - -@property (class, readonly, nonatomic) UIColor *scrollButtonBackgroundColor; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Utilities/Theme.m b/SignalUtilitiesKit/Utilities/Theme.m deleted file mode 100644 index 7f298eb6d..000000000 --- a/SignalUtilitiesKit/Utilities/Theme.m +++ /dev/null @@ -1,217 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "Theme.h" -#import "UIColor+OWS.h" -#import "UIUtil.h" -#import -#import -#import - -#import - -NS_ASSUME_NONNULL_BEGIN - -NSString *const ThemeDidChangeNotification = @"ThemeDidChangeNotification"; - -NSString *const ThemeCollection = @"ThemeCollection"; -NSString *const ThemeKeyThemeEnabled = @"ThemeKeyThemeEnabled"; - - -@interface Theme () - -@property (nonatomic) NSNumber *isDarkThemeEnabledNumber; - -@end - -@implementation Theme - -+ (instancetype)sharedInstance -{ - static dispatch_once_t onceToken; - static Theme *instance; - dispatch_once(&onceToken, ^{ - instance = [Theme new]; - }); - - return instance; -} - -+ (BOOL)isDarkThemeEnabled -{ - return LKAppModeUtilities.isDarkMode; -} - -- (BOOL)isDarkThemeEnabled -{ - OWSAssertIsOnMainThread(); - - return LKAppModeUtilities.isDarkMode; -} - -+ (void)setIsDarkThemeEnabled:(BOOL)value -{ - return [self.sharedInstance setIsDarkThemeEnabled:value]; -} - -- (void)setIsDarkThemeEnabled:(BOOL)value -{ - return; -} - -+ (UIColor *)backgroundColor -{ - return LKColors.navigationBarBackground; -} - -+ (UIColor *)offBackgroundColor -{ - return LKColors.unimportant; -} - -+ (UIColor *)primaryColor -{ - return LKColors.text; -} - -+ (UIColor *)secondaryColor -{ - return LKColors.separator; -} - -+ (UIColor *)boldColor -{ - return (Theme.isDarkThemeEnabled ? UIColor.ows_whiteColor : UIColor.blackColor); -} - -+ (UIColor *)middleGrayColor -{ - return [UIColor colorWithWhite:0.5f alpha:1.f]; -} - -+ (UIColor *)placeholderColor -{ - return LKColors.navigationBarBackground; -} - -+ (UIColor *)hairlineColor -{ - return LKColors.separator; -} - -#pragma mark - Global App Colors - -+ (UIColor *)navbarBackgroundColor -{ - return UIColor.lokiDarkestGray; -} - -+ (UIColor *)darkThemeNavbarBackgroundColor -{ - return UIColor.ows_blackColor; -} - -+ (UIColor *)navbarIconColor -{ - return UIColor.lokiGreen; -} - -+ (UIColor *)darkThemeNavbarIconColor; -{ - return LKColors.text; -} - -+ (UIColor *)navbarTitleColor -{ - return Theme.primaryColor; -} - -+ (UIColor *)toolbarBackgroundColor -{ - return self.navbarBackgroundColor; -} - -+ (UIColor *)cellSelectedColor -{ - return UIColor.lokiDarkGray; -} - -+ (UIColor *)cellSeparatorColor -{ - return Theme.hairlineColor; -} - -+ (UIColor *)darkThemeBackgroundColor -{ - return LKColors.navigationBarBackground; -} - -+ (UIColor *)darkThemePrimaryColor -{ - return LKColors.text; -} - -+ (UIColor *)galleryHighlightColor -{ - return UIColor.lokiGreen; -} - -+ (UIColor *)conversationButtonBackgroundColor -{ - return (Theme.isDarkThemeEnabled ? [UIColor colorWithWhite:0.35f alpha:1.f] : UIColor.ows_gray02Color); -} - -+ (UIBlurEffect *)barBlurEffect -{ - return Theme.isDarkThemeEnabled ? self.darkThemeBarBlurEffect - : [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; -} - -+ (UIBlurEffect *)darkThemeBarBlurEffect -{ - return [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; -} - -+ (UIKeyboardAppearance)keyboardAppearance -{ - return LKAppModeUtilities.isLightMode ? UIKeyboardAppearanceDefault : UIKeyboardAppearanceDark; -} - -+ (UIKeyboardAppearance)darkThemeKeyboardAppearance; -{ - return UIKeyboardAppearanceDark; -} - -#pragma mark - Search Bar - -+ (UIBarStyle)barStyle -{ - return Theme.isDarkThemeEnabled ? UIBarStyleBlack : UIBarStyleDefault; -} - -+ (UIColor *)searchFieldBackgroundColor -{ - return Theme.isDarkThemeEnabled ? Theme.offBackgroundColor : UIColor.ows_gray05Color; -} - -#pragma mark - - -+ (UIColor *)toastForegroundColor -{ - return (Theme.isDarkThemeEnabled ? UIColor.ows_whiteColor : UIColor.ows_whiteColor); -} - -+ (UIColor *)toastBackgroundColor -{ - return (Theme.isDarkThemeEnabled ? UIColor.ows_gray75Color : UIColor.ows_gray60Color); -} - -+ (UIColor *)scrollButtonBackgroundColor -{ - return UIColor.lokiDarkerGray; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalUtilitiesKit/Utilities/UIUtil.m b/SignalUtilitiesKit/Utilities/UIUtil.m index 940657363..8110cc330 100644 --- a/SignalUtilitiesKit/Utilities/UIUtil.m +++ b/SignalUtilitiesKit/Utilities/UIUtil.m @@ -3,7 +3,6 @@ // #import "UIUtil.h" -#import "Theme.h" #import "UIColor+OWS.h" #import diff --git a/SignalUtilitiesKit/Utilities/UIViewController+OWS.m b/SignalUtilitiesKit/Utilities/UIViewController+OWS.m index 18ed7599f..bb20c7a7d 100644 --- a/SignalUtilitiesKit/Utilities/UIViewController+OWS.m +++ b/SignalUtilitiesKit/Utilities/UIViewController+OWS.m @@ -4,7 +4,6 @@ #import -#import "Theme.h" #import "UIColor+OWS.h" #import "UIUtil.h" #import "UIView+OWS.h" @@ -96,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; OWSAssertDebug(backImage); [backButton setImage:backImage forState:UIControlStateNormal]; - backButton.tintColor = Theme.navbarIconColor; + backButton.tintColor = UIColor.lokiGreen; backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; diff --git a/SignalUtilitiesKit/Utilities/VersionMigrations.m b/SignalUtilitiesKit/Utilities/VersionMigrations.m index 5198baf42..70a121039 100644 --- a/SignalUtilitiesKit/Utilities/VersionMigrations.m +++ b/SignalUtilitiesKit/Utilities/VersionMigrations.m @@ -7,7 +7,6 @@ #import #import #import -#import #import #import #import From f7091dca2800e7224ecc8fa57ce68ab89b3a8db1 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Wed, 30 Mar 2022 09:48:34 +1100 Subject: [PATCH 3/4] Removed an empty notification observer --- SessionMessagingKit/Utilities/OWSIdentityManager.m | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/SessionMessagingKit/Utilities/OWSIdentityManager.m b/SessionMessagingKit/Utilities/OWSIdentityManager.m index 82b9b3c26..17b041673 100644 --- a/SessionMessagingKit/Utilities/OWSIdentityManager.m +++ b/SessionMessagingKit/Utilities/OWSIdentityManager.m @@ -73,8 +73,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa _dbConnection = primaryStorage.newDatabaseConnection; self.dbConnection.objectCacheEnabled = NO; - [self observeNotifications]; - return self; } @@ -85,14 +83,6 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa #pragma mark - -- (void)observeNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive:) - name:UIApplicationDidBecomeActiveNotification - object:nil]; -} - - (void)generateNewIdentityKeyPair { ECKeyPair *keyPair = [Curve25519 generateKeyPair]; From 89e80da625655483e511c7fffdae3e75612fec76 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Wed, 30 Mar 2022 10:05:47 +1100 Subject: [PATCH 4/4] Removed AppPreferences (unused) --- Session.xcodeproj/project.pbxproj | 4 -- .../Database/AppPreferences.swift | 37 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 SignalUtilitiesKit/Database/AppPreferences.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 58947be29..40d7fd1fb 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -547,7 +547,6 @@ C38EF30C255B6DBF007E1867 /* OWSScreenLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E2255B6DB9007E1867 /* OWSScreenLock.swift */; }; C38EF30D255B6DBF007E1867 /* OWSUnreadIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */; }; C38EF30E255B6DBF007E1867 /* FullTextSearcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E4255B6DB9007E1867 /* FullTextSearcher.swift */; }; - C38EF30F255B6DBF007E1867 /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */; }; C38EF313255B6DBF007E1867 /* OWSUnreadIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2E9255B6DBA007E1867 /* OWSUnreadIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38EF317255B6DBF007E1867 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */; }; C38EF31A255B6DBF007E1867 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */; }; @@ -1556,7 +1555,6 @@ C38EF2E2255B6DB9007E1867 /* OWSScreenLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSScreenLock.swift; path = "SignalUtilitiesKit/Screen Lock/OWSScreenLock.swift"; sourceTree = SOURCE_ROOT; }; C38EF2E3255B6DB9007E1867 /* OWSUnreadIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSUnreadIndicator.m; path = SignalUtilitiesKit/Messaging/OWSUnreadIndicator.m; sourceTree = SOURCE_ROOT; }; C38EF2E4255B6DB9007E1867 /* FullTextSearcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FullTextSearcher.swift; path = SignalUtilitiesKit/Messaging/FullTextSearcher.swift; sourceTree = SOURCE_ROOT; }; - C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppPreferences.swift; path = SignalUtilitiesKit/Database/AppPreferences.swift; sourceTree = SOURCE_ROOT; }; C38EF2E9255B6DBA007E1867 /* OWSUnreadIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSUnreadIndicator.h; path = SignalUtilitiesKit/Messaging/OWSUnreadIndicator.h; sourceTree = SOURCE_ROOT; }; C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProximityMonitoringManager.swift; path = SessionMessagingKit/Utilities/ProximityMonitoringManager.swift; sourceTree = SOURCE_ROOT; }; C38EF2ED255B6DBB007E1867 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisplayableText.swift; path = SignalUtilitiesKit/Utilities/DisplayableText.swift; sourceTree = SOURCE_ROOT; }; @@ -3109,7 +3107,6 @@ isa = PBXGroup; children = ( C379DCE82567330E0002D4EB /* Migrations */, - C38EF2E5255B6DB9007E1867 /* AppPreferences.swift */, C33FDBBA255A581600E217F9 /* OWSPrimaryStorage+keyFromIntLong.h */, C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */, C38EF3D2255B6DEE007E1867 /* ThreadViewHelper.h */, @@ -4409,7 +4406,6 @@ C38EF3FD255B6DF7007E1867 /* OWSTextView.m in Sources */, C38EF3C6255B6DE7007E1867 /* ImageEditorModel.swift in Sources */, C38EF317255B6DBF007E1867 /* DisplayableText.swift in Sources */, - C38EF30F255B6DBF007E1867 /* AppPreferences.swift in Sources */, C38EF3C3255B6DE7007E1867 /* ImageEditorTextItem.swift in Sources */, C33FDC7D255A582000E217F9 /* OWSDispatch.m in Sources */, C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */, diff --git a/SignalUtilitiesKit/Database/AppPreferences.swift b/SignalUtilitiesKit/Database/AppPreferences.swift deleted file mode 100644 index a0721dc63..000000000 --- a/SignalUtilitiesKit/Database/AppPreferences.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - -@objc -public class AppPreferences: NSObject { - // Never instantiate this class. - private override init() {} - - private static let collection = "AppPreferences" - - // MARK: - - - private static let hasDimissedFirstConversationCueKey = "hasDimissedFirstConversationCue" - - @objc - public static var hasDimissedFirstConversationCue: Bool { - get { - return getBool(key: hasDimissedFirstConversationCueKey) - } - set { - setBool(newValue, key: hasDimissedFirstConversationCueKey) - } - } - - // MARK: - - - private class func getBool(key: String, defaultValue: Bool = false) -> Bool { - return OWSPrimaryStorage.dbReadConnection().bool(forKey: key, inCollection: collection, defaultValue: defaultValue) - } - - private class func setBool(_ value: Bool, key: String) { - OWSPrimaryStorage.dbReadWriteConnection().setBool(value, forKey: key, inCollection: collection) - } -}