Finish integration

This commit is contained in:
Niels Andriesse 2019-09-30 12:08:55 +10:00
parent 2459388a3a
commit 7031bde5a2
13 changed files with 144 additions and 80 deletions

View File

@ -19,5 +19,6 @@
#import <SignalCoreKit/OWSAsserts.h>
#import <SignalServiceKit/SSKAsserts.h>
#import <SignalServiceKit/OWSAnalytics.h>
#import <SignalServiceKit/NSArray+Functional.h>
#import <SignalServiceKit/NSObject+Casting.h>
#endif

View File

@ -5,11 +5,11 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
private var mode: Mode = .register { didSet { if mode != oldValue { handleModeChanged() } } }
private var seed: Data! { didSet { updateMnemonic() } }
private var mnemonic: String! { didSet { handleMnemonicChanged() } }
private var timer: Timer?
private var linkingRequestMessageSendingTimer: Timer?
// MARK: Components
private lazy var registerStackView: UIStackView = {
let result = UIStackView(arrangedSubviews: [ explanationLabel1, UIView.spacer(withHeight: 32), mnemonicLabel, UIView.spacer(withHeight: 24), copyButton, UIView.spacer(withHeight: 8), restoreButton1, linkButton1 ])
let result = UIStackView(arrangedSubviews: [ explanationLabel1, UIView.spacer(withHeight: 32), mnemonicLabel, UIView.spacer(withHeight: 24), copyButton, restoreButton1, linkButton1 ])
result.accessibilityIdentifier = "onboarding.keyPairStep.registerStackView"
result.axis = .vertical
return result
@ -346,7 +346,7 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
present(deviceLinkingModal, animated: true, completion: nil)
let masterHexEncodedPublicKey = masterHexEncodedPublicKeyTextField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
let linkingRequestMessage = DeviceLinkingUtilities.getLinkingRequestMessage(for: masterHexEncodedPublicKey)
timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
linkingRequestMessageSendingTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
self?.sendLinkingRequestMessage(linkingRequestMessage)
}
sendLinkingRequestMessage(linkingRequestMessage)
@ -363,13 +363,13 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
}
func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) {
timer?.invalidate()
linkingRequestMessageSendingTimer?.invalidate()
UserDefaults.standard.set(true, forKey: "didUpdateForMainnet")
onboardingController.verificationDidComplete(fromView: self)
}
func handleDeviceLinkingModalDismissed() {
timer?.invalidate()
linkingRequestMessageSendingTimer?.invalidate()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.stopLongPollerIfNeeded()
TSAccountManager.sharedInstance().resetForReregistration()

View File

@ -33,11 +33,25 @@ public final class LokiAPI : NSObject {
}
}
internal struct Destination {
internal let hexEncodedPublicKey: String
internal let kind: Kind
@objc(LKDestination)
public final class Destination : NSObject {
@objc public let hexEncodedPublicKey: String
@objc(kind)
public let objc_kind: String
enum Kind { case master, slave }
public var kind: Kind { return Kind(rawValue: objc_kind)! }
public enum Kind : String { case master, slave }
public init(hexEncodedPublicKey: String, kind: Kind) {
self.hexEncodedPublicKey = hexEncodedPublicKey
self.objc_kind = kind.rawValue
}
@objc public init(hexEncodedPublicKey: String, kind: String) {
self.hexEncodedPublicKey = hexEncodedPublicKey
self.objc_kind = kind
}
}
public typealias MessageListPromise = Promise<[SSKProtoEnvelope]>
@ -67,8 +81,47 @@ public final class LokiAPI : NSObject {
let timeout: TimeInterval? = useLongPolling ? longPollingTimeout : nil
return invoke(.getMessages, on: target, associatedWith: userHexEncodedPublicKey, parameters: parameters, headers: headers, timeout: timeout)
}
internal static func internalSendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
// MARK: Public API
public static func getMessages() -> Promise<Set<MessageListPromise>> {
return getTargetSnodes(for: userHexEncodedPublicKey).mapValues { targetSnode in
return getRawMessages(from: targetSnode, usingLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
}.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
public static func getDestinations(for hexEncodedPublicKey: String) -> Promise<[Destination]> {
let (promise, seal) = Promise<[Destination]>.pending()
func getDestinations() {
storage.dbReadConnection.read { transaction in
var destinations: [Destination] = []
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
let masterDestination = Destination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master)
destinations.append(masterDestination)
let deviceLinks = storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction)
let slaveDestinations = deviceLinks.map { Destination(hexEncodedPublicKey: $0.slave.hexEncodedPublicKey, kind: .slave) }
destinations.append(contentsOf: slaveDestinations)
destinations = destinations.filter { $0.hexEncodedPublicKey != userHexEncodedPublicKey }
seal.fulfill(destinations)
}
}
let timeSinceLastUpdate: TimeInterval
if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] {
timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate)
} else {
timeSinceLastUpdate = .infinity
}
if timeSinceLastUpdate > deviceLinkUpdateInterval {
storage.dbReadConnection.read { transaction in
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
LokiStorageAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey).done { _ in getDestinations() }.catch { seal.reject($0) }
}
} else {
getDestinations()
}
return promise
}
public static func sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
guard let lokiMessage = LokiMessage.from(signalMessage: signalMessage) else { return Promise(error: Error.messageConversionFailed) }
let destination = lokiMessage.destination
func sendLokiMessage(_ lokiMessage: LokiMessage, to target: LokiAPITarget) -> RawResponsePromise {
@ -116,59 +169,13 @@ public final class LokiAPI : NSObject {
}
}
internal static func getDestinations(for hexEncodedPublicKey: String) -> Promise<[Destination]> {
let (promise, seal) = Promise<[Destination]>.pending()
func getDestinations() {
storage.dbReadConnection.read { transaction in
var destinations: [Destination] = []
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
let masterDestination = Destination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master)
destinations.append(masterDestination)
let deviceLinks = storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction)
let slaveDestinations = deviceLinks.map { Destination(hexEncodedPublicKey: $0.slave.hexEncodedPublicKey, kind: .slave) }
destinations.append(contentsOf: slaveDestinations)
seal.fulfill(destinations)
}
}
let timeSinceLastUpdate: TimeInterval
if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] {
timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate)
} else {
timeSinceLastUpdate = .infinity
}
if timeSinceLastUpdate > deviceLinkUpdateInterval {
storage.dbReadConnection.read { transaction in
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
LokiStorageAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey).done { _ in getDestinations() }.catch { seal.reject($0) }
}
} else {
getDestinations()
}
return promise
}
// MARK: Public API
public static func getMessages() -> Promise<Set<MessageListPromise>> {
return getTargetSnodes(for: userHexEncodedPublicKey).mapValues { targetSnode in
return getRawMessages(from: targetSnode, usingLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
}.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
public static func sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
return getDestinations(for: signalMessage.recipientID).then { destinations -> Promise<Set<RawResponsePromise>> in
// Use a best attempt approach for multi device for now
let slaveDestinations = destinations.filter { $0.kind == .slave }
slaveDestinations.forEach { destination in
let signalMessageCopy = signalMessage.copy(with: destination.hexEncodedPublicKey)
internalSendSignalMessage(signalMessageCopy) { }
}
let masterDestination = destinations.first { $0.kind == .master }!
let signalMessageCopy = signalMessage.copy(with: masterDestination.hexEncodedPublicKey)
return internalSendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess)
}
}
// MARK: Public API (Obj-C)
@objc(getDestinationsFor:)
public static func objc_getDestinations(for hexEncodedPublicKey: String) -> AnyPromise {
let promise = getDestinations(for: hexEncodedPublicKey)
return AnyPromise.from(promise)
}
@objc(sendSignalMessage:onP2PSuccess:)
public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> AnyPromise {
let promise = sendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess).mapValues { AnyPromise.from($0) }.map { Set($0) }

