Merge branch 'dev' of https://github.com/loki-project/session-ios into PN-with-preview

This commit is contained in:
ryanzhao 2020-03-25 13:15:54 +11:00
commit 0e604c1291
14 changed files with 98 additions and 56 deletions

2
Pods

@ -1 +1 @@
Subproject commit f636079cbdf8e7a0b959f25917b1bcc0b64b0f0c
Subproject commit 7d2822b86d1ff3bf09eb6cab2f01a03e7e214081

View File

@ -8,8 +8,8 @@ extern NSString *const AppDelegateStoryboardMain;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
- (void)startLongPollerIfNeeded;
- (void)stopLongPollerIfNeeded;
- (void)startPollerIfNeeded;
- (void)stopPollerIfNeeded;
- (void)setUpDefaultPublicChatsIfNeeded;
- (void)startOpenGroupPollersIfNeeded;
- (void)stopOpenGroupPollersIfNeeded;

View File

@ -69,7 +69,7 @@ static BOOL isInternalTestVersion = NO;
// Loki
@property (nonatomic) LKP2PServer *lokiP2PServer;
@property (nonatomic) LKLongPoller *lokiLongPoller;
@property (nonatomic) LKPoller *lokiPoller;
@property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller;
@property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller;
@ -181,7 +181,7 @@ static BOOL isInternalTestVersion = NO;
[DDLog flushLog];
// Loki: Stop pollers
[self stopLongPollerIfNeeded];
[self stopPollerIfNeeded];
[self stopOpenGroupPollersIfNeeded];
}
@ -202,7 +202,7 @@ static BOOL isInternalTestVersion = NO;
[DDLog flushLog];
// Loki: Stop pollers
[self stopLongPollerIfNeeded];
[self stopPollerIfNeeded];
[self stopOpenGroupPollersIfNeeded];
if (self.lokiP2PServer) { [self.lokiP2PServer stop]; }
@ -784,7 +784,7 @@ static BOOL isInternalTestVersion = NO;
[LKP2PAPI broadcastOnlineStatus];
// Loki: Start pollers
[self startLongPollerIfNeeded];
[self startPollerIfNeeded];
[self startOpenGroupPollersIfNeeded];
// Loki: Get device links
@ -1477,7 +1477,7 @@ static BOOL isInternalTestVersion = NO;
[self.lokiFriendRequestExpirationJob startIfNecessary];
// Loki: Start pollers
[self startLongPollerIfNeeded];
[self startPollerIfNeeded];
[self startOpenGroupPollersIfNeeded];
// Loki: Get device links
@ -1591,12 +1591,12 @@ static BOOL isInternalTestVersion = NO;
#pragma mark - Loki
- (void)setUpLongPollerIfNeeded
- (void)setUpPollerIfNeeded
{
if (self.lokiLongPoller != nil) { return; }
if (self.lokiPoller != nil) { return; }
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
if (userHexEncodedPublicKey == nil) { return; }
self.lokiLongPoller = [[LKLongPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
self.lokiPoller = [[LKPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
for (SSKProtoEnvelope *message in messages) {
NSData *data = [message serializedDataAndReturnError:nil];
if (data != nil) {
@ -1608,15 +1608,15 @@ static BOOL isInternalTestVersion = NO;
}];
}
- (void)startLongPollerIfNeeded
- (void)startPollerIfNeeded
{
[self setUpLongPollerIfNeeded];
[self.lokiLongPoller startIfNeeded];
[self setUpPollerIfNeeded];
[self.lokiPoller startIfNeeded];
}
- (void)stopLongPollerIfNeeded
- (void)stopPollerIfNeeded
{
[self.lokiLongPoller stopIfNeeded];
[self.lokiPoller stopIfNeeded];
}
- (void)setUpDefaultPublicChatsIfNeeded
@ -1713,7 +1713,7 @@ static BOOL isInternalTestVersion = NO;
[SSKEnvironment.shared.messageSenderJobQueue clearAllJobs];
[SSKEnvironment.shared.identityManager clearIdentityKey];
[LKAPI clearRandomSnodePool];
[self stopLongPollerIfNeeded];
[self stopPollerIfNeeded];
[self stopOpenGroupPollersIfNeeded];
[self.lokiNewsFeedPoller stop];
[self.lokiMessengerUpdatesFeedPoller stop];

View File

@ -5,5 +5,19 @@ class BaseVC : UIViewController {
override func viewDidLoad() {
setNeedsStatusBarAppearanceUpdate()
NotificationCenter.default.addObserver(self, selector: #selector(handleUnexpectedDeviceLinkRequestReceivedNotification), name: .unexpectedDeviceLinkRequestReceived, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc private func handleUnexpectedDeviceLinkRequestReceivedNotification() {
guard DeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert else { return }
DispatchQueue.main.async {
let alert = UIAlertController(title: "Device Link Request Received", message: "Open the device link screen by going to \"Settings\"> \"Devices\" > \"Link a Device\" to link your devices.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}

View File

@ -42,7 +42,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
private lazy var subtitleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.smallFontSize)
result.numberOfLines = 0
result.lineBreakMode = .byWordWrapping
@ -131,7 +131,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
}()
subtitleLabel.text = {
switch mode {
case .master: return NSLocalizedString("Open Session on your secondary device and tap \"Link to an existing account\"", comment: "")
case .master: return NSLocalizedString("Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.", comment: "")
case .slave: return NSLocalizedString("Please check that the words below match those shown on your other device", comment: "")
}
}()

View File

@ -155,7 +155,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
TSAccountManager.sharedInstance().didRegister()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.startLongPollerIfNeeded()
appDelegate.startPollerIfNeeded()
let deviceLinkingModal = DeviceLinkingModal(mode: .slave, delegate: self)
deviceLinkingModal.modalPresentationStyle = .overFullScreen
deviceLinkingModal.modalTransitionStyle = .crossDissolve
@ -176,7 +176,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
func handleDeviceLinkingModalDismissed() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.stopLongPollerIfNeeded()
appDelegate.stopPollerIfNeeded()
TSAccountManager.sharedInstance().resetForReregistration()
}

View File

@ -33,7 +33,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
}()
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
let message = NSLocalizedString("Link to your existing account by going into your in-app settings and clicking \"Devices\".", comment: "")
let message = NSLocalizedString("Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then scan the QR code that comes up to start the linking process.", comment: "")
let result = ScanQRCodeWrapperVC(message: message)
result.delegate = self
return result
@ -167,7 +167,7 @@ private final class EnterPublicKeyVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = "Enter your Session ID to start the linking process."
explanationLabel.text = "Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then enter your Session ID here to start the linking process."
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
// Link button

View File

@ -444,6 +444,11 @@ typedef enum : NSUInteger {
selector:@selector(handleMessageFailedNotification:)
name:NSNotification.messageFailed
object:nil];
// Device linking
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleUnexpectedDeviceLinkRequestReceivedNotification)
name:NSNotification.unexpectedDeviceLinkRequestReceived
object:nil];
}
- (BOOL)isGroupConversation
@ -5479,6 +5484,16 @@ typedef enum : NSUInteger {
});
}
- (void)handleUnexpectedDeviceLinkRequestReceivedNotification
{
if (!LKDeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert) { return; }
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Device Link Request Received" message:@"Open the device link screen by going to \"Settings\"> \"Devices\" > \"Link a Device\" to link your devices." preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
});
}
@end
NS_ASSUME_NONNULL_END

