From 1dd06a5e6c21e756ad334be0f67f4b284c52f202 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 10 Oct 2016 16:02:09 -0400 Subject: [PATCH] Fix registration flow / Keep push tokens in sync * Separate registering an account from registering for push notifications * Allows us to complete registration without prompting user for notification settings. UX Changes ---------- * Automatically keep push tokens in sync on startup. Push tokens *can* change, though they rarely do. It happens more often for people switching between appstore/beta builds. fixes #1174 * Show alert with registration failure * add secret 8-tap debug log gesture to registration flow * Move registration to separate flow * don't see flash of inbox when first launching * show useful error messages when given wrong code / no code * remove background fetch We werent using it, but only relying on a side effect of it which is no longer necessary. Code Changes ------------ * More registration logging. * Install PromiseKit with carthage Our dependencies are not yet framework compatible, so we can't use cocoapods. * Merge preferences util "category" into superclass. The immediate reason for this is Swift interop was assuming optional types were not optional, and exploding when a value was nil. This is clearer anyway, since we were treating it like a subclass, and it was the only thing using the class anyway. * auto-genstrings now searches *.swift (and *.h, which was previously broken) for translateable strings. // FREEBIE --- .gitmodules | 3 + .travis.yml | 2 +- Cartfile | 1 + Cartfile.resolved | 1 + Carthage | 1 + Makefile | 10 +- Podfile.lock | 6 +- Signal.xcodeproj/project.pbxproj | 121 +++- .../xcshareddata/xcschemes/Signal.xcscheme | 9 +- .../xcshareddata/Signal.xcscmblueprint | 7 + Signal/Signal-Info.plist | 3 +- Signal/src/AppDelegate.h | 3 + Signal/src/AppDelegate.m | 71 +- Signal/src/Models/AccountManager.swift | 104 +++ Signal/src/Models/SyncPushTokensJob.swift | 84 +++ Signal/src/Signal-Bridging-Header.h | 9 + ...{Storyboard.storyboard => Main.storyboard} | 643 +----------------- .../src/Storyboards/Registration.storyboard | 593 ++++++++++++++++ .../processing/DesiredBufferDepthController.m | 4 +- Signal/src/environment/NotificationsManager.m | 8 +- Signal/src/environment/PreferencesUtil.h | 50 -- Signal/src/environment/PreferencesUtil.m | 152 ----- .../src/environment/PropertyListPreferences.h | 63 +- .../src/environment/PropertyListPreferences.m | 212 +++++- Signal/src/environment/VersionMigrations.m | 19 +- Signal/src/network/PushManager.h | 16 +- Signal/src/network/PushManager.m | 30 +- Signal/src/network/http/RPAPICall.h | 13 +- Signal/src/network/http/RPAPICall.m | 42 +- .../network/http/RPServerRequestsManager.h | 4 - .../network/http/RPServerRequestsManager.m | 24 +- Signal/src/phone/RPAccountManager.h | 25 +- Signal/src/phone/RPAccountManager.m | 133 ++-- Signal/src/util/OWSLogger.h | 20 + Signal/src/util/OWSLogger.m | 37 + .../AdvancedSettingsTableViewController.m | 63 +- .../CodeVerificationViewController.m | 285 ++++---- .../FingerprintViewController.m | 2 +- .../src/view controllers/InboxTableViewCell.m | 2 +- .../view controllers/MessagesViewController.m | 2 +- ...otificationSettingsOptionsViewController.m | 4 +- .../NotificationSettingsViewController.m | 2 +- .../PrivacySettingsTableViewController.m | 4 +- .../SettingsTableViewController.m | 2 +- .../view controllers/SignalsViewController.h | 2 +- .../view controllers/SignalsViewController.m | 98 ++- Signal/src/views/CopyableLabel.swift | 1 + Signal/test/Models/AccountManagerTest.swift | 202 ++++++ Signal/test/SignalTests-Bridging-Header.h | 4 + .../network/http/HttpRequestResponseTest.m | 4 +- Signal/translations/bin/auto-genstrings | 2 +- .../translations/en.lproj/Localizable.strings | Bin 60034 -> 60818 bytes 52 files changed, 1985 insertions(+), 1217 deletions(-) create mode 100644 .gitmodules create mode 100644 Cartfile create mode 100644 Cartfile.resolved create mode 160000 Carthage create mode 100644 Signal/src/Models/AccountManager.swift create mode 100644 Signal/src/Models/SyncPushTokensJob.swift rename Signal/src/Storyboard/{Storyboard.storyboard => Main.storyboard} (71%) create mode 100644 Signal/src/Storyboards/Registration.storyboard delete mode 100644 Signal/src/environment/PreferencesUtil.h delete mode 100644 Signal/src/environment/PreferencesUtil.m create mode 100644 Signal/src/util/OWSLogger.h create mode 100644 Signal/src/util/OWSLogger.m create mode 100644 Signal/test/Models/AccountManagerTest.swift create mode 100644 Signal/test/SignalTests-Bridging-Header.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..0f5af92bc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Carthage"] + path = Carthage + url = https://github.com/WhisperSystems/Signal-Carthage.git diff --git a/.travis.yml b/.travis.yml index 660a49237..a284e7e9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,5 +15,5 @@ before_install: install: travis_wait 30 pod install # OpenSSL takes a long time to compile -script: make +script: make ci diff --git a/Cartfile b/Cartfile new file mode 100644 index 000000000..e404e9ffc --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "mxcl/PromiseKit" diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 000000000..6f5e4b14d --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "mxcl/PromiseKit" "4.0.5" diff --git a/Carthage b/Carthage new file mode 160000 index 000000000..859dc35d6 --- /dev/null +++ b/Carthage @@ -0,0 +1 @@ +Subproject commit 859dc35d6a725b36d4f71c7cd774b12723790486 diff --git a/Makefile b/Makefile index a387dc817..e59ad1dc2 100644 --- a/Makefile +++ b/Makefile @@ -12,17 +12,19 @@ XCODE_BUILD = xcrun xcodebuild -workspace $(SCHEME).xcworkspace -scheme $(SCHEME default: test -test: pod_install retest +ci: build_dependencies test -pod_install: +build_dependencies: cd $(WORKING_DIR) && \ + git submodule update --init pod install + carthage build --platform iOS -build: pod_install +build: build_dependencies cd $(WORKING_DIR) && \ $(XCODE_BUILD) build | xcpretty -retest: optional_early_start_simulator +test: optional_early_start_simulator cd $(WORKING_DIR) && \ $(XCODE_BUILD) \ -destination '${BUILD_DESTINATION}' \ diff --git a/Podfile.lock b/Podfile.lock index f23fe77f6..44d7ce478 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -44,7 +44,7 @@ PODS: - Reachability (3.2) - SAMKeychain (1.5.2) - SCWaveformView (1.0.0) - - SignalServiceKit (0.3.0): + - SignalServiceKit (0.4.0): - '25519' - AFNetworking - AxolotlKit @@ -140,7 +140,7 @@ CHECKOUT OPTIONS: :commit: 03cde781234ade464dd26914d87e6e95afde1119 :git: https://github.com/WhisperSystems/JSQMessagesViewController.git SignalServiceKit: - :commit: 03f05f217cd8f058f4371831ca89a4a32f93d511 + :commit: 47cad611e5a14127200747667b7ed9f50e8337ce :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 41b57bb2fc292a814f758441a05243eb38457027 @@ -164,7 +164,7 @@ SPEC CHECKSUMS: Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 1865333198217411f35327e8da61b43de79b635b SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07 - SignalServiceKit: 8b115cfd63f9b814fa03fe61fd5d38ef9a548460 + SignalServiceKit: 5c3877241082a778c8c130e1fed17d0904085205 SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b66ca5c94..e1f14ad7d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -13,6 +13,9 @@ 450873C41D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */; }; 450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; }; 450873C81D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; }; + 451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* AccountManager.swift */; }; + 451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; }; + 451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; }; 4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; }; 452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; }; 452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; }; @@ -26,11 +29,13 @@ 45666F761D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F751D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m */; }; 45666F7B1D9C0533008FE134 /* OWSDatabaseMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F7A1D9C0533008FE134 /* OWSDatabaseMigration.m */; }; 45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */; }; + 456C38961DC7B882007536A7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 451DE9F11DC1585F00810E42 /* PromiseKit.framework */; }; 45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; 45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; 45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; }; 45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; }; 45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; }; + 458967111DC117CC00E9DD21 /* AccountManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */; }; 458E38311D6682450094BD24 /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */; }; 458E38341D66873D0094BD24 /* OWSLinkDeviceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38331D66873D0094BD24 /* OWSLinkDeviceViewController.m */; }; 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */; }; @@ -51,6 +56,9 @@ 45C681C81D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */; }; 45C681C91D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */; }; 45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; }; + 45CD81A61DBFF8FC004C9430 /* Registration.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CD81A51DBFF8FC004C9430 /* Registration.storyboard */; }; + 45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* AccountManager.swift */; }; + 45CD81F21DC03A22004C9430 /* OWSLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81F11DC03A22004C9430 /* OWSLogger.m */; }; 45EB32CF1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EB32CE1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m */; }; 45F2B1941D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */; }; 45F2B1971D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */; }; @@ -72,7 +80,6 @@ 76EB057A18170B33006006FC /* OWSContactsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB040918170B33006006FC /* OWSContactsManager.m */; }; 76EB058218170B33006006FC /* Environment.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041318170B33006006FC /* Environment.m */; }; 76EB058418170B33006006FC /* LocalizableText.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041518170B33006006FC /* LocalizableText.m */; }; - 76EB058618170B33006006FC /* PreferencesUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041718170B33006006FC /* PreferencesUtil.m */; }; 76EB058818170B33006006FC /* PropertyListPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041918170B33006006FC /* PropertyListPreferences.m */; }; 76EB058A18170B33006006FC /* Release.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041B18170B33006006FC /* Release.m */; }; 76EB058C18170B33006006FC /* DnsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB042018170B33006006FC /* DnsManager.m */; }; @@ -169,7 +176,7 @@ A1C32D5117A06544000A904E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4D17A0652C000A904E /* AddressBook.framework */; }; A507A3B11A6C60E300BEED0D /* InboxTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A507A3AF1A6C60E300BEED0D /* InboxTableViewCell.xib */; }; A547DD741A70A87800103EC7 /* DJWActionSheet+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = A547DD721A70A87800103EC7 /* DJWActionSheet+OWS.m */; }; - A5509ECA1A69AB8B00ABA4BC /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A5509EC91A69AB8B00ABA4BC /* Storyboard.storyboard */; }; + A5509ECA1A69AB8B00ABA4BC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A5509EC91A69AB8B00ABA4BC /* Main.storyboard */; }; A5509ECD1A69B1D600ABA4BC /* CountryCodeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */; }; A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */; }; A5E9D4BB1A65FAD800E4481C /* TSVideoAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = A5E9D4B91A65FAD800E4481C /* TSVideoAttachmentAdapter.m */; }; @@ -281,7 +288,6 @@ B660F71B1C29988E00687D6E /* Environment.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041318170B33006006FC /* Environment.m */; }; B660F71C1C29988E00687D6E /* DebugLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C93C4D199567AD00EDF894 /* DebugLogger.m */; }; B660F71D1C29988E00687D6E /* LocalizableText.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041518170B33006006FC /* LocalizableText.m */; }; - B660F71E1C29988E00687D6E /* PreferencesUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041718170B33006006FC /* PreferencesUtil.m */; }; B660F71F1C29988E00687D6E /* PropertyListPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041918170B33006006FC /* PropertyListPreferences.m */; }; B660F7201C29988E00687D6E /* Release.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB041B18170B33006006FC /* Release.m */; }; B660F7211C29988E00687D6E /* SignalKeyingStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B1013B196D213F007E3930 /* SignalKeyingStorage.m */; }; @@ -531,6 +537,8 @@ 450873C51D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIncomingMessageCollectionViewCell.h; sourceTree = ""; }; 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIncomingMessageCollectionViewCell.m; sourceTree = ""; }; 450873C91D9D86F4006B54F2 /* OWSExpirableMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirableMessageView.h; sourceTree = ""; }; + 451DE9F11DC1585F00810E42 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/iOS/PromiseKit.framework; sourceTree = ""; }; + 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncPushTokensJob.swift; sourceTree = ""; }; 4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; }; 4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageEditing.h; sourceTree = ""; }; 452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsTableViewController.h; sourceTree = ""; }; @@ -560,6 +568,8 @@ 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = ""; }; 45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactAvatarBuilder.h; sourceTree = ""; }; 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactAvatarBuilder.m; sourceTree = ""; }; + 4589670F1DC117CC00E9DD21 /* SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SignalTests-Bridging-Header.h"; sourceTree = ""; }; + 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AccountManagerTest.swift; path = Models/AccountManagerTest.swift; sourceTree = ""; }; 458E382F1D6682450094BD24 /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = ""; }; 458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = ""; }; 458E38321D66873D0094BD24 /* OWSLinkDeviceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkDeviceViewController.h; sourceTree = ""; }; @@ -585,6 +595,10 @@ 45C681C21D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisplayedMessageCollectionViewCell.m; sourceTree = ""; }; 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSDisplayedMessageCollectionViewCell.xib; sourceTree = ""; }; 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = "Launch Screen.storyboard"; path = "Signal/src/util/Launch Screen.storyboard"; sourceTree = SOURCE_ROOT; }; + 45CD81A51DBFF8FC004C9430 /* Registration.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Registration.storyboard; path = Storyboards/Registration.storyboard; sourceTree = ""; }; + 45CD81EE1DC030E7004C9430 /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; + 45CD81F01DC03A22004C9430 /* OWSLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLogger.h; sourceTree = ""; }; + 45CD81F11DC03A22004C9430 /* OWSLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSLogger.m; sourceTree = ""; }; 45E282DE1D08E67800ADD4C8 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = translations/gl.lproj/Localizable.strings; sourceTree = ""; }; 45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = ""; }; 45EB32CD1D7465C900735B2E /* OWSLinkedDevicesTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkedDevicesTableViewController.h; sourceTree = ""; }; @@ -622,8 +636,6 @@ 76EB041318170B33006006FC /* Environment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Environment.m; sourceTree = ""; }; 76EB041418170B33006006FC /* LocalizableText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalizableText.h; sourceTree = ""; }; 76EB041518170B33006006FC /* LocalizableText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalizableText.m; sourceTree = ""; }; - 76EB041618170B33006006FC /* PreferencesUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesUtil.h; sourceTree = ""; }; - 76EB041718170B33006006FC /* PreferencesUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesUtil.m; sourceTree = ""; }; 76EB041818170B33006006FC /* PropertyListPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyListPreferences.h; sourceTree = ""; }; 76EB041918170B33006006FC /* PropertyListPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PropertyListPreferences.m; sourceTree = ""; }; 76EB041A18170B33006006FC /* Release.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Release.h; sourceTree = ""; }; @@ -811,7 +823,7 @@ A507A3AF1A6C60E300BEED0D /* InboxTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = InboxTableViewCell.xib; path = "Signal/src/view controllers/InboxTableViewCell.xib"; sourceTree = SOURCE_ROOT; }; A547DD721A70A87800103EC7 /* DJWActionSheet+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DJWActionSheet+OWS.m"; path = "util/DJWActionSheet+OWS.m"; sourceTree = ""; }; A547DD731A70A87800103EC7 /* DJWActionSheet+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DJWActionSheet+OWS.h"; path = "util/DJWActionSheet+OWS.h"; sourceTree = ""; }; - A5509EC91A69AB8B00ABA4BC /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Storyboard.storyboard; path = Storyboard/Storyboard.storyboard; sourceTree = ""; }; + A5509EC91A69AB8B00ABA4BC /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Storyboard/Main.storyboard; sourceTree = ""; }; A5509ECB1A69B1D600ABA4BC /* CountryCodeTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryCodeTableViewCell.h; sourceTree = ""; }; A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryCodeTableViewCell.m; sourceTree = ""; }; A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ShowGroupMembersViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -1116,6 +1128,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 456C38961DC7B882007536A7 /* PromiseKit.framework in Frameworks */, 4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */, B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */, B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */, @@ -1199,6 +1212,8 @@ 45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */, 45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */, 45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */, + 45CD81EE1DC030E7004C9430 /* AccountManager.swift */, + 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */, ); path = Models; sourceTree = ""; @@ -1207,6 +1222,7 @@ isa = PBXGroup; children = ( 458E38391D6699FA0094BD24 /* OWSDeviceProvisioningURLParserTest.m */, + 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */, ); name = Models; sourceTree = ""; @@ -1228,6 +1244,16 @@ name = Observers; sourceTree = ""; }; + 45CD81A41DBFF8CF004C9430 /* Storyboards */ = { + isa = PBXGroup; + children = ( + 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */, + A5509EC91A69AB8B00ABA4BC /* Main.storyboard */, + 45CD81A51DBFF8FC004C9430 /* Registration.storyboard */, + ); + name = Storyboards; + sourceTree = ""; + }; 70B8009F190C529C0042E3F0 /* Products */ = { isa = PBXGroup; children = ( @@ -1261,7 +1287,7 @@ 76EB03C118170B33006006FC /* src */ = { isa = PBXGroup; children = ( - A5509EC91A69AB8B00ABA4BC /* Storyboard.storyboard */, + 45CD81A41DBFF8CF004C9430 /* Storyboards */, 76EB03C218170B33006006FC /* AppDelegate.h */, 76EB03C318170B33006006FC /* AppDelegate.m */, 76EB03D918170B33006006FC /* audio */, @@ -1338,8 +1364,6 @@ B6C93C4D199567AD00EDF894 /* DebugLogger.m */, 76EB041418170B33006006FC /* LocalizableText.h */, 76EB041518170B33006006FC /* LocalizableText.m */, - 76EB041618170B33006006FC /* PreferencesUtil.h */, - 76EB041718170B33006006FC /* PreferencesUtil.m */, 76EB041818170B33006006FC /* PropertyListPreferences.h */, 76EB041918170B33006006FC /* PropertyListPreferences.m */, 76EB041A18170B33006006FC /* Release.h */, @@ -1718,6 +1742,8 @@ B62F5E0F1C2980B4000D370C /* NSData+ows_StripToken.m */, 45666F541D9B2827008FE134 /* OWSScrubbingLogFormatter.h */, 45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */, + 45CD81F01DC03A22004C9430 /* OWSLogger.h */, + 45CD81F11DC03A22004C9430 /* OWSLogger.m */, ); path = util; sourceTree = ""; @@ -1807,7 +1833,6 @@ isa = PBXGroup; children = ( A507A3AF1A6C60E300BEED0D /* InboxTableViewCell.xib */, - 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */, ); path = xibs; sourceTree = ""; @@ -1903,6 +1928,7 @@ B660F66C1C29867F00687D6E /* test */ = { isa = PBXGroup; children = ( + 4589670F1DC117CC00E9DD21 /* SignalTests-Bridging-Header.h */, 458E38381D6699110094BD24 /* Models */, 459C3F0E1C9B3A20003ACF51 /* TSMessageAdapters */, B660F66D1C29867F00687D6E /* audio */, @@ -2167,6 +2193,7 @@ D221A08C169C9E5E00537ABF /* Frameworks */ = { isa = PBXGroup; children = ( + 451DE9F11DC1585F00810E42 /* PromiseKit.framework */, 4520D8D41D417D8E00123472 /* Photos.framework */, B6B226961BE4B7D200860F4D /* ContactsUI.framework */, B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */, @@ -2396,6 +2423,7 @@ D221A087169C9E5E00537ABF /* Resources */, 59C9DBA462715B5C999FFB02 /* [CP] Embed Pods Frameworks */, 3465F381B1856CC06933B3A8 /* [CP] Copy Pods Resources */, + 451DE9EE1DC1546A00810E42 /* [Carthage] Copy Frameworks */, ); buildRules = ( ); @@ -2418,6 +2446,7 @@ D221A0A7169C9E5F00537ABF /* Resources */, B4E9B04E862FB64FC9A8F79B /* [CP] Embed Pods Frameworks */, F76686434770E2BBEBD9665A /* [CP] Copy Pods Resources */, + 451DE9FB1DC18D4500810E42 /* [Carthage] Copy Frameworks */, ); buildRules = ( ); @@ -2463,6 +2492,7 @@ }; D221A0A9169C9E5F00537ABF = { DevelopmentTeam = U68MSDN6DR; + LastSwiftMigration = 0800; TestTargetID = D221A088169C9E5E00537ABF; }; }; @@ -2559,7 +2589,7 @@ files = ( AD41D7B61A6F6F0600241130 /* play_button@2x.png in Resources */, AD83FF3F1A73426500B5C81A /* audio_pause_button_blue.png in Resources */, - A5509ECA1A69AB8B00ABA4BC /* Storyboard.storyboard in Resources */, + A5509ECA1A69AB8B00ABA4BC /* Main.storyboard in Resources */, A507A3B11A6C60E300BEED0D /* InboxTableViewCell.xib in Resources */, 45F2B1971D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib in Resources */, AD83FF421A73426500B5C81A /* audio_play_button.png in Resources */, @@ -2570,6 +2600,7 @@ AD83FF441A73426500B5C81A /* audio_pause_button.png in Resources */, B6F509971AA53F760068F56A /* Localizable.strings in Resources */, AD41D7B51A6F6F0600241130 /* play_button.png in Resources */, + 45CD81A61DBFF8FC004C9430 /* Registration.storyboard in Resources */, B633C59D1A1D190B0059AC12 /* endcall@2x.png in Resources */, FC5CDF391A3393DD00B47253 /* error_white@2x.png in Resources */, B633C5D21A1D190B0059AC12 /* savephoto@2x.png in Resources */, @@ -2648,6 +2679,36 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Signal/Pods-Signal-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 451DE9EE1DC1546A00810E42 /* [Carthage] Copy Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/PromiseKit.framework", + ); + name = "[Carthage] Copy Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; + 451DE9FB1DC18D4500810E42 /* [Carthage] Copy Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/PromiseKit.framework", + ); + name = "[Carthage] Copy Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; 59C9DBA462715B5C999FFB02 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2729,17 +2790,18 @@ 76EB062418170B33006006FC /* PriorityQueue.m in Sources */, B6BADBE71B88D1AC0086A80D /* LockInteractionController.m in Sources */, 76EB061A18170B33006006FC /* DiscardingLog.m in Sources */, + 45CD81F21DC03A22004C9430 /* OWSLogger.m in Sources */, 76EB05AC18170B33006006FC /* SrtpSocket.m in Sources */, FCB11D931A12A4AA002F93FB /* FullImageViewController.m in Sources */, B60C16651988999D00E97A6C /* VersionMigrations.m in Sources */, B97940271832BD2400BD66CB /* UIUtil.m in Sources */, 4CE0E3771B954546007210CF /* TSAnimatedAdapter.m in Sources */, 76EB05BE18170B33006006FC /* ConfirmPacket.m in Sources */, - 76EB058618170B33006006FC /* PreferencesUtil.m in Sources */, 76EB05A818170B33006006FC /* RtpSocket.m in Sources */, E197B61818BBEC1A00F073E5 /* RemoteIOAudio.m in Sources */, B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */, 76EB059418170B33006006FC /* HttpManager.m in Sources */, + 45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */, 76EB05EC18170B33006006FC /* CallState.m in Sources */, 76EB05D218170B33006006FC /* ZrtpInitiator.m in Sources */, 76EB05E018170B33006006FC /* NetworkStream.m in Sources */, @@ -2861,6 +2923,7 @@ 76EB05CE18170B33006006FC /* ZrtpHandshakeResult.m in Sources */, 45EB32CF1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m in Sources */, B63761EE19E1FBE8005735D1 /* HttpRequestUtil.m in Sources */, + 451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */, 76EB05B618170B33006006FC /* MasterSecret.m in Sources */, 76EB05F418170B33006006FC /* CallConnectResult.m in Sources */, FCFD256F1A151BCB00F4C644 /* NewGroupViewController.m in Sources */, @@ -2940,6 +3003,7 @@ B660F7131C29988E00687D6E /* SoundPlayer.m in Sources */, B660F7141C29988E00687D6E /* RecentCall.m in Sources */, B660F7151C29988E00687D6E /* RecentCallManager.m in Sources */, + 451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */, B660F7161C29988E00687D6E /* GroupContactsResult.m in Sources */, B660F7171C29988E00687D6E /* OWSContactsManager.m in Sources */, B660F7181C29988E00687D6E /* CryptoTools.m in Sources */, @@ -2948,7 +3012,6 @@ B660F71B1C29988E00687D6E /* Environment.m in Sources */, B660F71C1C29988E00687D6E /* DebugLogger.m in Sources */, B660F71D1C29988E00687D6E /* LocalizableText.m in Sources */, - B660F71E1C29988E00687D6E /* PreferencesUtil.m in Sources */, B660F71F1C29988E00687D6E /* PropertyListPreferences.m in Sources */, B660F7201C29988E00687D6E /* Release.m in Sources */, B660F7211C29988E00687D6E /* SignalKeyingStorage.m in Sources */, @@ -2956,6 +3019,7 @@ B660F7231C29988E00687D6E /* DnsManager.m in Sources */, B660F7241C29988E00687D6E /* HostNameEndPoint.m in Sources */, B660F7251C29988E00687D6E /* IgnoredPacketFailure.m in Sources */, + 458967111DC117CC00E9DD21 /* AccountManagerTest.swift in Sources */, B660F7261C29988E00687D6E /* UnrecognizedRequestFailure.m in Sources */, B660F7271C29988E00687D6E /* RPAPICall.m in Sources */, B660F7281C29988E00687D6E /* RPServerRequestsManager.m in Sources */, @@ -2982,6 +3046,7 @@ B660F73A1C29988E00687D6E /* EC25KeyAgreementParticipant.m in Sources */, B660F73B1C29988E00687D6E /* EC25KeyAgreementProtocol.m in Sources */, B660F73C1C29988E00687D6E /* EvpKeyAgreement.m in Sources */, + 451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */, B660F73D1C29988E00687D6E /* HashChain.m in Sources */, B660F73E1C29988E00687D6E /* MasterSecret.m in Sources */, B660F73F1C29988E00687D6E /* NegotiationFailed.m in Sources */, @@ -3311,7 +3376,8 @@ DEVELOPMENT_TEAM = U68MSDN6DR; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)\"", + "$(SRCROOT)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -3352,7 +3418,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TEST_AFTER_BUILD = YES; - VALID_ARCHS = "arm64 armv7 armv7s i386"; + VALID_ARCHS = "arm64 armv7 armv7s"; WRAPPER_EXTENSION = app; }; name = Debug; @@ -3369,7 +3435,8 @@ DEVELOPMENT_TEAM = U68MSDN6DR; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)\"", + "$(SRCROOT)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -3410,7 +3477,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Signal/src/Signal-Bridging-Header.h"; SWIFT_VERSION = 3.0; TEST_AFTER_BUILD = YES; - VALID_ARCHS = "arm64 armv7 armv7s i386"; + VALID_ARCHS = "arm64 armv7 armv7s"; WRAPPER_EXTENSION = app; }; name = "App Store Release"; @@ -3422,13 +3489,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Signal.app/Signal"; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; DEFINES_MODULE = YES; FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(SRCROOT)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); GCC_GENERATE_TEST_COVERAGE_FILES = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -3451,6 +3518,7 @@ ); INFOPLIST_FILE = "Signal/test/Supporting Files/SignalTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", @@ -3463,6 +3531,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.whispersystems.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = SignalTests; PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Signal/test/SignalTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUNDLE_LOADER)"; VALID_ARCHS = "arm64 armv7s armv7 i386 x86_64"; }; @@ -3475,13 +3546,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Signal.app/Signal"; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; DEFINES_MODULE = YES; FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(SRCROOT)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); GCC_GENERATE_TEST_COVERAGE_FILES = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -3504,6 +3575,7 @@ ); INFOPLIST_FILE = "Signal/test/Supporting Files/SignalTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", @@ -3516,6 +3588,9 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.whispersystems.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = SignalTests; PROVISIONING_PROFILE = "c15eac58-5aa7-4660-b874-b9f7ed3dab70"; + SWIFT_OBJC_BRIDGING_HEADER = "Signal/test/SignalTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUNDLE_LOADER)"; VALID_ARCHS = "arm64 armv7s armv7 i386 x86_64"; }; diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme index 739548326..4f3550ae9 100644 --- a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme +++ b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme @@ -28,7 +28,7 @@ buildForAnalyzing = "YES"> @@ -93,6 +93,13 @@ ReferencedContainer = "container:Signal.xcodeproj"> + + + + diff --git a/Signal.xcworkspace/xcshareddata/Signal.xcscmblueprint b/Signal.xcworkspace/xcshareddata/Signal.xcscmblueprint index 0b164cb0a..8a4f9f914 100644 --- a/Signal.xcworkspace/xcshareddata/Signal.xcscmblueprint +++ b/Signal.xcworkspace/xcshareddata/Signal.xcscmblueprint @@ -5,6 +5,7 @@ }, "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { "ABB939127996C66F7E852A780552ADEEF03C6B13" : 0, + "8176314449001F06FB0E5B588C62133EAA2FE911" : 9223372036854775807, "37054CE35CE656680D6FFFA9EE19249E0D149C5E" : 0, "5D79A077E31B3FE97A3C6613CBFFDD71C314D14C" : 0, "90530B99EB0008E7A50951FDFBE02169118FA649" : 0, @@ -13,6 +14,7 @@ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "D0F297E7-A82D-4657-A941-96B268F80ABC", "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { "ABB939127996C66F7E852A780552ADEEF03C6B13" : "SocketRocket\/", + "8176314449001F06FB0E5B588C62133EAA2FE911" : "Signal-iOS\/Carthage\/", "37054CE35CE656680D6FFFA9EE19249E0D149C5E" : "SignalServiceKit\/", "5D79A077E31B3FE97A3C6613CBFFDD71C314D14C" : "Signal-iOS\/", "90530B99EB0008E7A50951FDFBE02169118FA649" : "JSQMessagesViewController\/", @@ -32,6 +34,11 @@ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "5D79A077E31B3FE97A3C6613CBFFDD71C314D14C" }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:michaelkirk\/Signal-Carthage.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8176314449001F06FB0E5B588C62133EAA2FE911" + }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:WhisperSystems\/JSQMessagesViewController.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 2bc1ec4ac..0420b14bd 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -38,7 +38,7 @@ CFBundleVersion - 26315 + 2.6.4.2 ITSAppUsesNonExemptEncryption LOGS_EMAIL @@ -93,7 +93,6 @@ UIBackgroundModes audio - fetch remote-notification voip diff --git a/Signal/src/AppDelegate.h b/Signal/src/AppDelegate.h index 351edddf9..e02ba51dd 100644 --- a/Signal/src/AppDelegate.h +++ b/Signal/src/AppDelegate.h @@ -2,6 +2,9 @@ #import "SignalsViewController.h" +extern NSString *const AppDelegateStoryboardMain; +extern NSString *const AppDelegateStoryboardRegistration; + @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 19d677e16..25408c41b 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -7,20 +7,26 @@ #import "NotificationsManager.h" #import "OWSContactsManager.h" #import "OWSStaleNotificationObserver.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "PushManager.h" +#import "RPAccountManager.h" #import "Release.h" -#import "TSAccountManager.h" +#import "Signal-Swift.h" #import "TSMessagesManager.h" #import "TSPreKeyManager.h" #import "TSSocketManager.h" #import "TextSecureKitEnv.h" #import "VersionMigrations.h" +#import +#import #import #import #import +#import + +NSString *const AppDelegateStoryboardMain = @"Main"; +NSString *const AppDelegateStoryboardRegistration = @"Registration"; -static NSString *const kStoryboardName = @"Storyboard"; static NSString *const kInitialViewControllerIdentifier = @"UserInitialViewController"; static NSString *const kURLSchemeSGNLKey = @"sgnl"; static NSString *const kURLHostVerifyPrefix = @"verify"; @@ -80,13 +86,15 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self setupTSKitEnv]; - UIStoryboard *storyboard = [UIStoryboard storyboardWithName:kStoryboardName bundle:[NSBundle mainBundle]]; - UIViewController *viewController = - [storyboard instantiateViewControllerWithIdentifier:kInitialViewControllerIdentifier]; - - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = viewController; + UIStoryboard *storyboard; + if ([TSAccountManager isRegistered]) { + storyboard = [UIStoryboard storyboardWithName:AppDelegateStoryboardMain bundle:[NSBundle mainBundle]]; + } else { + storyboard = [UIStoryboard storyboardWithName:AppDelegateStoryboardRegistration bundle:[NSBundle mainBundle]]; + } + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.rootViewController = [storyboard instantiateInitialViewController]; [self.window makeKeyAndVisible]; [VersionMigrations performUpdateCheck]; // this call must be made after environment has been initialized because in @@ -101,9 +109,10 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self prepareScreenProtection]; - // Avoid blocking app launch by putting all possible DB access in async thread. + // At this point, potentially lengthy DB locking migrations could be running. + // Avoid blocking app launch by putting all further possible DB access in async thread. UIApplicationState launchState = application.applicationState; - [TSAccountManager runIfRegistered:^{ + [[TSAccountManager sharedInstance] ifRegistered:YES runAsync:^{ if (launchState == UIApplicationStateInactive) { DDLogWarn(@"The app was launched from inactive"); [TSSocketManager becomeActiveFromForeground]; @@ -114,14 +123,34 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; DDLogWarn(@"The app was launched in an unknown way"); } - [[PushManager sharedManager] validateUserNotificationSettings]; + OWSAccountManager *accountManager = + [[OWSAccountManager alloc] initWithTextSecureAccountManager:[TSAccountManager sharedInstance] + redPhoneAccountManager:[RPAccountManager sharedInstance]]; + + [OWSSyncPushTokensJob runWithPushManager:[PushManager sharedManager] + accountManager:accountManager + preferences:[Environment preferences]].then(^{ + DDLogDebug(@"%@ Successfully ran syncPushTokensJob.", self.tag); + }).catch(^(NSError *_Nonnull error) { + DDLogDebug(@"%@ Failed to run syncPushTokensJob with error: %@", self.tag, error); + }); + [TSPreKeyManager refreshPreKeys]; // Clean up any messages that expired since last launch. [[[OWSDisappearingMessagesJob alloc] initWithStorageManager:[TSStorageManager sharedManager]] run]; + [AppStoreRating setupRatingLibrary]; + }]; + + [[TSAccountManager sharedInstance] ifRegistered:NO runAsync:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:[Pastelog class] + action:@selector(submitLogs)]; + gesture.numberOfTapsRequired = 8; + [self.window addGestureRecognizer:gesture]; + }); }]; - [AppStoreRating setupRatingLibrary]; return YES; } @@ -202,12 +231,14 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; return; } - [TSAccountManager runIfRegistered:^{ - // We're double checking that the app is active, to be sure since we can't verify in production env due to code - // signing. - [TSSocketManager becomeActiveFromForeground]; - [[Environment getCurrent].contactsManager verifyABPermission]; - }]; + [[TSAccountManager sharedInstance] ifRegistered:YES + runAsync:^{ + // We're double checking that the app is active, to be sure since we + // can't verify in production env due to code + // signing. + [TSSocketManager becomeActiveFromForeground]; + [[Environment getCurrent].contactsManager verifyABPermission]; + }]; [self removeScreenProtection]; } @@ -376,6 +407,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; return NO; } +#pragma mark - Logging + + (NSString *)tag { return [NSString stringWithFormat:@"[%@]", self.class]; diff --git a/Signal/src/Models/AccountManager.swift b/Signal/src/Models/AccountManager.swift new file mode 100644 index 000000000..17d13facb --- /dev/null +++ b/Signal/src/Models/AccountManager.swift @@ -0,0 +1,104 @@ +// Created by Michael Kirk on 10/25/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +import Foundation +import PromiseKit + +@objc(OWSAccountManager) +class AccountManager : NSObject { + let TAG = "[AccountManager]" + let textSecureAccountManager: TSAccountManager + let redPhoneAccountManager: RPAccountManager + + required init(textSecureAccountManager:TSAccountManager, redPhoneAccountManager:RPAccountManager) { + self.textSecureAccountManager = textSecureAccountManager + self.redPhoneAccountManager = redPhoneAccountManager + } + + @objc func register(verificationCode: String) -> AnyPromise { + return AnyPromise(register(verificationCode: verificationCode)); + } + + func register(verificationCode: String) -> Promise { + return firstly { + Promise { fulfill, reject in + if verificationCode.characters.count == 0 { + let error = OWSErrorWithCodeDescription(.userError, + NSLocalizedString("REGISTRATION_ERROR_BLANK_VERIFICATION_CODE", + comment: "alert body during registration")) + reject(error) + } + fulfill() + } + }.then { + Logger.debug("\(self.TAG) verification code looks well formed."); + return self.registerForTextSecure(verificationCode: verificationCode) + }.then { + Logger.debug("\(self.TAG) successfully registered for TextSecure") + return self.fetchRedPhoneToken() + }.then { (redphoneToken: String) in + Logger.debug("\(self.TAG) successfully fetched redPhone token") + return self.registerForRedPhone(tsToken:redphoneToken) + }.then { + Logger.debug("\(self.TAG) successfully registered with RedPhone") + } + } + + func updatePushTokens(pushToken: String, voipToken: String) -> Promise { + return firstly { + return self.updateTextSecurePushTokens(pushToken: pushToken, voipToken: voipToken) + }.then { + Logger.info("\(self.TAG) Successfully updated text secure push tokens.") + // TODO should be possible to do these in parallel. + // We want to make sure that either can complete independently of the other. + return self.updateRedPhonePushTokens(pushToken:pushToken, voipToken:voipToken) + }.then { + Logger.info("\(self.TAG) Successfully updated red phone push tokens.") + return Promise { fulfill, reject in + fulfill(); + } + } + } + + private func updateTextSecurePushTokens(pushToken: String, voipToken: String) -> Promise { + return Promise { fulfill, reject in + self.textSecureAccountManager.registerForPushNotifications(pushToken:pushToken, + voipToken:voipToken, + success:fulfill, + failure:reject) + } + } + + private func updateRedPhonePushTokens(pushToken: String, voipToken: String) -> Promise { + return Promise { fulfill, reject in + self.redPhoneAccountManager.registerForPushNotifications(pushToken:pushToken, + voipToken:voipToken, + success:fulfill, + failure:reject) + } + } + + private func registerForTextSecure(verificationCode: String) -> Promise { + return Promise { fulfill, reject in + self.textSecureAccountManager.verifyAccount(withCode:verificationCode, + success:fulfill, + failure:reject) + } + } + + private func fetchRedPhoneToken() -> Promise { + return Promise { fulfill, reject in + self.textSecureAccountManager.obtainRPRegistrationToken(success:fulfill, + failure:reject) + + } + } + + private func registerForRedPhone(tsToken: String) -> Promise { + return Promise { fulfill, reject in + self.redPhoneAccountManager.register(withTsToken:tsToken, + success:fulfill, + failure:reject) + } + } +} diff --git a/Signal/src/Models/SyncPushTokensJob.swift b/Signal/src/Models/SyncPushTokensJob.swift new file mode 100644 index 000000000..103f3189f --- /dev/null +++ b/Signal/src/Models/SyncPushTokensJob.swift @@ -0,0 +1,84 @@ +// Created by Michael Kirk on 10/26/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +import Foundation +import PromiseKit + +@objc(OWSSyncPushTokensJob) +class SyncPushTokensJob : NSObject { + let TAG = "[SyncPushTokensJob]" + let pushManager: PushManager + let accountManager: AccountManager + let preferences: PropertyListPreferences + var uploadOnlyIfStale = true + + required init(pushManager: PushManager, accountManager: AccountManager, preferences: PropertyListPreferences) { + self.pushManager = pushManager + self.accountManager = accountManager + self.preferences = preferences + } + + @objc class func run(pushManager: PushManager, accountManager: AccountManager, preferences: PropertyListPreferences) -> AnyPromise { + let job = self.init(pushManager: pushManager, accountManager: accountManager, preferences: preferences) + return AnyPromise(job.run()) + } + + @objc func run() -> AnyPromise { + return AnyPromise(run()) + } + + func run() -> Promise { + Logger.debug("\(TAG) Starting.") + + // Required to potentially prompt user for notifications settings + // before `requestPushTokens` will return. + self.pushManager.validateUserNotificationSettings() + + return self.requestPushTokens().then { (pushToken: String, voipToken: String) in + + if self.preferences.getPushToken() == pushToken && self.preferences.getVoipToken() == voipToken { + Logger.debug("\(self.TAG) push tokens are already up to date.") + if (self.uploadOnlyIfStale) { + return Promise { fulfill, reject in fulfill(); } + } else { + Logger.debug("\(self.TAG) proceeding with upload even though tokens aren't stale") + } + } else { + Logger.debug("\(self.TAG) push tokens changed.") + } + + Logger.debug("\(self.TAG) Sending new tokens to account servers.") + return self.accountManager.updatePushTokens(pushToken:pushToken, voipToken:voipToken).then { + Logger.info("\(self.TAG) Recording tokens locally.") + return self.recordNewPushTokens(pushToken:pushToken, voipToken:voipToken); + } + } + } + + private func requestPushTokens() -> Promise<(pushToken: String, voipToken: String)> { + return Promise { fulfill, reject in + self.pushManager.requestPushToken( + success: { (pushToken: String, voipToken: String) in + fulfill((pushToken:pushToken, voipToken:voipToken)) + }, + failure: reject + ); + } + } + + private func recordNewPushTokens(pushToken: String, voipToken: String) -> Promise { + Logger.info("\(TAG) Recording new push tokens.") + + if (pushToken != self.preferences.getPushToken()) { + Logger.info("\(TAG) Recording new plain push token") + self.preferences.setPushToken(pushToken); + } + + if (voipToken != self.preferences.getVoipToken()) { + Logger.info("\(TAG) Recording new voip token") + self.preferences.setVoipToken(voipToken); + } + + return Promise { fulfill, reject in fulfill(); } + } +} diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index e11d920b1..c724768da 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -1,3 +1,12 @@ // // Use this file to import your target's public headers that you would like to expose to Swift. // +#import + +#import "OWSLogger.h" +#import "PropertyListPreferences.h" +#import "PushManager.h" +#import "RPAccountManager.h" +#import +#import +#import diff --git a/Signal/src/Storyboard/Storyboard.storyboard b/Signal/src/Storyboard/Main.storyboard similarity index 71% rename from Signal/src/Storyboard/Storyboard.storyboard rename to Signal/src/Storyboard/Main.storyboard index aa259390a..9f6ebc085 100644 --- a/Signal/src/Storyboard/Storyboard.storyboard +++ b/Signal/src/Storyboard/Main.storyboard @@ -81,7 +81,6 @@ - @@ -115,7 +114,7 @@ - + @@ -353,11 +352,11 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1120,250 +820,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1381,7 +837,7 @@ - + @@ -1396,7 +852,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m b/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m index 9d16f00e3..36ccaa1e2 100644 --- a/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m +++ b/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m @@ -1,6 +1,6 @@ -#import "AudioPacker.h" #import "DesiredBufferDepthController.h" -#import "PreferencesUtil.h" +#import "AudioPacker.h" +#import "PropertyListPreferences.h" #import "Util.h" #define MAX_DESIRED_FRAME_DELAY 12 diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index c4ab94bd2..2052c4164 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -6,16 +6,16 @@ // Copyright © 2015 Open Whisper Systems. All rights reserved. // +#import "NotificationsManager.h" +#import "Environment.h" +#import "PropertyListPreferences.h" +#import "PushManager.h" #import #import #import #import #import #import -#import "Environment.h" -#import "NotificationsManager.h" -#import "PreferencesUtil.h" -#import "PushManager.h" @interface NotificationsManager () diff --git a/Signal/src/environment/PreferencesUtil.h b/Signal/src/environment/PreferencesUtil.h deleted file mode 100644 index 79364de49..000000000 --- a/Signal/src/environment/PreferencesUtil.h +++ /dev/null @@ -1,50 +0,0 @@ -#import "PropertyListPreferences.h" - -typedef NS_ENUM(NSUInteger, NotificationType) { - NotificationNoNameNoPreview, - NotificationNameNoPreview, - NotificationNamePreview, -}; - -typedef NS_ENUM(NSUInteger, TSImageQuality) { - TSImageQualityUncropped = 1, - TSImageQualityHigh = 2, - TSImageQualityMedium = 3, - TSImageQualityLow = 4 -}; - -@class PhoneNumber; - -@interface PropertyListPreferences (PropertyUtil) - -- (NSTimeInterval)getCachedOrDefaultDesiredBufferDepth; -- (void)setCachedDesiredBufferDepth:(double)value; - -- (BOOL)getHasSentAMessage; -- (void)setHasSentAMessage:(BOOL)enabled; - -- (BOOL)getHasArchivedAMessage; -- (void)setHasArchivedAMessage:(BOOL)enabled; - -- (BOOL)loggingIsEnabled; -- (void)setLoggingEnabled:(BOOL)flag; - -- (BOOL)screenSecurityIsEnabled; -- (void)setScreenSecurity:(BOOL)flag; - -- (NotificationType)notificationPreviewType; -- (void)setNotificationPreviewType:(NotificationType)type; -- (NSString *)nameForNotificationPreviewType:(NotificationType)notificationType; - -- (BOOL)soundInForeground; -- (void)setSoundInForeground:(BOOL)enabled; - -- (BOOL)hasRegisteredVOIPPush; -- (void)setHasRegisteredVOIPPush:(BOOL)enabled; - -- (TSImageQuality)imageUploadQuality; - -- (NSString *)lastRanVersion; -- (NSString *)setAndGetCurrentVersion; - -@end diff --git a/Signal/src/environment/PreferencesUtil.m b/Signal/src/environment/PreferencesUtil.m deleted file mode 100644 index e5742f2e2..000000000 --- a/Signal/src/environment/PreferencesUtil.m +++ /dev/null @@ -1,152 +0,0 @@ -#import "Constraints.h" -#import "PreferencesUtil.h" - -#define CALL_STREAM_DES_BUFFER_LEVEL_KEY @"CallStreamDesiredBufferLevel" -#define DEFAULT_CALL_STREAM_DES_BUFFER_LEVEL 0.5 -#define SCREEN_SECURITY_KEY @"Screen Security Key" -#define DEBUG_IS_ENABLED_KEY @"Debugging Log Enabled Key" -#define NOTIFICATION_PREVIEW_TYPE_KEY @"Notification Preview Type Key" -#define HAS_SENT_A_MESSAGE_KEY @"User has sent a message" // TODO remove? -#define HAS_ARCHIVED_A_MESSAGE_KEY @"User archived a message" // TODO remove? -#define kSignalVersionKey @"SignalUpdateVersionKey" -#define PLAY_SOUND_IN_FOREGROUND_KEY @"NotificationSoundInForeground" -#define HAS_REGISTERED_VOIP_PUSH @"VOIPPushEnabled" - -@implementation PropertyListPreferences (PropertyUtil) - -- (NSTimeInterval)getCachedOrDefaultDesiredBufferDepth { - id v = [self tryGetValueForKey:CALL_STREAM_DES_BUFFER_LEVEL_KEY]; - if (v == nil) - return DEFAULT_CALL_STREAM_DES_BUFFER_LEVEL; - return [v doubleValue]; -} - -- (void)setCachedDesiredBufferDepth:(double)value { - ows_require(value >= 0); - [self setValueForKey:CALL_STREAM_DES_BUFFER_LEVEL_KEY toValue:@(value)]; -} - -- (BOOL)loggingIsEnabled { - NSNumber *preference = [self tryGetValueForKey:DEBUG_IS_ENABLED_KEY]; - if (preference) { - return [preference boolValue]; - } else { - return YES; - } -} - -- (BOOL)screenSecurityIsEnabled -{ - NSNumber *preference = [self tryGetValueForKey:SCREEN_SECURITY_KEY]; - return preference ? [preference boolValue] : YES; -} - -- (BOOL)getHasSentAMessage { - NSNumber *preference = [self tryGetValueForKey:HAS_SENT_A_MESSAGE_KEY]; - if (preference) { - return [preference boolValue]; - } else { - return NO; - } -} - -- (BOOL)getHasArchivedAMessage { - NSNumber *preference = [self tryGetValueForKey:HAS_ARCHIVED_A_MESSAGE_KEY]; - if (preference) { - return [preference boolValue]; - } else { - return NO; - } -} - -- (BOOL)hasRegisteredVOIPPush { - NSNumber *preference = [self tryGetValueForKey:HAS_REGISTERED_VOIP_PUSH]; - if (preference) { - return [preference boolValue]; - } else { - return YES; - } -} - -- (TSImageQuality)imageUploadQuality { - // always return average image quality - return TSImageQualityMedium; -} - -- (void)setScreenSecurity:(BOOL)flag -{ - [self setValueForKey:SCREEN_SECURITY_KEY toValue:@(flag)]; -} - -- (void)setHasRegisteredVOIPPush:(BOOL)enabled { - [self setValueForKey:HAS_REGISTERED_VOIP_PUSH toValue:@(enabled)]; -} - -- (void)setLoggingEnabled:(BOOL)flag { - [self setValueForKey:DEBUG_IS_ENABLED_KEY toValue:@(flag)]; -} - -- (NSString *)lastRanVersion { - return [NSUserDefaults.standardUserDefaults objectForKey:kSignalVersionKey]; -} - -- (void)setHasSentAMessage:(BOOL)enabled { - [self setValueForKey:HAS_SENT_A_MESSAGE_KEY toValue:@(enabled)]; -} - -- (void)setHasArchivedAMessage:(BOOL)enabled { - [self setValueForKey:HAS_ARCHIVED_A_MESSAGE_KEY toValue:@(enabled)]; -} - -- (NSString *)setAndGetCurrentVersion { - NSString *currentVersion = - [NSString stringWithFormat:@"%@", NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"]]; - [NSUserDefaults.standardUserDefaults setObject:currentVersion forKey:kSignalVersionKey]; - [NSUserDefaults.standardUserDefaults synchronize]; - return currentVersion; -} - -#pragma mark Notification Preferences - -- (BOOL)soundInForeground { - NSNumber *preference = [self tryGetValueForKey:PLAY_SOUND_IN_FOREGROUND_KEY]; - if (preference) { - return [preference boolValue]; - } else { - return YES; - } -} - -- (void)setSoundInForeground:(BOOL)enabled { - [self setValueForKey:PLAY_SOUND_IN_FOREGROUND_KEY toValue:@(enabled)]; -} - -- (void)setNotificationPreviewType:(NotificationType)type { - [self setValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY toValue:@(type)]; -} - -- (NotificationType)notificationPreviewType { - NSNumber *preference = [self tryGetValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY]; - - if (preference) { - return [preference unsignedIntegerValue]; - } else { - return NotificationNamePreview; - } -} - -- (NSString *)nameForNotificationPreviewType:(NotificationType)notificationType { - switch (notificationType) { - case NotificationNamePreview: - return NSLocalizedString(@"NOTIFICATIONS_SENDER_AND_MESSAGE", nil); - case NotificationNameNoPreview: - return NSLocalizedString(@"NOTIFICATIONS_SENDER_ONLY", nil); - case NotificationNoNameNoPreview: - return NSLocalizedString(@"NOTIFICATIONS_NONE", nil); - default: - DDLogWarn(@"Undefined NotificationType in Settings"); - return @""; - } -} - -@end diff --git a/Signal/src/environment/PropertyListPreferences.h b/Signal/src/environment/PropertyListPreferences.h index 8acb793c3..2844829f9 100644 --- a/Signal/src/environment/PropertyListPreferences.h +++ b/Signal/src/environment/PropertyListPreferences.h @@ -1,7 +1,66 @@ +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, NotificationType) { + NotificationNoNameNoPreview, + NotificationNameNoPreview, + NotificationNamePreview, +}; + +typedef NS_ENUM(NSUInteger, TSImageQuality) { + TSImageQualityUncropped = 1, + TSImageQualityHigh = 2, + TSImageQualityMedium = 3, + TSImageQualityLow = 4 +}; + @interface PropertyListPreferences : NSObject -- (id)tryGetValueForKey:(NSString *)key; -- (void)setValueForKey:(NSString *)key toValue:(id)value; +#pragma mark - Helpers + +- (nullable id)tryGetValueForKey:(NSString *)key; +- (void)setValueForKey:(NSString *)key toValue:(nullable id)value; - (void)clear; +#pragma mark - Specific Preferences + +- (NSTimeInterval)getCachedOrDefaultDesiredBufferDepth; +- (void)setCachedDesiredBufferDepth:(double)value; + +- (BOOL)getHasSentAMessage; +- (void)setHasSentAMessage:(BOOL)enabled; + +- (BOOL)getHasArchivedAMessage; +- (void)setHasArchivedAMessage:(BOOL)enabled; + +- (BOOL)loggingIsEnabled; +- (void)setLoggingEnabled:(BOOL)flag; + +- (BOOL)screenSecurityIsEnabled; +- (void)setScreenSecurity:(BOOL)flag; + +- (NotificationType)notificationPreviewType; +- (void)setNotificationPreviewType:(NotificationType)type; +- (NSString *)nameForNotificationPreviewType:(NotificationType)notificationType; + +- (BOOL)soundInForeground; +- (void)setSoundInForeground:(BOOL)enabled; + +- (BOOL)hasRegisteredVOIPPush; +- (void)setHasRegisteredVOIPPush:(BOOL)enabled; + +- (TSImageQuality)imageUploadQuality; + +- (nullable NSString *)lastRanVersion; +- (NSString *)setAndGetCurrentVersion; + +#pragma mark - Push Tokens + +- (void)setPushToken:(NSString *)value; +- (nullable NSString *)getPushToken; + +- (void)setVoipToken:(NSString *)value; +- (nullable NSString *)getVoipToken; + @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/environment/PropertyListPreferences.m b/Signal/src/environment/PropertyListPreferences.m index c9cf35851..8f9825ef8 100644 --- a/Signal/src/environment/PropertyListPreferences.m +++ b/Signal/src/environment/PropertyListPreferences.m @@ -1,12 +1,28 @@ -#import "Constraints.h" #import "PropertyListPreferences.h" +#import "Constraints.h" #import "TSStorageHeaders.h" -#define SignalDatabaseCollection @"SignalPreferences" +NS_ASSUME_NONNULL_BEGIN +double const PropertyListPreferencesDefaultCallStreamDESBufferLevel = 0.5; +NSString *const PropertyListPreferencesSignalDatabaseCollection = @"SignalPreferences"; + +NSString *const PropertyListPreferencesKeyCallStreamDESBufferLevel = @"CallStreamDesiredBufferLevel"; +NSString *const PropertyListPreferencesKeyScreenSecurity = @"Screen Security Key"; +NSString *const PropertyListPreferencesKeyEnableDebugLog = @"Debugging Log Enabled Key"; +NSString *const PropertyListPreferencesKeyNotificationPreviewType = @"Notification Preview Type Key"; +NSString *const PropertyListPreferencesKeyHasSentAMessage = @"User has sent a message"; +NSString *const PropertyListPreferencesKeyHasArchivedAMessage = @"User archived a message"; +NSString *const PropertyListPreferencesKeyLastRunSignalVersion = @"SignalUpdateVersionKey"; +NSString *const PropertyListPreferencesKeyPlaySoundInForeground = @"NotificationSoundInForeground"; +NSString *const PropertyListPreferencesKeyHasRegisteredVoipPush = @"VOIPPushEnabled"; +NSString *const PropertyListPreferencesKeyLastRecordedPushToken = @"LastRecordedPushToken"; +NSString *const PropertyListPreferencesKeyLastRecordedVoipToken = @"LastRecordedVoipToken"; @implementation PropertyListPreferences +#pragma mark - Helpers + - (void)clear { @synchronized(self) { NSString *appDomain = NSBundle.mainBundle.bundleIdentifier; @@ -14,16 +30,200 @@ } } -- (id)tryGetValueForKey:(NSString *)key { +- (nullable id)tryGetValueForKey:(NSString *)key +{ ows_require(key != nil); - return [TSStorageManager.sharedManager objectForKey:key inCollection:SignalDatabaseCollection]; + return + [TSStorageManager.sharedManager objectForKey:key inCollection:PropertyListPreferencesSignalDatabaseCollection]; } -- (void)setValueForKey:(NSString *)key toValue:(id)value { +- (void)setValueForKey:(NSString *)key toValue:(nullable id)value +{ ows_require(key != nil); - [TSStorageManager.sharedManager setObject:value forKey:key inCollection:SignalDatabaseCollection]; + [TSStorageManager.sharedManager setObject:value + forKey:key + inCollection:PropertyListPreferencesSignalDatabaseCollection]; } +#pragma mark - Specific Preferences + +- (NSTimeInterval)getCachedOrDefaultDesiredBufferDepth +{ + id v = [self tryGetValueForKey:PropertyListPreferencesKeyCallStreamDESBufferLevel]; + if (v == nil) + return PropertyListPreferencesDefaultCallStreamDESBufferLevel; + return [v doubleValue]; +} + +- (void)setCachedDesiredBufferDepth:(double)value +{ + ows_require(value >= 0); + [self setValueForKey:PropertyListPreferencesKeyCallStreamDESBufferLevel toValue:@(value)]; +} + +- (BOOL)loggingIsEnabled +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyEnableDebugLog]; + if (preference) { + return [preference boolValue]; + } else { + return YES; + } +} + +- (BOOL)screenSecurityIsEnabled +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyScreenSecurity]; + return preference ? [preference boolValue] : YES; +} + +- (BOOL)getHasSentAMessage +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyHasSentAMessage]; + if (preference) { + return [preference boolValue]; + } else { + return NO; + } +} + +- (BOOL)getHasArchivedAMessage +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyHasArchivedAMessage]; + if (preference) { + return [preference boolValue]; + } else { + return NO; + } +} + +- (BOOL)hasRegisteredVOIPPush +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyHasRegisteredVoipPush]; + if (preference) { + return [preference boolValue]; + } else { + return YES; + } +} + +- (TSImageQuality)imageUploadQuality +{ + // always return average image quality + return TSImageQualityMedium; +} + +- (void)setScreenSecurity:(BOOL)flag +{ + [self setValueForKey:PropertyListPreferencesKeyScreenSecurity toValue:@(flag)]; +} + +- (void)setHasRegisteredVOIPPush:(BOOL)enabled +{ + [self setValueForKey:PropertyListPreferencesKeyHasRegisteredVoipPush toValue:@(enabled)]; +} + +- (void)setLoggingEnabled:(BOOL)flag +{ + [self setValueForKey:PropertyListPreferencesKeyEnableDebugLog toValue:@(flag)]; +} + +- (nullable NSString *)lastRanVersion +{ + return [NSUserDefaults.standardUserDefaults objectForKey:PropertyListPreferencesKeyLastRunSignalVersion]; +} + +- (void)setHasSentAMessage:(BOOL)enabled +{ + [self setValueForKey:PropertyListPreferencesKeyHasSentAMessage toValue:@(enabled)]; +} + +- (void)setHasArchivedAMessage:(BOOL)enabled +{ + [self setValueForKey:PropertyListPreferencesKeyHasArchivedAMessage toValue:@(enabled)]; +} + +- (NSString *)setAndGetCurrentVersion +{ + NSString *currentVersion = + [NSString stringWithFormat:@"%@", NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"]]; + [NSUserDefaults.standardUserDefaults setObject:currentVersion + forKey:PropertyListPreferencesKeyLastRunSignalVersion]; + [NSUserDefaults.standardUserDefaults synchronize]; + return currentVersion; +} + +#pragma mark Notification Preferences + +- (BOOL)soundInForeground +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyPlaySoundInForeground]; + if (preference) { + return [preference boolValue]; + } else { + return YES; + } +} + +- (void)setSoundInForeground:(BOOL)enabled +{ + [self setValueForKey:PropertyListPreferencesKeyPlaySoundInForeground toValue:@(enabled)]; +} + +- (void)setNotificationPreviewType:(NotificationType)type +{ + [self setValueForKey:PropertyListPreferencesKeyNotificationPreviewType toValue:@(type)]; +} + +- (NotificationType)notificationPreviewType +{ + NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyNotificationPreviewType]; + + if (preference) { + return [preference unsignedIntegerValue]; + } else { + return NotificationNamePreview; + } +} + +- (NSString *)nameForNotificationPreviewType:(NotificationType)notificationType +{ + switch (notificationType) { + case NotificationNamePreview: + return NSLocalizedString(@"NOTIFICATIONS_SENDER_AND_MESSAGE", nil); + case NotificationNameNoPreview: + return NSLocalizedString(@"NOTIFICATIONS_SENDER_ONLY", nil); + case NotificationNoNameNoPreview: + return NSLocalizedString(@"NOTIFICATIONS_NONE", nil); + default: + DDLogWarn(@"Undefined NotificationType in Settings"); + return @""; + } +} + +#pragma mark - Push Tokens + +- (void)setPushToken:(NSString *)value +{ + [self setValueForKey:PropertyListPreferencesKeyLastRecordedPushToken toValue:value]; +} + +- (nullable NSString *)getPushToken +{ + return [self tryGetValueForKey:PropertyListPreferencesKeyLastRecordedPushToken]; +} + +- (void)setVoipToken:(NSString *)value +{ + [self setValueForKey:PropertyListPreferencesKeyLastRecordedVoipToken toValue:value]; +} + +- (nullable NSString *)getVoipToken +{ + return [self tryGetValueForKey:PropertyListPreferencesKeyLastRecordedVoipToken]; +} @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/environment/VersionMigrations.m b/Signal/src/environment/VersionMigrations.m index 32b179f77..74ef40701 100644 --- a/Signal/src/environment/VersionMigrations.m +++ b/Signal/src/environment/VersionMigrations.m @@ -11,7 +11,7 @@ #import "Environment.h" #import "LockInteractionController.h" #import "OWSDatabaseMigrationRunner.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "PushManager.h" #import "RecentCallManager.h" #import "SignalKeyingStorage.h" @@ -102,16 +102,17 @@ #pragma mark Upgrading to 2.1 - Needs to register VOIP token + Removing video cache folder + (void)nonBlockingPushRegistration { - __block failedBlock failedBlock = ^(NSError *error) { - DDLogError(@"Failed to register VOIP push token: %@", error.debugDescription); + void (^failedBlock)(NSError *) = ^(NSError *error) { + DDLogError(@"Failed to register VOIP push token: %@", error.debugDescription); }; [[PushManager sharedManager] requestPushTokenWithSuccess:^(NSString *pushToken, NSString *voipToken) { - [TSAccountManager registerForPushNotifications:pushToken - voipToken:voipToken - success:^{ - DDLogWarn(@"Registered for VOIP Push."); - } - failure:failedBlock]; + [[TSAccountManager sharedInstance] + registerForPushNotificationsWithPushToken:pushToken + voipToken:voipToken + success:^{ + DDLogWarn(@"Registered for VOIP Push."); + } + failure:failedBlock]; } failure:failedBlock]; } diff --git a/Signal/src/network/PushManager.h b/Signal/src/network/PushManager.h index 572406568..38d8a97db 100644 --- a/Signal/src/network/PushManager.h +++ b/Signal/src/network/PushManager.h @@ -8,6 +8,11 @@ #import #import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UILocalNotification; #define Signal_Thread_UserInfo_Key @"Signal_Thread_Id" #define Signal_Message_UserInfo_Key @"Signal_Message_Id" @@ -43,23 +48,20 @@ typedef void (^pushTokensSuccessBlock)(NSString *pushToken, NSString *voipToken) * @param success Completion block that is passed the token as a parameter * @param failure Failure block, executed when failed to get push token */ - - (void)requestPushTokenWithSuccess:(pushTokensSuccessBlock)success failure:(void (^)(NSError *))failure; /** * Registers for Users Notifications. By doing this on launch, we are sure that the correct categories of user * notifications is registered. */ - - (void)validateUserNotificationSettings; /** * The pushNotification and userNotificationFutureSource are accessed by the App Delegate after requested permissions. */ - -@property TOCFutureSource *pushNotificationFutureSource; -@property TOCFutureSource *userNotificationFutureSource; -@property TOCFutureSource *pushKitNotificationFutureSource; +@property (nullable, atomic, readwrite, strong) TOCFutureSource *pushNotificationFutureSource; +@property (nullable, atomic, readwrite, strong) TOCFutureSource *userNotificationFutureSource; +@property (nullable, atomic, readwrite, strong) TOCFutureSource *pushKitNotificationFutureSource; - (TOCFuture *)registerPushKitNotificationFuture; - (BOOL)supportsVOIPPush; @@ -85,3 +87,5 @@ typedef void (^pushTokensSuccessBlock)(NSString *pushToken, NSString *voipToken) completionHandler:(void (^)())completionHandler; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index 1571d46b8..ce7a3dc75 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -13,7 +13,7 @@ #import "NSDate+millisecondTimeStamp.h" #import "NotificationTracker.h" #import "OWSContactsManager.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "RPServerRequestsManager.h" #import "TSOutgoingMessage.h" #import "TSSocketManager.h" @@ -346,20 +346,6 @@ }]; } -- (TOCFuture *)registerForUserNotificationsFuture { - self.userNotificationFutureSource = [TOCFutureSource new]; - - UIUserNotificationSettings *settings = - [UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes] - categories:[NSSet setWithObjects:[self userNotificationsCallCategory], - [self fullNewMessageNotificationCategory], - [self userNotificationsCallBackCategory], - nil]]; - - [UIApplication.sharedApplication registerUserNotificationSettings:settings]; - return self.userNotificationFutureSource.future; -} - - (UIUserNotificationCategory *)fullNewMessageNotificationCategory { UIMutableUserNotificationAction *action_markRead = [UIMutableUserNotificationAction new]; action_markRead.identifier = Signal_Message_MarkAsRead_Identifier; @@ -443,10 +429,16 @@ return UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge; } -- (void)validateUserNotificationSettings { - [[self registerForUserNotificationsFuture] thenDo:^(id value){ - // Nothing to do, just making sure we are registered for User Notifications. - }]; +- (void)validateUserNotificationSettings +{ + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes] + categories:[NSSet setWithObjects:[self userNotificationsCallCategory], + [self fullNewMessageNotificationCategory], + [self userNotificationsCallBackCategory], + nil]]; + + [UIApplication.sharedApplication registerUserNotificationSettings:settings]; } - (BOOL)applicationIsActive { diff --git a/Signal/src/network/http/RPAPICall.h b/Signal/src/network/http/RPAPICall.h index 08152c4e1..406ff33b5 100644 --- a/Signal/src/network/http/RPAPICall.h +++ b/Signal/src/network/http/RPAPICall.h @@ -7,7 +7,9 @@ // #import -#import + +NS_ASSUME_NONNULL_BEGIN + @class PhoneNumber; @interface RPAPICall : NSObject @@ -18,14 +20,15 @@ typedef NS_ENUM(NSInteger, HTTPMethod) { HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_DEL @property (nonatomic, readonly) NSString *endPoint; @property (nonatomic, readonly) HTTPMethod method; -@property (nonatomic, readonly) NSDictionary *parameters; +@property (nonatomic, readonly) NSMutableDictionary *parameters; @property (nonatomic, readonly) AFHTTPRequestSerializer *requestSerializer; @property (nonatomic, readonly) AFHTTPResponseSerializer *responseSerializer; #pragma mark API Call Contstructors -+ (RPAPICall *)registerPushNotificationWithPushToken:(NSData *)pushToken voipToken:(NSData *)voipToken; -+ (RPAPICall *)unregisterWithPushToken:(NSData *)pushToken; -+ (RPAPICall *)verifyWithTSToken:(NSString *)tsToken attributesParameters:(NSDictionary *)attributes; ++ (RPAPICall *)verifyWithTSToken:(NSString *)tsToken signalingKey:(NSData *)signalingKey; ++ (RPAPICall *)registerPushNotificationWithPushToken:(NSString *)pushToken voipToken:(NSString *)voipToken; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/network/http/RPAPICall.m b/Signal/src/network/http/RPAPICall.m index f4e2c332d..0e41b3014 100644 --- a/Signal/src/network/http/RPAPICall.m +++ b/Signal/src/network/http/RPAPICall.m @@ -9,51 +9,59 @@ #import #import "Constraints.h" #import "CryptoTools.h" -#import "NSData+ows_StripToken.h" #import "PhoneNumber.h" #import "RPAPICall.h" #import "SignalKeyingStorage.h" #import "Util.h" +NS_ASSUME_NONNULL_BEGIN + #define CLAIMED_INTEROP_VERSION_IN_INITIATE_SIGNAL 1 + +NSString *const RPAPICallPushTokenKey = @"apnRegistrationId"; +NSString *const RPAPICallVoipTokenKey = @"voipRegistrationId"; +NSString *const RPAPICallSignalingKeyKey = @"signalingKey"; + @interface RPAPICall () + @property (nonatomic, readwrite) NSString *endPoint; @property (nonatomic, readwrite) HTTPMethod method; -@property (nonatomic, readwrite) NSDictionary *parameters; +@property (nonatomic, readwrite) NSMutableDictionary *parameters; @property (nonatomic, readwrite) AFHTTPRequestSerializer *requestSerializer; @property (nonatomic, readwrite) AFHTTPResponseSerializer *responseSerializer; + @end @implementation RPAPICall + (RPAPICall *)defaultAPICall { RPAPICall *apiCall = [[RPAPICall alloc] init]; - apiCall.parameters = @{}; + apiCall.parameters = [NSMutableDictionary new]; apiCall.requestSerializer = [self basicAuthenticationSerializer]; apiCall.responseSerializer = [AFHTTPResponseSerializer serializer]; return apiCall; } -+ (RPAPICall *)verifyWithTSToken:(NSString *)tsToken attributesParameters:(NSDictionary *)attributes { ++ (RPAPICall *)verifyWithTSToken:(NSString *)tsToken signalingKey:(NSData *)signalingKey +{ RPAPICall *apiCall = [self defaultAPICall]; apiCall.method = HTTP_PUT; apiCall.endPoint = [NSString stringWithFormat:@"/api/v1/accounts/token/%@", tsToken]; - apiCall.parameters = attributes; - + apiCall.parameters[RPAPICallSignalingKeyKey] = [signalingKey encodedAsBase64]; return apiCall; } -+ (RPAPICall *)registerPushNotificationWithPushToken:(NSData *)pushToken voipToken:(NSData *)voipToken { ++ (RPAPICall *)registerPushNotificationWithPushToken:(NSString *)pushToken voipToken:(NSString *)voipToken +{ RPAPICall *apiCall = [self defaultAPICall]; - if (voipToken) { - apiCall.parameters = @{ @"voip" : [voipToken ows_tripToken] }; - } else { - DDLogWarn(@"No VoIP push token registered, might experience some issues while in background."); - } apiCall.method = HTTP_PUT; - apiCall.endPoint = [NSString stringWithFormat:@"/apn/%@", [pushToken ows_tripToken]]; + + apiCall.parameters[RPAPICallPushTokenKey] = pushToken; + apiCall.parameters[RPAPICallVoipTokenKey] = voipToken; + apiCall.endPoint = @"/api/v1/accounts/apn"; + return apiCall; } @@ -61,7 +69,6 @@ RPAPICall *apiCall = [self defaultAPICall]; apiCall.method = HTTP_DELETE; apiCall.endPoint = [NSString stringWithFormat:@"/apn/%@", pushToken.encodedAsHexString]; - apiCall.parameters = nil; apiCall.requestSerializer = [self basicAuthenticationSerializer]; return apiCall; } @@ -194,4 +201,11 @@ } */ +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@ %@", [super description], self.endPoint]; +} + @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/network/http/RPServerRequestsManager.h b/Signal/src/network/http/RPServerRequestsManager.h index e4a42dd77..477da14c0 100644 --- a/Signal/src/network/http/RPServerRequestsManager.h +++ b/Signal/src/network/http/RPServerRequestsManager.h @@ -9,8 +9,6 @@ #import #import "RPAPICall.h" -#import - @interface RPServerRequestsManager : NSObject + (instancetype)sharedManager; @@ -19,6 +17,4 @@ success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; -- (TOCFuture *)futureForRequest:(RPAPICall *)apiCall; - @end diff --git a/Signal/src/network/http/RPServerRequestsManager.m b/Signal/src/network/http/RPServerRequestsManager.m index f7c956b7f..24d7d215c 100644 --- a/Signal/src/network/http/RPServerRequestsManager.m +++ b/Signal/src/network/http/RPServerRequestsManager.m @@ -17,7 +17,6 @@ @end - @implementation RPServerRequestsManager + (instancetype)sharedManager { @@ -45,7 +44,10 @@ - (void)performRequest:(RPAPICall *)apiCall success:(void (^)(NSURLSessionDataTask *task, id responseObject))success - failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + DDLogInfo(@"%@ Making request: %@", self.tag, apiCall); + self.operationManager.requestSerializer = apiCall.requestSerializer; self.operationManager.responseSerializer = apiCall.responseSerializer; @@ -87,18 +89,16 @@ } } -- (TOCFuture *)futureForRequest:(RPAPICall *)apiCall { - TOCFutureSource *requestFutureSource = [TOCFutureSource new]; +#pragma mark - Logging - [self performRequest:apiCall - success:^(NSURLSessionDataTask *task, id responseObject) { - [requestFutureSource trySetResult:task.response]; - } - failure:^(NSURLSessionDataTask *task, NSError *error) { - [requestFutureSource trySetFailure:error]; - }]; ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} - return [requestFutureSource future]; +- (NSString *)tag +{ + return self.class.tag; } @end diff --git a/Signal/src/phone/RPAccountManager.h b/Signal/src/phone/RPAccountManager.h index ee944beef..07c7fcc8a 100644 --- a/Signal/src/phone/RPAccountManager.h +++ b/Signal/src/phone/RPAccountManager.h @@ -6,14 +6,27 @@ // Copyright © 2015 Open Whisper Systems. All rights reserved. // -#import +NS_ASSUME_NONNULL_BEGIN @interface RPAccountManager : NSObject -+ (void)registrationWithTsToken:(NSString *)tsToken - pushToken:(NSString *)pushToken - voipToken:(NSString *)voipPushToken - success:(void (^)())success - failure:(void (^)(NSError *))failure; ++ (instancetype)sharedInstance; + +- (void)registerWithTsToken:(NSString *)tsToken + success:(void (^)())success + failure:(void (^)(NSError *))failure; + +/** + * Register's the device's push notification token with the server + * + * @param pushToken Apple's Push Token + */ +- (void)registerForPushNotificationsWithPushToken:(NSString *)pushToken + voipToken:(NSString *)voipToken + success:(void (^)())successHandler + failure:(void (^)(NSError *error))failureHandler + NS_SWIFT_NAME(registerForPushNotifications(pushToken:voipToken:success:failure:)); @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/phone/RPAccountManager.m b/Signal/src/phone/RPAccountManager.m index d93bab863..ff35817f4 100644 --- a/Signal/src/phone/RPAccountManager.m +++ b/Signal/src/phone/RPAccountManager.m @@ -15,90 +15,97 @@ #import "RPServerRequestsManager.h" #import "SignalKeyingStorage.h" -@interface RedPhoneAccountAttributes : MTLModel +NS_ASSUME_NONNULL_BEGIN -@property (nonatomic, copy, readonly) NSString *signalingKey; -@property (nonatomic, copy, readonly) NSString *apnRegistrationId; -@property (nonatomic, copy, readonly) NSString *voipRegistrationId; +@interface RPAccountManager () - -- (instancetype)initWithSignalingCipherKey:(NSData *)signalingCipherKey - signalingMacKey:(NSData *)signalingMacKey - signalingExtraKey:(NSData *)signalingExtraKey - apnRegistrationId:(NSString *)apnRegistrationId - voipRegistrationId:(NSString *)voipRegistrationId; - -@end - -@implementation RedPhoneAccountAttributes - -- (instancetype)initWithSignalingCipherKey:(NSData *)signalingCipherKey - signalingMacKey:(NSData *)signalingMacKey - signalingExtraKey:(NSData *)signalingExtraKey - apnRegistrationId:(NSString *)apnRegistrationId - voipRegistrationId:(NSString *)voipRegistrationId { - self = [super init]; - - if (self) { - _signalingKey = @[ signalingCipherKey, signalingMacKey, signalingExtraKey ].ows_concatDatas.encodedAsBase64; - _apnRegistrationId = apnRegistrationId; - _voipRegistrationId = voipRegistrationId; - } - - return self; -} - -- (NSDictionary *)dictionaryValue { - NSMutableDictionary *dictionaryValue = [[super dictionaryValue] mutableCopy]; - if (!_voipRegistrationId) { - [dictionaryValue removeObjectForKey:@"voipRegistrationId"]; - } - - return dictionaryValue; -} +@property (nonatomic, readonly, strong) RPServerRequestsManager *requestManager; @end @implementation RPAccountManager -+ (void)generateKeyingMaterial { +- (instancetype)initWithRequestManager:(RPServerRequestsManager *)requestManager +{ + self = [super init]; + if (!self) { + return self; + } + + _requestManager = requestManager; + + return self; +} + ++ (instancetype)sharedInstance +{ + static dispatch_once_t onceToken; + static RPAccountManager *sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] initWithRequestManager:[RPServerRequestsManager sharedManager]]; + }); + return sharedInstance; +} + +- (NSData *)generateSignalingKey +{ [SignalKeyingStorage generateServerAuthPassword]; [SignalKeyingStorage generateSignaling]; -} -+ (NSDictionary *)attributesWithPushToken:(NSString *)pushToken voipToken:(NSString *)voipPushToken { NSData *signalingCipherKey = SignalKeyingStorage.signalingCipherKey; NSData *signalingMacKey = SignalKeyingStorage.signalingMacKey; - NSData *signalingExtraKeyData = SignalKeyingStorage.signalingExtraKey; + NSData *signalingExtraKey = SignalKeyingStorage.signalingExtraKey; - - return [[RedPhoneAccountAttributes alloc] initWithSignalingCipherKey:signalingCipherKey - signalingMacKey:signalingMacKey - signalingExtraKey:signalingExtraKeyData - apnRegistrationId:pushToken - voipRegistrationId:voipPushToken] - .dictionaryValue; + return @[ signalingCipherKey, signalingMacKey, signalingExtraKey ].ows_concatDatas; } -+ (void)registrationWithTsToken:(NSString *)tsToken - pushToken:(NSString *)pushToken - voipToken:(NSString *)voipPushToken - success:(void (^)())success - failure:(void (^)(NSError *))failure { - [self generateKeyingMaterial]; - - [[RPServerRequestsManager sharedManager] - performRequest:[RPAPICall verifyWithTSToken:tsToken - attributesParameters:[self attributesWithPushToken:pushToken voipToken:voipPushToken]] +- (void)registerWithTsToken:(NSString *)tsToken + success:(void (^)())success + failure:(void (^)(NSError *))failure +{ + RPAPICall *request = [RPAPICall verifyWithTSToken:tsToken signalingKey:[self generateSignalingKey]]; + [self.requestManager performRequest:request success:^(NSURLSessionDataTask *task, id responseObject) { - success(); + DDLogInfo(@"%@ Successfully verified RedPhone account.", self.tag); + success(); } failure:^(NSURLSessionDataTask *task, NSError *error) { - failure(error); + DDLogError(@"%@ Failed to verify RedPhone account with error: %@", self.tag, error); + failure(error); }]; } -+ (void)unregister { + +- (void)registerForPushNotificationsWithPushToken:(NSString *)pushToken + voipToken:(NSString *)voipToken + success:(void (^)())successHandler + failure:(void (^)(NSError *error))failureHandler +{ + RPAPICall *request = [RPAPICall registerPushNotificationWithPushToken:pushToken voipToken:voipToken]; + [self.requestManager performRequest:request + success:^(NSURLSessionDataTask *task, id responseObject) { + DDLogInfo(@"%@ Successfully updated push tokens.", self.tag); + successHandler(); + } + failure:^(NSURLSessionDataTask *task, NSError *error) { + DDLogError(@"%@ Failed to update push tokens: %@", self.tag, error); + failureHandler(error); + }]; +} + + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; } @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/OWSLogger.h b/Signal/src/util/OWSLogger.h new file mode 100644 index 000000000..2feac075b --- /dev/null +++ b/Signal/src/util/OWSLogger.h @@ -0,0 +1,20 @@ +// Created by Michael Kirk on 10/25/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +NS_ASSUME_NONNULL_BEGIN + +/** + * A minimal DDLog wrapper for swift. + */ +NS_SWIFT_NAME(Logger) +@interface OWSLogger : NSObject + ++ (void)verbose:(NSString *)logString; ++ (void)debug:(NSString *)logString; ++ (void)info:(NSString *)logString; ++ (void)warn:(NSString *)logString; ++ (void)error:(NSString *)logString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/OWSLogger.m b/Signal/src/util/OWSLogger.m new file mode 100644 index 000000000..5a51cb4bc --- /dev/null +++ b/Signal/src/util/OWSLogger.m @@ -0,0 +1,37 @@ +// Created by Michael Kirk on 10/25/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "OWSLogger.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation OWSLogger + ++ (void)verbose:(NSString *)logString +{ + DDLogVerbose(logString); +} + ++ (void)debug:(NSString *)logString +{ + DDLogDebug(logString); +} + ++ (void)info:(NSString *)logString +{ + DDLogInfo(logString); +} + ++ (void)warn:(NSString *)logString +{ + DDLogWarn(logString); +} + ++ (void)error:(NSString *)logString +{ + DDLogError(logString); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/view controllers/AdvancedSettingsTableViewController.m b/Signal/src/view controllers/AdvancedSettingsTableViewController.m index 7a988e728..db3814dfd 100644 --- a/Signal/src/view controllers/AdvancedSettingsTableViewController.m +++ b/Signal/src/view controllers/AdvancedSettingsTableViewController.m @@ -7,23 +7,28 @@ // #import "AdvancedSettingsTableViewController.h" - -#import #import "DebugLogger.h" #import "Environment.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "PushManager.h" +#import "RPAccountManager.h" +#import "Signal-Swift.h" #import "TSAccountManager.h" +#import +#import +NS_ASSUME_NONNULL_BEGIN @interface AdvancedSettingsTableViewController () @property NSArray *sectionsArray; + @property (strong, nonatomic) UITableViewCell *enableLogCell; @property (strong, nonatomic) UITableViewCell *submitLogCell; @property (strong, nonatomic) UITableViewCell *registerPushCell; @property (strong, nonatomic) UISwitch *enableLogSwitch; + @end @implementation AdvancedSettingsTableViewController @@ -35,8 +40,10 @@ } - (instancetype)init { - self.sectionsArray = - @[ NSLocalizedString(@"LOGGING_SECTION", nil), NSLocalizedString(@"PUSH_REGISTER_TITLE", nil) ]; + self.sectionsArray = @[ + NSLocalizedString(@"LOGGING_SECTION", nil), + NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts") + ]; return [super initWithStyle:UITableViewStyleGrouped]; } @@ -84,7 +91,8 @@ } } -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { +- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section +{ return [self.sectionsArray objectAtIndex:(NSUInteger)section]; } @@ -111,20 +119,23 @@ if ([tableView cellForRowAtIndexPath:indexPath] == self.submitLogCell) { [Pastelog submitLogs]; } else if ([tableView cellForRowAtIndexPath:indexPath] == self.registerPushCell) { - __block failedPushRegistrationBlock failure = ^(NSError *error) { - SignalAlertView(NSLocalizedString(@"PUSH_REGISTER_TITLE", nil), NSLocalizedString(@"REGISTRATION_BODY", nil)); - }; + OWSAccountManager *accountManager = + [[OWSAccountManager alloc] initWithTextSecureAccountManager:[TSAccountManager sharedInstance] + redPhoneAccountManager:[RPAccountManager sharedInstance]]; + OWSSyncPushTokensJob *syncJob = [[OWSSyncPushTokensJob alloc] initWithPushManager:[PushManager sharedManager] + accountManager:accountManager + preferences:[Environment preferences]]; + syncJob.uploadOnlyIfStale = NO; + [syncJob run] + .then(^{ + SignalAlertView(NSLocalizedString(@"PUSH_REGISTER_SUCCESS", @"Alert title"), nil); + }) + .catch(^(NSError *error) { + SignalAlertView(NSLocalizedString(@"REGISTRATION_BODY", @"Alert title"), error.localizedDescription); + }); - [[PushManager sharedManager] requestPushTokenWithSuccess:^(NSString *pushToken, NSString *voipToken) { - [TSAccountManager registerForPushNotifications:pushToken - voipToken:voipToken - success:^{ - SignalAlertView(NSLocalizedString(@"PUSH_REGISTER_TITLE", nil), - NSLocalizedString(@"PUSH_REGISTER_SUCCESS", nil)); - } - failure:failure]; - } - failure:failure]; + } else { + DDLogDebug(@"%@ Ignoring cell selection at indexPath: %@", self.tag, indexPath); } } @@ -142,4 +153,18 @@ [self.tableView reloadData]; } +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/view controllers/CodeVerificationViewController.m b/Signal/src/view controllers/CodeVerificationViewController.m index 4c63c1b63..3f04a7e0d 100644 --- a/Signal/src/view controllers/CodeVerificationViewController.m +++ b/Signal/src/view controllers/CodeVerificationViewController.m @@ -7,22 +7,54 @@ // #import "CodeVerificationViewController.h" - -#import -#import "OWSContactsManager.h" -#import "Environment.h" -#import "LocalizableText.h" -#import "PushManager.h" +#import "AppDelegate.h" #import "RPAccountManager.h" -#import "RPServerRequestsManager.h" -#import "TSAccountManager.h" +#import "Signal-Swift.h" +#import "SignalsNavigationController.h" +#import "SignalsViewController.h" +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +NSString *const kCompletedRegistrationSegue = @"CompletedRegistration"; @interface CodeVerificationViewController () +@property (nonatomic, strong, readonly) OWSAccountManager *accountManager; + @end @implementation CodeVerificationViewController +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (!self) { + return self; + } + + _accountManager = [[OWSAccountManager alloc] initWithTextSecureAccountManager:[TSAccountManager sharedInstance] + redPhoneAccountManager:[RPAccountManager sharedInstance]]; + + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (!self) { + return self; + } + + _accountManager = [[OWSAccountManager alloc] initWithTextSecureAccountManager:[TSAccountManager sharedInstance] + redPhoneAccountManager:[RPAccountManager sharedInstance]]; + + return self; +} + - (void)viewDidLoad { [super viewDidLoad]; [self initializeKeyboardHandlers]; @@ -50,156 +82,80 @@ [self adjustScreenSizes]; } -- (IBAction)verifyChallengeAction:(id)sender { +- (void)startActivityIndicator +{ + [self.submitCodeSpinner startAnimating]; [self enableServerActions:NO]; - [_challengeTextField resignFirstResponder]; - - [self registerWithSuccess:^{ - [_submitCodeSpinner stopAnimating]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - [TSAccountManager didRegister]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self.navigationController - dismissViewControllerAnimated:YES - completion:^{ - if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined || - ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted) { - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"REGISTER_CONTACTS_WELCOME", nil) - message:NSLocalizedString(@"REGISTER_CONTACTS_BODY", nil) - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction - actionWithTitle:NSLocalizedString( - @"REGISTER_CONTACTS_CONTINUE", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [self setupContacts]; - }]]; - - [[UIApplication sharedApplication] - .keyWindow.rootViewController presentViewController:controller - animated:YES - completion:nil]; - - } else { - [self setupContacts]; - } - - }]; - }); - }); - } - failure:^(NSError *error) { - [self enableServerActions:YES]; - [_submitCodeSpinner stopAnimating]; - DDLogError(@"Error: %@", error.localizedDescription); - }]; + [self.challengeTextField resignFirstResponder]; } -- (void)setupContacts { - [[Environment getCurrent].contactsManager doAfterEnvironmentInitSetup]; +- (void)stopActivityIndicator +{ + [self enableServerActions:YES]; + [self.submitCodeSpinner stopAnimating]; +} + +- (IBAction)verifyChallengeAction:(id)sender +{ + [self startActivityIndicator]; + [self.accountManager registerWithVerificationCode:[self validationCodeFromTextField]] + .then(^{ + DDLogInfo(@"%@ Successfully registered Signal account.", self.tag); + dispatch_async(dispatch_get_main_queue(), ^{ + [self stopActivityIndicator]; + [self performSegueWithIdentifier:kCompletedRegistrationSegue sender:nil]; + }); + }) + .catch(^(NSError *_Nonnull error) { + DDLogError(@"%@ error verifying challenge: %@", self.tag, error); + dispatch_async(dispatch_get_main_queue(), ^{ + [self stopActivityIndicator]; + [self presentAlertWithVerificationError:error]; + }); + }); +} + + +- (void)presentAlertWithVerificationError:(NSError *)error +{ + UIAlertController *alertController = [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"REGISTRATION_VERIFICATION_FAILED_TITLE", @"Alert view title") + message:error.localizedDescription + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil) + style:UIAlertActionStyleDefault + handler:nil]; + [alertController addAction:dismissAction]; + + [self presentViewController:alertController animated:YES completion:nil]; } - (NSString *)validationCodeFromTextField { - return [_challengeTextField.text stringByReplacingOccurrencesOfString:@"-" withString:@""]; + return [self.challengeTextField.text stringByReplacingOccurrencesOfString:@"-" withString:@""]; } -- (TOCFuture *)pushRegistration { - TOCFutureSource *pushAndRegisterFuture = [[TOCFutureSource alloc] init]; - - [[PushManager sharedManager] validateUserNotificationSettings]; - [[PushManager sharedManager] requestPushTokenWithSuccess:^(NSString *pushToken, NSString *voipToken) { - NSMutableArray *pushTokens = [NSMutableArray arrayWithObject:pushToken]; - - if (voipToken) { - [pushTokens addObject:voipToken]; - } - - [pushAndRegisterFuture trySetResult:pushTokens]; - } - failure:^(NSError *error) { - [pushAndRegisterFuture trySetFailure:error]; - }]; - - return pushAndRegisterFuture.future; -} - -- (TOCFuture *)textSecureRegistrationFuture:(NSArray *)pushTokens { - TOCFutureSource *textsecureRegistration = [[TOCFutureSource alloc] init]; - - [TSAccountManager verifyAccountWithCode:[self validationCodeFromTextField] - pushToken:pushTokens[0] - voipToken:([pushTokens count] == 2) ? pushTokens.lastObject : nil - success:^{ - [textsecureRegistration trySetResult:@YES]; +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender +{ + DDLogInfo(@"%@ preparing for CompletedRegistrationSeque", self.tag); + if ([segue.identifier isEqualToString:kCompletedRegistrationSegue]) { + if (![segue.destinationViewController isKindOfClass:[SignalsNavigationController class]]) { + DDLogError(@"%@ Unexpected destination view controller: %@", self.tag, segue.destinationViewController); + return; } - failure:^(NSError *error) { - [textsecureRegistration trySetFailure:error]; - }]; - return textsecureRegistration.future; -} + SignalsNavigationController *snc = (SignalsNavigationController *)segue.destinationViewController; - -- (void)registerWithSuccess:(void (^)())success failure:(void (^)(NSError *))failure { - [_submitCodeSpinner startAnimating]; - - __block NSArray *pushTokens; - - TOCFuture *tsRegistrationFuture = [[self pushRegistration] then:^id(NSArray *tokens) { - pushTokens = tokens; - return [self textSecureRegistrationFuture:pushTokens]; - }]; - - TOCFuture *redphoneRegistrationFuture = [tsRegistrationFuture then:^id(id value) { - return [[self getRPRegistrationToken] then:^(NSString *registrationFuture) { - return [self redphoneRegistrationWithTSToken:registrationFuture - pushToken:pushTokens[0] - voipToken:([pushTokens count] == 2) ? pushTokens.lastObject : nil]; - }]; - }]; - - [redphoneRegistrationFuture thenDo:^(id value) { - success(); - }]; - - [redphoneRegistrationFuture catchDo:^(NSError *error) { - failure(error); - }]; -} - - -- (TOCFuture *)getRPRegistrationToken { - TOCFutureSource *redPhoneTokenFuture = [[TOCFutureSource alloc] init]; - - [TSAccountManager obtainRPRegistrationToken:^(NSString *rpRegistrationToken) { - [redPhoneTokenFuture trySetResult:rpRegistrationToken]; - } - failure:^(NSError *error) { - [redPhoneTokenFuture trySetFailure:error]; - }]; - - return redPhoneTokenFuture.future; -} - -- (TOCFuture *)redphoneRegistrationWithTSToken:(NSString *)tsToken - pushToken:(NSString *)pushToken - voipToken:(NSString *)voipToken { - TOCFutureSource *rpRegistration = [[TOCFutureSource alloc] init]; - - [RPAccountManager registrationWithTsToken:tsToken - pushToken:pushToken - voipToken:voipToken - success:^{ - [rpRegistration trySetResult:@YES]; + AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; + appDelegate.window.rootViewController = snc; + if (![snc.topViewController isKindOfClass:[SignalsViewController class]]) { + DDLogError(@"%@ Unexpected top view controller: %@", self.tag, snc.topViewController); + return; } - failure:^(NSError *error) { - [rpRegistration trySetFailure:error]; - }]; - return rpRegistration.future; + DDLogDebug(@"%@ notifying signals view controller of new user.", self.tag); + SignalsViewController *signalsViewController = (SignalsViewController *)snc.topViewController; + signalsViewController.newlyRegisteredUser = YES; + } } #pragma mark - Send codes again @@ -208,13 +164,15 @@ [_requestCodeAgainSpinner startAnimating]; [TSAccountManager rerequestSMSWithSuccess:^{ - [self enableServerActions:YES]; - [_requestCodeAgainSpinner stopAnimating]; + DDLogInfo(@"%@ Successfully requested SMS code", self.tag); + [self enableServerActions:YES]; + [_requestCodeAgainSpinner stopAnimating]; } failure:^(NSError *error) { - [self showRegistrationErrorMessage:error]; - [self enableServerActions:YES]; - [_requestCodeAgainSpinner stopAnimating]; + DDLogError(@"%@ Failed to request SMS code with error: %@", self.tag, error); + [self showRegistrationErrorMessage:error]; + [self enableServerActions:YES]; + [_requestCodeAgainSpinner stopAnimating]; }]; } @@ -223,13 +181,16 @@ [_requestCallSpinner startAnimating]; [TSAccountManager rerequestVoiceWithSuccess:^{ - [self enableServerActions:YES]; - [_requestCallSpinner stopAnimating]; + DDLogInfo(@"%@ Successfully requested voice code", self.tag); + + [self enableServerActions:YES]; + [_requestCallSpinner stopAnimating]; } failure:^(NSError *error) { - [self showRegistrationErrorMessage:error]; - [self enableServerActions:YES]; - [_requestCallSpinner stopAnimating]; + DDLogError(@"%@ Failed to request voice code with error: %@", self.tag, error); + [self showRegistrationErrorMessage:error]; + [self enableServerActions:YES]; + [_requestCallSpinner stopAnimating]; }]; } @@ -303,4 +264,18 @@ _headerConstraint.constant = blueHeaderHeight; } +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/view controllers/FingerprintViewController.m b/Signal/src/view controllers/FingerprintViewController.m index 82a950702..bd8f43d3e 100644 --- a/Signal/src/view controllers/FingerprintViewController.m +++ b/Signal/src/view controllers/FingerprintViewController.m @@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) IBOutlet UIView *qrContainer; @property (strong, nonatomic) IBOutlet UIView *privacyVerificationQRCodeFrame; @property (strong, nonatomic) IBOutlet UIImageView *privacyVerificationQRCode; -@property (strong, nonatomic) IBOutlet CopyableLabel *privacyVerificationFingerprint; +@property (strong, nonatomic) IBOutlet OWSCopyableLabel *privacyVerificationFingerprint; @property (strong, nonatomic) IBOutlet UILabel *instructionsLabel; @property (strong, nonatomic) IBOutlet UIButton *scanButton; diff --git a/Signal/src/view controllers/InboxTableViewCell.m b/Signal/src/view controllers/InboxTableViewCell.m index ad136b9f3..405be9932 100644 --- a/Signal/src/view controllers/InboxTableViewCell.m +++ b/Signal/src/view controllers/InboxTableViewCell.m @@ -4,7 +4,7 @@ #import "InboxTableViewCell.h" #import "Environment.h" #import "OWSAvatarBuilder.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "TSContactThread.h" #import "TSGroupThread.h" #import "TSMessagesManager.h" diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 8019f2f97..49d3012cc 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -26,7 +26,7 @@ #import "OWSMessagesBubblesSizeCalculator.h" #import "OWSOutgoingMessageCollectionViewCell.h" #import "PhoneManager.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "SignalKeyingStorage.h" #import "TSAttachmentPointer.h" #import "TSCall.h" diff --git a/Signal/src/view controllers/NotificationSettingsOptionsViewController.m b/Signal/src/view controllers/NotificationSettingsOptionsViewController.m index 7a6e9ed3d..eb9bb5f63 100644 --- a/Signal/src/view controllers/NotificationSettingsOptionsViewController.m +++ b/Signal/src/view controllers/NotificationSettingsOptionsViewController.m @@ -6,9 +6,9 @@ // Copyright (c) 2015 Open Whisper Systems. All rights reserved. // -#import "Environment.h" #import "NotificationSettingsOptionsViewController.h" -#import "PreferencesUtil.h" +#import "Environment.h" +#import "PropertyListPreferences.h" @interface NotificationSettingsOptionsViewController () @property NSArray *options; diff --git a/Signal/src/view controllers/NotificationSettingsViewController.m b/Signal/src/view controllers/NotificationSettingsViewController.m index 502e73436..3a7b83457 100644 --- a/Signal/src/view controllers/NotificationSettingsViewController.m +++ b/Signal/src/view controllers/NotificationSettingsViewController.m @@ -10,7 +10,7 @@ #import "Environment.h" #import "NotificationSettingsOptionsViewController.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" @interface NotificationSettingsViewController () diff --git a/Signal/src/view controllers/PrivacySettingsTableViewController.m b/Signal/src/view controllers/PrivacySettingsTableViewController.m index 7e9c68fae..11aae7c35 100644 --- a/Signal/src/view controllers/PrivacySettingsTableViewController.m +++ b/Signal/src/view controllers/PrivacySettingsTableViewController.m @@ -8,11 +8,11 @@ #import "PrivacySettingsTableViewController.h" -#import <25519/Curve25519.h> #import "DJWActionSheet+OWS.h" #import "Environment.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "UIUtil.h" +#import <25519/Curve25519.h> @interface PrivacySettingsTableViewController () diff --git a/Signal/src/view controllers/SettingsTableViewController.m b/Signal/src/view controllers/SettingsTableViewController.m index 689e18760..b7191af73 100644 --- a/Signal/src/view controllers/SettingsTableViewController.m +++ b/Signal/src/view controllers/SettingsTableViewController.m @@ -9,7 +9,7 @@ #import "SettingsTableViewController.h" #import "Environment.h" -#import "PreferencesUtil.h" +#import "PropertyListPreferences.h" #import "TSAccountManager.h" #import "UIUtil.h" diff --git a/Signal/src/view controllers/SignalsViewController.h b/Signal/src/view controllers/SignalsViewController.h index 29492ad23..20f6dd07d 100644 --- a/Signal/src/view controllers/SignalsViewController.h +++ b/Signal/src/view controllers/SignalsViewController.h @@ -18,7 +18,7 @@ @property (nonatomic, retain) IBOutlet UITableView *tableView; @property (nonatomic, strong) IBOutlet UILabel *emptyBoxLabel; - +@property (nonatomic) BOOL newlyRegisteredUser; @property (nonatomic, retain) CallState *latestCall; - (void)presentThread:(TSThread *)thread keyboardOnViewAppearing:(BOOL)keyboardOnViewAppearing; diff --git a/Signal/src/view controllers/SignalsViewController.m b/Signal/src/view controllers/SignalsViewController.m index 544067439..f3e52e2cb 100644 --- a/Signal/src/view controllers/SignalsViewController.m +++ b/Signal/src/view controllers/SignalsViewController.m @@ -6,19 +6,22 @@ // Copyright (c) 2014 Open Whisper Systems. All rights reserved. // -#import "InboxTableViewCell.h" -#import "UIUtil.h" - +#import "SignalsViewController.h" +#import "AppDelegate.h" #import "InCallViewController.h" +#import "InboxTableViewCell.h" #import "MessagesViewController.h" #import "NSDate+millisecondTimeStamp.h" #import "OWSContactsManager.h" -#import "PreferencesUtil.h" -#import "SignalsViewController.h" +#import "PropertyListPreferences.h" +#import "PushManager.h" +#import "RPAccountManager.h" +#import "Signal-Swift.h" #import "TSAccountManager.h" #import "TSDatabaseView.h" #import "TSGroupThread.h" #import "TSStorageManager.h" +#import "UIUtil.h" #import "VersionMigrations.h" #import #import @@ -29,8 +32,6 @@ #define CELL_HEIGHT 72.0f #define HEADER_HEIGHT 44.0f -static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; - @interface SignalsViewController () @property (nonatomic, strong) YapDatabaseConnection *editingDbConnection; @@ -183,11 +184,62 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - if (![TSAccountManager isRegistered]) { - [self performSegueWithIdentifier:kShowSignupFlowSegue sender:self]; + if (self.newlyRegisteredUser) { + [self didAppearForNewlyRegisteredUser]; } } +- (void)didAppearForNewlyRegisteredUser +{ + [self promptNewUserForContactsWithCompletion:^{ + [self ensureNotificationsUpToDate]; + }]; +} + +- (void)promptNewUserForContactsWithCompletion:(void (^)())completionHandler +{ + ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus(); + switch (status) { + case kABAuthorizationStatusNotDetermined: + case kABAuthorizationStatusRestricted: { + UIAlertController *controller = + [UIAlertController alertControllerWithTitle:NSLocalizedString(@"REGISTER_CONTACTS_WELCOME", nil) + message:NSLocalizedString(@"REGISTER_CONTACTS_BODY", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [controller + addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"REGISTER_CONTACTS_CONTINUE", nil) + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + [[Environment getCurrent].contactsManager doAfterEnvironmentInitSetup]; + }]]; + + [self presentViewController:controller animated:YES completion:completionHandler]; + break; + } + default: { + DDLogError(@"%@ Unexpected for new user to have kABAuthorizationStatus:%ld", self.tag, status); + [[Environment getCurrent].contactsManager doAfterEnvironmentInitSetup]; + completionHandler(); + break; + } + } +} + +- (void)ensureNotificationsUpToDate +{ + OWSAccountManager *accountManager = + [[OWSAccountManager alloc] initWithTextSecureAccountManager:[TSAccountManager sharedInstance] + redPhoneAccountManager:[RPAccountManager sharedInstance]]; + + OWSSyncPushTokensJob *syncPushTokensJob = + [[OWSSyncPushTokensJob alloc] initWithPushManager:[PushManager sharedManager] + accountManager:accountManager + preferences:[Environment preferences]]; + [syncPushTokensJob run]; +} + - (void)tableViewSetUp { self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; } @@ -363,16 +415,16 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; - (void)presentThread:(TSThread *)thread keyboardOnViewAppearing:(BOOL)keyboardOnViewAppearing { dispatch_async(dispatch_get_main_queue(), ^{ - MessagesViewController *mvc = [[UIStoryboard storyboardWithName:@"Storyboard" bundle:NULL] - instantiateViewControllerWithIdentifier:@"MessagesViewController"]; + MessagesViewController *mvc = [[UIStoryboard storyboardWithName:AppDelegateStoryboardMain bundle:NULL] + instantiateViewControllerWithIdentifier:@"MessagesViewController"]; - if (self.presentedViewController) { - [self.presentedViewController dismissViewControllerAnimated:YES completion:nil]; - } - [self.navigationController popToRootViewControllerAnimated:YES]; + if (self.presentedViewController) { + [self.presentedViewController dismissViewControllerAnimated:YES completion:nil]; + } + [self.navigationController popToRootViewControllerAnimated:YES]; - [mvc configureForThread:thread keyboardOnViewAppearing:keyboardOnViewAppearing]; - [self.navigationController pushViewController:mvc animated:YES]; + [mvc configureForThread:thread keyboardOnViewAppearing:keyboardOnViewAppearing]; + [self.navigationController pushViewController:mvc animated:YES]; }); } @@ -565,4 +617,16 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; _emptyBoxLabel.attributedText = fullLabelString; } +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + @end diff --git a/Signal/src/views/CopyableLabel.swift b/Signal/src/views/CopyableLabel.swift index e12eab538..797ac3a71 100644 --- a/Signal/src/views/CopyableLabel.swift +++ b/Signal/src/views/CopyableLabel.swift @@ -3,6 +3,7 @@ import UIKit +@objc(OWSCopyableLabel) class CopyableLabel : UILabel { deinit { diff --git a/Signal/test/Models/AccountManagerTest.swift b/Signal/test/Models/AccountManagerTest.swift new file mode 100644 index 000000000..3d8952340 --- /dev/null +++ b/Signal/test/Models/AccountManagerTest.swift @@ -0,0 +1,202 @@ +// Created by Michael Kirk on 10/26/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +import XCTest +import PromiseKit + +struct VerificationFailedError : Error { } +struct FailedToGetRPRegistrationTokenError : Error { } +struct FailedToRegisterWithRedphoneError : Error { } + +enum PushNotificationRequestResult : String { + case FailTSOnly = "FailTSOnly", + FailRPOnly = "FailRPOnly", + FailBoth = "FailBoth", + Succeed = "Succeed" +} + +class FailingTSAccountManager : TSAccountManager { + let phoneNumberAwaitingVerification = "+13235555555" + + override func verifyAccount(withCode: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) -> Void { + failure(VerificationFailedError()) + } + + override func registerForPushNotifications(pushToken: String, voipToken: String, success successHandler: @escaping () -> Void, failure failureHandler: @escaping (Error) -> Void) { + if pushToken == PushNotificationRequestResult.FailTSOnly.rawValue || pushToken == PushNotificationRequestResult.FailBoth.rawValue { + failureHandler(OWSErrorMakeUnableToProcessServerResponseError()) + } else { + successHandler() + } + } +} + +class VerifyingTSAccountManager : FailingTSAccountManager { + override func verifyAccount(withCode: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) -> Void { + success() + } + + override func obtainRPRegistrationToken(success: @escaping (String) -> Void, failure failureBlock: @escaping (Error) -> Void) { + failureBlock(FailedToGetRPRegistrationTokenError()) + } +} + +class TokenObtainingTSAccountManager : VerifyingTSAccountManager { + override func obtainRPRegistrationToken(success: @escaping (String) -> Void, failure failureBlock: @escaping (Error) -> Void) { + success("fakeRegistrationToken") + } +} + +class FailingRPAccountManager : RPAccountManager { + override func register(withTsToken tsToken: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) { + failure(FailedToRegisterWithRedphoneError()); + } +} + +class SuccessfulRPAccountManager : RPAccountManager { + override func register(withTsToken tsToken: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) { + if tsToken == "fakeRegistrationToken" { + success() + } else { + XCTFail("Unexpected registration token:\(tsToken)") + } + } + + override func registerForPushNotifications(pushToken: String, voipToken: String, success successHandler: @escaping () -> Void, failure failureHandler: @escaping (Error) -> Void) { + if pushToken == PushNotificationRequestResult.FailRPOnly.rawValue || pushToken == PushNotificationRequestResult.FailBoth.rawValue { + failureHandler(OWSErrorMakeUnableToProcessServerResponseError()) + } else { + successHandler() + } + } +} + +class AccountManagerTest: XCTestCase { + + let tsAccountManager = FailingTSAccountManager() + let rpAccountManager = FailingRPAccountManager() + + func testRegisterWhenEmptyCode() { + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + let expectation = self.expectation(description: "should fail") + + firstly { + accountManager.register(verificationCode: "") + }.then { + XCTFail("Should fail") + }.catch { error in + let nserror = error as NSError + if OWSErrorCode(rawValue: nserror.code) == OWSErrorCode.userError { + expectation.fulfill() + } else { + XCTFail("Unexpected error: \(error)") + } + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + + func testRegisterWhenVerificationFails() { + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + let expectation = self.expectation(description: "should fail") + + firstly { + accountManager.register(verificationCode: "123456") + }.then { + XCTFail("Should fail") + }.catch { error in + if error is VerificationFailedError { + expectation.fulfill() + } else { + XCTFail("Unexpected error: \(error)") + } + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + + func testObtainingTokenFails() { + let tsAccountManager = VerifyingTSAccountManager() + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + let expectation = self.expectation(description: "should fail") + + firstly { + accountManager.register(verificationCode: "123456") + }.then { + XCTFail("Should fail") + }.catch { error in + if error is FailedToGetRPRegistrationTokenError { + expectation.fulfill() + } else { + XCTFail("Unexpected error: \(error)") + } + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + + func testRedPhoneRegistrationFails() { + let tsAccountManager = TokenObtainingTSAccountManager() + let rpAccountManager = FailingRPAccountManager() + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + let expectation = self.expectation(description: "should fail") + + firstly { + accountManager.register(verificationCode: "123456") + }.then { + XCTFail("Should fail") + }.catch { error in + if error is FailedToRegisterWithRedphoneError { + expectation.fulfill() + } else { + XCTFail("Unexpected error: \(error)") + } + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + + func testSuccessfulRegistration() { + let tsAccountManager = TokenObtainingTSAccountManager() + let rpAccountManager = SuccessfulRPAccountManager() + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + let expectation = self.expectation(description: "should succeed") + + firstly { + accountManager.register(verificationCode: "123456") + }.then { + expectation.fulfill() + }.catch { error in + XCTFail("Unexpected error: \(error)") + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + + func testUpdatePushTokens() { + let accountManager = AccountManager(textSecureAccountManager: tsAccountManager, + redPhoneAccountManager: rpAccountManager) + + + let expectation = self.expectation(description: "should fail") + + accountManager.updatePushTokens(pushToken: PushNotificationRequestResult.FailTSOnly.rawValue, voipToken: "whatever").then { + XCTFail("Expected to fail.") + }.catch { error in + expectation.fulfill() + } + + self.waitForExpectations(timeout: 1.0, handler: nil) + } + +} diff --git a/Signal/test/SignalTests-Bridging-Header.h b/Signal/test/SignalTests-Bridging-Header.h new file mode 100644 index 000000000..f098150cf --- /dev/null +++ b/Signal/test/SignalTests-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// +#import "Signal-Bridging-Header.h" diff --git a/Signal/test/network/http/HttpRequestResponseTest.m b/Signal/test/network/http/HttpRequestResponseTest.m index af4753431..798ff87ac 100644 --- a/Signal/test/network/http/HttpRequestResponseTest.m +++ b/Signal/test/network/http/HttpRequestResponseTest.m @@ -22,7 +22,7 @@ - (void)testRequestToInitiate { [SignalKeyingStorage storeString:@"shall_not_password" forKey:SAVED_PASSWORD_KEY]; [SignalKeyingStorage storeString:[@2356 stringValue] forKey:PASSWORD_COUNTER_KEY]; - [TSStorageManager storePhoneNumber:@"+19027778888"]; + [[TSStorageManager sharedManager] storePhoneNumber:@"+19027778888"]; HttpRequest *h = [HttpRequest httpRequestToInitiateToRemoteNumber:[PhoneNumber phoneNumberFromE164:@"+19023334444"]]; @@ -51,7 +51,7 @@ - (void)testRequestToRing { [Environment setCurrent:testEnv]; - [TSStorageManager storePhoneNumber:@"+19025555555"]; + [[TSStorageManager sharedManager] storePhoneNumber:@"+19025555555"]; [SignalKeyingStorage storeString:@"shall_not_password" forKey:SAVED_PASSWORD_KEY]; [SignalKeyingStorage storeString:@"-1" forKey:PASSWORD_COUNTER_KEY]; HttpRequest *h = [HttpRequest httpRequestToRingWithSessionId:458847238]; diff --git a/Signal/translations/bin/auto-genstrings b/Signal/translations/bin/auto-genstrings index 831b502b6..9c5b58fe9 100755 --- a/Signal/translations/bin/auto-genstrings +++ b/Signal/translations/bin/auto-genstrings @@ -10,7 +10,7 @@ if [ ! -d "Signal/src" ]; then fi # Search directories for .m & .h files and collect string definitions with genstrings -find $TARGETS -name "*.m" -print0 -name "*.h" -print0 | xargs -0 genstrings -o $TMP +find $TARGETS -name "*.m" -print0 -o -name "*.h" -print0 -o -name "*.swift" -print0 | xargs -0 genstrings -o $TMP # We have to convert the old and new .strings files to UTF-8 in order to deal with them OLDUTF8=$(iconv -f UTF-16 -t UTF-8 $STRINGFILE) diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index a0cb16df288af3ae1c6f540cdbea40098c58d6d2..a87f1e56b101e255f9f57dd0f3afec5e5d89df62 100644 GIT binary patch delta 605 zcma)3-77<39RAHNtQn;oYTC|;3pZ@#Mv~-XDYW@aalyXqEVgOqY)Dd)QkL1TTn@#( zI+qH6f*Wp@@^`rMyzdUlmEPXZ-}^l8^E~g-tFuyb-anl4;hTq5zlRS>Zf-31I~I7Q zoNHbpgOZdWjnb55eM;rpbIV|qvhplSTd3yAM7@X`N!r26#LAQvjpCHUT@#gEj0S(6 z_gKM;is#pJE~}~f*uVp)?VSlKfDJEE3OsqBQrHAKm83!L?xAk)udPU~L?453O?K)@$|MoH>QRMmrMDmO6xV_T9Pp{lw2t{x+Wb z@bdnuSLSea>{5F{^AGzrht?`02-74&ux<0T((PE}?X!-91ljA3bOlF@bH2HhK#AMePDfUE$Vg?0<;>iLN@`65