From 47783a9df0cf4493f9f50bfcac1f16b05824ae30 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 26 May 2017 16:32:04 -0700 Subject: [PATCH 1/3] request confirmation when calling changed SN ...rather than failing with "SN Changed" error // FREEBIE --- .../ViewControllers/MessagesViewController.m | 24 ++++++++++++++++--- .../translations/en.lproj/Localizable.strings | 6 ++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 28f587515..94c7f0d01 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1583,6 +1583,7 @@ typedef enum : NSUInteger { } - (void)showConfirmIdentityUIForRecipientIdentity:(OWSRecipientIdentity *)recipientIdentity + confirmationText:(NSString *)confirmationText completion:(void (^)(BOOL didConfirmedIdentity))completionHandler { NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:recipientIdentity.recipientId]; @@ -1603,9 +1604,7 @@ typedef enum : NSUInteger { [actionSheet addAction:[UIAlertAction - actionWithTitle: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", - @"button title to confirm sending to a recipient whose safety number recently changed") + actionWithTitle:confirmationText style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { DDLogInfo(@"%@ Confirmed sending identity: %@", self.tag, recipientIdentity); @@ -1688,6 +1687,22 @@ typedef enum : NSUInteger { return; } + OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = [self unconfirmedIdentityThatShouldBlockSending]; + if (unconfirmedIdentityThatShouldBlockSending != nil) { + __weak MessagesViewController *weakSelf = self; + [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending + confirmationText:NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", + @"button title to confirm calling a recipient whose safety " + @"number recently changed") + completion:^(BOOL didConfirmedIdentity) { + if (didConfirmedIdentity) { + [weakSelf callAction:sender]; + } + }]; + return; + } + + [self.outboundCallInitiator initiateCallWithRecipientId:self.thread.contactIdentifier]; } @@ -1737,6 +1752,9 @@ typedef enum : NSUInteger { if (unconfirmedIdentityThatShouldBlockSending != nil) { __weak MessagesViewController *weakSelf = self; [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending + confirmationText:NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", + @"button title to confirm sending to a recipient whose " + @"safety number recently changed") completion:^(BOOL didConfirmedIdentity) { if (didConfirmedIdentity) { [weakSelf didPressSendButton:button diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 7ef055594..0ac295940 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1051,6 +1051,9 @@ /* Generic text for button that retries whatever the last action was. */ "RETRY_BUTTON_TEXT" = "Retry"; +/* button title to confirm calling a recipient whose safety number recently changed */ +"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION" = "Confirm and Call"; + /* button title to confirm sending to a recipient whose safety number recently changed */ "SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION" = "Confirm and Send"; @@ -1363,7 +1366,8 @@ /* Label indicating the phone number currently being verified. */ "VERIFICATION_PHONE_NUMBER_FORMAT" = "Enter the verification code we sent to %@."; -/* table cell label in conversation settings */ +/* Action sheet item + table cell label in conversation settings */ "VERIFY_PRIVACY" = "Show Safety Number"; /* Indicates how to cancel a voice message. */ From 2d7f03a1c815ad2152fedddc371f11b03d712fd4 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 26 May 2017 16:56:24 -0700 Subject: [PATCH 2/3] interstitial SN confirmation for attachments/voicenotes // FREEBIE --- .../ViewControllers/MessagesViewController.m | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 94c7f0d01..953e01ff4 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1702,7 +1702,6 @@ typedef enum : NSUInteger { return; } - [self.outboundCallInitiator initiateCallWithRecipientId:self.thread.contactIdentifier]; } @@ -3706,6 +3705,20 @@ typedef enum : NSUInteger { return; } + OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = [self unconfirmedIdentityThatShouldBlockSending]; + if (unconfirmedIdentityThatShouldBlockSending != nil) { + __weak MessagesViewController *weakSelf = self; + [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending + confirmationText:NSLocalizedString(@"CONFIRMATION_TITLE", + @"Generic button text to proceed with an action") + completion:^(BOOL didConfirmedIdentity) { + if (didConfirmedIdentity) { + [weakSelf didPressAccessoryButton:nil]; + } + }]; + return; + } + UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -3946,6 +3959,23 @@ typedef enum : NSUInteger { return; } + OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = + [self unconfirmedIdentityThatShouldBlockSending]; + if (unconfirmedIdentityThatShouldBlockSending != nil) { + __weak MessagesViewController *weakSelf = self; + [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending + confirmationText:NSLocalizedString( + @"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", + @"button title to confirm sending to a recipient whose " + @"safety number recently changed") + completion:^(BOOL didConfirmedIdentity) { + if (didConfirmedIdentity) { + [weakSelf tryToSendAttachmentIfApproved:attachment]; + } + }]; + return; + } + if (attachment == nil || [attachment hasError]) { DDLogWarn(@"%@ %s Invalid attachment: %@.", self.tag, From 9a2f218bf367dad29ef8025b3a7d7dd48e99c12b Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 26 May 2017 18:19:46 -0700 Subject: [PATCH 3/3] show SN confirmation before adding to group * Extract SN confirmation dialog // FREEBIE --- Signal.xcodeproj/project.pbxproj | 4 + Signal/src/Signal-Bridging-Header.h | 3 + Signal/src/UIStoryboard+OWS.swift | 4 + .../AddToGroupViewController.m | 50 ++++- .../FingerprintViewController.h | 12 +- .../FingerprintViewController.m | 8 +- .../ViewControllers/MessagesViewController.m | 197 ++++++------------ .../ViewControllers/NewGroupViewController.m | 42 ++++ ...SConversationSettingsTableViewController.m | 5 +- .../SafetyNumberConfirmationAlert.swift | 119 +++++++++++ .../translations/en.lproj/Localizable.strings | 9 +- 11 files changed, 298 insertions(+), 155 deletions(-) create mode 100644 Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index faece5f8a..d8d9f524c 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -143,6 +143,7 @@ 45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; }; 4585C4601ED4FD0400896AEA /* OWS104CreateRecipientIdentities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4585C45F1ED4FD0400896AEA /* OWS104CreateRecipientIdentities.m */; }; 4585C4661ED5DF7A00896AEA /* ProfileFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585C4651ED5DF7A00896AEA /* ProfileFetcherJob.swift */; }; + 4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */; }; 458967111DC117CC00E9DD21 /* AccountManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */; }; 458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */; }; 458DE9D91DEE7B360071BB03 /* OWSWebRTCDataProtos.pb.m in Sources */ = {isa = PBXBuildFile; fileRef = 458DE9D81DEE7B360071BB03 /* OWSWebRTCDataProtos.pb.m */; }; @@ -559,6 +560,7 @@ 4585C45E1ED4FD0400896AEA /* OWS104CreateRecipientIdentities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS104CreateRecipientIdentities.h; path = Migrations/OWS104CreateRecipientIdentities.h; sourceTree = ""; }; 4585C45F1ED4FD0400896AEA /* OWS104CreateRecipientIdentities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS104CreateRecipientIdentities.m; path = Migrations/OWS104CreateRecipientIdentities.m; sourceTree = ""; }; 4585C4651ED5DF7A00896AEA /* ProfileFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileFetcherJob.swift; sourceTree = ""; }; + 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafetyNumberConfirmationAlert.swift; 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 = ""; }; 458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerConnectionClient.swift; sourceTree = ""; }; @@ -976,6 +978,7 @@ 340CB2261EAC25820001CAA1 /* UpdateGroupViewController.m */, 34B3F8A01E8EA6040035BE1A /* ViewControllerUtils.h */, 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */, + 4585C4671ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift */, ); path = ViewControllers; sourceTree = ""; @@ -2071,6 +2074,7 @@ EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */, 45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */, 45794E861E00620000066731 /* CallUIAdapter.swift in Sources */, + 4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */, FCFA64B71A24F6730007FB87 /* UIFont+OWS.m in Sources */, B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */, 45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */, diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 5c1f3dd43..5a2fad749 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -7,6 +7,7 @@ #import "AttachmentSharing.h" #import "Environment.h" #import "FLAnimatedImage.h" +#import "FingerprintViewController.h" #import "NotificationsManager.h" #import "OWSAnyTouchGestureRecognizer.h" #import "OWSAudioAttachmentPlayer.h" @@ -56,6 +57,8 @@ #import #import #import +#import +#import #import #import #import diff --git a/Signal/src/UIStoryboard+OWS.swift b/Signal/src/UIStoryboard+OWS.swift index 0b93a7ea9..0b57c7342 100644 --- a/Signal/src/UIStoryboard+OWS.swift +++ b/Signal/src/UIStoryboard+OWS.swift @@ -12,4 +12,8 @@ extension UIStoryboard { class var main: UIStoryboard { return UIStoryboard(name: StoryboardName.main.rawValue, bundle: Bundle.main) } + + class func instantiateFingerprintViewController() -> FingerprintViewController { + return self.main.instantiateViewController(withIdentifier: "FingerprintViewController") as! FingerprintViewController + } } diff --git a/Signal/src/ViewControllers/AddToGroupViewController.m b/Signal/src/ViewControllers/AddToGroupViewController.m index abd9b45ed..e4b85f952 100644 --- a/Signal/src/ViewControllers/AddToGroupViewController.m +++ b/Signal/src/ViewControllers/AddToGroupViewController.m @@ -6,6 +6,7 @@ #import "BlockListUIUtils.h" #import "ContactsViewHelper.h" #import "OWSContactsManager.h" +#import "Signal-Swift.h" #import NS_ASSUME_NONNULL_BEGIN @@ -50,6 +51,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(phoneNumber.length > 0); __weak AddToGroupViewController *weakSelf = self; + ContactsViewHelper *helper = self.contactsViewHelper; if ([helper isRecipientIdBlocked:phoneNumber]) { [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber @@ -61,9 +63,27 @@ NS_ASSUME_NONNULL_BEGIN [weakSelf addToGroup:phoneNumber]; } }]; - } else { - [self addToGroup:phoneNumber]; + return; } + + BOOL didShowSNAlert = [SafetyNumberConfirmationAlert + presentAlertIfNecessaryFromViewController:self + recipientId:phoneNumber + confirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", + @"button title to confirm adding a recipient to a group when their safety " + @"number has recently changed") + contactsManager:helper.contactsManager + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addToGroup:phoneNumber]; + } + }]; + if (didShowSNAlert) { + return; + } + + [self addToGroup:phoneNumber]; } - (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount @@ -83,7 +103,9 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(0); return; - } else if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { + } + + if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount fromViewController:self blockingManager:helper.blockingManager @@ -93,9 +115,27 @@ NS_ASSUME_NONNULL_BEGIN [weakSelf addToGroup:signalAccount.recipientId]; } }]; - } else { - [self addToGroup:signalAccount.recipientId]; + return; + } + + BOOL didShowSNAlert = [SafetyNumberConfirmationAlert + presentAlertIfNecessaryFromViewController:self + recipientId:signalAccount.recipientId + confirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", + @"button title to confirm adding a recipient to a group when their safety " + @"number has recently changed") + contactsManager:helper.contactsManager + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addToGroup:signalAccount.recipientId]; + } + }]; + if (didShowSNAlert) { + return; } + + [self addToGroup:signalAccount.recipientId]; } - (void)addToGroup:(NSString *)recipientId diff --git a/Signal/src/ViewControllers/FingerprintViewController.h b/Signal/src/ViewControllers/FingerprintViewController.h index 915c5f590..826650e6f 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.h +++ b/Signal/src/ViewControllers/FingerprintViewController.h @@ -1,16 +1,11 @@ // -// FingerprintViewController.h -// Signal -// -// Created by Dylan Bourgeois on 02/11/14. -// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSQRCodeScanningViewController.h" NS_ASSUME_NONNULL_BEGIN -@class TSThread; @class OWSFingerprint; @class OWSConversationSettingsTableViewController; @@ -18,9 +13,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable) OWSConversationSettingsTableViewController *dismissDelegate; -- (void)configureWithThread:(TSThread *)thread - fingerprint:(OWSFingerprint *)fingerprint - contactName:(NSString *)contactName; +- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint + contactName:(NSString *)contactName NS_SWIFT_NAME(configure(fingerprint:contactName:)); - (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data; diff --git a/Signal/src/ViewControllers/FingerprintViewController.m b/Signal/src/ViewControllers/FingerprintViewController.m index 1f396afc4..913c2406e 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.m +++ b/Signal/src/ViewControllers/FingerprintViewController.m @@ -15,14 +15,12 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN @interface FingerprintViewController () @property (strong, nonatomic) TSStorageManager *storageManager; -@property (strong, nonatomic) TSThread *thread; @property (strong, nonatomic) OWSFingerprint *fingerprint; @property (strong, nonatomic) NSString *contactName; @property (strong, nonatomic) OWSQRCodeScanningViewController *qrScanningController; @@ -45,11 +43,9 @@ NS_ASSUME_NONNULL_BEGIN @implementation FingerprintViewController -- (void)configureWithThread:(TSThread *)thread - fingerprint:(OWSFingerprint *)fingerprint - contactName:(NSString *)contactName +- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint + contactName:(NSString *)contactName { - self.thread = thread; self.fingerprint = fingerprint; self.contactName = contactName; } diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 953e01ff4..6f4bcbb04 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1567,75 +1567,20 @@ typedef enum : NSUInteger { #pragma mark - Identity /** - * Returns the first unconfirmed recipient identity in the thread. + * Shows confirmation dialog if at least one of the recipient id's is not confirmed. + * + * returns YES if an alert was shown + * NO if there were no unconfirmed identities */ -- (nullable OWSRecipientIdentity *)unconfirmedIdentityThatShouldBlockSending -{ - for (NSString *recipientId in self.thread.recipientIdentifiers) { - OWSRecipientIdentity *unconfirmedIdentity = - [self.storageManager unconfirmedIdentityThatShouldBlockSendingForRecipientId:recipientId]; - if (unconfirmedIdentity != nil) { - DDLogInfo(@"%@ unconfirmedIdentityThatShouldBlockSending: %@", self.tag, recipientId); - return unconfirmedIdentity; - } - } - return nil; -} - -- (void)showConfirmIdentityUIForRecipientIdentity:(OWSRecipientIdentity *)recipientIdentity - confirmationText:(NSString *)confirmationText - completion:(void (^)(BOOL didConfirmedIdentity))completionHandler -{ - NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:recipientIdentity.recipientId]; - - NSString *titleFormat = NSLocalizedString(@"CONFIRM_SENDING_TO_CHANGED_IDENTITY_TITLE_FORMAT", - @"Action sheet title presented when a users's SN have recently changed. Embeds {{contact's name or phone " - @"number}}"); - NSString *title = [NSString stringWithFormat:titleFormat, displayName]; - - NSString *bodyFormat = NSLocalizedString(@"CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT", - @"Action sheet body presented when a users's SN have recently changed. Embeds {{contact's name or phone " - @"number}}"); - NSString *body = [NSString stringWithFormat:bodyFormat, displayName]; - - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:title - message:body - preferredStyle:UIAlertControllerStyleActionSheet]; - - [actionSheet - addAction:[UIAlertAction - actionWithTitle:confirmationText - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - DDLogInfo(@"%@ Confirmed sending identity: %@", self.tag, recipientIdentity); - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - [[TSStorageManager sharedManager] saveRemoteIdentity:recipientIdentity.identityKey - recipientId:recipientIdentity.recipientId - approvedForBlockingUse:YES - approvedForNonBlockingUse:YES]; - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(YES); - }); - }); - }]]; - - [actionSheet addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"VERIFY_PRIVACY", @"Action sheet item") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - DDLogInfo(@"%@ verifying sending identity: %@", self.tag, recipientIdentity); - [self showFingerprintWithTheirIdentityKey:recipientIdentity.identityKey - theirSignalId:recipientIdentity.recipientId]; - completionHandler(NO); - }]]; - - [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil) - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *_Nonnull action) { - completionHandler(NO); - }]]; - - [self presentViewController:actionSheet animated:YES completion:nil]; +- (BOOL)showSafetyNumberConfirmationIfNecessaryWithConfirmationText:(NSString *)confirmationText + completion: + (void (^)(BOOL didConfirmedIdentity))completionHandler +{ + return [SafetyNumberConfirmationAlert presentAlertIfNecessaryFromViewController:self + recipientIds:self.thread.recipientIdentifiers + confirmationText:confirmationText + contactsManager:self.contactsManager + completion:completionHandler]; } - (void)showFingerprintWithTheirIdentityKey:(NSData *)theirIdentityKey theirSignalId:(NSString *)theirSignalId @@ -1663,7 +1608,7 @@ typedef enum : NSUInteger { } FingerprintViewController *fingerprintViewController = (FingerprintViewController *)viewController; - [fingerprintViewController configureWithThread:self.thread fingerprint:fingerprint contactName:contactName]; + [fingerprintViewController configureWithFingerprint:fingerprint contactName:contactName]; [self presentViewController:fingerprintViewController animated:YES completion:nil]; } @@ -1677,8 +1622,8 @@ typedef enum : NSUInteger { return; } + __weak MessagesViewController *weakSelf = self; if ([self isBlockedContactConversation]) { - __weak MessagesViewController *weakSelf = self; [self showUnblockContactUI:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf callAction:nil]; @@ -1687,18 +1632,16 @@ typedef enum : NSUInteger { return; } - OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = [self unconfirmedIdentityThatShouldBlockSending]; - if (unconfirmedIdentityThatShouldBlockSending != nil) { - __weak MessagesViewController *weakSelf = self; - [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending - confirmationText:NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", - @"button title to confirm calling a recipient whose safety " - @"number recently changed") - completion:^(BOOL didConfirmedIdentity) { - if (didConfirmedIdentity) { - [weakSelf callAction:sender]; - } - }]; + BOOL didShowSNAlert = [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", + @"button title to confirm calling a recipient whose safety " + @"number recently changed") + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf callAction:sender]; + } + }]; + if (didShowSNAlert) { return; } @@ -1732,8 +1675,9 @@ typedef enum : NSUInteger { date:(NSDate *)date updateKeyboardState:(BOOL)updateKeyboardState { + + __weak MessagesViewController *weakSelf = self; if ([self isBlockedContactConversation]) { - __weak MessagesViewController *weakSelf = self; [self showUnblockContactUI:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf didPressSendButton:button @@ -1747,23 +1691,22 @@ typedef enum : NSUInteger { return; } - OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = [self unconfirmedIdentityThatShouldBlockSending]; - if (unconfirmedIdentityThatShouldBlockSending != nil) { - __weak MessagesViewController *weakSelf = self; - [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending - confirmationText:NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", - @"button title to confirm sending to a recipient whose " - @"safety number recently changed") - completion:^(BOOL didConfirmedIdentity) { - if (didConfirmedIdentity) { - [weakSelf didPressSendButton:button - withMessageText:text - senderId:senderId - senderDisplayName:senderDisplayName - date:date - updateKeyboardState:NO]; - } - }]; + BOOL didShowSNAlert = + [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", + @"button title to confirm sending to a recipient whose " + @"safety number recently changed") + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf didPressSendButton:button + withMessageText:text + senderId:senderId + senderDisplayName:senderDisplayName + date:date + updateKeyboardState:NO]; + } + }]; + if (didShowSNAlert) { return; } @@ -2757,7 +2700,7 @@ typedef enum : NSUInteger { [[UIStoryboard main] instantiateViewControllerWithIdentifier:@"FingerprintViewController"]; NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:signalId]; - [fingerprintViewController configureWithThread:self.thread fingerprint:fingerprint contactName:contactName]; + [fingerprintViewController configureWithFingerprint:fingerprint contactName:contactName]; [self presentViewController:fingerprintViewController animated:YES completion:nil]; } @@ -3695,8 +3638,8 @@ typedef enum : NSUInteger { - (void)didPressAccessoryButton:(UIButton *)sender { + __weak MessagesViewController *weakSelf = self; if ([self isBlockedContactConversation]) { - __weak MessagesViewController *weakSelf = self; [self showUnblockContactUI:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf didPressAccessoryButton:nil]; @@ -3705,20 +3648,19 @@ typedef enum : NSUInteger { return; } - OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = [self unconfirmedIdentityThatShouldBlockSending]; - if (unconfirmedIdentityThatShouldBlockSending != nil) { - __weak MessagesViewController *weakSelf = self; - [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending - confirmationText:NSLocalizedString(@"CONFIRMATION_TITLE", - @"Generic button text to proceed with an action") - completion:^(BOOL didConfirmedIdentity) { - if (didConfirmedIdentity) { - [weakSelf didPressAccessoryButton:nil]; - } - }]; + BOOL didShowSNAlert = + [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText: + NSLocalizedString(@"CONFIRMATION_TITLE", @"Generic button text to proceed with an action") + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf didPressAccessoryButton:nil]; + } + }]; + if (didShowSNAlert) { return; } + UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -3949,8 +3891,8 @@ typedef enum : NSUInteger { DDLogError(@"%@ %s", self.tag, __PRETTY_FUNCTION__); DispatchMainThreadSafe(^{ + __weak MessagesViewController *weakSelf = self; if ([self isBlockedContactConversation]) { - __weak MessagesViewController *weakSelf = self; [self showUnblockContactUI:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf tryToSendAttachmentIfApproved:attachment]; @@ -3959,20 +3901,18 @@ typedef enum : NSUInteger { return; } - OWSRecipientIdentity *unconfirmedIdentityThatShouldBlockSending = - [self unconfirmedIdentityThatShouldBlockSending]; - if (unconfirmedIdentityThatShouldBlockSending != nil) { - __weak MessagesViewController *weakSelf = self; - [self showConfirmIdentityUIForRecipientIdentity:unconfirmedIdentityThatShouldBlockSending - confirmationText:NSLocalizedString( - @"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", - @"button title to confirm sending to a recipient whose " - @"safety number recently changed") - completion:^(BOOL didConfirmedIdentity) { - if (didConfirmedIdentity) { - [weakSelf tryToSendAttachmentIfApproved:attachment]; - } - }]; + BOOL didShowSNAlert = [self + showSafetyNumberConfirmationIfNecessaryWithConfirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION", + @"button title to confirm sending to a recipient whose " + @"safety number recently changed") + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf + tryToSendAttachmentIfApproved:attachment]; + } + }]; + if (didShowSNAlert) { return; } @@ -3985,7 +3925,6 @@ typedef enum : NSUInteger { } else if (skipApprovalDialog) { [self sendMessageAttachment:attachment]; } else { - __weak MessagesViewController *weakSelf = self; UIViewController *viewController = [[AttachmentApprovalViewController alloc] initWithAttachment:attachment successCompletion:^{ diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index b9b1c8aeb..9b7dd114f 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -250,6 +250,28 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; } }]; } else { + + BOOL didShowSNAlert = [SafetyNumberConfirmationAlert + presentAlertIfNecessaryFromViewController:self + recipientId:recipientId + confirmationText:NSLocalizedString( + @"SAFETY_NUMBER_CHANGED_CONFIRM_" + @"ADD_TO_GROUP_ACTION", + @"button title to confirm adding " + @"a recipient to a group when " + @"their safety " + @"number has recently changed") + contactsManager:contactsViewHelper.contactsManager + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addRecipientId:recipientId]; + } + }]; + if (didShowSNAlert) { + return; + } + + [weakSelf addRecipientId:recipientId]; } }]]; @@ -319,6 +341,26 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; } }]; } else { + BOOL didShowSNAlert = [SafetyNumberConfirmationAlert + presentAlertIfNecessaryFromViewController:self + recipientId:signalAccount.recipientId + confirmationText:NSLocalizedString( + @"SAFETY_NUMBER_CHANGED_CONFIRM_" + @"ADD_TO_GROUP_ACTION", + @"button title to confirm adding " + @"a recipient to a group when " + @"their safety " + @"number has recently changed") + contactsManager:contactsViewHelper.contactsManager + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addRecipientId:recipientId]; + } + }]; + if (didShowSNAlert) { + return; + } + [weakSelf addRecipientId:recipientId]; } }]]; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 22bbd47ad..dd3611e4b 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -249,9 +249,8 @@ NS_ASSUME_NONNULL_BEGIN OWSFingerprint *fingerprint = [fingerprintBuilder fingerprintWithTheirSignalId:strongSelf.thread.contactIdentifier]; - [fingerprintViewController configureWithThread:strongSelf.thread - fingerprint:fingerprint - contactName:[strongSelf threadName]]; + [fingerprintViewController configureWithFingerprint:fingerprint + contactName:[strongSelf threadName]]; fingerprintViewController.dismissDelegate = strongSelf; [strongSelf presentViewController:fingerprintViewController animated:YES completion:nil]; diff --git a/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift new file mode 100644 index 000000000..f9dae9b40 --- /dev/null +++ b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift @@ -0,0 +1,119 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +import Foundation + +class SafetyNumberConfirmationAlert: NSObject { + + let TAG = "[SafetyNumberConfirmationAlert]" + + private let contactsManager: OWSContactsManager + private let storageManager: TSStorageManager + + init(contactsManager: OWSContactsManager) { + self.contactsManager = contactsManager + self.storageManager = TSStorageManager.shared() + } + + public class func presentAlertIfNecessary(fromViewController: UIViewController, recipientId: String, confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { + return self.presentAlertIfNecessary(fromViewController: fromViewController, recipientIds: [recipientId], confirmationText: confirmationText, contactsManager: contactsManager, completion: completion) + } + + public class func presentAlertIfNecessary(fromViewController: UIViewController, recipientIds: [String], confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { + return SafetyNumberConfirmationAlert(contactsManager: contactsManager).presentIfNecessary(fromViewController: fromViewController, + recipientIds: recipientIds, + confirmationText: confirmationText, + completion: completion) + } + + /** + * Shows confirmation dialog if at least one of the recipient id's is not confirmed. + * + * @returns true if an alert was shown + * false if there were no unconfirmed identities + */ + public func presentIfNecessary(fromViewController: UIViewController, recipientIds: [String], confirmationText: String, completion: @escaping (Bool) -> Void) -> Bool { + + guard let unconfirmedIdentity = self.unconfirmedIdentityThatShouldBlockSending(recipientIds: recipientIds) else { + // No identities to confirm, no alert to present. + return false + } + + let displayName: String = { + if let signalAccount = contactsManager.signalAccountMap[unconfirmedIdentity.recipientId] { + return contactsManager.displayName(for: signalAccount) + } else { + return contactsManager.displayName(forPhoneIdentifier: unconfirmedIdentity.recipientId) + } + }() + + let titleFormat = NSLocalizedString("CONFIRM_SENDING_TO_CHANGED_IDENTITY_TITLE_FORMAT", + comment: "Action sheet title presented when a users's SN have recently changed. Embeds {{contact's name or phone number}}") + let title = String(format: titleFormat, displayName) + + let bodyFormat = NSLocalizedString("CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT", + comment: "Action sheet body presented when a user's SN have recently changed. Embeds {{contact's name or phone nubmer}}") + let body = String(format: bodyFormat, displayName) + + let actionSheetController = UIAlertController(title: title, message:body, preferredStyle: .actionSheet) + + let confirmAction = UIAlertAction(title: confirmationText, style: .default) { _ in + Logger.info("\(self.TAG) Confirmed identity: \(unconfirmedIdentity)") + OWSDispatch.sessionStoreQueue().async { + self.storageManager.saveRemoteIdentity(unconfirmedIdentity.identityKey, + recipientId: unconfirmedIdentity.recipientId, + approvedForBlockingUse: true, + approvedForNonBlockingUse: true) + DispatchQueue.main.async { + completion(true) + } + } + } + actionSheetController.addAction(confirmAction) + + let showSafetyNumberAction = UIAlertAction(title: NSLocalizedString("VERIFY_PRIVACY", comment: "Action sheet item"), style: .default) { _ in + Logger.info("\(self.TAG) Opted to show Safety Number for identity: \(unconfirmedIdentity)") + + self.presentSafetyNumberViewController(fromViewController: fromViewController, + theirIdentityKey: unconfirmedIdentity.identityKey, + theirRecipientId: unconfirmedIdentity.recipientId, + theirDisplayName: displayName, + completion: { completion(false) }) + + } + actionSheetController.addAction(showSafetyNumberAction) + + let dismissAction = UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: "generic cancel text"), style: .cancel) + actionSheetController.addAction(dismissAction) + + fromViewController.present(actionSheetController, animated: true) + return true + } + + public func presentSafetyNumberViewController(fromViewController: UIViewController, theirIdentityKey: Data, theirRecipientId: String, theirDisplayName: String, completion: (() -> Void)? = nil) { + let fingerprintViewController = UIStoryboard.instantiateFingerprintViewController() + + let fingerprintBuilder = OWSFingerprintBuilder(storageManager: self.storageManager, contactsManager: self.contactsManager) + let fingerprint = fingerprintBuilder.fingerprint(withTheirSignalId: theirRecipientId, theirIdentityKey: theirIdentityKey) + + fingerprintViewController.configure(fingerprint: fingerprint, contactName: theirDisplayName) + + fromViewController.present(fingerprintViewController, animated: true, completion: completion) + } + + private func unconfirmedIdentitiesThatShouldBlockSending(recipientIds: [String]) -> [OWSRecipientIdentity] { + return recipientIds.flatMap { + return self.storageManager.unconfirmedIdentityThatShouldBlockSending(forRecipientId: $0) + } + } + + private func unconfirmedIdentityThatShouldBlockSending(recipientIds: [String]) -> OWSRecipientIdentity? { + return unconfirmedIdentitiesThatShouldBlockSending(recipientIds: recipientIds).first + } + + private func shouldShow(recipientIds: [String]) -> Bool { + return !unconfirmedIdentitiesThatShouldBlockSending(recipientIds: recipientIds).isEmpty + } + +} diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0ac295940..dfccf5130 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -241,13 +241,13 @@ /* Button text */ "CONFIRM_LINK_NEW_DEVICE_ACTION" = "Link New Device"; -/* Action sheet body presented when a users's SN have recently changed. Embeds {{contact's name or phone number}} */ +/* Action sheet body presented when a user's SN have recently changed. Embeds {{contact's name or phone nubmer}} */ "CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT" = "%@ may have reinstalled or changed devices. Verify your Safety Number with them to ensure privacy."; /* Action sheet title presented when a users's SN have recently changed. Embeds {{contact's name or phone number}} */ "CONFIRM_SENDING_TO_CHANGED_IDENTITY_TITLE_FORMAT" = "Safety Number with %@ has Changed"; -/* No comment provided by engineer. */ +/* Generic button text to proceed with an action */ "CONFIRMATION_TITLE" = "Confirm"; /* An indicator that a contact has been blocked. */ @@ -1051,6 +1051,9 @@ /* Generic text for button that retries whatever the last action was. */ "RETRY_BUTTON_TEXT" = "Retry"; +/* button title to confirm adding a recipient to a group when their safety number has recently changed */ +"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION" = "Confirm and Add to Group"; + /* button title to confirm calling a recipient whose safety number recently changed */ "SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION" = "Confirm and Call"; @@ -1276,7 +1279,7 @@ /* No comment provided by engineer. */ "SUCCESSFUL_VERIFICATION_TITLE" = "Safety Number Verified!"; -/* No comment provided by engineer. */ +/* generic cancel text */ "TXT_CANCEL_TITLE" = "Cancel"; /* No comment provided by engineer. */