Untangle receipts, transcripts & typing indicators logic

pull/178/head
nielsandriesse 5 years ago
parent 9ae54412bd
commit 4098b9638c

@ -2827,5 +2827,5 @@
"Please make sure the Session ID you entered is correct and try again." = "Please make sure the Session ID you entered is correct and try again.";
"Device Linking Failed" = "Device Linking Failed";
"Please check your internet connection and try again" = "Please check your internet connection and try again";
"Authorization Device Link" = "Authorization Device Link";
"Authorizing Device Link" = "Authorizing Device Link";
"Please wait while the device link is created. This can take up to a minute." = "Please wait while the device link is created. This can take up to a minute.";

@ -182,7 +182,8 @@ typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMess
// Loki: If we're not friends then always set the message to a friend request message.
// If we're friends then the assumption is that we have the other user's pre key bundle.
NSString *messageClassAsString = (thread.isContactFriend || thread.isGroupThread) ? @"TSOutgoingMessage" : @"LKFriendRequestMessage";
BOOL isNoteToSelf = [LKSessionMetaProtocol isMessageNoteToSelf:thread];
NSString *messageClassAsString = (thread.isContactFriend || thread.isGroupThread || isNoteToSelf) ? @"TSOutgoingMessage" : @"LKFriendRequestMessage";
Class messageClass = NSClassFromString(messageClassAsString);
TSOutgoingMessage *message =

@ -216,7 +216,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
if (!IsNoteToSelfEnabled()) {
return NO;
}
return [LKSessionProtocol isMessageNoteToSelf:self];
return [LKSessionMetaProtocol isMessageNoteToSelf:self];
}
#pragma mark - To be subclassed.

@ -22,5 +22,6 @@
#pragma mark Settings
- (BOOL)shouldBeSaved { return NO; }
- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeSessionRequest]; }
- (BOOL)shouldSyncTranscript { return NO; }
@end

