Review fixes

This commit is contained in:
Mikunj 2020-05-04 10:17:28 +10:00
parent e091923850
commit 3ed3e00c4c
3 changed files with 325 additions and 329 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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<NSString *> *)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<NSString *> *_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.
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 mayHaveLinkedDevices = [OWSDeviceManager.sharedManager mayHaveLinkedDevices:self.dbConnection];
BOOL hasDeviceMessages = NO;
for (NSDictionary<NSString *, id> *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;
}
}
BOOL hasDeviceMessages = NO;
for (NSDictionary<NSString *, id> *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);
OWSLogInfo(@"mayHaveLinkedDevices: %d, hasDeviceMessages: %d", mayHaveLinkedDevices, hasDeviceMessages);
if (!mayHaveLinkedDevices && !hasDeviceMessages) {
OWSLogInfo(@"Ignoring sync message without secondary devices: %@", [message class]);
OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]);
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();
});
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.");
}
}
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]);
}
[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];
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if (extraDevices.count < 1 && missingDevices.count < 1) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]);
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
completionHandler();
});
}];
[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];
}
}
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];
}];
sendSyncTranscriptForMessage:message
isRecipientUpdate:isRecipientUpdate
success:^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithHasSyncedTranscript:YES transaction:transaction];
}];
success();
}
failure:failure];
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.");
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]);
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<NSString *> *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<CipherMessage> 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<TSAttachmentStream *> *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<TSAttachmentStream *> *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;