diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 7a79cf509..1d23b102b 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -920,7 +920,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); TSGroupThread *groupThread = (TSGroupThread *)thread; NSData *groupId = groupThread.groupModel.groupId; return [self isGroupIdInProfileWhitelist:groupId]; - } else if ([LKFriendRequestProtocol isFriendsWithLinkedDevicesOfHexEncodedPublicKey:thread.contactIdentifier]) { + } else if ([LKFriendRequestProtocol isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:thread.contactIdentifier]) { return true; } else { NSString *recipientId = thread.contactIdentifier; diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index 64744bbea..c069d6d91 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -724,7 +724,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa { // If we're friends with the person then we don't need to remove any friend request messages if (self.isGroupThread) { return; } - if ([LKFriendRequestProtocol isFriendsWithLinkedDevicesOfHexEncodedPublicKey:self.contactIdentifier]) { return; } + if ([LKFriendRequestProtocol isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:self.contactIdentifier]) { return; } NSMutableArray *idsToRemove = [NSMutableArray new]; __block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it diff --git a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift index 884a438b7..81a9b4912 100644 --- a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift @@ -15,44 +15,43 @@ public final class FriendRequestProtocol : NSObject { internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() } - // MARK: - Enums - // FIXME: Better naming :( - @objc public enum FriendRequestUIState : Int { + // MARK: - Friend Request UI Status + @objc public enum FriendRequestUIStatus : Int { case friends, received, sent, none } // MARK: - General - @objc(isFriendsWithLinkedDevicesOfHexEncodedPublicKey:) - public static func isFriendsWithLinkedDevices(of hexEncodedPublicKey: String) -> Bool { - var isFriends = false + @objc(isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:) + public static func isFriendsWithAnyLinkedDevice(of hexEncodedPublicKey: String) -> Bool { + var result = false storage.dbReadConnection.read { transaction in let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) - let friendRequestStatuses = linkedDevices.map { device in - return storage.getFriendRequestStatus(for: device, transaction: transaction) + let friendRequestStatuses = linkedDevices.map { + storage.getFriendRequestStatus(for: $0, transaction: transaction) } - isFriends = friendRequestStatuses.contains(where: { $0 == .friends }) + result = friendRequestStatuses.contains { $0 == .friends } } - return isFriends + return result } @objc(shouldInputBarBeEnabledForThread:) public static func shouldInputBarBeEnabled(for thread: TSThread) -> Bool { // Friend requests have nothing to do with groups, so if this isn't a contact thread the input bar should be enabled guard let thread = thread as? TSContactThread else { return true } - // If this is a note to self, the input bar should be enabled + // If this is a note to self the input bar should be enabled if thread.isNoteToSelf() { return true } let contactID = thread.contactIdentifier() - var friendRequestStatuses: [LKFriendRequestStatus] = [] + var linkedDeviceFriendRequestStatuses: [LKFriendRequestStatus] = [] storage.dbReadConnection.read { transaction in let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction) - friendRequestStatuses = linkedDevices.map { device in - return storage.getFriendRequestStatus(for: device, transaction: transaction) + linkedDeviceFriendRequestStatuses = linkedDevices.map { + storage.getFriendRequestStatus(for: $0, transaction: transaction) } } // If the current user is friends with any of the other user's devices, the input bar should be enabled - if friendRequestStatuses.contains(where: { $0 == .friends }) { return true } + if linkedDeviceFriendRequestStatuses.contains(where: { $0 == .friends }) { return true } // If no friend request has been sent, the input bar should be enabled - if friendRequestStatuses.allSatisfy({ $0 == .none || $0 == .requestExpired }) { return true } + if linkedDeviceFriendRequestStatuses.allSatisfy({ $0 == .none || $0 == .requestExpired }) { return true } // There must be a pending friend request return false } @@ -64,50 +63,47 @@ public final class FriendRequestProtocol : NSObject { // If this is a note to self, the attachment button should be enabled if thread.isNoteToSelf() { return true } let contactID = thread.contactIdentifier() - var friendRequestStatuses: [LKFriendRequestStatus] = [] + var linkedDeviceFriendRequestStatuses: [LKFriendRequestStatus] = [] storage.dbReadConnection.read { transaction in let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction) - friendRequestStatuses = linkedDevices.map { device in - storage.getFriendRequestStatus(for: device, transaction: transaction) + linkedDeviceFriendRequestStatuses = linkedDevices.map { + storage.getFriendRequestStatus(for: $0, transaction: transaction) } } // If the current user is friends with any of the other user's devices, the attachment button should be enabled - if friendRequestStatuses.contains(where: { $0 == .friends }) { return true } - // Otherwise don't allow attachments at all + if linkedDeviceFriendRequestStatuses.contains(where: { $0 == .friends }) { return true } + // Otherwise don't allow attachments return false } - @objc(getFriendRequestUIStateForThread:) - public static func getFriendRequestUIState(for thread: TSThread) -> FriendRequestUIState { + @objc(getFriendRequestUIStatusForThread:) + public static func getFriendRequestUIStatus(for thread: TSThread) -> FriendRequestUIStatus { // Friend requests have nothing to do with groups guard let thread = thread as? TSContactThread else { return .none } // If this is a note to self then we don't want to show the friend request - if thread.isNoteToSelf() { return .none } + guard !thread.isNoteToSelf() else { return .none } var friendRequestStatuses: [LKFriendRequestStatus] = [] storage.dbReadConnection.read { transaction in let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: thread.contactIdentifier(), in: transaction) - friendRequestStatuses = linkedDevices.map { device in - return storage.getFriendRequestStatus(for: device, transaction: transaction) + friendRequestStatuses = linkedDevices.map { + storage.getFriendRequestStatus(for: $0, transaction: transaction) } } - if friendRequestStatuses.contains(where: { $0 == .friends }) { return .friends } if friendRequestStatuses.contains(where: { $0 == .requestReceived }) { return .received } if friendRequestStatuses.contains(where: { $0 == .requestSent }) { return .sent } - return .none } // MARK: - Sending @objc(acceptFriendRequestFromHexEncodedPublicKey:using:) public static func acceptFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { + guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { + print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).") + return + } // Accept all outstanding friend requests associated with this user and try to establish sessions with the // subset of their devices that haven't sent a friend request. - guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { - assertionFailure("Invalid session ID \(hexEncodedPublicKey)") - return; - } - let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) for device in linkedDevices { let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction) @@ -116,12 +112,13 @@ public final class FriendRequestProtocol : NSObject { sendFriendRequestAcceptanceMessage(to: device, using: transaction) } else if friendRequestStatus == .requestSent { // We sent a friend request to this device before, how can we be sure that it hasn't expired? + // TODO: Shouldn't the status then be .expired? } else if friendRequestStatus == .none || friendRequestStatus == .requestExpired { - // TODO: Need to track these so that we can expire them and resend incase the other user wasn't online after we sent - MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: device, in: transaction) // NOT hexEncodedPublicKey - .done(on: OWSDispatch.sendingQueue()) { autoGeneratedFRMessageSend in - let messageSender = SSKEnvironment.shared.messageSender - messageSender.sendMessage(autoGeneratedFRMessageSend) + // TODO: We should track these so that we can expire them and resend if needed + MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: device, in: transaction) + .done(on: OWSDispatch.sendingQueue()) { autoGeneratedFRMessageSend in + let messageSender = SSKEnvironment.shared.messageSender + messageSender.sendMessage(autoGeneratedFRMessageSend) } } } @@ -130,10 +127,9 @@ public final class FriendRequestProtocol : NSObject { @objc(sendFriendRequestAcceptanceMessageToHexEncodedPublicKey:using:) public static func sendFriendRequestAcceptanceMessage(to hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { - assertionFailure("Invalid session ID \(hexEncodedPublicKey)") - return; + print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).") + return } - let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction) let ephemeralMessage = EphemeralMessage(in: thread) let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue @@ -143,15 +139,14 @@ public final class FriendRequestProtocol : NSObject { @objc(declineFriendRequestFromHexEncodedPublicKey:using:) public static func declineFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { - assertionFailure("Invalid session ID \(hexEncodedPublicKey)") - return; + print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).") + return } - let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) for device in linkedDevices { let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction) - // We only want to decline any incoming requests - if (friendRequestStatus == .requestReceived) { + // We only want to decline incoming requests + if friendRequestStatus == .requestReceived { // Delete the pre key bundle for the given contact. This ensures that if we send a // new message after this, it restarts the friend request process from scratch. storage.removePreKeyBundle(forContact: device, transaction: transaction) @@ -160,28 +155,25 @@ public final class FriendRequestProtocol : NSObject { } } - @objc(sendingFriendRequestToHexEncodedPublicKey:transaction:) - public static func sendingFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { + @objc(setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:transaction:) + public static func setFriendRequestStatusToSendingIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) - if (friendRequestStatus == .none || friendRequestStatus == .requestExpired) { - storage.setFriendRequestStatus(.requestSending, for: hexEncodedPublicKey, transaction: transaction) - } + guard friendRequestStatus == .none || friendRequestStatus == .requestExpired else { return } + storage.setFriendRequestStatus(.requestSending, for: hexEncodedPublicKey, transaction: transaction) } - @objc(sentFriendRequestToHexEncodedPublicKey:transaction:) - public static func sentFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { + @objc(setFriendRequestStatusToSentIfNeededForHexEncodedPublicKey:transaction:) + public static func setFriendRequestStatusToSentIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) - if (friendRequestStatus == .none || friendRequestStatus == .requestExpired || friendRequestStatus == .requestSending) { - storage.setFriendRequestStatus(.requestSent, for: hexEncodedPublicKey, transaction: transaction) - } + guard friendRequestStatus == .none || friendRequestStatus == .requestExpired || friendRequestStatus == .requestSending else { return } + storage.setFriendRequestStatus(.requestSent, for: hexEncodedPublicKey, transaction: transaction) } - @objc(failedToSendFriendRequestToHexEncodedPublicKey:transaction:) - public static func failedToSendFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { + @objc(setFriendRequestStatusToFailedIfNeededForHexEncodedPublicKey:transaction:) + public static func setFriendRequestStatusToFailedIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) - if (friendRequestStatus == .requestSending) { - storage.setFriendRequestStatus(.none, for: hexEncodedPublicKey, transaction: transaction) - } + guard friendRequestStatus == .requestSending else { return } + storage.setFriendRequestStatus(.none, for: hexEncodedPublicKey, transaction: transaction) } // MARK: - Receiving @@ -212,7 +204,9 @@ public final class FriendRequestProtocol : NSObject { if userLinkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey) { return true } // Auto-accept if the user is friends with any of the sender's linked devices. let senderLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) - if senderLinkedDevices.contains(where: { storage.getFriendRequestStatus(for: $0, transaction: transaction) == .friends }) { + if senderLinkedDevices.contains(where: { + storage.getFriendRequestStatus(for: $0, transaction: transaction) == .friends + }) { return true } // We can't auto-accept diff --git a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocolTests.swift b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocolTests.swift index 8061284d3..a8cdaa8aa 100644 --- a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocolTests.swift +++ b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocolTests.swift @@ -351,7 +351,7 @@ class FriendRequestProtocolTests : XCTestCase { let bob = generateHexEncodedPublicKey() let bobThread = createContactThread(for: bob) - let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIState] = [ + let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIStatus] = [ .none: .none, .requestExpired: .none, .requestSending: .none, @@ -384,7 +384,7 @@ class FriendRequestProtocolTests : XCTestCase { let masterThread = createContactThread(for: master) let slaveThread = createContactThread(for: slave) - let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIState] = [ + let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIStatus] = [ .none: .none, .requestExpired: .none, .requestSending: .none,