220 lines
10 KiB
Objective-C
220 lines
10 KiB
Objective-C
//
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "OWSLinkDeviceViewController.h"
|
|
#import "OWSDeviceProvisioningURLParser.h"
|
|
#import "OWSLinkedDevicesTableViewController.h"
|
|
#import "Signal-Swift.h"
|
|
#import <SignalCoreKit/Cryptography.h>
|
|
#import <SignalMessaging/OWSProfileManager.h>
|
|
#import <SignalServiceKit/OWSDevice.h>
|
|
#import <SignalServiceKit/OWSDeviceProvisioner.h>
|
|
#import <SignalServiceKit/OWSIdentityManager.h>
|
|
#import <SignalServiceKit/OWSReadReceiptManager.h>
|
|
#import <SignalServiceKit/TSAccountManager.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface OWSLinkDeviceViewController ()
|
|
|
|
@property (nonatomic) YapDatabaseConnection *dbConnection;
|
|
@property (nonatomic) IBOutlet UIView *qrScanningView;
|
|
@property (nonatomic) IBOutlet UILabel *scanningInstructionsLabel;
|
|
@property (nonatomic) OWSQRCodeScanningViewController *qrScanningController;
|
|
@property (nonatomic, readonly) OWSReadReceiptManager *readReceiptManager;
|
|
|
|
@end
|
|
|
|
@implementation OWSLinkDeviceViewController
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
|
|
self.dbConnection = [[OWSPrimaryStorage sharedManager] newDatabaseConnection];
|
|
|
|
// HACK to get full width preview layer
|
|
CGRect oldFrame = self.qrScanningView.frame;
|
|
self.qrScanningView.frame = CGRectMake(
|
|
oldFrame.origin.x, oldFrame.origin.y, self.view.frame.size.width, self.view.frame.size.height / 2.0f - 32.0f);
|
|
// END HACK to get full width preview layer
|
|
|
|
self.scanningInstructionsLabel.text = NSLocalizedString(@"LINK_DEVICE_SCANNING_INSTRUCTIONS",
|
|
@"QR Scanning screen instructions, placed alongside a camera view for scanning QR Codes");
|
|
self.title
|
|
= NSLocalizedString(@"LINK_NEW_DEVICE_TITLE", "Navigation title when scanning QR code to add new device.");
|
|
}
|
|
|
|
- (OWSProfileManager *)profileManager
|
|
{
|
|
return [OWSProfileManager sharedManager];
|
|
}
|
|
|
|
- (OWSReadReceiptManager *)readReceiptManager
|
|
{
|
|
return [OWSReadReceiptManager sharedManager];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
[super viewWillAppear:animated];
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
[super viewDidAppear:animated];
|
|
[self.qrScanningController startCapture];
|
|
}
|
|
|
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
|
|
{
|
|
if ([segue.identifier isEqualToString:@"embedDeviceQRScanner"]) {
|
|
OWSQRCodeScanningViewController *qrScanningController
|
|
= (OWSQRCodeScanningViewController *)segue.destinationViewController;
|
|
qrScanningController.scanDelegate = self;
|
|
self.qrScanningController = qrScanningController;
|
|
}
|
|
}
|
|
|
|
|
|
// pragma mark - OWSQRScannerDelegate
|
|
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithString:(NSString *)string
|
|
{
|
|
OWSDeviceProvisioningURLParser *parser = [[OWSDeviceProvisioningURLParser alloc] initWithProvisioningURL:string];
|
|
if (!parser.isValid) {
|
|
OWSLogError(@"Unable to parse provisioning params from QRCode: %@", string);
|
|
|
|
NSString *title = NSLocalizedString(@"LINK_DEVICE_INVALID_CODE_TITLE", @"report an invalid linking code");
|
|
NSString *body = NSLocalizedString(@"LINK_DEVICE_INVALID_CODE_BODY", @"report an invalid linking code");
|
|
|
|
UIAlertController *alertController =
|
|
[UIAlertController alertControllerWithTitle:title message:body preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
UIAlertAction *cancelAction =
|
|
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
|
|
style:UIAlertActionStyleCancel
|
|
handler:^(UIAlertAction *action) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.navigationController popViewControllerAnimated:YES];
|
|
});
|
|
}];
|
|
[alertController addAction:cancelAction];
|
|
|
|
UIAlertAction *proceedAction =
|
|
[UIAlertAction actionWithTitle:NSLocalizedString(@"LINK_DEVICE_RESTART", @"attempt another linking")
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction *action) {
|
|
[self.qrScanningController startCapture];
|
|
}];
|
|
[alertController addAction:proceedAction];
|
|
|
|
[self presentViewController:alertController animated:YES completion:nil];
|
|
} else {
|
|
NSString *title = NSLocalizedString(
|
|
@"LINK_DEVICE_PERMISSION_ALERT_TITLE", @"confirm the users intent to link a new device");
|
|
NSString *linkingDescription
|
|
= NSLocalizedString(@"LINK_DEVICE_PERMISSION_ALERT_BODY", @"confirm the users intent to link a new device");
|
|
|
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
|
|
message:linkingDescription
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
UIAlertAction *cancelAction =
|
|
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
|
|
style:UIAlertActionStyleCancel
|
|
handler:^(UIAlertAction *action) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.navigationController popViewControllerAnimated:YES];
|
|
});
|
|
}];
|
|
[alertController addAction:cancelAction];
|
|
|
|
UIAlertAction *proceedAction =
|
|
[UIAlertAction actionWithTitle:NSLocalizedString(@"CONFIRM_LINK_NEW_DEVICE_ACTION", @"Button text")
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction *action) {
|
|
[self provisionWithParser:parser];
|
|
}];
|
|
[alertController addAction:proceedAction];
|
|
|
|
[self presentViewController:alertController animated:YES completion:nil];
|
|
}
|
|
}
|
|
|
|
- (void)provisionWithParser:(OWSDeviceProvisioningURLParser *)parser
|
|
{
|
|
// Optimistically set this flag.
|
|
[OWSDeviceManager.sharedManager setMayHaveLinkedDevices];
|
|
|
|
ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair];
|
|
OWSAssertDebug(identityKeyPair);
|
|
NSData *myPublicKey = identityKeyPair.publicKey;
|
|
NSData *myPrivateKey = identityKeyPair.privateKey;
|
|
NSString *accountIdentifier = [TSAccountManager localNumber];
|
|
NSData *myProfileKeyData = self.profileManager.localProfileKey.keyData;
|
|
BOOL areReadReceiptsEnabled = self.readReceiptManager.areReadReceiptsEnabled;
|
|
|
|
OWSDeviceProvisioner *provisioner = [[OWSDeviceProvisioner alloc] initWithMyPublicKey:myPublicKey
|
|
myPrivateKey:myPrivateKey
|
|
theirPublicKey:parser.publicKey
|
|
theirEphemeralDeviceId:parser.ephemeralDeviceId
|
|
accountIdentifier:accountIdentifier
|
|
profileKey:myProfileKeyData
|
|
readReceiptsEnabled:areReadReceiptsEnabled];
|
|
|
|
[provisioner
|
|
provisionWithSuccess:^{
|
|
OWSLogInfo(@"Successfully provisioned device.");
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.linkedDevicesTableViewController expectMoreDevices];
|
|
[self.navigationController popToViewController:self.linkedDevicesTableViewController animated:YES];
|
|
|
|
// The service implementation of the socket connection caches the linked device state,
|
|
// so all sync message sends will fail on the socket until it is cycled.
|
|
[TSSocketManager.sharedManager cycleSocket];
|
|
});
|
|
}
|
|
failure:^(NSError *error) {
|
|
OWSLogError(@"Failed to provision device with error: %@", error);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self presentViewController:[self retryAlertControllerWithError:error
|
|
retryBlock:^{
|
|
[self provisionWithParser:parser];
|
|
}]
|
|
animated:YES
|
|
completion:nil];
|
|
});
|
|
}];
|
|
}
|
|
|
|
- (UIAlertController *)retryAlertControllerWithError:(NSError *)error retryBlock:(void (^)(void))retryBlock
|
|
{
|
|
NSString *title = NSLocalizedString(@"LINKING_DEVICE_FAILED_TITLE", @"Alert Title");
|
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
|
|
message:error.localizedDescription
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:[CommonStrings retryButton]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction *action) {
|
|
retryBlock();
|
|
}];
|
|
[alertController addAction:retryAction];
|
|
|
|
UIAlertAction *cancelAction =
|
|
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
|
|
style:UIAlertActionStyleCancel
|
|
handler:^(UIAlertAction *action) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self dismissViewControllerAnimated:YES completion:nil];
|
|
});
|
|
}];
|
|
[alertController addAction:cancelAction];
|
|
return alertController;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|