From 3ed3e00c4ca3ca943db7efa32cae1cdd29c0f837 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 4 May 2020 10:17:28 +1000 Subject: [PATCH] Review fixes --- .../Sync Messages/SyncMessagesProtocol.swift | 5 +- .../src/Loki/Shelved/LokiP2PAPI.swift | 2 +- .../src/Messages/OWSMessageSender.m | 659 +++++++++--------- 3 files changed, 331 insertions(+), 335 deletions(-) diff --git a/SignalServiceKit/src/Loki/Protocol/Sync Messages/SyncMessagesProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Sync Messages/SyncMessagesProtocol.swift index 84c78c28b..5165f375f 100644 --- a/SignalServiceKit/src/Loki/Protocol/Sync Messages/SyncMessagesProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Sync Messages/SyncMessagesProtocol.swift @@ -173,10 +173,7 @@ public final class SyncMessagesProtocol : NSObject { switch friendRequestStatus { case .none, .requestExpired: let messageSender = SSKEnvironment.shared.messageSender - // TODO: Does the function below need to handle multi device?? - // We need to send a FR message here directly to the user. Multi device doesn't come into play. - // TODO: Say A1 has a conversation with B1 and B2. A2 is now linked to A1. We'd want A2 to establish - // sessions with both B1 and B2, right? + // We need to send the FR message to all of the users device as the contact sync message excludes slave devices let autoGeneratedFRMessage = MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessage(for: hexEncodedPublicKey, in: transaction) thread.isForceHidden = true thread.save(with: transaction) diff --git a/SignalServiceKit/src/Loki/Shelved/LokiP2PAPI.swift b/SignalServiceKit/src/Loki/Shelved/LokiP2PAPI.swift index d4925e5f5..60a297b85 100644 --- a/SignalServiceKit/src/Loki/Shelved/LokiP2PAPI.swift +++ b/SignalServiceKit/src/Loki/Shelved/LokiP2PAPI.swift @@ -213,7 +213,7 @@ public class LokiP2PAPI : NSObject { var friendThreadIDs: [String] = [] storage.dbReadConnection.read { transaction in TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in - guard let thread = object as? TSContactThread, let uniqueID = thread.uniqueId, thread.contactIdentifier() == ourHexEncodedPubKey else { return } + guard let thread = object as? TSContactThread, let uniqueID = thread.uniqueId, thread.contactIdentifier() != ourHexEncodedPubKey else { return } let status = storage.getFriendRequestStatus(for: thread.contactIdentifier(), transaction: transaction) guard status == .friends else { return } friendThreadIDs.append(uniqueID) diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 81a7f8dde..c23e67cea 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -221,12 +221,12 @@ void AssertIsOnSendingQueue() } [self.messageSender sendMessageToService:self.message - success:^{ - [self reportSuccess]; - } - failure:^(NSError *error) { - [self reportError:error]; - }]; + success:^{ + [self reportSuccess]; + } + failure:^(NSError *error) { + [self reportError:error]; + }]; } - (void)didSucceed @@ -382,16 +382,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // unorthodox. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [allAttachmentIds - addObjectsFromArray:[OutgoingMessagePreparer prepareMessageForSending:message transaction:transaction]]; + addObjectsFromArray:[OutgoingMessagePreparer prepareMessageForSending:message transaction:transaction]]; }]; NSOperationQueue *sendingQueue = [self sendingQueueForMessage:message]; OWSSendMessageOperation *sendMessageOperation = - [[OWSSendMessageOperation alloc] initWithMessage:message - messageSender:self - dbConnection:self.dbConnection - success:successHandler - failure:failureHandler]; + [[OWSSendMessageOperation alloc] initWithMessage:message + messageSender:self + dbConnection:self.dbConnection + success:successHandler + failure:failureHandler]; for (NSString *attachmentId in allAttachmentIds) { OWSUploadOperation *uploadAttachmentOperation = [[OWSUploadOperation alloc] initWithAttachmentId:attachmentId threadID:message.thread.uniqueId dbConnection:self.dbConnection]; @@ -466,12 +466,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [OutgoingMessagePreparer prepareAttachments:attachmentInfos inMessage:message completionHandler:^(NSError *_Nullable error) { - if (error) { - failure(error); - return; - } - [self sendMessage:message success:success failure:failure]; - }]; + if (error) { + failure(error); + return; + } + [self sendMessage:message success:success failure:failure]; + }]; } - (void)sendMessageToService:(TSOutgoingMessage *)message @@ -479,16 +479,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; failure:(RetryableFailureHandler)failure { [self.udManager - ensureSenderCertificateWithSuccess:^(SMKSenderCertificate *senderCertificate) { - dispatch_async([OWSDispatch sendingQueue], ^{ - [self sendMessageToService:message senderCertificate:senderCertificate success:success failure:failure]; - }); - } - failure:^(NSError *error) { - dispatch_async([OWSDispatch sendingQueue], ^{ - [self sendMessageToService:message senderCertificate:nil success:success failure:failure]; - }); - }]; + ensureSenderCertificateWithSuccess:^(SMKSenderCertificate *senderCertificate) { + dispatch_async([OWSDispatch sendingQueue], ^{ + [self sendMessageToService:message senderCertificate:senderCertificate success:success failure:failure]; + }); + } + failure:^(NSError *error) { + dispatch_async([OWSDispatch sendingQueue], ^{ + [self sendMessageToService:message senderCertificate:nil success:success failure:failure]; + }); + }]; } - (nullable NSArray *)unsentRecipientsForMessage:(TSOutgoingMessage *)message @@ -547,7 +547,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { for (NSString *recipientId in recipientIds) { SignalRecipient *recipient = - [SignalRecipient getOrBuildUnsavedRecipientForRecipientId:recipientId transaction:transaction]; + [SignalRecipient getOrBuildUnsavedRecipientForRecipientId:recipientId transaction:transaction]; [recipients addObject:recipient]; } }]; @@ -576,21 +576,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } OWSMessageSend *messageSend = [[OWSMessageSend alloc] initWithMessage:message - thread:thread - recipient:recipient - senderCertificate:senderCertificate - udAccess:theirUDAccess - localNumber:self.tsAccountManager.localNumber - success:^{ - // The value doesn't matter, we just need any non-NSError value. - resolve(@(1)); - } - failure:^(NSError *error) { - @synchronized(sendErrors) { - [sendErrors addObject:error]; + thread:thread + recipient:recipient + senderCertificate:senderCertificate + udAccess:theirUDAccess + localNumber:self.tsAccountManager.localNumber + success:^{ + // The value doesn't matter, we just need any non-NSError value. + resolve(@(1)); } - resolve(error); - }]; + failure:^(NSError *error) { + @synchronized(sendErrors) { + [sendErrors addObject:error]; + } + resolve(error); + }]; if ([LKMultiDeviceProtocol isMultiDeviceRequiredForMessage:message]) { // Avoid the write transaction if possible dispatch_async(dispatch_get_main_queue(), ^{ @@ -623,34 +623,34 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; void (^successHandler)(void) = ^() { dispatch_async([OWSDispatch sendingQueue], ^{ [self handleMessageSentLocally:message - success:^{ - successHandlerParam(); - } - failure:^(NSError *error) { - OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu.", - message.class, - message.timestamp); + success:^{ + successHandlerParam(); + } + failure:^(NSError *error) { + OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu.", + message.class, + message.timestamp); - failureHandlerParam(error); - }]; + failureHandlerParam(error); + }]; }); }; void (^failureHandler)(NSError *) = ^(NSError *error) { if (message.wasSentToAnyRecipient) { dispatch_async([OWSDispatch sendingQueue], ^{ [self handleMessageSentLocally:message - success:^{ - failureHandlerParam(error); - } - failure:^(NSError *syncError) { - OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu, %@.", - message.class, - message.timestamp, - syncError); + success:^{ + failureHandlerParam(error); + } + failure:^(NSError *syncError) { + OWSLogError(@"Error sending sync message for message: %@ timestamp: %llu, %@.", + message.class, + message.timestamp, + syncError); - // Discard the sync message error in favor of the original error - failureHandlerParam(error); - }]; + // Discard the sync message error in favor of the original error + failureHandlerParam(error); + }]; }); return; } @@ -665,7 +665,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // This thread has been deleted since the message was enqueued. NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageSendNoValidRecipients, - NSLocalizedString(@"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS", @"Error indicating that an outgoing message had no valid recipients.")); + NSLocalizedString(@"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS", @"Error indicating that an outgoing message had no valid recipients.")); [error setIsRetryable:NO]; return failureHandler(error); } @@ -686,7 +686,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSArray *_Nullable recipientIds = [self unsentRecipientsForMessage:message thread:thread error:&error]; if (error || !recipientIds) { error = SSKEnsureError( - error, OWSErrorCodeMessageSendNoValidRecipients, @"Could not build recipients list for message."); + error, OWSErrorCodeMessageSendNoValidRecipients, @"Could not build recipients list for message."); [error setIsRetryable:NO]; return failureHandler(error); } @@ -723,9 +723,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; thread:thread senderCertificate:senderCertificate sendErrors:sendErrors] - .then(^(id value) { - successHandler(); - }); + .then(^(id value) { + successHandler(); + }); sendPromise.catch(^(id failure) { NSError *firstRetryableError = nil; @@ -773,7 +773,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // not be sent to any recipient. if (message.sentRecipientsCount == 0) { NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageSendNoValidRecipients, - NSLocalizedString(@"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS", @"Error indicating that an outgoing message had no valid recipients.")); + NSLocalizedString(@"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS", @"Error indicating that an outgoing message had no valid recipients.")); [error setIsRetryable:NO]; failureHandler(error); } else { @@ -802,7 +802,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [SignalRecipient markRecipientAsUnregistered:recipient.recipientId transaction:transaction]; [[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:recipient.recipientId] - saveWithTransaction:transaction]; + saveWithTransaction:transaction]; // TODO: Should we deleteAllSessionsForContact here? // If so, we'll need to avoid doing a prekey fetch every @@ -829,7 +829,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // we silently discard these message if there is no pre-existing session // for the recipient. NSError *error = OWSErrorWithCodeDescription( - OWSErrorCodeNoSessionForTransientMessage, @"No session for transient message."); + OWSErrorCodeNoSessionForTransientMessage, @"No session for transient message."); [error setIsRetryable:NO]; [error setIsFatal:YES]; *errorHandle = error; @@ -842,12 +842,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSProdInfo([OWSAnalyticsEvents messageSendErrorFailedDueToUntrustedKey]); NSString *localizedErrorDescriptionFormat - = NSLocalizedString(@"FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_KEY", - @"action sheet header when re-sending message which failed because of untrusted identity keys"); + = NSLocalizedString(@"FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_KEY", + @"action sheet header when re-sending message which failed because of untrusted identity keys"); NSString *localizedErrorDescription = - [NSString stringWithFormat:localizedErrorDescriptionFormat, - [self.contactsManager displayNameForPhoneIdentifier:recipient.recipientId]]; + [NSString stringWithFormat:localizedErrorDescriptionFormat, + [self.contactsManager displayNameForPhoneIdentifier:recipient.recipientId]]; NSError *error = OWSErrorMakeUntrustedIdentityError(localizedErrorDescription, recipient.recipientId); // Key will continue to be unaccepted, so no need to retry. It'll only cause us to hit the Pre-Key request @@ -889,8 +889,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if ([exception.name isEqualToString:OWSMessageSenderRateLimitedException]) { NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceRateLimited, - NSLocalizedString(@"FAILED_SENDING_BECAUSE_RATE_LIMIT", - @"action sheet header when re-sending message which failed because of too many attempts")); + NSLocalizedString(@"FAILED_SENDING_BECAUSE_RATE_LIMIT", + @"action sheet header when re-sending message which failed because of too many attempts")); // We're already rate-limited. No need to exacerbate the problem. [error setIsRetryable:NO]; // Avoid exacerbating the rate limiting. @@ -916,11 +916,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; TSOutgoingMessage *message = messageSend.message; SignalRecipient *recipient = messageSend.recipient; - + OWSLogInfo(@"Attempting to send message: %@, timestamp: %llu, recipient: %@.", - message.class, - message.timestamp, - recipient.uniqueId); + message.class, + message.timestamp, + recipient.uniqueId); AssertIsOnSendingQueue(); if ([TSPreKeyManager isAppLockedDueToPreKeyUpdateFailures]) { @@ -932,16 +932,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // Only try to update the signed prekey; updating it is sufficient to // re-enable message sending. [TSPreKeyManager - rotateSignedPreKeyWithSuccess:^{ - OWSLogInfo(@"New pre keys registered with server."); - NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError(); - [error setIsRetryable:YES]; - return messageSend.failure(error); - } - failure:^(NSError *error) { - OWSLogWarn(@"Failed to update pre keys with the server due to error: %@.", error); - return messageSend.failure(error); - }]; + rotateSignedPreKeyWithSuccess:^{ + OWSLogInfo(@"New pre keys registered with server."); + NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError(); + [error setIsRetryable:YES]; + return messageSend.failure(error); + } + failure:^(NSError *error) { + OWSLogWarn(@"Failed to update pre keys with the server due to error: %@.", error); + return messageSend.failure(error); + }]; } if (messageSend.remainingAttempts <= 0) { @@ -977,79 +977,79 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } /* - if (messageSend.isLocalNumber) { - OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]); - // Messages sent to the "local number" should be sync messages. - // - // We can skip sending sync messages if we know that we have no linked - // devices. However, we need to be sure to handle the case where the - // linked device list has just changed. - // - // The linked device list is reflected in two separate pieces of state: - // - // * OWSDevice's state is updated when you link or unlink a device. - // * SignalRecipient's state is updated by 409 "Mismatched devices" - // responses from the service. - // - // If _both_ of these pieces of state agree that there are no linked - // devices, then can safely skip sending sync message. - // - // NOTE: Sync messages sent via UD include the local device. - - BOOL mayHaveLinkedDevices = [OWSDeviceManager.sharedManager mayHaveLinkedDevices:self.dbConnection]; - - BOOL hasDeviceMessages = NO; - for (NSDictionary *deviceMessage in deviceMessages) { - NSString *_Nullable destination = deviceMessage[@"destination"]; - if (!destination) { - OWSFailDebug(@"Sync device message missing destination: %@", deviceMessage); - continue; - } - if (![destination isEqualToString:messageSend.localNumber]) { - OWSFailDebug(@"Sync device message has invalid destination: %@", deviceMessage); - continue; - } - NSNumber *_Nullable destinationDeviceId = deviceMessage[@"destinationDeviceId"]; - if (!destinationDeviceId) { - OWSFailDebug(@"Sync device message missing destination device id: %@", deviceMessage); - continue; - } - if (destinationDeviceId.intValue != OWSDevicePrimaryDeviceId) { - hasDeviceMessages = YES; - break; - } - } - - OWSLogInfo(@"mayHaveLinkedDevices: %d, hasDeviceMessages: %d", mayHaveLinkedDevices, hasDeviceMessages); - - if (!mayHaveLinkedDevices && !hasDeviceMessages) { - OWSLogInfo(@"Ignoring sync message without secondary devices: %@", [message class]); - OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]); - - dispatch_async([OWSDispatch sendingQueue], ^{ - // This emulates the completion logic of an actual successful send (see below). - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [message updateWithSkippedRecipient:messageSend.localNumber transaction:transaction]; - }]; - messageSend.success(); - }); - - return; - } else if (mayHaveLinkedDevices && !hasDeviceMessages) { - // We may have just linked a new secondary device which is not yet reflected in - // the SignalRecipient that corresponds to ourself. Proceed. Client should learn - // of new secondary devices via 409 "Mismatched devices" response. - OWSLogWarn(@"account has secondary devices, but sync message has no device messages"); - } else if (!mayHaveLinkedDevices && hasDeviceMessages) { - OWSFailDebug(@"sync message has device messages for unknown secondary devices."); - } - } else { - // This can happen for users who have unregistered. - // We still want to try sending to them in case they have re-registered. - if (deviceMessages.count < 1) { - OWSLogWarn(@"Message send attempt with no device messages."); - } - } + if (messageSend.isLocalNumber) { + OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]); + // Messages sent to the "local number" should be sync messages. + // + // We can skip sending sync messages if we know that we have no linked + // devices. However, we need to be sure to handle the case where the + // linked device list has just changed. + // + // The linked device list is reflected in two separate pieces of state: + // + // * OWSDevice's state is updated when you link or unlink a device. + // * SignalRecipient's state is updated by 409 "Mismatched devices" + // responses from the service. + // + // If _both_ of these pieces of state agree that there are no linked + // devices, then can safely skip sending sync message. + // + // NOTE: Sync messages sent via UD include the local device. + + BOOL mayHaveLinkedDevices = [OWSDeviceManager.sharedManager mayHaveLinkedDevices:self.dbConnection]; + + BOOL hasDeviceMessages = NO; + for (NSDictionary *deviceMessage in deviceMessages) { + NSString *_Nullable destination = deviceMessage[@"destination"]; + if (!destination) { + OWSFailDebug(@"Sync device message missing destination: %@", deviceMessage); + continue; + } + if (![destination isEqualToString:messageSend.localNumber]) { + OWSFailDebug(@"Sync device message has invalid destination: %@", deviceMessage); + continue; + } + NSNumber *_Nullable destinationDeviceId = deviceMessage[@"destinationDeviceId"]; + if (!destinationDeviceId) { + OWSFailDebug(@"Sync device message missing destination device id: %@", deviceMessage); + continue; + } + if (destinationDeviceId.intValue != OWSDevicePrimaryDeviceId) { + hasDeviceMessages = YES; + break; + } + } + + OWSLogInfo(@"mayHaveLinkedDevices: %d, hasDeviceMessages: %d", mayHaveLinkedDevices, hasDeviceMessages); + + if (!mayHaveLinkedDevices && !hasDeviceMessages) { + OWSLogInfo(@"Ignoring sync message without secondary devices: %@", [message class]); + OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]); + + dispatch_async([OWSDispatch sendingQueue], ^{ + // This emulates the completion logic of an actual successful send (see below). + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [message updateWithSkippedRecipient:messageSend.localNumber transaction:transaction]; + }]; + messageSend.success(); + }); + + return; + } else if (mayHaveLinkedDevices && !hasDeviceMessages) { + // We may have just linked a new secondary device which is not yet reflected in + // the SignalRecipient that corresponds to ourself. Proceed. Client should learn + // of new secondary devices via 409 "Mismatched devices" response. + OWSLogWarn(@"account has secondary devices, but sync message has no device messages"); + } else if (!mayHaveLinkedDevices && hasDeviceMessages) { + OWSFailDebug(@"sync message has device messages for unknown secondary devices."); + } + } else { + // This can happen for users who have unregistered. + // We still want to try sending to them in case they have re-registered. + if (deviceMessages.count < 1) { + OWSLogWarn(@"Message send attempt with no device messages."); + } + } */ for (NSDictionary *deviceMessage in deviceMessages) { @@ -1127,7 +1127,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } NSString *body = (message.body != nil && message.body.length > 0) ? message.body : [NSString stringWithFormat:@"%@", @(message.timestamp)]; // Workaround for the fact that the back-end doesn't accept messages without a body LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:body type:LKPublicChatAPI.publicChatMessageType - timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0]; + timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0]; OWSLinkPreview *linkPreview = message.linkPreview; if (linkPreview != nil) { TSAttachmentStream *attachment = [TSAttachmentStream fetchObjectWithUniqueID:linkPreview.imageAttachmentId]; @@ -1144,14 +1144,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } message.actualSenderHexEncodedPublicKey = userHexEncodedPublicKey; [[LKPublicChatAPI sendMessage:groupMessage toGroup:publicChat.channel onServer:publicChat.server] - .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { + .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [message saveOpenGroupServerMessageID:groupMessage.serverID in:transaction]; [self.primaryStorage setIDForMessageWithServerID:groupMessage.serverID to:message.uniqueId in:transaction]; }]; [self messageSendDidSucceed:messageSend deviceMessages:deviceMessages wasSentByUD:messageSend.isUDSend wasSentByWebsocket:false]; }) - .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { + .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { failedMessageSend(error); }) retainUntilComplete]; } else { @@ -1216,11 +1216,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; __block NSUInteger errorCount = 0; for (AnyPromise *promise in promises) { [promise - .thenOn(OWSDispatch.sendingQueue, ^(id result) { + .thenOn(OWSDispatch.sendingQueue, ^(id result) { if (isSuccess) { return; } // Succeed as soon as the first promise succeeds [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.messageSent object:[[NSNumber alloc] initWithUnsignedLongLong:signalMessage.timestamp]]; isSuccess = YES; - if (signalMessage.isFriendRequest) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { if (!message.skipSave) { @@ -1239,7 +1238,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // Invoke the completion handler [self messageSendDidSucceed:messageSend deviceMessages:deviceMessages wasSentByUD:messageSend.isUDSend wasSentByWebsocket:false]; }) - .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { + .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { errorCount += 1; if (errorCount != promiseCount) { return; } // Only error out if all promises failed [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.messageFailed object:[[NSNumber alloc] initWithUnsignedLongLong:signalMessage.timestamp]]; @@ -1247,7 +1246,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; }) retainUntilComplete]; } }) - .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { + .catchOn(OWSDispatch.sendingQueue, ^(NSError *error) { handleError(error); }) retainUntilComplete]; } @@ -1308,9 +1307,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; SignalRecipient *recipient = messageSend.recipient; OWSLogInfo(@"failed to send message: %@, timestamp: %llu, to recipient: %@", - message.class, - message.timestamp, - recipient.uniqueId); + message.class, + message.timestamp, + recipient.uniqueId); void (^retrySend)(void) = ^void() { if (messageSend.remainingAttempts <= 0) { @@ -1359,8 +1358,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSLogWarn(@"Unable to send due to invalid credentials. Did the user's client get de-authed by " @"registering elsewhere?"); NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceFailure, - NSLocalizedString( - @"ERROR_DESCRIPTION_SENDING_UNAUTHORIZED", @"Error message when attempting to send message")); + NSLocalizedString( + @"ERROR_DESCRIPTION_SENDING_UNAUTHORIZED", @"Error message when attempting to send message")); // No need to retry if we've been de-authed. [error setIsRetryable:NO]; return messageSend.failure(error); @@ -1450,28 +1449,28 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } [self.dbConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - if (extraDevices.count < 1 && missingDevices.count < 1) { - OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]); - } + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + if (extraDevices.count < 1 && missingDevices.count < 1) { + OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]); + } - [recipient updateRegisteredRecipientWithDevicesToAdd:missingDevices - devicesToRemove:extraDevices - transaction:transaction]; + [recipient updateRegisteredRecipientWithDevicesToAdd:missingDevices + devicesToRemove:extraDevices + transaction:transaction]; - if (extraDevices && extraDevices.count > 0) { - OWSLogInfo(@"Deleting sessions for extra devices: %@", extraDevices); - for (NSNumber *extraDeviceId in extraDevices) { - [self.primaryStorage deleteSessionForContact:recipient.uniqueId - deviceId:extraDeviceId.intValue - protocolContext:transaction]; + if (extraDevices && extraDevices.count > 0) { + OWSLogInfo(@"Deleting sessions for extra devices: %@", extraDevices); + for (NSNumber *extraDeviceId in extraDevices) { + [self.primaryStorage deleteSessionForContact:recipient.uniqueId + deviceId:extraDeviceId.intValue + protocolContext:transaction]; + } } - } - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - completionHandler(); - }); - }]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completionHandler(); + }); + }]; } - (void)handleMessageSentLocally:(TSOutgoingMessage *)message @@ -1510,16 +1509,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; BOOL isRecipientUpdate = message.hasSyncedTranscript; [self - sendSyncTranscriptForMessage:message - isRecipientUpdate:isRecipientUpdate - success:^{ - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [message updateWithHasSyncedTranscript:YES transaction:transaction]; - }]; - - success(); - } - failure:failure]; + sendSyncTranscriptForMessage:message + isRecipientUpdate:isRecipientUpdate + success:^{ + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [message updateWithHasSyncedTranscript:YES transaction:transaction]; + }]; + + success(); + } + failure:failure]; } - (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message @@ -1528,7 +1527,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; failure:(RetryableFailureHandler)failure { OWSOutgoingSentMessageTranscript *sentMessageTranscript = - [[OWSOutgoingSentMessageTranscript alloc] initWithOutgoingMessage:message isRecipientUpdate:isRecipientUpdate]; + [[OWSOutgoingSentMessageTranscript alloc] initWithOutgoingMessage:message isRecipientUpdate:isRecipientUpdate]; NSString *recipientId = self.tsAccountManager.localNumber; __block SignalRecipient *recipient; @@ -1543,21 +1542,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } OWSMessageSend *messageSend = [[OWSMessageSend alloc] initWithMessage:sentMessageTranscript - thread:message.thread - recipient:recipient - senderCertificate:senderCertificate - udAccess:recipientUDAccess - localNumber:self.tsAccountManager.localNumber - success:^{ - OWSLogInfo(@"Successfully sent sync transcript."); - - success(); - } - failure:^(NSError *error) { - OWSLogInfo(@"Failed to send sync transcript: %@ (isRetryable: %d)", error, [error isRetryable]); + thread:message.thread + recipient:recipient + senderCertificate:senderCertificate + udAccess:recipientUDAccess + localNumber:self.tsAccountManager.localNumber + success:^{ + OWSLogInfo(@"Successfully sent sync transcript."); + + success(); + } + failure:^(NSError *error) { + OWSLogInfo(@"Failed to send sync transcript: %@ (isRetryable: %d)", error, [error isRetryable]); - failure(error); - }]; + failure(error); + }]; if ([LKMultiDeviceProtocol isMultiDeviceRequiredForMessage:message]) { // Avoid the write transaction if possible dispatch_async(dispatch_get_main_queue(), ^{ [self.primaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { @@ -1585,10 +1584,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSLogDebug(@"Built message: %@ plainTextData.length: %lu", [messageSend.message class], (unsigned long)plainText.length); OWSLogVerbose(@"Building device messages for: %@ %@ (isLocalNumber: %d, isUDSend: %d).", - recipient.recipientId, - recipient.devices, - messageSend.isLocalNumber, - messageSend.isUDSend); + recipient.recipientId, + recipient.devices, + messageSend.isLocalNumber, + messageSend.isUDSend); // Loki: Multi device is handled elsewhere so just send to the provided recipient ID here NSArray *recipientIDs = @[ recipient.recipientId ]; @@ -1605,16 +1604,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; __block NSDictionary *_Nullable messageDict; __block NSException *encryptionException; [self.dbConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - @try { - messageDict = [self throws_encryptedMessageForMessageSend:messageSend - recipientID:recipientID - plainText:plainText - transaction:transaction]; - } @catch (NSException *exception) { - encryptionException = exception; - } - }]; + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + @try { + messageDict = [self throws_encryptedMessageForMessageSend:messageSend + recipientID:recipientID + plainText:plainText + transaction:transaction]; + } @catch (NSException *exception) { + encryptionException = exception; + } + }]; if (encryptionException) { OWSLogInfo(@"Exception during encryption: %@.", encryptionException); @@ -1669,35 +1668,35 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; /** Loki: Original code * ================ - __block dispatch_semaphore_t sema = dispatch_semaphore_create(0); - __block PreKeyBundle *_Nullable bundle; - __block NSException *_Nullable exception; - [self makePrekeyRequestForMessageSend:messageSend - deviceId:deviceId - success:^(PreKeyBundle *_Nullable responseBundle) { - bundle = responseBundle; - dispatch_semaphore_signal(sema); - } - failure:^(NSUInteger statusCode) { - if (statusCode == 404) { - // Can't throw exception from within callback as it's probabably a different thread. - exception = [NSException exceptionWithName:OWSMessageSenderInvalidDeviceException - reason:@"Device not registered" - userInfo:nil]; - } else if (statusCode == 413) { - // Can't throw exception from within callback as it's probabably a different thread. - exception = [NSException exceptionWithName:OWSMessageSenderRateLimitedException - reason:@"Too many prekey requests" - userInfo:nil]; - } - dispatch_semaphore_signal(sema); - }]; - dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - if (exception) { - @throw exception; - } - * ================ - */ + __block dispatch_semaphore_t sema = dispatch_semaphore_create(0); + __block PreKeyBundle *_Nullable bundle; + __block NSException *_Nullable exception; + [self makePrekeyRequestForMessageSend:messageSend + deviceId:deviceId + success:^(PreKeyBundle *_Nullable responseBundle) { + bundle = responseBundle; + dispatch_semaphore_signal(sema); + } + failure:^(NSUInteger statusCode) { + if (statusCode == 404) { + // Can't throw exception from within callback as it's probabably a different thread. + exception = [NSException exceptionWithName:OWSMessageSenderInvalidDeviceException + reason:@"Device not registered" + userInfo:nil]; + } else if (statusCode == 413) { + // Can't throw exception from within callback as it's probabably a different thread. + exception = [NSException exceptionWithName:OWSMessageSenderRateLimitedException + reason:@"Too many prekey requests" + userInfo:nil]; + } + dispatch_semaphore_signal(sema); + }]; + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + if (exception) { + @throw exception; + } + * ================ + */ if (!bundle) { NSString *missingPrekeyBundleException = @"missingPrekeyBundleException"; @@ -1722,8 +1721,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (exception) { if ([exception.name isEqualToString:UntrustedIdentityKeyException]) { OWSRaiseExceptionWithUserInfo(UntrustedIdentityKeyException, - (@{ TSInvalidPreKeyBundleKey : bundle, TSInvalidRecipientKey : recipientID }), - @""); + (@{ TSInvalidPreKeyBundleKey : bundle, TSInvalidRecipientKey : recipientID }), + @""); } @throw exception; } @@ -1742,50 +1741,50 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSAssertDebug(recipientId.length > 0); OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc] initWithLabel:@"Prekey Fetch" - requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) { - return [OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId - deviceId:[deviceId stringValue] - udAccessKey:udAccessKey]; - } - udAuthFailureBlock:^{ - // Note the UD auth failure so subsequent retries - // to this recipient also use basic auth. - [messageSend setHasUDAuthFailed]; - } - websocketFailureBlock:^{ - // Note the websocket failure so subsequent retries - // to this recipient also use REST. - messageSend.hasWebsocketSendFailed = YES; - } - recipientId:recipientId - udAccess:messageSend.udAccess - canFailoverUDAuth:YES]; - [[requestMaker makeRequestObjc] - .then(^(OWSRequestMakerResult *result) { - // We _do not_ want to dispatch to the sendingQueue here; we're - // using a semaphore on the sendingQueue to block on this request. - const id responseObject = result.responseObject; - PreKeyBundle *_Nullable bundle = - [PreKeyBundle preKeyBundleFromDictionary:responseObject forDeviceNumber:deviceId]; - success(bundle); - }) - .catch(^(NSError *error) { - // We _do not_ want to dispatch to the sendingQueue here; we're - // using a semaphore on the sendingQueue to block on this request. - NSUInteger statusCode = 0; - if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) { - statusCode = error.code; - } else { - OWSFailDebug(@"Unexpected error: %@", error); + requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) { + return [OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId + deviceId:[deviceId stringValue] + udAccessKey:udAccessKey]; } + udAuthFailureBlock:^{ + // Note the UD auth failure so subsequent retries + // to this recipient also use basic auth. + [messageSend setHasUDAuthFailed]; + } + websocketFailureBlock:^{ + // Note the websocket failure so subsequent retries + // to this recipient also use REST. + messageSend.hasWebsocketSendFailed = YES; + } + recipientId:recipientId + udAccess:messageSend.udAccess + canFailoverUDAuth:YES]; + [[requestMaker makeRequestObjc] + .then(^(OWSRequestMakerResult *result) { + // We _do not_ want to dispatch to the sendingQueue here; we're + // using a semaphore on the sendingQueue to block on this request. + const id responseObject = result.responseObject; + PreKeyBundle *_Nullable bundle = + [PreKeyBundle preKeyBundleFromDictionary:responseObject forDeviceNumber:deviceId]; + success(bundle); + }) + .catch(^(NSError *error) { + // We _do not_ want to dispatch to the sendingQueue here; we're + // using a semaphore on the sendingQueue to block on this request. + NSUInteger statusCode = 0; + if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) { + statusCode = error.code; + } else { + OWSFailDebug(@"Unexpected error: %@", error); + } - failure(statusCode); - }) retainUntilComplete]; + failure(statusCode); + }) retainUntilComplete]; } - (nullable NSDictionary *)throws_encryptedFriendRequestOrDeviceLinkMessageForMessageSend:(OWSMessageSend *)messageSend - deviceId:(NSNumber *)deviceId - plainText:(NSData *)plainText + deviceId:(NSNumber *)deviceId + plainText:(NSData *)plainText { OWSAssertDebug(messageSend); OWSAssertDebug(deviceId); @@ -1845,14 +1844,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if ([LKSessionManagementProtocol isSessionRequiredForMessage:messageSend.message] && ![storage containsSession:recipientID deviceId:@(OWSDevicePrimaryDeviceId).intValue protocolContext:transaction]) { NSString *missingSessionException = @"missingSessionException"; OWSRaiseException(missingSessionException, - @"Unexpectedly missing session for recipient: %@, device: %@.", - recipientID, - @(OWSDevicePrimaryDeviceId)); + @"Unexpectedly missing session for recipient: %@, device: %@.", + recipientID, + @(OWSDevicePrimaryDeviceId)); } BOOL isFriendRequest = [messageSend.message isKindOfClass:LKFriendRequestMessage.class]; BOOL isDeviceLinkMessage = [messageSend.message isKindOfClass:LKDeviceLinkMessage.class] - && ((LKDeviceLinkMessage *)messageSend.message).kind == LKDeviceLinkMessageKindRequest; + && ((LKDeviceLinkMessage *)messageSend.message).kind == LKDeviceLinkMessageKindRequest; SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storage preKeyStore:storage @@ -1866,11 +1865,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (messageSend.isUDSend) { NSError *error; SMKSecretSessionCipher *_Nullable secretCipher = - [[SMKSecretSessionCipher alloc] initWithSessionStore:self.primaryStorage - preKeyStore:self.primaryStorage - signedPreKeyStore:self.primaryStorage - identityStore:self.identityManager - error:&error]; + [[SMKSecretSessionCipher alloc] initWithSessionStore:self.primaryStorage + preKeyStore:self.primaryStorage + signedPreKeyStore:self.primaryStorage + identityStore:self.identityManager + error:&error]; if (error || !secretCipher) { OWSRaiseException(@"SecretSessionCipherFailure", @"Can't create secret session cipher."); } @@ -1892,7 +1891,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } else { // This may throw an exception id encryptedMessage = - [cipher throws_encryptMessage:[plainText paddedMessageBody] protocolContext:transaction]; + [cipher throws_encryptMessage:[plainText paddedMessageBody] protocolContext:transaction]; serializedMessage = encryptedMessage.serialized; messageType = [self messageTypeForCipherMessage:encryptedMessage]; } @@ -1903,16 +1902,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; LKAddressMessage *addressMessage = [message as:[LKAddressMessage class]]; BOOL isPing = addressMessage != nil && addressMessage.isPing; OWSMessageServiceParams *messageParams = - [[OWSMessageServiceParams alloc] initWithType:messageType - recipientId:recipientID - device:@(OWSDevicePrimaryDeviceId).intValue - content:serializedMessage - isSilent:isSilent - isOnline:isOnline - registrationId:[cipher throws_remoteRegistrationId:transaction] - ttl:message.ttl - isPing:isPing - isFriendRequest:isFriendRequest || isDeviceLinkMessage]; + [[OWSMessageServiceParams alloc] initWithType:messageType + recipientId:recipientID + device:@(OWSDevicePrimaryDeviceId).intValue + content:serializedMessage + isSilent:isSilent + isOnline:isOnline + registrationId:[cipher throws_remoteRegistrationId:transaction] + ttl:message.ttl + isPing:isPing + isFriendRequest:isFriendRequest || isDeviceLinkMessage]; NSError *error; NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error]; @@ -2014,7 +2013,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // suggests this could change. The logic is intended to work with multiple, but // if we ever actually want to send multiple, we should do more testing. NSArray *quotedThumbnailAttachments = - [message.quotedMessage createThumbnailAttachmentsIfNecessaryWithTransaction:transaction]; + [message.quotedMessage createThumbnailAttachmentsIfNecessaryWithTransaction:transaction]; for (TSAttachmentStream *attachment in quotedThumbnailAttachments) { [attachmentIds addObject:attachment.uniqueId]; } @@ -2031,7 +2030,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (message.linkPreview.imageAttachmentId != nil) { TSAttachment *attachment = - [TSAttachment fetchObjectWithUniqueID:message.linkPreview.imageAttachmentId transaction:transaction]; + [TSAttachment fetchObjectWithUniqueID:message.linkPreview.imageAttachmentId transaction:transaction]; if ([attachment isKindOfClass:[TSAttachmentStream class]]) { [attachmentIds addObject:attachment.uniqueId]; } else { @@ -2058,11 +2057,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSMutableArray *attachmentStreams = [NSMutableArray new]; for (OWSOutgoingAttachmentInfo *attachmentInfo in attachmentInfos) { TSAttachmentStream *attachmentStream = - [[TSAttachmentStream alloc] initWithContentType:attachmentInfo.contentType - byteCount:(UInt32)attachmentInfo.dataSource.dataLength - sourceFilename:attachmentInfo.sourceFilename - caption:attachmentInfo.caption - albumMessageId:attachmentInfo.albumMessageId]; + [[TSAttachmentStream alloc] initWithContentType:attachmentInfo.contentType + byteCount:(UInt32)attachmentInfo.dataSource.dataLength + sourceFilename:attachmentInfo.sourceFilename + caption:attachmentInfo.caption + albumMessageId:attachmentInfo.albumMessageId]; if (outgoingMessage.isVoiceMessage) { attachmentStream.attachmentType = TSAttachmentTypeVoiceMessage;