View File

@ -681,7 +681,7 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
[SSKEnvironment.shared.identityManager clearIdentityKey];
[LKAPI clearRandomSnodePool];
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
[appDelegate stopLongPollerIfNeeded];
[appDelegate stopPollerIfNeeded];
[appDelegate stopOpenGroupPollersIfNeeded];
[SSKEnvironment.shared.tsAccountManager resetForReregistration];
UIViewController *rootViewController = [[OnboardingController new] initialViewController];

View File

@ -2768,7 +2768,7 @@
"Link to an existing account" = "Link to an existing account";
"Enter your public key" = "Enter your public key";
"Link to your existing account by going into your in-app settings and clicking \"Devices\"." = "Link to your existing account by going into your in-app settings and clicking \"Devices\".";
"Open Session on your secondary device and tap \"Link to an existing account\"" = "Open Session on your secondary device and tap \"Link to an existing account\"";
"Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first." = "Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.";
"Group Settings" = "Group Settings";
"Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design." = "Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.";
"Enter the recovery phrase that was given to you when you signed up to restore your account." = "Enter the recovery phrase that was given to you when you signed up to restore your account.";

View File

@ -1,20 +1,16 @@
import PromiseKit
@objc(LKLongPoller)
public final class LokiLongPoller : NSObject {
@objc(LKPoller)
public final class LokiPoller : NSObject {
private let onMessagesReceived: ([SSKProtoEnvelope]) -> Void
private let storage = OWSPrimaryStorage.shared()
private var hasStarted = false
private var hasStopped = false
private var connections = Set<Promise<Void>>()
private var usedSnodes = Set<LokiAPITarget>()
// MARK: Settings
private let connectionCount = 3
private let retryInterval: TimeInterval = 4
// MARK: Convenience
private var userHexEncodedPublicKey: String { return getUserHexEncodedPublicKey() }
private static let pollInterval: TimeInterval = 1
private static let retryInterval: TimeInterval = 4
// MARK: Initialization
@objc public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) {
@ -25,7 +21,7 @@ public final class LokiLongPoller : NSObject {
// MARK: Public API
@objc public func startIfNeeded() {
guard !hasStarted else { return }
print("[Loki] Started long polling.")
print("[Loki] Started polling.")
hasStarted = true
hasStopped = false
openConnections()
@ -33,7 +29,7 @@ public final class LokiLongPoller : NSObject {
@objc public func stopIfNeeded() {
guard !hasStopped else { return }
print("[Loki] Stopped long polling.")
print("[Loki] Stopped polling.")
hasStarted = false
hasStopped = true
usedSnodes.removeAll()
@ -42,49 +38,46 @@ public final class LokiLongPoller : NSObject {
// MARK: Private API
private func openConnections() {
guard !hasStopped else { return }
LokiAPI.getSwarm(for: userHexEncodedPublicKey).then { [weak self] _ -> Guarantee<[Result<Void>]> in
guard let strongSelf = self else { return Guarantee.value([Result<Void>]()) }
LokiAPI.getSwarm(for: getUserHexEncodedPublicKey()).then { [weak self] _ -> Promise<Void> in
guard let strongSelf = self else { return Promise { $0.fulfill(()) } }
strongSelf.usedSnodes.removeAll()
let connections: [Promise<Void>] = (0..<strongSelf.connectionCount).map { _ in
let (promise, seal) = Promise<Void>.pending()
strongSelf.openConnectionToNextSnode(seal: seal)
return promise
}
strongSelf.connections = Set(connections)
return when(resolved: connections)
let (promise, seal) = Promise<Void>.pending()
strongSelf.pollNextSnode(seal: seal)
return promise
}.ensure { [weak self] in
guard let strongSelf = self else { return }
Timer.scheduledTimer(withTimeInterval: strongSelf.retryInterval, repeats: false) { _ in
Timer.scheduledTimer(withTimeInterval: LokiPoller.retryInterval, repeats: false) { _ in
guard let strongSelf = self else { return }
strongSelf.openConnections()
}
}
}
private func openConnectionToNextSnode(seal: Resolver<Void>) {
private func pollNextSnode(seal: Resolver<Void>) {
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
let swarm = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? []
let userHexEncodedPublicKey = self.userHexEncodedPublicKey
let unusedSnodes = Set(swarm).subtracting(usedSnodes)
if !unusedSnodes.isEmpty {
// randomElement() uses the system's default random generator, which is cryptographically secure
let nextSnode = unusedSnodes.randomElement()!
usedSnodes.insert(nextSnode)
print("[Loki] Opening long polling connection to \(nextSnode).")
longPoll(nextSnode, seal: seal).catch(on: LokiAPI.errorHandlingQueue) { [weak self] error in
print("[Loki] Long polling connection to \(nextSnode) failed; dropping it and switching to next snode.")
print("[Loki] Polling \(nextSnode).")
poll(nextSnode, seal: seal).catch(on: LokiAPI.errorHandlingQueue) { [weak self] error in
print("[Loki] Polling \(nextSnode) failed; dropping it and switching to next snode.")
LokiAPI.dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey)
self?.openConnectionToNextSnode(seal: seal)
self?.pollNextSnode(seal: seal)
}
} else {
seal.fulfill(())
}
}
private func longPoll(_ target: LokiAPITarget, seal: Resolver<Void>) -> Promise<Void> {
return LokiAPI.getRawMessages(from: target, usingLongPolling: true).then(on: DispatchQueue.global()) { [weak self] rawResponse -> Promise<Void> in
private func poll(_ target: LokiAPITarget, seal: Resolver<Void>) -> Promise<Void> {
return LokiAPI.getRawMessages(from: target, usingLongPolling: false).then(on: DispatchQueue.global()) { [weak self] rawResponse -> Promise<Void> in
guard let strongSelf = self, !strongSelf.hasStopped else { return Promise.value(()) }
let messages = LokiAPI.parseRawMessagesResponse(rawResponse, from: target)
strongSelf.onMessagesReceived(messages)
return strongSelf.longPoll(target, seal: seal)
return strongSelf.poll(target, seal: seal)
}
}
}

View File

@ -1,6 +1,19 @@
public enum DeviceLinkingUtilities {
@objc(LKDeviceLinkingUtilities)
public final class DeviceLinkingUtilities : NSObject {
private static var lastUnexpectedDeviceLinkRequestDate: Date? = nil
private override init() { }
@objc public static var shouldShowUnexpectedDeviceLinkRequestReceivedAlert: Bool {
let now = Date()
if let lastUnexpectedDeviceLinkRequestDate = lastUnexpectedDeviceLinkRequestDate {
if now.timeIntervalSince(lastUnexpectedDeviceLinkRequestDate) < 30 { return false }
}
lastUnexpectedDeviceLinkRequestDate = now
return true
}
// When requesting a device link, the slave device signs the master device's public key. When authorizing
// a device link, the master device signs the slave device's public key.

View File

@ -17,6 +17,8 @@ public extension Notification.Name {
public static let seedViewed = Notification.Name("seedViewed")
// Interaction
public static let dataNukeRequested = Notification.Name("dataNukeRequested")
// Device linking
public static let unexpectedDeviceLinkRequestReceived = Notification.Name("unexpectedDeviceLinkRequestReceived")
}
@objc public extension NSNotification {
@ -37,4 +39,6 @@ public extension Notification.Name {
@objc public static let seedViewed = Notification.Name.seedViewed.rawValue as NSString
// Interaction
@objc public static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString
// Device linking
@objc public static let unexpectedDeviceLinkRequestReceived = Notification.Name.unexpectedDeviceLinkRequestReceived.rawValue as NSString
}

View File

@ -494,6 +494,9 @@ NS_ASSUME_NONNULL_BEGIN
}
} else if (slaveSignature != nil) { // Request
OWSLogInfo(@"[Loki] Received a device linking request from: %@", envelope.source); // Not slaveHexEncodedPublicKey
if (LKDeviceLinkingSession.current == nil) {
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.unexpectedDeviceLinkRequestReceived object:nil];
}
[LKDeviceLinkingSession.current processLinkingRequestFrom:slaveHexEncodedPublicKey to:masterHexEncodedPublicKey with:slaveSignature];
}
} else if (contentProto.syncMessage) {