diff --git a/SignalServiceKit/src/Loki/Protocol/Session Management/SessionManagementProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Session Management/SessionManagementProtocol.swift index b8653c0b0..f86aef698 100644 --- a/SignalServiceKit/src/Loki/Protocol/Session Management/SessionManagementProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Session Management/SessionManagementProtocol.swift @@ -94,12 +94,12 @@ public final class SessionManagementProtocol : NSObject { // Check that we don't already have a session let hasSession = storage.containsSession(publicKey, deviceId: Int32(OWSDevicePrimaryDeviceId), protocolContext: transaction) guard !hasSession else { return } - // Check that we didn't already send a session request - var hasSentSessionRequest = false + // Check that we didn't already send or process a session request + var hasSentOrProcessedSessionRequest = false storage.dbReadConnection.read { transaction in - hasSentSessionRequest = storage.getSessionRequestTimestamp(for: publicKey, in: transaction) != nil + hasSentOrProcessedSessionRequest = storage.getSessionRequestTimestamp(for: publicKey, in: transaction) != nil } - guard !hasSentSessionRequest else { return } + guard !hasSentOrProcessedSessionRequest else { return } // Create the thread if needed let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction) thread.save(with: transaction) @@ -184,9 +184,9 @@ public final class SessionManagementProtocol : NSObject { return print("[Loki] Couldn't parse pre key bundle received from: \(publicKey).") } if isSessionRequestMessage(protoContent.dataMessage), - let sentSessionRequestTimestamp = storage.getSessionRequestTimestamp(for: publicKey, in: transaction), - envelope.timestamp < NSDate.ows_millisecondsSince1970(for: sentSessionRequestTimestamp) { - // We sent a session request after this one was sent + let sessionRequestTimestamp = storage.getSessionRequestTimestamp(for: publicKey, in: transaction), + envelope.timestamp < NSDate.ows_millisecondsSince1970(for: sessionRequestTimestamp) { + // We sent or processed a session request after this one was sent return print("[Loki] Ignoring session request from: \(publicKey).") } storage.setPreKeyBundle(preKeyBundle, forContact: publicKey, transaction: transaction) @@ -199,10 +199,11 @@ public final class SessionManagementProtocol : NSObject { let publicKey = envelope.source! // Set during UD decryption if let sentSessionRequestTimestamp = storage.getSessionRequestTimestamp(for: publicKey, in: transaction), envelope.timestamp < NSDate.ows_millisecondsSince1970(for: sentSessionRequestTimestamp) { - // We sent a session request after this one was sent + // We sent or processed a session request after this one was sent print("[Loki] Ignoring session request from: \(publicKey).") return false } + storage.setSessionRequestTimestamp(for: publicKey, to: Date(), in: transaction) sendSessionEstablishedMessage(to: publicKey, in: transaction) return true } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index d162a0949..92f60d365 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -25,6 +25,7 @@ #import "OWSIncomingSentMessageTranscript.h" #import "OWSMessageSender.h" #import "OWSMessageUtils.h" +#import "OWSOutgoingNullMessage.h" #import "OWSOutgoingReceiptManager.h" #import "OWSPrimaryStorage+SessionStore.h" #import "OWSPrimaryStorage+Loki.h" @@ -482,7 +483,53 @@ NS_ASSUME_NONNULL_BEGIN } else if (contentProto.typingMessage) { [self handleIncomingEnvelope:envelope withTypingMessage:contentProto.typingMessage transaction:transaction]; } else if (contentProto.nullMessage) { - OWSLogInfo(@"Received null message."); + // Loki: This is needed for compatibility with refactored desktop clients + // ======== + NSString *publicKey = envelope.source; + if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) { + TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:publicKey transaction:transaction]; + [thread saveWithTransaction:transaction]; + OWSOutgoingNullMessage *sessionEstablishedMessage = [[OWSOutgoingNullMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + messageBody:nil + attachmentIds:[NSMutableArray new] + expiresInSeconds:0 + expireStartedAt:0 + isVoiceMessage:NO + groupMetaMessage:TSGroupMetaMessageUnspecified + quotedMessage:nil + contactShare:nil + linkPreview:nil]; + SSKMessageSenderJobQueue *messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue; + [messageSenderJobQueue addMessage:sessionEstablishedMessage transaction:transaction]; + } else { + OWSLogWarn(@"Ignoring null message from: %@.", publicKey); + } + LKFriendRequestStatus friendRequestStatus = [self.primaryStorage getFriendRequestStatusForContact:publicKey transaction:transaction]; + if (friendRequestStatus == LKFriendRequestStatusNone || friendRequestStatus == LKFriendRequestStatusRequestExpired) { + [self.primaryStorage setFriendRequestStatus:LKFriendRequestStatusRequestReceived forContact:publicKey transaction:transaction]; + } else if (friendRequestStatus == LKFriendRequestStatusRequestSent) { + // Loki: Update device links in a blocking way + // FIXME: This is horrible for performance + // FIXME: ======== + // The envelope source is set during UD decryption + if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:publicKey]) { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [[LKMultiDeviceProtocol updateDeviceLinksIfNeededForPublicKey:envelope.source transaction:transaction].ensureOn(queue, ^() { + dispatch_semaphore_signal(semaphore); + }).catchOn(queue, ^(NSError *error) { + dispatch_semaphore_signal(semaphore); + }) retainUntilComplete]; + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)); + } + // FIXME: ======== + [self.primaryStorage setFriendRequestStatus:LKFriendRequestStatusFriends forContact:publicKey transaction:transaction]; + [LKFriendRequestProtocol sendFriendRequestAcceptedMessageToPublicKey:publicKey using:transaction]; + NSString *masterPublicKey = ([LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:publicKey in:transaction] ?: publicKey); + [LKSyncMessagesProtocol syncContactWithPublicKey:masterPublicKey]; + } + // ======== } else if (contentProto.receiptMessage) { [self handleIncomingEnvelope:envelope withReceiptMessage:contentProto.receiptMessage @@ -1551,26 +1598,6 @@ NS_ASSUME_NONNULL_BEGIN // Loki: Handle friend request if needed [LKFriendRequestProtocol handleFriendRequestMessageIfNeededFromEnvelope:envelope using:transaction]; - - if (body.length == 0 && attachmentPointers.count < 1 && !contact) { - if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) { - // Loki: This is needed for compatibility with refactored desktop clients - [LKSessionManagementProtocol sendSessionEstablishedMessageToPublicKey:publicKey transaction:transaction]; - } else { - OWSLogWarn(@"Ignoring empty incoming message from: %@ with timestamp: %lu.", publicKey, (unsigned long)timestamp); - } - return nil; - } - - // Loki: This is needed for compatibility with refactored desktop clients - LKFriendRequestStatus friendRequestStatus = [self.primaryStorage getFriendRequestStatusForContact:publicKey transaction:transaction]; - if (friendRequestStatus == LKFriendRequestStatusNone || friendRequestStatus == LKFriendRequestStatusRequestExpired) { - [self.primaryStorage setFriendRequestStatus:LKFriendRequestStatusRequestReceived forContact:publicKey transaction:transaction]; - } else if (friendRequestStatus == LKFriendRequestStatusRequestSent) { - [self.primaryStorage setFriendRequestStatus:LKFriendRequestStatusFriends forContact:publicKey transaction:transaction]; - [LKFriendRequestProtocol sendFriendRequestAcceptedMessageToPublicKey:publicKey using:transaction]; - [LKSyncMessagesProtocol syncContactWithPublicKey:masterPublicKey]; - } [self finalizeIncomingMessage:incomingMessage thread:thread diff --git a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.h b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.h index c58f95fa3..eb8b1c806 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.h +++ b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.h @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage quotedMessage:(nullable TSQuotedMessage *)quotedMessage contactShare:(nullable OWSContact *)contactShare - linkPreview:(nullable OWSLinkPreview *)linkPreview NS_UNAVAILABLE; + linkPreview:(nullable OWSLinkPreview *)linkPreview; - (instancetype)initWithContactThread:(TSContactThread *)contactThread verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage; diff --git a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m index a72baf1eb..a4b96a0f2 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m @@ -51,16 +51,21 @@ NS_ASSUME_NONNULL_BEGIN { SSKProtoNullMessageBuilder *nullMessageBuilder = [SSKProtoNullMessage builder]; - NSUInteger contentLength = self.verificationStateSyncMessage.unpaddedVerifiedLength; - - OWSAssertDebug(self.verificationStateSyncMessage.paddingBytesLength > 0); - - // We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that - // the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad - // the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally* - // padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a - // verification sync which is ~1-512 bytes larger then that. - contentLength += self.verificationStateSyncMessage.paddingBytesLength; + NSUInteger contentLength; + if (self.verificationStateSyncMessage != nil) { + contentLength = self.verificationStateSyncMessage.unpaddedVerifiedLength; + + OWSAssertDebug(self.verificationStateSyncMessage.paddingBytesLength > 0); + + // We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that + // the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad + // the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally* + // padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a + // verification sync which is ~1-512 bytes larger then that. + contentLength += self.verificationStateSyncMessage.paddingBytesLength; + } else { + contentLength = arc4random_uniform(512); + } OWSAssertDebug(contentLength > 0); @@ -68,8 +73,8 @@ NS_ASSUME_NONNULL_BEGIN NSError *error; SSKProtoNullMessage *_Nullable nullMessage = [nullMessageBuilder buildAndReturnError:&error]; - if (error || !nullMessage) { - OWSFailDebug(@"could not build protobuf: %@", error); + if (error != nil || nullMessage == nil) { + OWSFailDebug(@"Couldn't build protobuf due to error: %@.", error); return nil; } @@ -77,10 +82,11 @@ NS_ASSUME_NONNULL_BEGIN contentBuilder.nullMessage = nullMessage; NSData *_Nullable contentData = [contentBuilder buildSerializedDataAndReturnError:&error]; - if (error || !contentData) { - OWSFailDebug(@"could not serialize protobuf: %@", error); + if (error != nil || contentData == nil) { + OWSFailDebug(@"Couldn't serialize protobuf due to error: %@.", error); return nil; } + return contentData; }