View File

@ -25,10 +25,4 @@ public final class SignalMessage : NSObject {
self.isPing = isPing
super.init()
}
public func copy(with recipientID: String) -> SignalMessage {
return SignalMessage(type: type, timestamp: timestamp, senderID: senderID, senderDeviceID: senderDeviceID, content: content,
recipientID: recipientID, ttl: objc_ttl, isPing: isPing)
}
}

View File

@ -7,6 +7,7 @@ extension OWSPrimaryStorage {
public func setDeviceLinks(_ deviceLinks: Set<DeviceLink>, in transaction: YapDatabaseReadWriteTransaction) {
let masterHexEncodedPublicKeys = Set(deviceLinks.map { $0.master.hexEncodedPublicKey })
guard !masterHexEncodedPublicKeys.isEmpty else { return }
guard masterHexEncodedPublicKeys.count == 1 else {
print("[Loki] Found inconsistent set of device links.")
return

View File

@ -1,5 +1,5 @@
public enum BuildConfiguration : CustomStringConvertible {
public enum BuildConfiguration : String, CustomStringConvertible {
case debug, production
public static let current: BuildConfiguration = {
@ -10,12 +10,7 @@ public enum BuildConfiguration : CustomStringConvertible {
#endif
}()
public var description: String {
switch self {
case .debug: return "debug"
case .production: return "production"
}
}
public var description: String { return rawValue }
}
@objc public final class LKBuildConfiguration : NSObject {

View File

@ -0,0 +1,6 @@
@interface NSArray (Functional)
- (NSArray *)filtered:(BOOL (^)(NSObject *))isIncluded;
@end

View File

@ -0,0 +1,15 @@
#import "NSArray+Functional.h"
@implementation NSArray (Functional)
- (NSArray *)filtered:(BOOL (^)(NSObject *))isIncluded {
NSMutableArray *result = [NSMutableArray new];
for (NSObject *object in self) {
if (isIncluded(object)) {
[result addObject:object];
}
}
return result;
}
@end

View File

@ -80,7 +80,7 @@ NS_ASSUME_NONNULL_BEGIN
TSOutgoingMessageRecipientState *_Nullable recipientState =
[self.message recipientStateForRecipientId:recipientId];
if (!recipientState) {
OWSFailDebug(@"missing recipient state for: %@", recipientId);
// OWSFailDebug(@"missing recipient state for: %@", recipientId);
continue;
}
if (recipientState.state != OWSOutgoingMessageRecipientStateSent) {

View File

@ -694,7 +694,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
TSOutgoingMessageRecipientState *_Nullable recipientState
= message.recipientStateMap[recipientId];
if (!recipientState) {
OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
// OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
return;
}
recipientState.state = OWSOutgoingMessageRecipientStateSent;
@ -713,7 +713,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
TSOutgoingMessageRecipientState *_Nullable recipientState
= message.recipientStateMap[recipientId];
if (!recipientState) {
OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
// OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
return;
}
recipientState.state = OWSOutgoingMessageRecipientStateSkipped;

View File

@ -93,4 +93,15 @@ public class OWSMessageSend: NSObject {
// We "fail over" to non-UD sends after auth errors sending via UD.
disableUD()
}
@objc(copyWithDestination:)
public func copy(with destination: LokiAPI.Destination) -> OWSMessageSend {
var recipient: SignalRecipient!
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.hexEncodedPublicKey, transaction: transaction)
}
let success = (destination.kind == .master) ? self.success : { }
let failure = (destination.kind == .master) ? self.failure : { _ in }
return OWSMessageSend(message: message, thread: thread, recipient: recipient, senderCertificate: senderCertificate, udAccess: udAccess, localNumber: localNumber, success: success, failure: failure)
}
}

View File

@ -902,6 +902,37 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
- (void)sendMessageToRecipient:(OWSMessageSend *)messageSend
{
if (messageSend.thread.isGroupThread) {
[self sendMessage:messageSend];
} else {
[[LKAPI getDestinationsFor:messageSend.recipient.recipientId]
.thenOn(OWSDispatch.sendingQueue, ^(NSArray<LKDestination *> *destinations) {
// Use a best attempt approach for multi device for now
NSArray *slaveDestinations = [destinations filtered:^BOOL(NSObject *object) {
LKDestination *destination = [object as:LKDestination.class];
return [destination.kind isEqual:@"slave"];
}];
for (LKDestination *slaveDestination in slaveDestinations) {
OWSMessageSend *messageSendCopy = [messageSend copyWithDestination:slaveDestination];
[self sendMessage:messageSendCopy];
}
LKDestination *masterDestination = [destinations filtered:^BOOL(NSObject *object) {
LKDestination *destination = [object as:LKDestination.class];
return [destination.kind isEqual:@"master"];
}].firstObject;
if (masterDestination != nil) {
OWSMessageSend *messageSendCopy = [messageSend copyWithDestination:masterDestination];
[self sendMessage:messageSendCopy];
}
})
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) {
[self messageSendDidFail:messageSend deviceMessages:@{} statusCode:0 error:error responseData:nil];
}) retainUntilComplete];
}
}
- (void)sendMessage:(OWSMessageSend *)messageSend
{
OWSAssertDebug(messageSend);
OWSAssertDebug(messageSend.thread || [messageSend.message isKindOfClass:[OWSOutgoingSyncMessage class]]);
@ -968,6 +999,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return messageSend.failure(deviceMessagesError);
}
/*
if (messageSend.isLocalNumber) {
OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]);
// Messages sent to the "local number" should be sync messages.
@ -1041,6 +1073,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSLogWarn(@"Message send attempt with no device messages.");
}
}
*/
for (NSDictionary *deviceMessage in deviceMessages) {
NSNumber *_Nullable messageType = deviceMessage[@"type"];
@ -1523,7 +1556,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:messageSend.recipient];
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:recipient];
if (!plainText) {
OWSRaiseException(InvalidMessageException, @"Failed to build message proto");
}

View File

@ -12,6 +12,7 @@ static const NSUInteger ddLogLevel = DDLogLevelAll;
static const NSUInteger ddLogLevel = DDLogLevelInfo;
#endif
#import "OWSAnalytics.h"
#import "NSArray+Functional.h"
#import "NSObject+Casting.h"
#import "SSKAsserts.h"
#import "TSConstants.h"