diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 42fb63766..b5f2741d2 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -31,17 +31,17 @@ extern ConversationColorName const kConversationColorName_Default; // Loki: Friend request state typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) { - // New conversation, no messages sent or received + /// New conversation, no messages sent or received TSThreadFriendRequestStatusNone, - // This state is used to lock the input early while sending - TSThreadFriendRequestStatusPendingSend, - // Friend request sent, awaiting response + /// This state is used to lock the input early while sending + TSThreadFriendRequestStatusRequestSending, + /// Friend request sent, awaiting response TSThreadFriendRequestStatusRequestSent, - // Friend request received, awaiting user input + /// Friend request received, awaiting user input TSThreadFriendRequestStatusRequestReceived, - // We are friends with the user of this thread + /// We are friends with the user of this thread TSThreadFriendRequestStatusFriends, - // Friend request sent but it timed out (user didn't accept within x time) + /// Friend request sent but it timed out (user didn't accept within x time) TSThreadFriendRequestStatusRequestExpired }; @@ -54,13 +54,15 @@ typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) { @property (nonatomic, readonly) NSDate *creationDate; @property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting; // Loki friend request handling +// ======== @property (nonatomic) TSThreadFriendRequestStatus friendRequestStatus; -/// Shorthand for checking that `friendRequestStatus` is `TSThreadFriendRequestStatusPendingSend`, `TSThreadFriendRequestStatusRequestSent` +/// Shorthand for checking that `friendRequestStatus` is `TSThreadFriendRequestStatusRequestSending`, `TSThreadFriendRequestStatusRequestSent` /// or `TSThreadFriendRequestStatusRequestReceived`. @property (nonatomic, readonly) BOOL hasPendingFriendRequest; @property (nonatomic, readonly) BOOL isContactFriend; @property (nonatomic, readonly) BOOL hasCurrentUserSentFriendRequest; @property (nonatomic, readonly) BOOL hasCurrentUserReceivedFriendRequest; +// ======== /** * Whether the object is a group thread or not. diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index 4d197d756..a1c3c2d69 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -710,7 +710,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa - (BOOL)hasPendingFriendRequest { - return self.friendRequestStatus == TSThreadFriendRequestStatusPendingSend || self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent + return self.friendRequestStatus == TSThreadFriendRequestStatusRequestSending || self.friendRequestStatus == TSThreadFriendRequestStatusRequestSent || self.friendRequestStatus == TSThreadFriendRequestStatusRequestReceived; } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index c8a9d9a1a..aebe58f44 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) TSQuotedMessage *quotedMessage; @property (nonatomic, readonly, nullable) OWSContact *contactShare; @property (nonatomic, readonly, nullable) OWSLinkPreview *linkPreview; -@property (nonatomic) BOOL isFriendRequest; +@property (nonatomic) BOOL isFriendRequest; // Loki - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index af8d4517b..e2c333603 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1446,12 +1446,32 @@ NS_ASSUME_NONNULL_BEGIN // ======== if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) { if (thread.hasCurrentUserSentFriendRequest) { + // This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his + // mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request + // and send a friend request accepted message back to Bob. We don't check that sending the + // friend request accepted message succeeded. Even if it doesn't, the thread's current friend + // request status will be set to TSThreadFriendRequestStatusFriends for Alice making it possible + // for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status + // will then be set to TSThreadFriendRequestStatusFriends. If we do check for a successful send + // before updating Alice's thread's friend request status to TSThreadFriendRequestStatusFriends, + // we can end up in a deadlock where both users' threads' friend request statuses are + // TSThreadFriendRequestStatusRequestSent. [thread setFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction]; - } else { + // The two lines below are equivalent to calling [ThreadUtil enqueueAcceptFriendRequestMessageInThread:thread] + TSOutgoingMessage *message = [TSOutgoingMessage createEmptyOutgoingMessageInThread:thread]; + [self.messageSenderJobQueue addMessage:message transaction:transaction]; + } else if (!thread.isContactFriend) { + // Checking that the sender of the message isn't already a friend is necessary because otherwise + // the following situation can occur: Alice and Bob are friends. Bob loses his database and his + // friend request status is reset to TSThreadFriendRequestStatusNone. Bob now sends Alice a friend + // request. Alice's thread's friend request status is reset to + // TSThreadFriendRequestStatusRequestReceived. [thread setFriendRequestStatus:TSThreadFriendRequestStatusRequestReceived withTransaction:transaction]; incomingMessage.isFriendRequest = YES; // Saved below } } else if (!thread.isContactFriend) { + // If the thread's friend request status is not TSThreadFriendRequestStatusFriends, but we're receiving a message, + // it must be a friend request accepted message. Declining a friend request doesn't send a message. [thread setFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:transaction]; } // ======== diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 73f7146bf..0416f226e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1105,13 +1105,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // Update the state to show that proof of work is being calculated [self setIsCalculatingProofOfWorkForMessage:messageSend]; - // Convert the message to a Loki message and send it using the Loki messaging API NSDictionary *signalMessage = deviceMessages.firstObject; // Update the thread's friend request status if needed NSInteger *messageType = ((NSNumber *)signalMessage[@"type"]).integerValue; if (messageType == TSFriendRequestMessageType) { - [message.thread setFriendRequestStatus:TSThreadFriendRequestStatusPendingSend withTransaction:nil]; + [message.thread setFriendRequestStatus:TSThreadFriendRequestStatusRequestSending withTransaction:nil]; [message setIsFriendRequest:YES withTransaction:nil]; } BOOL isPoWRequired = YES; // TODO: Base on message type @@ -1134,10 +1133,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; wasSentByWebsocket:false]; }) .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { - // Update the thread's friend request status if needed + // Loki + // ======== if (messageType == TSFriendRequestMessageType) { [message.thread setFriendRequestStatus:TSThreadFriendRequestStatusNone withTransaction:nil]; } + // ======== // Handle the error NSUInteger statusCode = 0; NSData *_Nullable responseData = nil;