pull/175/head
nielsandriesse 5 years ago
parent 96331ecdd5
commit 0d48031e9c

@ -920,7 +920,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
TSGroupThread *groupThread = (TSGroupThread *)thread; TSGroupThread *groupThread = (TSGroupThread *)thread;
NSData *groupId = groupThread.groupModel.groupId; NSData *groupId = groupThread.groupModel.groupId;
return [self isGroupIdInProfileWhitelist:groupId]; return [self isGroupIdInProfileWhitelist:groupId];
} else if ([LKFriendRequestProtocol isFriendsWithLinkedDevicesOfHexEncodedPublicKey:thread.contactIdentifier]) { } else if ([LKFriendRequestProtocol isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:thread.contactIdentifier]) {
return true; return true;
} else { } else {
NSString *recipientId = thread.contactIdentifier; NSString *recipientId = thread.contactIdentifier;

@ -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 we're friends with the person then we don't need to remove any friend request messages
if (self.isGroupThread) { return; } if (self.isGroupThread) { return; }
if ([LKFriendRequestProtocol isFriendsWithLinkedDevicesOfHexEncodedPublicKey:self.contactIdentifier]) { return; } if ([LKFriendRequestProtocol isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:self.contactIdentifier]) { return; }
NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new]; NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new];
__block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it __block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it