@ -21,7 +21,7 @@ public final class FriendRequestProtocol : NSObject {
// 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 SessionProtocol.isMessageNoteToSelf(thread) { return true }
if SessionMetaProtocol.isMessageNoteToSelf(thread) { return true }
let contactID = thread.contactIdentifier()
var linkedDeviceThreads: Set<TSContactThread> = []
storage.dbReadConnection.read { transaction in
@ -40,7 +40,7 @@ public final class FriendRequestProtocol : NSObject {
// Friend requests have nothing to do with groups, so if this isn't a contact thread the attachment button should be enabled
guard let thread = thread as? TSContactThread else { return true }
// If this is a note to self, the attachment button should be enabled
if SessionProtocol.isMessageNoteToSelf(thread) { return true }
if SessionMetaProtocol.isMessageNoteToSelf(thread) { return true }
let contactID = thread.contactIdentifier()
var linkedDeviceThreads: Set<TSContactThread> = []
storage.dbReadConnection.read { transaction in

@ -9,8 +9,8 @@ import PromiseKit
// Document the expected cases for everything.
// Express those cases in tests.
@objc(LKSessionProtocol)
public final class SessionProtocol : NSObject {
@objc(LKSessionMetaProtocol)
public final class SessionMetaProtocol : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
@ -58,15 +58,6 @@ public final class SessionProtocol : NSObject {
}
// MARK: Note to Self
// BEHAVIOR NOTE: OWSMessageSender.sendMessageToService:senderCertificate:success:failure: aborts early and just sends
// a sync message instead if the message it's supposed to send is considered a note to self (INCLUDING linked devices).
// BEHAVIOR NOTE: OWSMessageSender.sendMessage: aborts early and does nothing if the message is target at
// the current user (EXCLUDING linked devices).
// BEHAVIOR NOTE: OWSMessageSender.handleMessageSentLocally:success:failure: doesn't send a sync transcript if the message
// that was sent is considered a note to self (INCLUDING linked devices) but it does then mark the message as read.
// TODO: Check that the behaviors described above make sense
@objc(isMessageNoteToSelf:)
public static func isMessageNoteToSelf(_ thread: TSThread) -> Bool {
guard let thread = thread as? TSContactThread else { return false }
@ -77,23 +68,12 @@ public final class SessionProtocol : NSObject {
return isNoteToSelf
}
@objc(isMessageNoteToSelf:inThread:)
public static func isMessageNoteToSelf(_ message: TSOutgoingMessage, in thread: TSThread) -> Bool {
guard let thread = thread as? TSContactThread, !(message is OWSOutgoingSyncMessage) && !(message is DeviceLinkMessage) else { return false }
var isNoteToSelf = false
storage.dbReadConnection.read { transaction in
isNoteToSelf = LokiDatabaseUtilities.isUserLinkedDevice(thread.contactIdentifier(), transaction: transaction)
}
return isNoteToSelf
}
// MARK: Transcripts
@objc(shouldSendTranscriptForMessage:in:)
public static func shouldSendTranscript(for message: TSOutgoingMessage, in thread: TSThread) -> Bool {
let isNoteToSelf = isMessageNoteToSelf(message, in: thread)
let isOpenGroupMessage = (thread as? TSGroupThread)?.isPublicChat == true
let wouldSignalRequireTranscript = (AreRecipientUpdatesEnabled() || !message.hasSyncedTranscript)
return wouldSignalRequireTranscript && !isNoteToSelf && !isOpenGroupMessage && !(message is DeviceLinkMessage)
return wouldSignalRequireTranscript && !isOpenGroupMessage
}
// MARK: Typing Indicators
@ -101,43 +81,16 @@ public final class SessionProtocol : NSObject {
/// send them if certain conditions are met.
@objc(shouldSendTypingIndicatorForThread:)
public static func shouldSendTypingIndicator(for thread: TSThread) -> Bool {
return !thread.isGroupThread() && !isMessageNoteToSelf(thread)
return thread.friendRequestStatus == .friends && !thread.isGroupThread()
}
// MARK: Receipts
// Used from OWSReadReceiptManager
@objc(shouldSendReadReceiptForThread:)
public static func shouldSendReadReceipt(for thread: TSThread) -> Bool {
return !isMessageNoteToSelf(thread) && !thread.isGroupThread()
}
// TODO: Not sure how these two relate
// EDIT: I think the one below is used to block delivery receipts. That means that
// right now we do send delivery receipts in note to self, but not read receipts. Other than that their behavior should
// be identical. Should we just not send any kind of receipt in note to self?
// Used from OWSOutgoingReceiptManager
@objc(shouldSendReceiptForThread:)
public static func shouldSendReceipt(for thread: TSThread) -> Bool {
return thread.friendRequestStatus == .friends && !thread.isGroupThread()
}
// MARK: - Receiving
// 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
// 2. Handles friend request acceptance if needed
// 3. Checks if the message is a duplicate sync message and ignores it if so
// 4. Handles pre keys if needed (this also might trigger a session reset)
// 5. Updates P2P info if the message is a P2P address message
// 6. Handle device linking requests or authorizations if needed (it now doesn't continue along the normal message handling path)
// - If the message is a data message and has the session request flag set, processing stops here
// - If the message is a data message and has the session restore flag set, processing stops here
// 7. If the message got to this point, and it has an updated profile key attached, it'll now handle the profile key
// - If the message is a closed group message, it'll now check if it needs to be ignored
// ...
// MARK: - Decryption
@objc(shouldSkipMessageDecryptResult:)
public static func shouldSkipMessageDecryptResult(_ result: OWSMessageDecryptResult) -> Bool {
// Called from OWSMessageReceiver to prevent messages from even being added to the processing queue

@ -219,8 +219,8 @@ public final class MultiDeviceProtocol : NSObject {
}
// Set any profile info (the device link authorization also includes the master device's profile info)
if let dataMessage = protoContent.dataMessage {
SessionProtocol.updateDisplayNameIfNeeded(for: master, using: dataMessage, appendingShortID: false, in: transaction)
SessionProtocol.updateProfileKeyIfNeeded(for: master, using: dataMessage)
SessionMetaProtocol.updateDisplayNameIfNeeded(for: master, using: dataMessage, appendingShortID: false, in: transaction)
SessionMetaProtocol.updateProfileKeyIfNeeded(for: master, using: dataMessage)
}
} else { // Request
print("[Loki] Received a device link request from: \(hexEncodedPublicKey).") // Intentionally not `slave`

@ -117,8 +117,8 @@ public final class SyncMessagesProtocol : NSObject {
guard let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: getUserHexEncodedPublicKey(), in: transaction) else { return }
let wasSentByMasterDevice = (masterHexEncodedPublicKey == hexEncodedPublicKey)
guard wasSentByMasterDevice else { return }
SessionProtocol.updateDisplayNameIfNeeded(for: masterHexEncodedPublicKey, using: dataMessage, appendingShortID: false, in: transaction)
SessionProtocol.updateProfileKeyIfNeeded(for: masterHexEncodedPublicKey, using: dataMessage)
SessionMetaProtocol.updateDisplayNameIfNeeded(for: masterHexEncodedPublicKey, using: dataMessage, appendingShortID: false, in: transaction)
SessionMetaProtocol.updateProfileKeyIfNeeded(for: masterHexEncodedPublicKey, using: dataMessage)
}
@objc(handleClosedGroupUpdatedSyncMessageIfNeeded:using:)

@ -441,7 +441,7 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Handle address message if needed
/*
[LKSessionProtocol handleP2PAddressMessageIfNeeded:contentProto wrappedIn:envelope];
[LKSessionMetaProtocol handleP2PAddressMessageIfNeeded:contentProto wrappedIn:envelope];
*/
// Loki: Handle device linking message if needed
@ -1296,10 +1296,10 @@ NS_ASSUME_NONNULL_BEGIN
}
// Loki: Handle profile key update if needed
[LKSessionProtocol updateProfileKeyIfNeededForHexEncodedPublicKey:senderMasterHexEncodedPublicKey using:dataMessage];
[LKSessionMetaProtocol updateProfileKeyIfNeededForHexEncodedPublicKey:senderMasterHexEncodedPublicKey using:dataMessage];
// Loki: Handle display name update if needed
[LKSessionProtocol updateDisplayNameIfNeededForHexEncodedPublicKey:senderMasterHexEncodedPublicKey using:dataMessage appendingShortID:NO in:transaction];
[LKSessionMetaProtocol updateDisplayNameIfNeededForHexEncodedPublicKey:senderMasterHexEncodedPublicKey using:dataMessage appendingShortID:NO in:transaction];
switch (dataMessage.group.type) {
case SSKProtoGroupContextTypeUpdate: {
@ -1520,8 +1520,8 @@ NS_ASSUME_NONNULL_BEGIN
wasReceivedByUD:wasReceivedByUD];
// TODO: Are we sure this works correctly with multi device?
[LKSessionProtocol updateDisplayNameIfNeededForHexEncodedPublicKey:incomingMessage.authorId using:dataMessage appendingShortID:YES in:transaction];
[LKSessionProtocol updateProfileKeyIfNeededForHexEncodedPublicKey:thread.contactIdentifier using:dataMessage];
[LKSessionMetaProtocol updateDisplayNameIfNeededForHexEncodedPublicKey:incomingMessage.authorId using:dataMessage appendingShortID:YES in:transaction];
[LKSessionMetaProtocol updateProfileKeyIfNeededForHexEncodedPublicKey:thread.contactIdentifier using:dataMessage];
// Loki: Parse Loki specific properties if needed
/*

@ -398,7 +398,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
OWSAssertDebug(transaction);
// Loki: Don't process any messages from ourself
if ([LKSessionProtocol shouldSkipMessageDecryptResult:result]) {
if ([LKSessionMetaProtocol shouldSkipMessageDecryptResult:result]) {
dispatch_async(self.serialQueue, ^{
completion(YES);
});

@ -500,10 +500,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
__block NSMutableSet<NSString *> *recipientIds = [NSMutableSet new];
if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) {
recipientIds = [LKSessionProtocol getDestinationsForOutgoingSyncMessage];
recipientIds = [LKSessionMetaProtocol getDestinationsForOutgoingSyncMessage];
} else if (thread.isGroupThread) {
TSGroupThread *groupThread = (TSGroupThread *)thread;
recipientIds = [LKSessionProtocol getDestinationsForOutgoingGroupMessage:message inThread:thread];
recipientIds = [LKSessionMetaProtocol getDestinationsForOutgoingGroupMessage:message inThread:thread];
} else if ([thread isKindOfClass:[TSContactThread class]]) {
NSString *recipientContactId = ((TSContactThread *)thread).contactIdentifier;
@ -670,10 +670,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return failureHandler(error);
}
// Loki: Abort early and send a sync transcript if this is a note to self
if ([LKSessionProtocol isMessageNoteToSelf:message inThread:thread]) {
// FIXME: I think this is where the duplicate sync messages might be coming from. Signal just invokes successHandler() here.
[self sendSyncTranscriptForMessage:message isRecipientUpdate:NO success:^{ } failure:^(NSError *error) { }];
// In the "self-send" special case, we ony need to send a sync message with a delivery receipt
// Loki: Take into account multi device
if ([LKSessionMetaProtocol isMessageNoteToSelf:thread] && !([message isKindOfClass:LKDeviceLinkMessage.class])) {
// Don't mark self-sent messages as read (or sent) until the sync transcript is sent
successHandler();
return;
}
@ -916,14 +916,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
TSOutgoingMessage *message = messageSend.message;
SignalRecipient *recipient = messageSend.recipient;
// Loki: Ignore messages addressed to self
// TODO: Why?
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
if ([messageSend.recipient.recipientId isEqual:userHexEncodedPublicKey]) {
[LKLogger print:[NSString stringWithFormat:@"[Loki] Ignoring %@ addressed to self.", message.class]];
return messageSend.success();
}
OWSLogInfo(@"Attempting to send message: %@, timestamp: %llu, recipient: %@.",
message.class,
@ -1488,9 +1480,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
failure:(RetryableFailureHandler)failure
{
dispatch_block_t success = ^{
// Loki: Handle note to self case
BOOL isNoteToSelf = [LKSessionProtocol isMessageNoteToSelf:message inThread:message.thread];
if (isNoteToSelf) {
// Don't mark self-sent messages as read (or sent) until the sync transcript is sent
// Loki: Take into account multi device
BOOL isNoteToSelf = [LKSessionMetaProtocol isMessageNoteToSelf:message.thread];
if (isNoteToSelf && !([message isKindOfClass:LKDeviceLinkMessage.class])) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *recipientId in message.sendingRecipientIds) {
[message updateWithReadRecipientId:recipientId readTimestamp:message.timestamp transaction:transaction];
@ -1511,7 +1504,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return success();
}
BOOL shouldSendTranscript = [LKSessionProtocol shouldSendTranscriptForMessage:message in:message.thread];
BOOL shouldSendTranscript = [LKSessionMetaProtocol shouldSendTranscriptForMessage:message in:message.thread];
if (!shouldSendTranscript) {
return success();
}

@ -174,7 +174,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
if (![LKSessionProtocol shouldSendReceiptForThread:thread]) {
if (![LKSessionMetaProtocol shouldSendReceiptForThread:thread]) {
continue;
}

@ -286,7 +286,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
self.toLinkedDevicesReadReceiptMap[threadUniqueId] = newReadReceipt;
}
if (![LKSessionProtocol shouldSendReadReceiptForThread:message.thread]) {
if (![LKSessionMetaProtocol shouldSendReceiptForThread:message.thread]) {
return;
}

@ -323,7 +323,7 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators {
return
}
if !SessionProtocol.shouldSendTypingIndicator(for: thread) { return }
if !SessionMetaProtocol.shouldSendTypingIndicator(for: thread) { return }
let message = TypingIndicatorMessage(thread: thread, action: action)
messageSender.sendPromise(message: message).retainUntilComplete()

Loading…
Cancel
Save