diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 1c16e26a6..53c313b90 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -5,6 +5,8 @@ import PromiseKit // for things that explicitly *can* be done in parallel and don't modify state, any which should then happen // on a global queue. +// TODO: Move mentions silliness out into its own file + @objc(LKAPI) public final class LokiAPI : NSObject { diff --git a/SignalServiceKit/src/Loki/API/LokiPoller.swift b/SignalServiceKit/src/Loki/API/LokiPoller.swift index 5163fa526..f4bffa92c 100644 --- a/SignalServiceKit/src/Loki/API/LokiPoller.swift +++ b/SignalServiceKit/src/Loki/API/LokiPoller.swift @@ -74,7 +74,6 @@ public final class LokiPoller : NSObject { private func poll(_ target: LokiAPITarget, seal longTermSeal: Resolver) -> Promise { return LokiAPI.getRawMessages(from: target, usingLongPolling: false).then(on: LokiAPI.workQueue) { [weak self] rawResponse -> Promise in - print("[Loki] Polled successfully") guard let strongSelf = self, !strongSelf.hasStopped else { return Promise { $0.fulfill(()) } } let messages = LokiAPI.parseRawMessagesResponse(rawResponse, from: target) strongSelf.onMessagesReceived(messages) diff --git a/SignalServiceKit/src/Loki/Messaging/LKAddressMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKAddressMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKAddressMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKAddressMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKAddressMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKAddressMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKAddressMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKAddressMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKDeviceLinkMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKDeviceLinkMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKDeviceLinkMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKDeviceLinkMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKEphemeralMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKEphemeralMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKEphemeralMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKEphemeralMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKEphemeralMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKEphemeralMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKEphemeralMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKEphemeralMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKFriendRequestMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKFriendRequestMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKFriendRequestMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKFriendRequestMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKFriendRequestMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKFriendRequestMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKFriendRequestMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKFriendRequestMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRequestMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRequestMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSessionRequestMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRequestMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRequestMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRequestMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSessionRequestMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRequestMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRestoreMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRestoreMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRestoreMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSessionRestoreMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKSyncOpenGroupsMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSyncOpenGroupsMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSyncOpenGroupsMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSyncOpenGroupsMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKSyncOpenGroupsMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKSyncOpenGroupsMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKSyncOpenGroupsMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKSyncOpenGroupsMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.h b/SignalServiceKit/src/Loki/Messaging/Message Types/LKUnlinkDeviceMessage.h similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.h rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKUnlinkDeviceMessage.h diff --git a/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.m b/SignalServiceKit/src/Loki/Messaging/Message Types/LKUnlinkDeviceMessage.m similarity index 100% rename from SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.m rename to SignalServiceKit/src/Loki/Messaging/Message Types/LKUnlinkDeviceMessage.m diff --git a/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Receiving.swift b/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Receiving.swift index bd7f27135..a9c7e91bf 100644 --- a/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Receiving.swift +++ b/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Receiving.swift @@ -9,7 +9,7 @@ import PromiseKit // TODO: Document the expected cases for everything and then express those cases in tests -public extension SessionProtocol { +@objc public extension SessionProtocol { // When a message comes in, OWSMessageManager does things in this order: // 1. Checks if the message is a friend request from before restoration and ignores it if so diff --git a/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Sending.swift b/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Sending.swift index 1dd9380bf..ed3df0fb1 100644 --- a/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Sending.swift +++ b/SignalServiceKit/src/Loki/Messaging/SessionProtocol+Sending.swift @@ -9,14 +9,14 @@ import PromiseKit // TODO: Document the expected cases for everything and then express those cases in tests -public extension SessionProtocol { +@objc public extension SessionProtocol { // MARK: - Message Destination - @objc(getDestinationsForOutgoingSyncMessage:) - public static func getDestinations(for outgoingSyncMessage: OWSOutgoingSyncMessage) -> Set { + @objc(getDestinationsForOutgoingSyncMessage) + public static func getDestinationsForOutgoingSyncMessage() -> Set { var result: Set = [] storage.dbReadConnection.read { transaction in - // NOTE: Aim the message at all linked devices, including this one + // Aim the message at all linked devices, including this one // TODO: Should we exclude the current device? result = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction) } @@ -37,7 +37,6 @@ public extension SessionProtocol { } } else { result = Set(outgoingGroupMessage.sendingRecipientIds()).intersection(thread.groupModel.groupMemberIds) // This is what Signal does - } return result } @@ -115,7 +114,7 @@ public extension SessionProtocol { - // MARK: - Multi Device + // MARK: - Multi Device (Part 1) @objc(sendMessageToDestinationAndLinkedDevices:in:) public static func sendMessageToDestinationAndLinkedDevices(_ messageSend: OWSMessageSend, in transaction: YapDatabaseReadWriteTransaction) { // TODO: I'm pretty sure there are quite a few holes in this logic @@ -175,48 +174,6 @@ public extension SessionProtocol { return AnyPromise.from(promise) } - private static func getMultiDeviceDestinations(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> Promise> { - // FIXME: Threading - let (promise, seal) = Promise>.pending() - func getDestinations(in transaction: YapDatabaseReadTransaction? = nil) { - storage.dbReadConnection.read { transaction in - var destinations: Set = [] - let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey - let masterDestination = MultiDeviceDestination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master) - destinations.insert(masterDestination) - let deviceLinks = storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction) - let slaveDestinations = deviceLinks.map { MultiDeviceDestination(hexEncodedPublicKey: $0.slave.hexEncodedPublicKey, kind: .slave) } - destinations.formUnion(slaveDestinations) - seal.fulfill(destinations) - } - } - let timeSinceLastUpdate: TimeInterval - if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] { - timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate) - } else { - timeSinceLastUpdate = .infinity - } - if timeSinceLastUpdate > deviceLinkUpdateInterval { - let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey - LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey, in: transaction).done(on: LokiAPI.workQueue) { _ in - getDestinations() - lastDeviceLinkUpdate[hexEncodedPublicKey] = Date() - }.catch(on: LokiAPI.workQueue) { error in - if (error as? LokiDotNetAPI.LokiDotNetAPIError) == LokiDotNetAPI.LokiDotNetAPIError.parsingFailed { - // Don't immediately re-fetch in case of failure due to a parsing error - lastDeviceLinkUpdate[hexEncodedPublicKey] = Date() - getDestinations() - } else { - print("[Loki] Failed to get device links due to error: \(error).") - seal.reject(error) - } - } - } else { - getDestinations() - } - return promise - } - @objc(getAutoGeneratedMultiDeviceFRMessageForHexEncodedPublicKey:in:) public static func getAutoGeneratedMultiDeviceFRMessage(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> FriendRequestMessage { let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction) @@ -325,6 +282,7 @@ public extension SessionProtocol { // MARK: - Typing Indicators + @objc(shouldSendTypingIndicatorForThread:) public static func shouldSendTypingIndicator(for thread: TSThread) -> Bool { return !thread.isGroupThread() && !isMessageNoteToSelf(thread) } @@ -355,6 +313,7 @@ public extension SessionProtocol { // TODO: Does the above make sense? + @objc(createPreKeys) public static func createPreKeys() { // We don't generate PreKeyRecords here. // This is because we need the records to be linked to a contact since we don't have a central server. @@ -367,6 +326,7 @@ public extension SessionProtocol { print("[Loki] Pre keys created successfully.") } + @objc(refreshPreKeys) public static func refreshPreKeys() { guard storage.currentSignedPrekeyId() == nil else { print("[Loki] Skipping pre key refresh; using existing signed pre key.") @@ -381,6 +341,7 @@ public extension SessionProtocol { print("[Loki] Pre keys refreshed successfully.") } + @objc(rotatePreKeys) public static func rotatePreKeys() { let signedPreKeyRecord = storage.generateRandomSignedRecord() signedPreKeyRecord.markAsAcceptedByService() @@ -391,6 +352,7 @@ public extension SessionProtocol { print("[Loki] Pre keys rotated successfully.") } + @objc(shouldUseFallbackEncryptionForMessage:) public static func shouldUseFallbackEncryption(_ message: TSOutgoingMessage) -> Bool { return !isSessionRequired(for: message) } @@ -403,3 +365,52 @@ public extension SessionProtocol { return true } } + + + +// MARK: - Multi Device (Part 2) +// Here (in a non-@objc extension) because it doesn't interoperate well with Obj-C +public extension SessionProtocol { + + fileprivate static func getMultiDeviceDestinations(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> Promise> { + // FIXME: Threading + let (promise, seal) = Promise>.pending() + func getDestinations(in transaction: YapDatabaseReadTransaction? = nil) { + storage.dbReadConnection.read { transaction in + var destinations: Set = [] + let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey + let masterDestination = MultiDeviceDestination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master) + destinations.insert(masterDestination) + let deviceLinks = storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction) + let slaveDestinations = deviceLinks.map { MultiDeviceDestination(hexEncodedPublicKey: $0.slave.hexEncodedPublicKey, kind: .slave) } + destinations.formUnion(slaveDestinations) + seal.fulfill(destinations) + } + } + let timeSinceLastUpdate: TimeInterval + if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] { + timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate) + } else { + timeSinceLastUpdate = .infinity + } + if timeSinceLastUpdate > deviceLinkUpdateInterval { + let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey + LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey, in: transaction).done(on: LokiAPI.workQueue) { _ in + getDestinations() + lastDeviceLinkUpdate[hexEncodedPublicKey] = Date() + }.catch(on: LokiAPI.workQueue) { error in + if (error as? LokiDotNetAPI.LokiDotNetAPIError) == LokiDotNetAPI.LokiDotNetAPIError.parsingFailed { + // Don't immediately re-fetch in case of failure due to a parsing error + lastDeviceLinkUpdate[hexEncodedPublicKey] = Date() + getDestinations() + } else { + print("[Loki] Failed to get device links due to error: \(error).") + seal.reject(error) + } + } + } else { + getDestinations() + } + return promise + } +} diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 2b6472b20..90e314433 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -500,7 +500,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; __block NSMutableSet *recipientIds = [NSMutableSet new]; if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) { - recipientIds = [SessionProtocol getDestinationsForOutgoingSyncMessage:message]; + recipientIds = [SessionProtocol getDestinationsForOutgoingSyncMessage]; } else if (thread.isGroupThread) { TSGroupThread *groupThread = (TSGroupThread *)thread; recipientIds = [SessionProtocol getDestinationsForOutgoingGroupMessage:message inThread:thread];