@ -15,44 +15,43 @@ public final class FriendRequestProtocol : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() } internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: - Enums // MARK: - Friend Request UI Status
// FIXME: Better naming :( @objc public enum FriendRequestUIStatus : Int {
@objc public enum FriendRequestUIState : Int {
case friends, received, sent, none case friends, received, sent, none
} }
// MARK: - General // MARK: - General
@objc(isFriendsWithLinkedDevicesOfHexEncodedPublicKey:) @objc(isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:)
public static func isFriendsWithLinkedDevices(of hexEncodedPublicKey: String) -> Bool { public static func isFriendsWithAnyLinkedDevice(of hexEncodedPublicKey: String) -> Bool {
var isFriends = false var result = false
storage.dbReadConnection.read { transaction in storage.dbReadConnection.read { transaction in
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction)
let friendRequestStatuses = linkedDevices.map { device in let friendRequestStatuses = linkedDevices.map {
return storage.getFriendRequestStatus(for: device, transaction: transaction) storage.getFriendRequestStatus(for: $0, transaction: transaction)
} }
isFriends = friendRequestStatuses.contains(where: { $0 == .friends }) result = friendRequestStatuses.contains { $0 == .friends }
} }
return isFriends return result
} }
@objc(shouldInputBarBeEnabledForThread:) @objc(shouldInputBarBeEnabledForThread:)
public static func shouldInputBarBeEnabled(for thread: TSThread) -> Bool { 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 // 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 } 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 } if thread.isNoteToSelf() { return true }
let contactID = thread.contactIdentifier() let contactID = thread.contactIdentifier()
var friendRequestStatuses: [LKFriendRequestStatus] = [] var linkedDeviceFriendRequestStatuses: [LKFriendRequestStatus] = []
storage.dbReadConnection.read { transaction in storage.dbReadConnection.read { transaction in
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction)
friendRequestStatuses = linkedDevices.map { device in linkedDeviceFriendRequestStatuses = linkedDevices.map {
return storage.getFriendRequestStatus(for: device, transaction: transaction) 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 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 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 // There must be a pending friend request
return false 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 this is a note to self, the attachment button should be enabled
if thread.isNoteToSelf() { return true } if thread.isNoteToSelf() { return true }
let contactID = thread.contactIdentifier() let contactID = thread.contactIdentifier()
var friendRequestStatuses: [LKFriendRequestStatus] = [] var linkedDeviceFriendRequestStatuses: [LKFriendRequestStatus] = []
storage.dbReadConnection.read { transaction in storage.dbReadConnection.read { transaction in
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: contactID, in: transaction)
friendRequestStatuses = linkedDevices.map { device in linkedDeviceFriendRequestStatuses = linkedDevices.map {
storage.getFriendRequestStatus(for: device, transaction: transaction) 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 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 } if linkedDeviceFriendRequestStatuses.contains(where: { $0 == .friends }) { return true }
// Otherwise don't allow attachments at all // Otherwise don't allow attachments
return false return false
} }
@objc(getFriendRequestUIStateForThread:) @objc(getFriendRequestUIStatusForThread:)
public static func getFriendRequestUIState(for thread: TSThread) -> FriendRequestUIState { public static func getFriendRequestUIStatus(for thread: TSThread) -> FriendRequestUIStatus {
// Friend requests have nothing to do with groups // Friend requests have nothing to do with groups
guard let thread = thread as? TSContactThread else { return .none } 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 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] = [] var friendRequestStatuses: [LKFriendRequestStatus] = []
storage.dbReadConnection.read { transaction in storage.dbReadConnection.read { transaction in
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: thread.contactIdentifier(), in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: thread.contactIdentifier(), in: transaction)
friendRequestStatuses = linkedDevices.map { device in friendRequestStatuses = linkedDevices.map {
return storage.getFriendRequestStatus(for: device, transaction: transaction) storage.getFriendRequestStatus(for: $0, transaction: transaction)
} }
} }
if friendRequestStatuses.contains(where: { $0 == .friends }) { return .friends } if friendRequestStatuses.contains(where: { $0 == .friends }) { return .friends }
if friendRequestStatuses.contains(where: { $0 == .requestReceived }) { return .received } if friendRequestStatuses.contains(where: { $0 == .requestReceived }) { return .received }
if friendRequestStatuses.contains(where: { $0 == .requestSent }) { return .sent } if friendRequestStatuses.contains(where: { $0 == .requestSent }) { return .sent }
return .none return .none
} }
// MARK: - Sending // MARK: - Sending
@objc(acceptFriendRequestFromHexEncodedPublicKey:using:) @objc(acceptFriendRequestFromHexEncodedPublicKey:using:)
public static func acceptFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { public static func acceptFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// 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 { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else {
assertionFailure("Invalid session ID \(hexEncodedPublicKey)") print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).")
return; 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.
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction)
for device in linkedDevices { for device in linkedDevices {
let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction) let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction)
@ -116,12 +112,13 @@ public final class FriendRequestProtocol : NSObject {
sendFriendRequestAcceptanceMessage(to: device, using: transaction) sendFriendRequestAcceptanceMessage(to: device, using: transaction)
} else if friendRequestStatus == .requestSent { } else if friendRequestStatus == .requestSent {
// We sent a friend request to this device before, how can we be sure that it hasn't expired? // 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 { } 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 // TODO: We should track these so that we can expire them and resend if needed
MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: device, in: transaction) // NOT hexEncodedPublicKey MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: device, in: transaction)
.done(on: OWSDispatch.sendingQueue()) { autoGeneratedFRMessageSend in .done(on: OWSDispatch.sendingQueue()) { autoGeneratedFRMessageSend in
let messageSender = SSKEnvironment.shared.messageSender let messageSender = SSKEnvironment.shared.messageSender
messageSender.sendMessage(autoGeneratedFRMessageSend) messageSender.sendMessage(autoGeneratedFRMessageSend)
} }
} }
} }
@ -130,10 +127,9 @@ public final class FriendRequestProtocol : NSObject {
@objc(sendFriendRequestAcceptanceMessageToHexEncodedPublicKey:using:) @objc(sendFriendRequestAcceptanceMessageToHexEncodedPublicKey:using:)
public static func sendFriendRequestAcceptanceMessage(to hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { public static func sendFriendRequestAcceptanceMessage(to hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else {
assertionFailure("Invalid session ID \(hexEncodedPublicKey)") print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).")
return; return
} }
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction) let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let ephemeralMessage = EphemeralMessage(in: thread) let ephemeralMessage = EphemeralMessage(in: thread)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
@ -143,15 +139,14 @@ public final class FriendRequestProtocol : NSObject {
@objc(declineFriendRequestFromHexEncodedPublicKey:using:) @objc(declineFriendRequestFromHexEncodedPublicKey:using:)
public static func declineFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { public static func declineFriendRequest(from hexEncodedPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else {
assertionFailure("Invalid session ID \(hexEncodedPublicKey)") print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).")
return; return
} }
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction)
for device in linkedDevices { for device in linkedDevices {
let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction) let friendRequestStatus = storage.getFriendRequestStatus(for: device, transaction: transaction)
// We only want to decline any incoming requests // We only want to decline incoming requests
if (friendRequestStatus == .requestReceived) { if friendRequestStatus == .requestReceived {
// Delete the pre key bundle for the given contact. This ensures that if we send a // 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. // new message after this, it restarts the friend request process from scratch.
storage.removePreKeyBundle(forContact: device, transaction: transaction) storage.removePreKeyBundle(forContact: device, transaction: transaction)
@ -160,28 +155,25 @@ public final class FriendRequestProtocol : NSObject {
} }
} }
@objc(sendingFriendRequestToHexEncodedPublicKey:transaction:) @objc(setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:transaction:)
public static func sendingFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { public static func setFriendRequestStatusToSendingIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) {
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)
if (friendRequestStatus == .none || friendRequestStatus == .requestExpired) { guard friendRequestStatus == .none || friendRequestStatus == .requestExpired else { return }
storage.setFriendRequestStatus(.requestSending, for: hexEncodedPublicKey, transaction: transaction) storage.setFriendRequestStatus(.requestSending, for: hexEncodedPublicKey, transaction: transaction)
}
} }
@objc(sentFriendRequestToHexEncodedPublicKey:transaction:) @objc(setFriendRequestStatusToSentIfNeededForHexEncodedPublicKey:transaction:)
public static func sentFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { public static func setFriendRequestStatusToSentIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) {
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)
if (friendRequestStatus == .none || friendRequestStatus == .requestExpired || friendRequestStatus == .requestSending) { guard friendRequestStatus == .none || friendRequestStatus == .requestExpired || friendRequestStatus == .requestSending else { return }
storage.setFriendRequestStatus(.requestSent, for: hexEncodedPublicKey, transaction: transaction) storage.setFriendRequestStatus(.requestSent, for: hexEncodedPublicKey, transaction: transaction)
}
} }
@objc(failedToSendFriendRequestToHexEncodedPublicKey:transaction:) @objc(setFriendRequestStatusToFailedIfNeededForHexEncodedPublicKey:transaction:)
public static func failedToSendFriendRequest(to hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) { public static func setFriendRequestStatusToFailedIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) {
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction) let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)
if (friendRequestStatus == .requestSending) { guard friendRequestStatus == .requestSending else { return }
storage.setFriendRequestStatus(.none, for: hexEncodedPublicKey, transaction: transaction) storage.setFriendRequestStatus(.none, for: hexEncodedPublicKey, transaction: transaction)
}
} }
// MARK: - Receiving // MARK: - Receiving
@ -212,7 +204,9 @@ public final class FriendRequestProtocol : NSObject {
if userLinkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey) { return true } if userLinkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey) { return true }
// Auto-accept if the user is friends with any of the sender's linked devices. // Auto-accept if the user is friends with any of the sender's linked devices.
let senderLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction) 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 return true
} }
// We can't auto-accept // We can't auto-accept

@ -351,7 +351,7 @@ class FriendRequestProtocolTests : XCTestCase {
let bob = generateHexEncodedPublicKey() let bob = generateHexEncodedPublicKey()
let bobThread = createContactThread(for: bob) let bobThread = createContactThread(for: bob)
let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIState] = [ let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIStatus] = [
.none: .none, .none: .none,
.requestExpired: .none, .requestExpired: .none,
.requestSending: .none, .requestSending: .none,
@ -384,7 +384,7 @@ class FriendRequestProtocolTests : XCTestCase {
let masterThread = createContactThread(for: master) let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave) let slaveThread = createContactThread(for: slave)
let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIState] = [ let expectedStates: [LKFriendRequestStatus : FriendRequestProtocol.FriendRequestUIStatus] = [
.none: .none, .none: .none,
.requestExpired: .none, .requestExpired: .none,
.requestSending: .none, .requestSending: .none,

Loading…
Cancel
Save